MNIST Neural Network Problem: Bersatu Kita Pintar Bercerai Kita Binary


Sebelum memulai pembahasan inti pada artikel ini, mari kita review sedikit tentang dasar-dasar dari neural network.

Pada pembahasan sebelumnya kita memakai fungsi sigmoid untuk memaping input ke dalam 2 class yang ada. Fungsi sigmoid mengambil input persamaan linier (wx +b). Summary, dalam memaping input kedalam 2 class tersebut kita masukkan input tersebut kedalam persamaan linier yang kita sebut sebagai fungsi pre-aktivasi dan kemudian hasil dari persamaan linier ini dimasukkan kedalam fungsi sigmoid yang kita sebut sebagai fungsi aktivasi.

Fungsi pre-aktivasi selalu persamaan linier tapi fungsi aktivasi bisa bermacam-macam. Fungsi sigmoid dan softmax (yang akan kita pakai kali ini) hanya 2 dari berbagai fungsi aktivasi. Fungsi aktivasi disini sejatinya berfungsi untuk 'menyulap' persamaan linear dari fungsi pre-aktivasi menjadi suatu probability (mendekati probability).

Satu set fungsi pre-aktivasi dan aktivasi kita anggap sebagai 1 neural. Satu neural ini hanya bisa menangani masalah yang parameter atau inputnya linear dengan outputnya dan hanya untuk klasifikasi 2 kemungkinan (biner) seperti pembahasan kita sebelumnya. Untuk klasifikasi lebih dari 2 kita cukup mengubah fungsi aktivasinya yang memungkinkan hal itu seperti fungsi softmax ini.

Untuk klasifikasi yang non-linear, kita juga perlu menambah neuralnya hanya saja neural tambahan ini akan menjadi hidden layer yang akan membuat hubungan yang awalnya tidak linear menjadi linear. Untuk kali ini, klasifikasi yang akan kita lakukan adalah klasifikasi linear karena tanpa hidden layer.

Linear, non-linear bla bla bla...apa sih itu? Well, linear itu hanya suatu fancy words untuk menggambarkan suatu hubungan input dan class output yang bisa digambarkan dengan garis (dalam konteks ini).


Ini hanya gambar rerandoman dari google, tapi pointnya adalah kita memisahkan 2 object disini (bulatan hitam dan putih) dengan garis merah (atau biru). Karena bulatan hitam dan putih ini bisa di pisahkan dengan garis (garis merah atau biru) maka ini adalah klasifikasi linear. Sedangkan non-linear sebaliknya, tidak bisa dipisahkan dengan suatu garis yang sederhana.

Contoh gambar rerandoman dari google tentang klasifikasi non linear (class titik merah dan hijau). Hint: Pada klasifikasi jenis ini kita bisa memanfaatkan persamaan lingkaran untuk hidden layernya.

Ok, cukup reviewnya mari kita kembali ke topik utama. Jadi di sini kita akan mencoba (mungkin bukan kata yang tepat karena scriptnya sudah tersedia di sini) membuat script yang akan kita latih agar bisa membaca tulisan tangan manusia. Meskipun begitu, tulisan tangan disini terbatas hanya tulisan angka 0-9 (10 class). Pada pembahasan sebelumnya, dengan bantuan fungsi (aktivasi) sigmoid, kita bisa mengelompokkan input ke 2 class (dan hanya terbatas pada 2 class) yaitu kelompok yang lulus ujian dan tidak lulus ujian. Untuk itu, kali ini kita akan memakai fungsi aktivasi lain yaitu fungsi softmax yang bisa mengelompokkan lebih dari 2 class.


dimana:
σ = probability input tersebut merupakan class j
z = persamaan linear fungsi pre-aktivasi
j = salah satu class dari K class yang ada (pada kasus ini ada 10 class)

Jika semua probability dalam class itu dijumlahkan maka hasilnya akan 100%. Sebenarnya fungsi ini sangat sederhana, dia hanya menormalkan eksponen dari fungsi pre-aktivasi.


dimana:
w = weight (koefisien)
b = bias (konstanta)

Untuk fungsi loss, kita pakai cross entropy


