Klasifikasi MNIST menggunakan Convolutional Neural Network


Apa itu Convolutional Neural Network (CNN)? Ini bukan portal berita, tapi ini adalah jenis Jaringan Syaraf Tiruan (JST) yang katanya paling bagus dalam menghandle data gambar atau citra. Ini adalah jenis JST feed forward network sama seperti Multi Layer Perceptron (MLP) (pernah di bahas sebelumnya di beberapa artikel di blog ini, salah satunya adalah pengenalan mengenal MLP ada di sini). Lalu apa yang spesial dari CNN? sesuai dengan namanya, layer Convolutional yang membuat JST jenis ini spesial.

CNN mempelajari pola-pola unik dari feature citra. Mula-mula CNN akan belajar feature yang paling sederhana seperti garis tepi, yang kemudian dari kompulan garis tepi CNN akan mempelajari suatu bentuk sederhana dan dari bentuk sederhana itu CNN bisa mempelajari bentuk yang lebih kompleks sehingga bisa belajar membedakan suatu citra dengan citra lainnya. Semua ini memungkinkan karena adanya convolutional layer pada CNN.


Selain Convolutional layer, masih ada layer lain yang spesial ada pada CNN seperti pooling layer dan  ReLU layer. Pembahasan mendalam mengenai CNN dan layer-layernya akan dibahas pada postingan terpisah.

Well, tapi kan komputer tidak tahu kalau masukan itu suatu data citra atau bukan. Komputer melihat data citra sebagi sekumpulan data yang kebetulan berbentuk matriks persegi/persegi panjang. Jadi menurut saya pribadi, selama data itu benar-benar ada polanya, CNN pasti bisa menangkap dan 'mempelajari'nya. Oleh karena itulah CNN dimanfaatkan bukan hanya untuk pemrosesan citra. CNN juga dimanfaatkan untuk Natural Languange Processing (NLP), bot game, dan drug discovery.

Di pembahasan kali ini, kita akan coba mengetes performa dari CNN untuk klasifikasi gambar MNIST. MNIST lagi...bosan! Well, niat awal saya sebenarnya ingin klasifikasi gambar arbitary yang berwarna tapi apa daya, untuk melakukan itu komputasi yang diperlukan sangatlah berat. Hanya untuk 100 data training gambar berwarna dengan dimensi sekitar 400 x 400 butuh RAM 6 gb. RAM di komputer saya hanya 4 GB sehingga tidak mampu menjalankan training datanya.

Sepertinya script saya bisa di optimasi untuk mengatasi kebutuhan RAM ini tapi karena terlalu merepotkan jadi yah sudahlah, kapan-kapan saja buatnya~

Library yang saya pakai di sini seperti biasa adalah tensorflow dengan tambahan Keras. Keras adalah library wrapping tensorflow dan theano. Walaupun tensorflow adalah library high level, paradigma tensorflow dalam membangun model JST terkadang bisa sangat merepotkan. Oleh karena itu, Keras membuatnya 'lebih high level lagi' sehingga pembuatan model JST menjadi jauh lebih mudah lagi.

Untuk menginstall keras pada Ubuntu, cukup dengan perintah
sudo -H pip install keras
view raw gistfile1.txt hosted with ❤ by GitHub

Pastikan sebelum install keras, tensorflow atau theano harus terlebih dahulu terinstall. Keras hanyalah wrapping tensorflow dan theano.

Setelah itu download data MNIST yang akan kita gunakan untuk percobaan kali ini. Data tersebut bisa di download di sini. Data tersebut dalam bentuk tar.gz sehingga harus di ekstrak terlebi dahulu.

Script yang kita gunakan ada di bawah ini. Jika tidak kelihatan, bisa di download di sini. Di sini kita mencoba menklasifikasikan digit 0, 1, 2, dan 3. Variabel data0, data1, data2, dan data3 silahkan ganti dengan alamat tempat kumpulan data digit 0, 1, 2, dan 3 berada (data yang MNIST telah di download sebelumnya).

