Fix ampersamp in syntaxhighlighter

This can be done directly from the webinterface. Just go to Plugins -> Plugin Editor -> select the Plugin SyntaxHighlighter Evolved -> add the snippet to the end

/**
 * Filter to fix issue with & in SyntaxHighlighter Evolved plugin.
 *
 * @param string $code Code to format.
 * @param array $atts Attributes.
 * @param string $tag Tag.
 *
 * @return string
 */
function kagg_syntaxhighlighter_precode( $code, $atts, $tag ) {
    if ( 'code' === $tag ) {
        $code = wp_specialchars_decode( $code );
    }
    return $code;
}
add_filter( 'syntaxhighlighter_precode', 'kagg_syntaxhighlighter_precode', 10, 3 );

Sumber: https://nocin.eu/wordpress-syntaxhighlighter-ampersand-character/

Antarmuka Port Serial dengan C++

Gunakan Class Serial berikut:

//File: Serial.h
#ifndef SERIALCLASS_H_INCLUDED
#define SERIALCLASS_H_INCLUDED

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

class Serial
{
    private:
        //Serial comm handler
        HANDLE hSerial;
        //Connection status
        bool connected;
        //Get various information about the connection
        COMSTAT status;
        //Keep track of last error
        DWORD errors;

    public:
        //Initialize Serial communication with the given COM port
        Serial(char *portName);
        //Close the connection
        ~Serial();
        //Read data in a buffer, if nbChar is greater than the
        //maximum number of bytes available, it will return only the
        //bytes available. The function return -1 when nothing could
        //be read, the number of bytes actually read.
        int ReadData(char *buffer, unsigned int nbChar);
        //Writes data from a buffer through the Serial connection
        //return true on success.
        bool WriteData(char *buffer, unsigned int nbChar);
        //Check if we are actually connected
        bool IsConnected();


};

#endif // SERIALCLASS_H_INCLUDED

//File: Serial.cpp
#include "Serial.h"