dimana y' distribusi sebenarnya (bernilai 0 jika input bukan class ini dan 1 jika input merupakan class ini) atau istilah kerennya one hot vector. vector? yup, misal ada 10 class seperti kasus sekarang jadi ada 1 vector dengan 10 elemen, dalam vector tersebut hanya elemen yang benarlah yang mempunyai nilai 1, yang lain bernilai 0.


y adalah persamaan linier fungsi pre-aktivasi.

Beralih ke koding, untuk scriptnya seperti biasa sudah saya sediakan bisa didownload di sini. Di sini kita memakai tensorflow, suatu library yang dikhususkan untuk aplikasi neural network. Algoritma dari script ini akan dijelaskan sebagai berikut.

Pertama kita akan read data input raw yang merupakan data gambar png dan akan menghasilkan array numpy 2 dimensi. For the sake of algorithm, kita akan meratakan array 2 dimensi ini menjadi 1 dimensi. Kemudian langkah yang paling pentingnya, sebelum kita mengolah data input ini, kita akan menormalkannya. Normalisasi ini berdasarkan formula dari paper ini.

 #######################  
 #read raw data session#  
 #######################  
 rawdata = readimage(samplenum, iterperlearn, dirnameacc, accessing)  
 rawdataflat = np.zeros((samplenum*iterperlearn, imgdim*imgdim))  
 for i in xrange(len(rawdata)):  
      rawdataflat[i] = rawdata[i].flatten()  
 #####################  
 #normalizing session#  
 #####################  
 for i in xrange(len(rawdataflat)):  
      rawdataflat[i] = batnorm(rawdataflat[i])  

Lalu kemudian training. Di sinilah bagian yang paling menariknya. Pertama, dari fungsi loss kita yaitu cross entropy yang sudah kita bahas tadi, tidak perlu diturunkan secara analitis untuk melakukan skema gradient descent. Karena tensorflow sangat aware pada komputasi yang kita lakukan dan variabel-variabel yang dia punya, sehingga memungkinkannya untuk melakukan teknik backpropagation. Apa itu teknik backpropagation? Ini adalah teknik sederhana yang sangat mirip dengan melakukan penurunan secara numerik (ingat fungsi loss kita di sini tidak diturunkan secara analitis seperti postingan sebelumnya untuk melakukan skema gradient descent) dengan skema finite difference (atau mungkin bisa dibilang backpropagation = finite difference). Kita tidak akan membahas teknik ini, tapi menurut saya referensi pada blog ini sangat baik menjelaskannya.

Kedua, tensorflow dengan pintarnya tahu array yang kita gunakan sebagai weight dan bias yang akan kita update setiap loopingnya pada training, jadi kita tidak perlu melakukan update secara manual. Mengapa bisa begitu? Well, seperti yang sudah disebutkan sebelumnya pada saat tensorflow sudah masuk pada session miliknya, dia sangat aware pada properti-properti miliknya sendiri. Pada tiap iterasi di saat training, tensorflow akan mengupdate semua variabel yang ada dan di perlukan agar fungsi loss semakin minimal.

 ##################  
 #training session#  
 ##################  
 #create one hot true label  
 onetruelabel = np.zeros((samplenum*iterperlearn, samplenum))  
 for i in xrange(samplenum*iterperlearn):  
      onetruelabel[i] = onehot(samplenum, i/iterperlearn)  
 with tf.Session() as sess:  
      tf.global_variables_initializer().run()  
      for i in range(iterasi):  
           sess.run(train_step, feed_dict={x: rawdataflat, y_: onetruelabel})  
      print(tf.argmax(y,1).eval(feed_dict={x: rawdataflat}))  
      print(tf.argmax(y_,1).eval(feed_dict={y_: onetruelabel}))  

Lalu terakhir kita akan mengevaluasi keakuratan dari script kita. Defaultnya, script kita akan mempelajari 200 sampel per angka 0-9 dan kita akan evaluasi dengan dataset yang sama.

