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.
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
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).
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sudo -H pip install keras |
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).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
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).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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)) |
Lalu setelah itu, nilai dari matriks yang kita dapatkan di normalisasi dengan membagi semua nilainya dengan 255 (nilai tertinggi yang bisa didapatkan).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
##################### | |
#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 |
Setelah itu kita masukan data training pada model yang sudah kita desain.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################# | |
#convnet process# | |
################# | |
mod = generatemodel(dimy, dimx) | |
mod.fit(traindata, trainlabel, epochs=100, verbose=1, batch_size=len(traindata)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#################### | |
#evaluation process# | |
#################### | |
# Final evaluation of the model | |
scores = mod.evaluate(testdata, testlabel, verbose=0) | |
print("CNN Error: %.2f%%" % (100-scores[1]*100)) |
Hasilnya adalah
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
Luar biasa, tanpa celah sama sekali. Tapi hasil ini bisa berbeda jika kita jalankan lagi. Contoh berikut hasil running kedua kalinya.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
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
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
Mau bertanya : list program saya error:
BalasHapusprint np.shape(trainlabel)
^
SyntaxError: invalid syntax
Mohon info solusinya