Serial::Serial(char *portName)
{
    //We're not yet connected
    this->connected = false;

    //Try to connect to the given port throuh CreateFile
 #if _MSC_VER && !__INTEL_COMPILER
    const size_t cSize = strlen(portName) + 1;
    wchar_t* wc = new wchar_t[cSize];
    mbstowcs_s(NULL, wc, cSize, portName, cSize);
    this->hSerial = CreateFile(wc,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
#else
    //Try to connect to the given port throuh CreateFile
    this->hSerial = CreateFile(portName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
#endif

    //Check if the connection was successfull
    if(this->hSerial==INVALID_HANDLE_VALUE)
    {
        //If not success full display an Error
        if(GetLastError()==ERROR_FILE_NOT_FOUND){

            //Print Error if neccessary
            printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);

        }
        else
        {
            printf("ERROR!!!");
        }
    }
    else
    {
        //If connected we try to set the comm parameters
        DCB dcbSerialParams = {0};

        //Try to get the current
        if (!GetCommState(this->hSerial, &dcbSerialParams))
        {
            //If impossible, show an error
            printf("failed to get current serial parameters!");
        }
        else
        {
            //Define serial connection parameters for the arduino board
            dcbSerialParams.BaudRate=CBR_9600;
            dcbSerialParams.ByteSize=8;
            dcbSerialParams.StopBits=ONESTOPBIT;
            dcbSerialParams.Parity=NOPARITY;
            //Setting the DTR to Control_Enable ensures that the Arduino is properly
            //reset upon establishing a connection
            dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;

             //Set the parameters and check for their proper application
             if(!SetCommState(hSerial, &dcbSerialParams))
             {
                printf("ALERT: Could not set Serial Port parameters");
             }
             else
             {
                 //If everything went fine we're connected
                 this->connected = true;
                 //Flush any remaining characters in the buffers
                 PurgeComm(this->hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);
                 //We wait 2s as the arduino board will be reseting
                 Sleep(2000);
             }
        }
    }

}

Serial::~Serial()
{
    //Check if we are connected before trying to disconnect
    if(this->connected)
    {
        //We're no longer connected
        this->connected = false;
        //Close the serial handler
        CloseHandle(this->hSerial);
    }
}

int Serial::ReadData(char *buffer, unsigned int nbChar)
{
    //Number of bytes we'll have read
    DWORD bytesRead;
    //Number of bytes we'll really ask to read
    unsigned int toRead;

    //Use the ClearCommError function to get status info on the Serial port
    ClearCommError(this->hSerial, &this->errors, &this->status);

    //Check if there is something to read
    if(this->status.cbInQue>0)
    {
        //If there is we check if there is enough data to read the required number
        //of characters, if not we'll read only the available characters to prevent
        //locking of the application.
        if(this->status.cbInQue>nbChar)
        {
            toRead = nbChar;
        }
        else
        {
            toRead = this->status.cbInQue;
        }

        //Try to read the require number of chars, and return the number of read bytes on success
        if(ReadFile(this->hSerial, buffer, toRead, &bytesRead, NULL) && bytesRead != 0)
        {
            return bytesRead;
        }

    }

    //If nothing has been read, or that an error was detected return -1
    return -1;

}


bool Serial::WriteData(char *buffer, unsigned int nbChar)
{
    DWORD bytesSend;

    //Try to write the buffer on the Serial port
    if(!WriteFile(this->hSerial, (void *)buffer, nbChar, &bytesSend, 0))
    {
        //In case it don't work get comm error and return false
        ClearCommError(this->hSerial, &this->errors, &this->status);

        return false;
    }
    else
        return true;
}

bool Serial::IsConnected()
{
    //Simply return the connection status
    return this->connected;
}


Berikut contoh koneksi pengiriman data

#include <iostream>
#include "windows.h"
#include "Serial.h"

#include <thread>
#include <chrono>

using namespace std;

void delay(int ms){
    std::this_thread::sleep_for (std::chrono::milliseconds(ms));
}

int main(int argc, char** argv) {

    char alamatPort[] = "\\\\.\\COM7";
    Serial* s = new Serial(alamatPort);

	char dataOn[256] = "on1\n"; //"Nyala!";
	char dataOff[256] = "off0\n"; //"mati!";

	cout<<"isikan angka 1 untuk menyalakan, atau 0 untuk mematikan"<<endl;


	int jawab;
	do{
        cin>>jawab;;

        if(jawab==0)
            s->WriteData(dataOff, sizeof(dataOff));
        if(jawab==1)
            s->WriteData(dataOn, sizeof(dataOn));
        if(jawab==2)
            for(int a=0;a<5;a++){
                s->WriteData(dataOn, sizeof(dataOn));
                delay(500);
                s->WriteData(dataOff, sizeof(dataOff));
                delay(500);
            }
	}while(jawab<3);

	return 0;
}

Berikut contoh untuk mengirim dan menerima data

#include <iostream>
#include <stdio.h>
#include <tchar.h>
#include "Serial.h" // Library described above
#include <string>


/* run this program using the console pauser or add your own getch, system("pause") or input loop */
char alamatPort[] = "\\\\.\\COM7";
Serial* SP = new Serial(alamatPort);    // adjust as needed

void* baca(void* data)
{

    char incomingData[256] = "";            // don't forget to pre-allocate memory
    //printf("%s\n",incomingData);
    int dataLength = 256;
    int readResult = 0;


    while (SP->IsConnected())
    {
        readResult = SP->ReadData(incomingData, dataLength);
        if (readResult > 0) {
            printf("%s\n", incomingData);
        }
        //printf("Bytes read: (-1 means no data available) %i\n",readResult);

        //std::string test(incomingData);

        //printf("%s",incomingData);
        //test = "";

        Sleep(500);
    }
    return NULL;
}

void* tulis(void* data)
{
    while (SP->IsConnected())
    {
        char outgoingData[256] = "";
        char keluar[256] = "keluar";
        int dataLength = sizeof(outgoingData);        // don't forget to pre-allocate memory
        scanf_s("%255s", outgoingData, dataLength);

        if (outgoingData != "")
        {
            if (strcmp(outgoingData, "OK") == 0) {
                printf("Pengiriman selesai\n");
                return NULL;

            }
            if (SP->WriteData(outgoingData, dataLength))
            {
                printf("Data send:%s\n", outgoingData);
            }
        }

        Sleep(500);
    }
    // do stuff...
    return NULL;
}

#if _MSC_VER && !__INTEL_COMPILER
#include <thread>

int main(int argc, char** argv) {
    printf("Welcome to the serial test app!\n\n");
    if (SP->IsConnected())
        printf("Serial sudah terhubung\n");
    int status;
    std::thread thrd_1(baca,(void*)0);
    std::thread thrd_2(tulis, (void*)0);

    thrd_1.join();
    thrd_2.join();

}
#else
// thread example
#include <pthread.h>

int main(int argc, char** argv) {
    printf("Welcome to the serial test app!\n\n");
    if (SP->IsConnected())
        printf("Serial sudah terhubung\n");

    pthread_t thrd_1;
    pthread_t thrd_2;
    int status;

    pthread_create(&thrd_1, NULL, baca, (void*)0);
    pthread_create(&thrd_2, NULL, tulis, (void*)0);

    pthread_join(thrd_1, (void**)&status);
    pthread_join(thrd_2, (void**)&status);

    printf("Terima kasih");

    return 0;
}
#endif

String dan Pointer C++

typedef struct
{
  int x, y;
  short life;
  char *nama;
} Man;

void setup() {
  Serial.begin(9600);

  //menggunakan 4 memori untuk string
  String hi = "Hello";
  String names = "World";
  String hello = hi + " " + names;

  //menggunakan 1 memori
  String greeting = hi;
  greeting.concat(" ");
  greeting.concat(names);



  String tempname = "Temperature";
  String temp = "23C";
  PrintVal1(tempname, temp); //membuat string redundan. Lihat fungsi
  PrintVal2(tempname, temp); //pass by reference, tidak redundan. Lihat fungsi

  char thestring[30] = "This is a string";
  //thestring = "New content"; //cara yang salah, panjang string harus sama!
  strcpy(thestring, "New content"); //good method

  char isgood[8] = "is good";
  //char allbad[38] = thestring + isgood; //cara yang salah, ini adalah operasi biner. gunakan strcat
  char allgood[38];
  strcat(allgood, thestring); strcat(allgood, isgood);



  char a[2] = "A"; char b[2] = "A";
  if (a == b) {}//cara yang salah, akan selalu bernilai false
  if (strcmp(a, b) == 0 ) {} //car yang benar, atau gunakan strcasecmp()


  //pointer
  int var = 0;
  int arr[] = {1, 2, 3};
  int *ptr;           //init, don't set to NULL (destroy), *ptr = 0, first address? no!
  ptr = &var;         //to address of var, not the value of var
  *ptr = 50;;         //pointer content is 50
  //ptr = 50;         //pointer (address) is 50
  //ptr++;            //pointer address + 1

  ptr = arr;         //to array
  ptr[0] = 50;      //array number 0, is 50
  ptr = &arr[0];     //equal to above, array number 0 is 50

  Man *ptrMan;
  Man manusia = {0, 0, 0, NULL};
  manusia.life = 9;       //kucing :D
  manusia.nama = "Todol"; //pointer in struct
  ptrMan = &manusia;
  ptrMan-> life = 0;         //mati
  
  char *pString = "This is text"; //not safe to change content! don't do!
  const char *pStringSafe = "This is text"; //display warning when change content

  char temperature[] = "23C"; //constant
  //Following syntax is not compiler friendly, it will be unhappy
  PrintValP("Temperature", temperature);
  //The following will make compiler happier (look at the fuction!)
  PrintValPC("Temperature", temperature);

  PrintString(temperature);
}


//make 2 new strings, redundant string
void PrintVal1(String tag, String value) {
  Serial.print(tag);
  Serial.print(" = ");
  Serial.println(value);
}

//pass by reference
void PrintVal2(String &tag, String &value) {
  Serial.print(tag);
  Serial.print(" = ");
  Serial.println(value);
}

//pass by pointer
void PrintValP(char *tag, char *value) {
  Serial.print(tag);
  Serial.print(" = ");
  Serial.println(value);
}
void PrintValPC(const char *tag, const char *value) {
  Serial.print(tag);
  Serial.print(" = ");
  Serial.println(value);
}
void PrintString(const char *str) {
  const char *p;
  p = str;
  while (*p) {
    Serial.print(*p);
    p++;
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

Raspberry Bluetooth Printer

You have to perform several steps in order to establish communication.

You have to pair your desired bluetooth device using bluetoothctl

sudo bluetoothctl -a

scan on

devices

pair XX:XX:XX:XX:XX:XX

trust XX:XX:XX:XX:XX:XX

quit

Where XX:XX:XX:XX:XX:XX is the MAC address of your bluetooth device.

You have to create the serial device that binds to your paired bluetooth device.

sudo rfcomm bind /dev/rfcomm0 XX:XX:XX:XX:XX:XX 1

The last number is the communication channel. It has to bee unique for all your connections.

Then you should be able to open a connection. (Assuming your bluetooth device actually supports the required SPP protocol.)

echo “test” > /dev/rfcomm0

source: https://raspberrypi.stackexchange.com/questions/78155/using-dev-rfcomm0-in-raspberry-pi/78301#78301?s=39be540452a34b55af09cb19d04ea700

Machine Learning #7 – Menuju Klasifikasi

Pada tulisan Machine Learning #5 telah diceritakan tentang Neural Network dengan 2 input dan 1 output. Mari kita kembangkan jika menggunakan 1 input dan 2 output. Percobaan ini mengarah ke klasifikasi.

Sabagai gambaran apabila inputnya adalah suara yang diubah menjadi nilai dalam bentuk matrik x dan hasilnya adalah 2 output, yaitu output y= [0 0]. Output y = [1 0], jika hasil klasifikasinya mengarah pada output pertama, misalkan saya kata “YA”, dan outputnya y = [0 1] jika hasil klasifikasinya adalah output kedua, misalkan kata “Tidak”.

Dari model di atas, tentunya agak kesulitan untuk diterapkan di mikrokontroler. Hasil dari training dalam bentuk TFLite >5Mb. Padahal mikrokontroler, katakanlah BLE 33 Sense, tidak memiliki Flash Memori yang berkapasitas 5Mb

Cara yang pertama yaitu mengubah kuantisasi dari float menjadi int pada mikrokontoler. Berikut contoh perbandingan kuantisasi menggunakan float dibandingkan menggunakan int. Memang akurasi menjadi sedikit berubah, namun penggunaan memori jauh lebih kecil.

Menghilangkan parameter yang tidak begitu penting, dengan pruning.

Dan beberapa proses yang dapat disimpulkan seperti gambar berikut:

Machine Learning #6 – Menerapkan Machine Learning ke Mikrokontroler (via Arduino + TinyML)

Dilihat dari situs resmi TensorFlow, saat ini board yang support dengan model training TensorFlowLite adalah board berikut:

Jadi mari kita gunakan yang saya punya, yaitu ESP-EYE (murah, 200an ribu) dan Nano 33 BLE Sense (mahal, 1,2 juta).

Untuk dapat menggunakan TinyML ada penjelasan di situs resminya, tapi cukup ribet. Mari gunakan milik Simone yang bernama EloquentTinyML. Siapkan dahulu library-nya melalui Library Manager

Selanjutnya pilih papan (board) yang tepat. Untuk Nano 33 BLE Sense lihat tutorialnya di tulisan Mencoba Nano 33 BLE Sense sedangkan jika menggunakan ESP-EYS, lihat tutorialnya di tulisan Memulai Percobaan ESP-CAM. Dicontohkan untuk kali ini kita gunakan Nano 33 BLE Sense:

Tuliskan sketch pada arduino dengan kode berikut:

#include <EloquentTinyML.h>
#include "sine_model.h"

#define NUMBER_OF_INPUTS 1
#define NUMBER_OF_OUTPUTS 1
#define TENSOR_ARENA_SIZE 2*1024

Eloquent::TinyML::TfLite<NUMBER_OF_INPUTS, NUMBER_OF_OUTPUTS, TENSOR_ARENA_SIZE> ml;


void setup() {
    Serial.begin(115200);
    ml.begin(sine_model);
}

void loop() {

    float x = 3.14 * random(100) / 100;
    float y = sin(x);
    float input[1] = { x };
    float predicted = ml.predict(input);

    Serial.print("Nilai sin(");
    Serial.print(x);
    Serial.print(") = ");
    Serial.print(y);
    Serial.print("\t Nilai prediksi: ");
    Serial.println(predicted);
    delay(1000);
}

Pada baris 2, isikan nama file hasil pengubahan tflile, misalkan hasilnya adalah sine_model.h Model yang akan digunakan adalah model 1 input dan 1 ouput sesuai pada artikel Machine Learning #3 Selanjutnya ketikkan jumlah input dan jumlah outputnya seperti yang dicontohkan di baris 4 dan 5, yaitu 1 input dan 1 output.

Di baris 8, adalah inisialisasi object EloquentTinyML sedangkan baris 13 adalah membaca model sine_model.h agar dapat diproses oleh library TinyML.

Untuk melihat hasil prediksi cukup gunakan .predict(nilai input) seperti yang terlihat di baris 21.

Saatnya upload skecth ke Nano 33 BLE Sense

Penampakan Nano 33 BLE Sense

Mari dilihat hasilnya, apakah mikrokontroler mampu memprediksi nilai y berdasarkan nilai input x? dimana rumus sebenarnya adalah y = sin(x). Mungkin ada sedikit selisih, namun untuk percobaan dengan 1 hidden layer ini bisa dimaklumi. Perlu banyak neuron dan hidden layer untuk mendapatkan nilai kesalahan (loss) yang sangat kecil.

Machine Learning #5 – Persiapan File Model untuk Mikrokontroler

Setelah mendapatkan model dari hasil training, model dapat disimpan ke file .tflite, file ini dapat kita gunakan untuk framework lain misalnya untuk mikrokontroller. Untuk membentuk file .tflite dari hasil training gunakan kode berikut:

# Convert model ke file tflite
tflite_model_name = 'sine_model'  # .tflite 

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
tflite_model = converter.convert()

#for 1.2 to 2.3
#open(tflite_model_name + '.tflite', 'wb').write(tflite_model)

#for 2.4 or later
with tf.io.gfile.GFile(tflite_model_name +'.tflite', 'wb') as f:
  f.write(tflite_model)


Hasil dari code diatas adalah file sine_model.tflite atau dapat mnegubah nama filenya di baris 2

Karena mikrokontroler yang akan digunakan dibangun menggunakan bahasa C, maka perlu mengubah tflite menjadi file header agar dapat dikenali pada bahasa pemrograman C. Ada code fungsi (prosedur) yang sudah dibuat oleh pegiat machine learning, tinggal dipakai saja.

# Function: Convert some hex value into an array for C programming
def hex_to_c_array(hex_data, var_name):

  c_str = ''

  # Create header guard
  c_str += '#ifndef ' + var_name.upper() + '_H\n'
  c_str += '#define ' + var_name.upper() + '_H\n\n'

  # Add array length at top of file
  c_str += '\nunsigned int ' + var_name + '_len = ' + str(len(hex_data)) + ';\n'

  # Declare C variable
  c_str += 'unsigned char ' + var_name + '[] = {'
  hex_array = []
  for i, val in enumerate(hex_data) :

    # Construct string from hex
    hex_str = format(val, '#04x')

    # Add formatting so each line stays within 80 characters
    if (i + 1) < len(hex_data):
      hex_str += ','
    if (i + 1) % 12 == 0:
      hex_str += '\n '
    hex_array.append(hex_str)

  # Add closing brace
  c_str += '\n ' + format(' '.join(hex_array)) + '\n};\n\n'

  # Close out header guard
  c_str += '#endif //' + var_name.upper() + '_H'

  return c_str


Selanjutnya, gunakan fungsi di atas untuk mengubah .tflite menjadi .h, nama filenya dapat diset pada baris ke-2. Kali ini dinamai dengan sine_model.h

# Write TFLite model to a C source (or header) file
c_model_name = 'sine_model'       # .h 

with open(c_model_name + '.h', 'w') as file:
  file.write(hex_to_c_array(tflite_model, c_model_name))

Machine Learning #4 – 2 input, 3 HL, 1 output

Kali ini mari membuat model untuk 2 input dan 1 output dengan menggunakan 3 hidden layer. Sebagai contoh kasus ada data dengan rumus matematika: y = cos(x1) – sin(x2) + sin(x1+x2) data ini memiliki 1 input yaitu x1 dan x2. Sedangkan nilai outputnya 1 , yaitu y. Apakah MESIN bisa memprediksi rumus matematika di atas? Mari kita mulai dengan membuat datanya.

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import math
from tensorflow.keras import layers

# Membuat 100, 77 dan 66 nilai random berdasarkan seed 1234
np.random.seed(1234)
x1_values = np.linspace(0,2*math.pi,100)
x2_values = np.linspace(0,2*math.pi,100)
x1_valuesV = np.linspace(0,2*math.pi,77)
x2_valuesV = np.linspace(0,2*math.pi,77)
x1_valuesT = np.linspace(0,2*math.pi,66)
x2_valuesT = np.linspace(0,2*math.pi,66)

# masing masing nilai dibuat dalam bentuk sinusoida, cos(x1) - sin(x2) + sin(x1+x2)
y_values = np.sin(x1_values) - np.cos(x2_values) - np.sin(x1_values+x2_values) + (0.2 * np.random.randn(x1_values.shape[0]))
y_valuesV = np.sin(x1_valuesV) - np.cos(x2_valuesV) - np.sin(x1_valuesV+x2_valuesV) + (0.2 * np.random.randn(x1_valuesV.shape[0]))
y_valuesT = np.sin(x1_valuesT) - np.cos(x2_valuesT) - np.sin(x1_valuesT+x2_valuesT) + (0.2 * np.random.randn(x1_valuesT.shape[0]))

plt.plot(x1_values, y_values, 'r.', label="Data u/ Training")
plt.plot(x1_valuesV, y_valuesV, 'b.', label="Data u/ Validasi")
plt.plot(x1_valuesT, y_valuesT, 'g.', label="Data u/ Test")
plt.legend()


Kode di atas adalah untuk membuat data training, data validasi dan data test. Hasil dari plot data x dan y adalah berikut:

Selanjutnya mari kita buat permodelan NN seperti berikut:

Karena inputnya 2, maka baris 28 kita ketikkan: input_shape=(2,)

# Membuat model dengan 3 hidden layer
model = tf.keras.Sequential()
model.add(layers.Dense(2, activation='tanh', input_shape=(2,)))
model.add(layers.Dense(10, activation='tanh'))
model.add(layers.Dense(10, activation='tanh'))
model.add(layers.Dense(10, activation='tanh'))
model.add(layers.Dense(1))
# Lihat pemodelan, lihat secara grafis di https://alexlenail.me/NN-SVG/index.html
model.summary()

Kedua input (x1 dan x2) perlu dibuat stack agar dapat diproses untuk permodelan. Kali ini, data training dan data validasi saja yang perlu dibuat stack. Lihat cara membuat stack pada baris 36-37.

x1x2_array = np.stack([x1_values, x2_values], axis=1)
x1x2V_array = np.stack([x1_valuesV, x2_valuesV], axis=1)

Saatnya membuat model. Kali ini menggunakan tensorboard untuk memvisualisasikannya. Jumlah epochnya adalah 1000 percobaan, dengan batch size 100

import datetime
%load_ext tensorboard
log_dir =  datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)

# Gunakan optimizer adam , loss function Mean square error
model.compile(optimizer='adam', loss='mse', metrics=['mse'])

# Training model
#model.fit(x1x2_array,y_values, batch_size=100, epochs=1000)
history = model.fit(x1x2_array, y_values, epochs=1000, batch_size=100,
   validation_data=(x1x2V_array, y_valuesV), 
          callbacks=[tensorboard_callback])

# %tensorboard --logdir logs/gradient_tape # buka komentar jika ingin melihat di tensorboard

Setelah proses belajar berlangsung, saatnya mencoba, membutktikan prediksi MESIN dengan data yang sebenarnya

# Plot hasil prediksi terhadap nilai sebenarnya yaitu y = sin(x)
x1x2T_array = np.stack([x1_valuesT, x2_valuesT], axis=1)

predictions = model.predict(x1x2T_array)

plt.clf()
plt.title("Perbandingan nilai Test dengan nilai Prediksi")
plt.plot(x1_valuesT, y_valuesT, 'b.', label='Nilai Test')
plt.plot(x1_valuesT, predictions, 'r.', label='Nilai  Prediksi')
plt.legend()
plt.show()

Ternyata hasil prediksi mesin mirip dengan data sebenarnya. Hanya saja pada nilai input 5 ke atas masih ada kesalahan. Titik warna merah(nilai prediksi), terletak agak jauh dengan titik warna biru(nilai seharusnya). Untuk memperbaikinya bisa dengan mengubah aktivasi, menambah neuron atau layer. Silahkan dicoba coba hingga hasil terbaik. Setidaknya MESIN sudah belajar dengan baik

Jika ingin menyimpan hasil model training, skrip kode berikut bisa digunakan untuk mengexport model hasil training ke tflite. Dengan file tflite ini kita bisa gunakan untuk menyimpan hasilnya dan dibuka di platform lain.

# Convert Keras model to a tflite model
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]
tflite_model = converter.convert()

#for 1.2 to 2.3
#open(tflite_model_name + '.tflite', 'wb').write(tflite_model)

#for 2.4 or later
with tf.io.gfile.GFile('2input.tflite', 'wb') as f:
  f.write(tflite_model)

Machine Learning #3 – Persamaan NonLinier

Pada Machine Learning #2 MESIN belajar (training) untuk menyelesaikan persamaan linear, sekarang selanjutnya training untuk persamaan non linear. Kita contohkan menggunaan persamaan sinusoida.

Mari gunakan persamaan sederhana y = sin(x).

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import math
from tensorflow.keras import layers

# Membuat 1000 nilai random berdasarkan seed 1234, silahkan diganti untuk pola random lain
nsamples=1000
np.random.seed(1234)
x_values = np.random.uniform(low=0, high=(2 * math.pi), size=nsamples)
# masing masing nilai dibuat dalam bentuk sinusoida
y_values = np.sin(x_values) + (0.1 * np.random.randn(x_values.shape[0]))
plt.plot(x_values, y_values, '.')

# membagi menjadi 3 kategori data, untuk validasi, untuk test dan untuk training
val_ratio = 0.2     # 20% untuk data validasi
test_ratio = 0.2    # 20% untuk data test, dan sisanya untuk data training

val_split = int(val_ratio * nsamples)
test_split = int(val_split + (test_ratio * nsamples))
x_val, x_test, x_train = np.split(x_values, [val_split, test_split])
y_val, y_test, y_train = np.split(y_values, [val_split, test_split])

# Cek hasil split apakah benar, jika salah maka munculkan assertment
assert(x_train.size + x_val.size + x_test.size) == nsamples

# TAmpilkan data dengan warna yang berbeda:
plt.plot(x_train, y_train, 'b.', label="Data u/ Train")
plt.plot(x_test, y_test, 'r.', label="Data u/ Test")
plt.plot(x_val, y_val, 'y.', label="Data u/ Validasi")
plt.legend()
plt.show()
# Membuat model dengan 2 layer
model = tf.keras.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(1,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1))
# Lihat pemodelan
model.summary()