#!/usr/bin/python
import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.sequence import pad_sequences
import os
from PIL import Image
#setting
numclass = 4
dimx = 28
dimy = 28
data0 = '/home/genomexyz/mnist_png/training/0'
data1 = '/home/genomexyz/mnist_png/training/1'
data2 = '/home/genomexyz/mnist_png/training/2'
data3 = '/home/genomexyz/mnist_png/training/3'
thresread = 500
def separatedata(mat):
trainmat = mat[0:400]
testmat = mat[400:420]
return trainmat, testmat
def generatemodel(ydim, xdim):
#this CNN model courtesy by https://machinelearningmastery.com/handwritten-digit-recognition-using-convolutional-neural-networks-python-keras/
# create model
model = Sequential()
model.add(Conv2D(30, (5, 5), input_shape=(28, 28, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(15, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(50, activation='relu'))
model.add(Dense(numclass, activation='softmax'))
# Compile model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
def readimage(addr):
matimage = []
iterimage = sorted(os.listdir(addr))
for i in xrange(1, len(iterimage)):
if (i > thresread):
break
img = Image.open(addr+'/'+iterimage[i])
imgarray = np.array(img)
#print np.shape(imgarray)
matimage.append(imgarray)
matimage = np.asarray(matimage)
return matimage
#get matrix of image
mat0 = readimage(data0)
mat1 = readimage(data1)
mat2 = readimage(data2)
mat3 = readimage(data3)
#separate matrix
train0 ,test0 = separatedata(mat0)
train1 ,test1 = separatedata(mat1)
train2 ,test2 = separatedata(mat2)
train3 ,test3 = separatedata(mat3)
#combine all train data and test data
traindata = np.concatenate((train0, train1, train2, train3))
testdata = np.concatenate((test0, test1, test2, test3))
#reshape train and test data
#VERY IMPORTANT: MAKE SURE TYPE OF DATA IS float32
traindata = np.reshape(traindata, (np.shape(traindata)[0], np.shape(traindata)[1], np.shape(traindata)[2], 1)).astype('float32')
testdata = np.reshape(testdata, (np.shape(testdata)[0], np.shape(testdata)[1], np.shape(testdata)[2], 1)).astype('float32')
#create on hot vector
trainlabel0 = np.zeros((len(train0), numclass))
trainlabel0[:,0] = 1
testlabel0 = np.zeros((len(test0), numclass))
testlabel0[:,0] = 1
trainlabel1 = np.zeros((len(train1), numclass))
trainlabel1[:,1] = 1
testlabel1 = np.zeros((len(test1), numclass))
testlabel1[:,1] = 1
trainlabel2 = np.zeros((len(train2), numclass))
trainlabel2[:,2] = 1
testlabel2 = np.zeros((len(test2), numclass))
testlabel2[:,2] = 1
trainlabel3 = np.zeros((len(train3), numclass))
trainlabel3[:,3] = 1
testlabel3 = np.zeros((len(test3), numclass))
testlabel3[:,3] = 1
trainlabel = np.concatenate((trainlabel0, trainlabel1, trainlabel2, trainlabel3))
testlabel = np.concatenate((testlabel0, testlabel1, testlabel2, testlabel3))
print np.shape(trainlabel)
print np.shape(testlabel)
#####################
#normalizing session#
#####################
for i in xrange(len(traindata)):
traindata[i] = traindata[i] / 255.0
for i in xrange(len(testdata)):
testdata[i] = testdata[i] / 255.0
#print np.mean(traindata)
#################
#convnet process#
#################
mod = generatemodel(dimy, dimx)
mod.fit(traindata, trainlabel, epochs=100, verbose=1, batch_size=len(traindata))
####################
#evaluation process#
####################
# Final evaluation of the model
scores = mod.evaluate(testdata, testlabel, verbose=0)
print("CNN Error: %.2f%%" % (100-scores[1]*100))
print scores
view raw mnistcnn.py hosted with ❤ by GitHub


Sekarang akan dijelaskan setiap bagian dari script ini.

Pertama kita membaca data MNIST dan di ekstrak ke dalam bentuk matriks 3 dimensi (total gambar x panjang x lebar).
#get matrix of image
mat0 = readimage(data0)
mat1 = readimage(data1)
mat2 = readimage(data2)
mat3 = readimage(data3)
#separate matrix
train0 ,test0 = separatedata(mat0)
train1 ,test1 = separatedata(mat1)
train2 ,test2 = separatedata(mat2)
train3 ,test3 = separatedata(mat3)
#combine all train data and test data
traindata = np.concatenate((train0, train1, train2, train3))
testdata = np.concatenate((test0, test1, test2, test3))
#reshape train and test data
#VERY IMPORTANT: MAKE SURE TYPE OF DATA IS float32
traindata = np.reshape(traindata, (np.shape(traindata)[0], np.shape(traindata)[1], np.shape(traindata)[2], 1)).astype('float32')
testdata = np.reshape(testdata, (np.shape(testdata)[0], np.shape(testdata)[1], np.shape(testdata)[2], 1)).astype('float32')
#create on hot vector
trainlabel0 = np.zeros((len(train0), numclass))
trainlabel0[:,0] = 1
testlabel0 = np.zeros((len(test0), numclass))
testlabel0[:,0] = 1
trainlabel1 = np.zeros((len(train1), numclass))
trainlabel1[:,1] = 1
testlabel1 = np.zeros((len(test1), numclass))
testlabel1[:,1] = 1
trainlabel2 = np.zeros((len(train2), numclass))
trainlabel2[:,2] = 1
testlabel2 = np.zeros((len(test2), numclass))
testlabel2[:,2] = 1
trainlabel3 = np.zeros((len(train3), numclass))
trainlabel3[:,3] = 1
testlabel3 = np.zeros((len(test3), numclass))
testlabel3[:,3] = 1
trainlabel = np.concatenate((trainlabel0, trainlabel1, trainlabel2, trainlabel3))
testlabel = np.concatenate((testlabel0, testlabel1, testlabel2, testlabel3))
view raw gistfile1.txt hosted with ❤ by GitHub


Lalu setelah itu, nilai dari matriks yang kita dapatkan di normalisasi dengan membagi semua nilainya dengan 255 (nilai tertinggi yang bisa didapatkan).
#####################
#normalizing session#
#####################
for i in xrange(len(traindata)):
traindata[i] = traindata[i] / 255.0
for i in xrange(len(testdata)):
testdata[i] = testdata[i] / 255.0
view raw gistfile1.txt hosted with ❤ by GitHub

Setelah itu kita masukan data training pada model yang sudah kita desain.
#################
#convnet process#
#################
mod = generatemodel(dimy, dimx)
mod.fit(traindata, trainlabel, epochs=100, verbose=1, batch_size=len(traindata))
view raw gistfile1.txt hosted with ❤ by GitHub
Model CNN ini didapatkan dari sini.
def generatemodel(ydim, xdim):
#this CNN model courtesy by https://machinelearningmastery.com/handwritten-digit-recognition-using-convolutional-neural-networks-python-keras/
# create model
model = Sequential()
model.add(Conv2D(30, (5, 5), input_shape=(28, 28, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(15, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(50, activation='relu'))
model.add(Dense(numclass, activation='softmax'))
# Compile model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
view raw gistfile1.txt hosted with ❤ by GitHub
Setelah memasukkan data training, kita uji kehandalan model CNN dengan memasukkan data test dan diukur berapa persen errornya.
####################
#evaluation process#
####################
# Final evaluation of the model
scores = mod.evaluate(testdata, testlabel, verbose=0)
print("CNN Error: %.2f%%" % (100-scores[1]*100))
view raw gistfile1.txt hosted with ❤ by GitHub

Hasilnya adalah
Epoch 97/100
1600/1600 [==============================] - 0s 197us/step - loss: 0.0226 - acc: 0.9906
Epoch 98/100
1600/1600 [==============================] - 0s 190us/step - loss: 0.0188 - acc: 0.9956
Epoch 99/100
1600/1600 [==============================] - 0s 192us/step - loss: 0.0243 - acc: 0.9944
Epoch 100/100
1600/1600 [==============================] - 0s 192us/step - loss: 0.0235 - acc: 0.9950
CNN Error: 0.00%
[0.0097163347527384758, 1.0]
view raw gistfile1.txt hosted with ❤ by GitHub

Luar biasa, tanpa celah sama sekali. Tapi hasil ini bisa berbeda jika kita jalankan lagi. Contoh berikut hasil running kedua kalinya.
Epoch 97/100
1600/1600 [==============================] - 0s 183us/step - loss: 0.0152 - acc: 0.9950
Epoch 98/100
1600/1600 [==============================] - 0s 183us/step - loss: 0.0187 - acc: 0.9956
Epoch 99/100
1600/1600 [==============================] - 0s 186us/step - loss: 0.0245 - acc: 0.9925
Epoch 100/100
1600/1600 [==============================] - 0s 186us/step - loss: 0.0187 - acc: 0.9944
CNN Error: 1.25%
[0.021798805519938468, 0.98750000000000004]
view raw gistfile1.txt hosted with ❤ by GitHub

Benar kan. Itu karena sama seperti JST pada umumnya, bobot JST pada awalnya di initialisasi menggunakan angka acak sehingga perjalanan menuju ke konvergenan dari setiap kali running akan sedikit berbeda jika model JST yang kita pakai adalah model yang baik.

Di percobaan ini ada kita menggunakan 80 data test, artinya pada percobaan kedua (error 1.25%) tadi CNN salah menklasifikasikan 1 kasus saja. Berbeda tipis dengan yang percobaan pertama yang benar semua tebakannya.

Pada postingan kali ini kita hanya mengetes kehandalan CNN. Real action dari penggunaan CNN mungkin akan dibahas lain waktu. Sekian.

referensi:
https://www.researchgate.net/figure/ConvNet-Architecture-used-for-Automatic-Feature-Learning-to-Detect-Shadows_fig2_282477990, di akses pada 15 Februari 2018
https://github.com/myleott, di akses pada 15 Februari 2018
https://en.wikipedia.org/wiki/Convolutional_neural_network, di akses pada 15 Februari 2018
https://stackoverflow.com/questions/45933434/is-there-a-way-to-display-the-feature-maps-in-the-tensorflow-object-detection-ap, di akses pada 15 Februari 2018
https://machinelearningmastery.com/handwritten-digit-recognition-using-convolutional-neural-networks-python-keras/, di akses pada 15 Februari 2018

Komentar

  1. Mau bertanya : list program saya error:
    print np.shape(trainlabel)
    ^
    SyntaxError: invalid syntax

    Mohon info solusinya

    BalasHapus

Posting Komentar