Siri Belajar AI: Mari Buat Jaringan Neural dari Kosong
Kita akan ajar AI kita untuk mengenal tulisan tangan
Segalanya bermula dengan jaringan neural. Jaringan Neural ataupun neural network merupakan bentuk yang paling asas sekali pada kebanyakan model AI yang kita nampak ketika ini. Ia merupakan satu konsep abstrak (dalam bentuk persamaan matematik) yang membayangkan bagaimana sel-sel neuron yang ada dalam otak manusia berfungsi. Ia bukanlah satu bentuk representasi yang sempurna, kerana hakikatnya otak manusia berfungsi melalui proses yang kompleks dan dinamik yang begitu sukar sekali untuk diterjemahkan begitu sahaja di atas kertas. Begitulah hebatnya kuasa Yang Maha Mencipta.
Kita akan cuba buat jaringan neural kita sendiri untuk mengenalpasti tulisan tangan
Mari kita cuba untuk buat jaringan neural kita sendiri untuk mengenalpasti tulisan nombor (digit). Kita akan menggunakan dataset yang popular, dataset dari MNIST (Modified National Institute of Standards and Technology) yang menyimpan koleksi gambar digit (nombor) dengan tulisan tangan. Dari database MNIST yang dikongsi oleh Yann LeCunn, dia sudah memuat naik 60,000 contoh tulisan tangan digit (untuk dilatih, train set), dan 10,000 contoh untuk diuji (test set).Untuk tutorial ini, kita hanya akan menggunakan 42,000 sahaja contoh, untuk memudahkan urusan (dan menyeragamkan contoh dengan yang ada di Kaggle).
Setiap gambar sudah dipecahkan kepada data mengikut piksel imej, yang diletakkan dengan nombor 1 dan juga kosong (1 bermaksud ruang putih, dan 0 bermakna ruang gelap). Setiap dataset akan mempunyai 785 column dengan 10 row mewakili 784 pixel dan juga nombor 1 hingga 10. Dengan cara ini kita boleh melatih jaringan neural kita untuk memahami imej tersebut dan mengenalpasti tulisan nombor berdasarkan data yang kita berikan.
Tapi kena sediakan dulu data kita
#kita install dulu semua library yang diperlukan untuk projek ini
!pip install torch torchvision
!pip install tensforflow#lepas tu kita import dulu semua library dan package yang perlu kita gunakan
import pandas as pd
import numpy as np
import matplotlib.pyplot as pltAda beberapa cara sebenarnya untuk memuat turun data MNIST Digit Handwriting ni. Antaranya dengan memuat turun data dari internet seperti di laman web
Dari Yann Lecunn: http://yann.lecun.com/exdb/mnist/
2. Kita boleh muat turun juga dari Kaggle: https://www.kaggle.com/competitions/digit-recognizer/overview
Ataupun boleh muat ikut cara dari bawah untuk menggunakan tensorflow mahupun pytorch untuk memuat turun dataset yang kita perlukan
Menggunakan Tensorflow
Di bawah ini, kita akan menggunakan tensorflow untuk memuat turun data MNIST. Kita akan ambil 42,000 contoh pertama dan pecahkan lagi data itu kepada 2 bahagian (41,000 untuk dilatih), (1,000 untuk diuji).
#muat turun menggunakan tensorflow
import tensorflow as tf
import numpy as np
# muat turun dataset dari MNIST
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# gabungkan train dan test menjadi satu dataset yang cukup
x_full = np.concatenate([x_train, x_test])
y_full = np.concatenate([y_train, y_test])
# ambil 42,000 contoh yang pertama
x_full = x_full[:42000]
y_full = y_full[:42000]
# Kita susun imej tersebut kepada 784 pixel agar mudah untuk diproses
x_full = x_full.reshape(-1, 28*28).astype('float32')
# kita gabungkan kesemua 42,000 contohy tersebut
data = np.column_stack((y_full, x_full))
# shuffle dulu bagi random
np.random.shuffle(data)
# pecahkan kepada train dan test(dev) dataset
df_dev = data[:1000].T
Y_dev = df_dev[0]
X_dev = df_dev[1:] / 255.0
df_train = data[1000:].T
Y_train = df_train[0]
X_train = df_train[1:] / 255.0
# jadikan dia kepada tensor (perwakilan matriks)
X_dev_tf = tf.convert_to_tensor(X_dev.T, dtype=tf.float32)
Y_dev_tf = tf.convert_to_tensor(Y_dev, dtype=tf.int64)
X_train_tf = tf.convert_to_tensor(X_train.T, dtype=tf.float32)
Y_train_tf = tf.convert_to_tensor(Y_train, dtype=tf.int64)
# periksa bentok tensor kita
print(X_dev_tf.shape) # (1000, 784)
print(Y_dev_tf.shape) # (1000,)
print(X_train_tf.shape) # (41000, 784)
print(Y_train_tf.shape) # (41000,)Menggunakan Pytorch
Kita juga boleh menggunakan pytorch untuk melakukan perkara yang sama.
#muat turun menggunakan pytorch
import torch
import torchvision
import numpy as np
from torchvision import transforms
# kita definisikan dulu transformasi kita
transform = transforms.Compose([
transforms.ToTensor()
])
# kita load dataset MNIST
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# gabungkan dataset
full_dataset = torch.utils.data.ConcatDataset([train_dataset, test_dataset])
# ambil 42,000 contoh pertama
subset_indices = list(range(42000))
full_subset = torch.utils.data.Subset(full_dataset, subset_indices)
# ambil dataset dan tutarkan kepada tatasusunan numpy
x_full = np.array([full_subset[i][0].numpy().reshape(-1) for i in range(len(full_subset))])
y_full = np.array([full_subset[i][1] for i in range(len(full_subset))])
# gabungkan kembali bagi membentuk dataset berbentuk (42000, 785)
data = np.column_stack((y_full, x_full))
# kita shuffle dulu bagi random
np.random.shuffle(data)
# pecahkan kepada train dan test(dev) dataset
df_dev = data[:1000].T
Y_dev = df_dev[0]
X_dev = df_dev[1:] / 255.0
df_train = data[1000:].T
Y_train = df_train[0]
X_train = df_train[1:] / 255.0
# tukarkan kepada bentuk tensors (perwaklilan matriks)
X_dev_torch = torch.tensor(X_dev.T, dtype=torch.float32)
Y_dev_torch = torch.tensor(Y_dev, dtype=torch.int64)
X_train_torch = torch.tensor(X_train.T, dtype=torch.float32)
Y_train_torch = torch.tensor(Y_train, dtype=torch.int64)
# periksa bentuk tensor
print(X_dev_torch.shape) # (1000, 784)
print(Y_dev_torch.shape) # (1000,)
print(X_train_torch.shape) # (41000, 784)
print(Y_train_torch.shape) # (41000,)Ataupun Muat Turun Dari Kaggle
Kita boleh memuat turun data dari kaggle. Boleh je pergi ke https://www.kaggle.com/competitions/digit-recognizer/overview dan muat turun data yang ada. Kita akan menggunakan data dari file csv bernama ‘train.csv’
#muat turun menggunakan data dari kaggle
import numpy as np
import pandas as pd
#kita load data dari csv
df = pd.read_csv('train.csv')
#tukarkan kepada bentuk tatasusunan dan shuffle
df=np.array(df)
m,n=df.shape
np.random.shuffle(df)
# pecahkan kepada train dan test(dev) dataset
df_dev=df[0:1000].T
y_dev=df_dev[0]
X_dev=df_dev[1:n]
X_dev=X_dev/255.
df_train=df[1000:m].T
y_train=df_train[0]
X_train=df_train[1:n]
X_train=X_train/255.
# periksa bentuk tensor
print(X_train.shape) # (784, 41000)
print(y_train.shape) # (41000,)
print(X_dev.shape) # (784, 1000)
print(y_dev.shape) # (1000,)Teori Jaringan Neural
Sebelum kita nak bina jaringan neural kita sendiri, kita perlulah faham teori di sebaliknya.
Secara ringkasnya jaringan neural boleh dipersembahkan dalam bentuk persamaan seperti di bawah.
Fungsi Pengaktifan
Seperti yang ditulis di atas, fungsi pengaktifan fungsinya untuk menambahkan hubungan tidak linear pada persamaan jaringan neural. Ada banyak fungsi pengaktifan, tapi untuk tutorial ini, kita hanya akan gunakan fungsi pengaktifan ReLU dan juga Softmax. Kedua-duanya boleh ditulis seperti di bawah:
Algoritma BackPropagation
Backpropagation merupakan satu algoritma yang digunakan oleh jaringan neural untuk membolehkan ianya ‘belajar’. Ia terdiri dari dua bahagian, iaitu:
1. Forward pass — di mana kita menggunakan transformasi linear dan juga fungsi pengaktifan dari kiri ke kanan yang dihantar ke lapisan output
2. Backward Pass — kita mengira perbezaan antara lapisan output yang dihasilkan melalui forward pass dengan jawapan sebenar bagi melihat pada julat kesilapan. Julat kesilapan ini akan digunakan untuk mengubahsuai pemberat dan bias.
Proses ini akan diteruskan sehinggalah kita mendapat pemberat dan bias yang paling sesuai dengan julat kesilapan yang paling rendah.
Praktikal
Mari kita bina jaringan neural kita. Apa yang kita akan bina ada satu jaringan neural dengan satu lapisan input, dua lapisan tersembunyi (yang mempunyai 10 nod) dan 1 lapisan output. Lapisan input akan mengambil 784 nilai vektor (yang disusun dalam matriks dan mewakili nilai piksel), dan akan melalui dua lapisan tersembunyi yang masing-masing membawa pemberat dan bias. Matriks input ini akan mengalami transformasi linear dan juga fungsi pengaktifan bagi menghasilkan nilai output yang sepatutnya membawa label yang tepat. Seluruh proses ini kita namakan sebagai forward pass.
Kemuadian label yang kita dapat perlulah ditolak nilainya dengan label yang sebenar bagi mendapatkan nilai perbezaan. Nilai perbezaan inilah yang kita panggil sebagai kerugian. Kerugian ini akan dihantar balik ke kiri melalu backward pass bagi memperkemaskinikan nilai pemberat dan bias.
Kita akan mulakan dengan meletakkan nilai awal untuk semua parameter kita (pemberat dan bias)
def init_params():
"""
Fungsi ini kita buat bagi menghasilkan nilai awal (initial value) untuk pemberat dan bias kita.
Kita tahu yang kita ada dua lapisan tersembunyi, jadi kita akan hasilkan 4 matriks yang mewakili
2 pemberat dan 2 bias. Matriks tersebut akan dihasilkan secara rambang dengan julat nombor -0.5 ke 0.5
dan disusun mengikut nilai input yang kita ada (784 pixel).
4 matriks tersebut adalah W1,b1,W2,b2
"""
W1=np.random.rand(10,784)-0.5
b1=np.random.rand(10,1)-0.5
W2=np.random.rand(10,10)-0.5
b2=np.random.rand(10,1)-0.5
return W1,b1,W2,b2
Kemudian kita buat kod untuk fungsi pengaktifan pula
def ReLU(Z):
"""
Ini merupakan fungsi pengaktifan ReLU, Rectified Linear Unit yang menukarkan input fungsi ini
(output dari transformasi linear) kepada hanya nilai positif
"""
return np.maximum(Z,0)
def softmax(Z):
"""
Ini merupakan fungsi pengaktifan softmax, yang menukarkan nilai input
kepada nilai kebarangkalian (dari 0-1). Semakin besar nilai input, semakin
dekat nilainya dengan 1. Semakin kecil nilai input, semakin dekat nilainya dengan 0
"""
A=np.exp(Z)/sum(np.exp(Z))
return A
def deriv_ReLU(Z):
"""
Fungsi pegaktifan ReLU (derivatif) untuk backward pass
"""
return Z>0Kita kod pula forward pass.ihat di sini kita melalui transformasi linear dua kali dan menggunakan dua fungsi pengaktifan yang berbeza (ReLU dan juga softmax). ReLU melalu lapisan tersembunyi pertama dan softmax melalui lapisan tersembunyi kedua. Kita perlukan nilai kebarangkalian di bahagian output bagi membolehkan kita melihat perbezaan antara label sebenar dan label yang kita hasilkan.
def forward_pass(W1,b1,W2,b2,X):
"""
Ini adalah fungsi yang kita gunakan untuk forward pass. Lihat di sini kita melalui transformasi linear
dua kali dan menggunakan dua fungsi pengaktifan yang berbeza (ReLU dan juga softmax). ReLU melalu lapisan tersembunyi
pertama dan softmax melalui lapisan tersembunyi kedua. Kita perlukan nilai kebarangkalian di bahagian output
bagi membolehkan kita melihat perbezaan antara label sebenar dan label yang kita hasilkan
"""
#lapisan tersembunyi pertama (transformasi linear)
Z1=W1.dot(X)+b1
#fungsi pengaktifan ReLU
A1=ReLU(Z1)
#lapisan tersembunyi kedua (transformasi linear)
Z2=W2.dot(A1)+b2
#fungsi pengaktifan softmax
A2=softmax(Z2)
return Z1,A1,Z2,A2Sebelum kita mengira kerugian, perlulah untuk kita tukarkan dulu nilai y mengikut format yang sesuai.
def one_hot(y):
"""
Fungsi ini menukarkan (encode) label kita (nilai 0 sampai 9) kepada satu bentuk matriks yang diwakili dengan nilai 1 dan 0.
Fungsi ini membolehkan kita memastikan proses yang lebih tersusun disebabkan output kita datang dalam bentuk matriks (maka
yang hendak ditolak pun kena dalam bentuk matriks).
"""
#establish dulu matriks nilai 0 dengan saiz mengikut jumlah kelas label kita
one_hot_y= np.zeros((y.size,y.max()+1))
#letakkan nilai 1 pada matriks 0 tadi mengikut pada kelas label
one_hot_y[np.arange(y.size),y]=1
#kita transpose matriks kita (terbalikkan)
one_hot_y=one_hot_y.T
return one_hot_yDi bawah pula, kita akan kod algoritma backward pass
def backward_pass(Z1,A1,Z2,A2,W1,W2,X,y):
"""
Fungsi backward pass. Perhatikan yang ia perlu melalu proses yang terbalik dari forward pass.
Kita perlu mendapatkan dulu nilai kerugian dan menggunakan nilai tersebut untuk mendapatkan
nilai perbezaan antara berat dan bias kita dan juga output.
"""
#kita encode dulu nilai y kita
one_hot_y=one_hot(y)
#dapatkan nilai kerugian lapisan kedua
dZ2=A2-one_hot_y
#dapatkan nilai derivatif (perbezaan) bias dan pemberat lapisan kedua
dW2=1/m*dZ2.dot(A1.T)
db2=1/m*np.sum(dZ2)
#dapatkan nilai kerugian lapisan pertama
dZ1 = W2.T.dot(dZ2) * deriv_ReLU(Z1)
#dapatkan nilai derivatif (perbezaan) bias dan pemberat lapisan pertaman
dW1 = 1 / m * dZ1.dot(X.T)
db1 = 1 / m * np.sum(dZ1)
return dW1, db1, dW2, db2Dan dari nilai derivatif yang kita dapat, kita boleh gunakannya untuk mempermaskini nilai pemberat dan bias
def update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha):
"""
Kita gunakan fungsi ini untuk memperkemaskini nilai pemberat dan bias melalui backward pass.
Alpha adalaha mewakili kadar pembelajaran (learning rate)
"""
W1 = W1 - alpha * dW1
b1 = b1 - alpha * db1
W2 = W2 - alpha * dW2
b2 = b2 - alpha * db2
return W1, b1, W2, b2Ok, akhirnya jaringan neural kita sudah siap. Sekarang sudah tiba masanya untuk kita melatih jaringan neural kita. Anggaplah ia sebagai budak kecil yang masih lagi baru hendak belajar macam mana hendak mengenal tulisan.
Latihan dan Ujian
Mari kita buat kod untuk melatih jaringan neural yang kita sudah bina. Kita boleh pastikan fungsi yang dibuat nanti boleh menguji ketepatan ramalan yang dibuat jaringan neural dan juga mengulangi proses forward dan backward pass mengikut jumlah iterasi. Kodnya seperti di bawah:
def get_predictions(A2):
"""
Fungsi untuk mendapatkan nilai ramalan
"""
return np.argmax(A2, 0)
def get_accuracy(predictions, Y):
"""
Fungsi untuk mengira ketepatan ramalan kita
"""
print(predictions, Y)
return np.sum(predictions == Y) / Y.size
def train(X, Y, alpha, iterations):
"""
Fungsi untuk melatih jaringan neural kita. Boleh lihat kita gunakan semua fungsi yang kita dah buat dalam satu bentuk iterasi
yang kita boleh setkan.
"""
#kita hasilkan nilai awal untuk pemberat dan bias
W1, b1, W2, b2 = init_params()
#kita lakukan iterasi dengan forward dan backward pass
for i in range(iterations):
Z1, A1, Z2, A2 = forward_pass(W1, b1, W2, b2, X)
dW1, db1, dW2, db2 = backward_pass(Z1, A1, Z2, A2, W1, W2, X, Y)
W1, b1, W2, b2 = update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha)
if i % 100 == 0:
print("Iteration: ", i)
predictions = get_predictions(A2)
print(get_accuracy(predictions, Y))
return W1, b1, W2, b2Mari kita mula melatih, hanya perlu jalankan kod di bawah, dan set jumlah iterasi mengikut kesesuaian.Untuk ini, saya meletak jumlah iterasi pada 1000 (lebih banyak mungkin lebih baik, tapi ia bergantung pada keadaan) dan kadar pembelajaran pada 0.10
W1, b1, W2, b2 = train(X_train, y_train, 0.10, 1000)Selepas melatih, kita boleh melihat sendiri ketepatan jaringan neural kita seperti di bawah:
Kita boleh lihat yang latihan kita hampir berjaya, ramalan yang dibuat oleh jaringan neural kita mendapat ketepatan (accuracy) sebanyak 88%. Mari kita cuba lihat sama ada jaringan neural kita mampu mengenal pasti gambar tulisan.
Mari kita menguji
Mari kita buat fungsi untuk menguji jaringan neural kita yang sudah dilatih.
#kita buat fungsi untuk menguji jaringan neural kita
def make_predictions(X,W1,b1,W2,b2):
"""
Fungsi untuk membuat ramalan mudah bila diberi nilai X
"""
_, _, _, A2 = forward_pass(W1,b1,W2,b2,X)
predictions = get_predictions(A2)
return predictions
def test_prediction(index,W1,b1,W2,b2):
"""
Fungsi untuk menguji ramalan yang kita sudah buat sama ada tepat bersandarkan pada imej
ataupun tidak
"""
current_image = X_train[:,index,None]
prediction = make_predictions(current_image,W1,b1,W2,b2)
label = y_train[index]
print("Prediction:",prediction)
print("Label:",label)
current_image = current_image.reshape((28,28))*255
plt.gray()
plt.imshow(current_image,interpolation = 'nearest')
plt.show()Dan bila kita menguji, dengan mengambil nilai index datakita secara rambang, kita boleh lihat hasil seperti di bawah:
test_prediction(11,W1,b1,W2,b2)Nampaknya jaringan neural kita sudahpun dapat mengenal tulisan.
Tapi kadang-kadang, tak adalah tepat mana, boleh lihat seperti di bawah:
Nombor 9 dikatanya 7, ada ke patut?
Tapi bolehlah, Alhamdulillah, jaringan neural kita sudahpun terlatih, dan boleh mengenal tulisan.
Jaringan neural hanyalah bentuk paling asas sekali. Lepas ni kita akan cuba tengok model yang lebih kompleks, model yang boleh mengenalpasti gambar. Kita boleh tengok CNN (Convolutional Neural Network).