Kode diatas adalah untuk membuat model 2 layer yang sekuensial. Layer pertama 16 neuron dengan 1 input. Layer kedua dengan 16 neuron, dan outpunya 1 neuron untuk 1 output. jenis aktivasinya adalah ReLU atau Rectified Linear Unit

Hasil Kompilasi
# Gunakan optimizer RMSprop , loss function Mean absolute error
model.compile(optimizer='rmsprop', loss='mae', metrics=['mae'])

# Training model
history = model.fit(x_train, y_train, epochs=500, batch_size=100,
   validation_data=(x_val, y_val))

Untuk kali ini, menggunakan RMSProp sebagai optimizer. Pada training untuk persamaan linear Machine Learning #1, yang digunakan adalah SGD (Stochastic Gradient Descent). Pada training ini SGD terlalu lambat dan bisa jadi tidak bisa menemukan nilai minimum lost terendah. Perhatikan perbandingan berikut, SGD ditunjukkan dot warna merah:

Sedangkan untuk mencari nilai loss, yang digunakan adalah MAE (Mean absolute error) dimana selisih erornya di absolutekan jadi nilai positif semua. Berbeda dengan yang sebelumnya, dimana menggunakan MSE (Mean Squared Error) yang mengakarkan total nilai error.

Pada baris ke 47, MESIN diberikan 500 epoch untuk proses training, berdasarkan data training (x_train, y_train) dan kemudian mengevaluasi dengan data evaluasi (x_val, y_val). Ukuran batch yang digunakan adalah 100.