Hasilnya akurasinya 100%, wow

 ####################  
 #predicting session#  
 ####################  
      #read raw  
      testdata = readimage(samplenum, 200, dirnameacc, accessing)  
      #flatten  
      rawdataflattest = np.zeros((samplenum*200, imgdim*imgdim))  
      for i in xrange(len(testdata)):  
           rawdataflattest[i] = testdata[i].flatten()  
      #normalizing  
      for i in xrange(len(rawdataflat)):  
           rawdataflattest[i] = batnorm(rawdataflat[i])  
      onetruelabeltest = np.zeros((samplenum*200, samplenum))  
      for i in xrange(samplenum*200):  
           onetruelabeltest[i] = onehot(samplenum, i/200)  
      print(sess.run(accuracy, feed_dict={x: rawdataflattest, y_: onetruelabeltest}))  

Kemudian, kita coba uji lagi masih sama dengan 200 sampel training per angka 0-9 tapi dengan sampel testing dari folder lain 200 sampel per angka.

dan hasilnya masih 100%

 ####################  
 #predicting session#  
 ####################  
      #read raw  
      testdata = readimage(samplenum, 200, '/home/genomexyz/mnist_png/testing/', accessing)  
      #flatten  
      rawdataflattest = np.zeros((samplenum*200, imgdim*imgdim))  
      for i in xrange(len(testdata)):  
           rawdataflattest[i] = testdata[i].flatten()  
      #normalizing  
      for i in xrange(len(rawdataflat)):  
           rawdataflattest[i] = batnorm(rawdataflat[i])  
      onetruelabeltest = np.zeros((samplenum*200, samplenum))  
      for i in xrange(samplenum*200):  
           onetruelabeltest[i] = onehot(samplenum, i/200)  
      print(sess.run(accuracy, feed_dict={x: rawdataflattest, y_: onetruelabeltest}))  

Luar biasa, kita coba lagi yang lebih ekstrim, dataset training masih sama tapi dataset testing 1000 sampel dari folder yang berbeda.

Turun drastis, menjadi 62.12%


Yup, ini bisa dimaklumi karena dataset yang kita pakai buat training kecil jumlahnya jika dibandingkan dengan dataset yang digunakan untuk testing. Secara logika ini wajar, ibarat seorang guru baru mengajar 1 hari dan hanya mengajarkan basic-basicnya tapi besoknya langsung disuruh ujian, tentu saja nilai murid-muridnya kebanyakan akan mendapat nilai jelek.

 ####################  
 #predicting session#  
 ####################  
      #read raw  
      testdata = readimage(samplenum, 1000, '/home/genomexyz/mnist_png/testing/', accessing)  
      #flatten  
      rawdataflattest = np.zeros((samplenum*1000, imgdim*imgdim))  
      for i in xrange(len(testdata)):  
           rawdataflattest[i] = testdata[i].flatten()  
      #normalizing  
      for i in xrange(len(rawdataflat)):  
           rawdataflattest[i] = batnorm(rawdataflat[i])  
      onetruelabeltest = np.zeros((samplenum*1000, samplenum))  
      for i in xrange(samplenum*1000):  
           onetruelabeltest[i] = onehot(samplenum, i/1000)  
      print(sess.run(accuracy, feed_dict={x: rawdataflattest, y_: onetruelabeltest}))  

Dan berakhirlah pembahasan kita kali ini. Dengan modal pengetahuan ini, kita masih belum bisa melakukan face recognition yang seperti facebook lakukan. Ada 1 teknik lagi yang harus kita tambahkan pada artificial neural network kita agar lebih canggih yaitu convolutional neural network. Jika pada artificial neural network biasa, ketika tulisan tangan angka 0-9 ini pada dataset training tulisannya berada di tengah sedangkan pada dataset testing tulisannya berada disamping, maka bisa dipastikan prediksi artificial neural network kita akan banyak kesalahan prediksinya tapi ini tidak terjadi untuk convolutional neural network. Penasaran? So stay tune! 

referensi:
https://www.tensorflow.org/get_started/mnist/beginners, diakses pada 12 Mei 2017
http://www.statistics4u.com/fundstat_eng/cc_linvsnonlin.html, diakses pada 13 Mei 2017
https://en.wikipedia.org/wiki/Softmax_function, diakses pada 13 Mei 2017
http://proceedings.mlr.press/v37/ioffe15.pdf, diakses pada 14 Mei 2017

Komentar