Jika ukuran tumpukan batch kecil, misalnya 1, maka gradien hanya dihitung dengan satu contoh pelatihan. Hal ini dapat membuat kerugian pelatihan yang berosilasi, karena setiap kali memperkirakan gradien hanya dengan satu contoh pelatihan, yang sering kali tidak mewakili seluruh data pelatihan. Jadi, semakin banyak contoh pelatihan yang digunakan, semakin baik dalam memperkirakan gradien (yang akan sesuai dengan semua contoh pelatihan), sehingga ini berpotensi mengarah pada konvergensi yang lebih cepat. Namun, jika menggunakan banyak contoh pelatihan, biayanya juga bisa mahal secara komputasi. Misalnya, bayangkan data pelatihan terdiri dari jutaan contoh pelatihan. Dalam hal ini, untuk melakukan satu langkah penurunan gradien, harus melalui semua pelatihan yang dapat memakan banyak waktu.

Line Plots of Classification Accuracy on Train and Test Datasets With Different Batch Sizes
Akurasi dari hasil training dan pengetesan pada nilai batch yang berbeda. Jika batch 1 maka banyak hasil test jauh meleset berdasarkan nilai proses training

Jika ingin melihat history loss dari hasil training, dapat gunakan skrip berikut:

# Plot hasil training, jika tidak diperlukan kode ini bisa diabaikan
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(loss) + 1)

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

Selanjutnya mari buat perbandingan hasil training berdasarkan dengan model matematikan sebenarnya (y=sin(x))

# Plot hasil prediksi terhadap nilai sebenarnya yaitu y = sin(x)
predictions = model.predict(x_test)

plt.clf()
plt.title("Perbandingan hasil training berdasarkan dengan model matematikan sebenarnya")
plt.plot(x_test, y_test, 'b.', label='Nilai Test')
plt.plot(x_test, predictions, 'r.', label='Nilai Prediksi')
plt.legend()
plt.show()

Warna biru adalah nilai data test hasil sinus yang sudah diberi noise. Warna merah adalah prediksi MESIN. Walaupun data training sudah ditambahkan noise (Baris 12), mesin dapat mesin dapat memprediksi bahwa data training adalah persamanan sinus.

Machine Learning #2 – Neural Network

Berdasarkan pengalaman Machine Learning #1 maka mari memanfaatkan Neural Network untuk proses pembelajaran MESIN

Dari:

Kita ubah menjadi:

import tensorflow as tf
import numpy as np
from tensorflow import keras


xs = np.array([-1.0,  0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)

"""#### Gunakan 2 Layer """

my_layer_1 = keras.layers.Dense(units=2, input_shape=[1])
my_layer_2 = keras.layers.Dense(units=1)
model = tf.keras.Sequential([my_layer_1, my_layer_2])
model.compile(optimizer='sgd', loss='mean_squared_error')

xs = np.array([-1.0,  0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)

model.fit(xs, ys, epochs=50)


print(my_layer_1.get_weights()[0])
print(my_layer_1.get_weights()[1])
print(my_layer_2.get_weights())

print(model.predict([4.0]))

Baris 11 membentuk layer ke 1 dengan 2 unit dense, dimana inputnya adalah 1, yaitu x

Baris 12 membentuk layer ke 2 dengan 1 unit dense

Baris 13 membentuk model dalam bentuk sequensial (berurutan) dari layer ke-1 dilanjutkan layer ke-2

Baris 14 membuat model training menggunakan optimizer SGD (Stochastic Gradient Descent), sedangkan lost function-nya (untuk mengurangi kesalahannannya) menggunakan metode Mean Square Error (MSE). Lihat di Machine Learning #1 untuk lebih jelasnya

Selanjutnya lakukan training 50 kali, sesuai perintah di baris 19

Hasil nilai w dan b di setiap dense adalah berikut:

Pada layer ke-1, dense 1: w=0,9896864 dan b=-1,0169075 sedangkan dense 2: w=-0,38317692 dan b=0,24202025

Pada layer ke-2, dense: w1=0,8393119; w2=-1,0042762; b=0,06092146

Hasil prediksinya saat nilai x 4 adalah 6,90 yang sudah lebih mendekati angka 7, dibandingkan dengan di Machine Learning #1 yang hasilnya 6,80