Pengenalan Wajah Menggunakan Jaringan Siam



Jaringan saraf siam adalah salah satu algoritma pembelajaran tunggal paling sederhana dan paling populer. Metode di mana untuk setiap kelas diambil hanya satu studi kasus. Dengan demikian, jaringan Siam biasanya digunakan dalam aplikasi di mana tidak ada banyak unit data di setiap kelas.

Misalkan kita perlu membuat model pengenalan wajah untuk organisasi yang mempekerjakan sekitar 500 orang. Jika kita membuat model seperti itu dari awal berdasarkan convolutional neural network (CNN), maka untuk melatih model dan mencapai akurasi pengenalan yang baik, kita akan memerlukan banyak gambar dari masing-masing 500 orang ini. Tetapi jelas bahwa kami tidak dapat mengumpulkan dataset tersebut, jadi Anda tidak boleh membuat model berdasarkan CNN atau algoritma pembelajaran mendalam lainnya jika kami tidak memiliki cukup data. Dalam kasus seperti itu, Anda dapat menggunakan algoritma pembelajaran satu kali yang kompleks, seperti jaringan Siam, yang dapat dilatih dengan lebih sedikit data.

Bahkan, jaringan Siam terdiri dari dua jaringan saraf simetris, dengan bobot dan arsitektur yang sama, yang pada akhirnya menggabungkan dan menggunakan fungsi energi - E.
Mari kita lihat jaringan siam, menciptakan model pengenalan wajah berdasarkan itu. Kami akan mengajarinya untuk menentukan kapan dua wajah adalah sama dan kapan tidak. Dan sebagai permulaan, kita akan menggunakan dataset AT&T Database of Faces, yang dapat diunduh dari situs web lab komputer Universitas Cambridge .

Unduh, buka paket dan lihat folder dari s1 ke s40:



Setiap folder berisi 10 foto berbeda dari satu orang yang diambil dari sudut yang berbeda. Berikut ini isi folder s1:



Dan inilah yang ada di folder s13:



Jaringan Siam perlu memasukkan nilai berpasangan dengan tanda, jadi mari kita buat set seperti itu. Ambil dua foto acak dari folder yang sama dan tandai sebagai pasangan "asli". Lalu kami mengambil dua foto dari folder yang berbeda dan menandainya sebagai pasangan "salah" (mustahil):



Setelah membagikan semua foto menjadi pasangan yang ditandai, kami akan mempelajari jaringan. Dari setiap pasangan, kami akan mentransfer satu foto ke jaringan A, dan yang kedua ke jaringan B. Kedua jaringan hanya mengekstrak vektor properti. Untuk melakukan ini, kami menggunakan dua lapisan konvolusional dengan aktivasi rectified linear unit (ReLU). Setelah mempelajari sifat-sifat, kami mentransfer vektor yang dihasilkan oleh kedua jaringan ke fungsi energi yang memperkirakan kesamaan. Kami menggunakan jarak Euclidean sebagai fungsi.

Sekarang pertimbangkan semua langkah ini secara lebih rinci.


Pertama, impor perpustakaan yang diperlukan:

import re import numpy as np from PIL import Image from sklearn.model_selection import train_test_split from keras import backend as K from keras.layers import Activation from keras.layers import Input, Lambda, Dense, Dropout, Convolution2D, MaxPooling2D, Flatten from keras.models import Sequential, Model from keras.optimizers import RMSprop 

Sekarang kita mendefinisikan fungsi untuk membaca gambar input. Fungsi read_image mengambil gambar dan mengembalikan array NumPy:

 def read_image(filename, byteorder='>'): #first we read the image, as a raw file to the buffer with open(filename, 'rb') as f: buffer = f.read() #using regex, we extract the header, width, height and maxval of the image header, width, height, maxval = re.search( b"(^P5\s(?:\s*#.*[\r\n])*" b"(\d+)\s(?:\s*#.*[\r\n])*" b"(\d+)\s(?:\s*#.*[\r\n])*" b"(\d+)\s(?:\s*#.*[\r\n]\s)*)", buffer).groups() #then we convert the image to numpy array using np.frombuffer which interprets buffer as one dimensional array return np.frombuffer(buffer, dtype='u1' if int(maxval) 

Misalnya, buka foto ini:

 Image.open("data/orl_faces/s1/1.pgm") 



Kami meneruskannya ke fungsi read_image dan mendapatkan array NumPy:

 img = read_image('data/orl_faces/s1/1.pgm') img.shape (112, 92) 

Sekarang kita mendefinisikan fungsi get_data yang akan menghasilkan data. Izinkan saya mengingatkan Anda bahwa jaringan Siam perlu mengirimkan pasangan data (asli dan tidak penting) dengan tanda biner.

Pertama, baca gambar ( img1 , img2 ) dari satu direktori, simpan di dalam array x_genuine_pair, atur y_genuine ke 1 . Kemudian kita membaca gambar ( img1 , img2 ) dari direktori yang berbeda, menyimpannya di pasangan x_imposite, dan mengatur y_imposite ke 0 .

x_genuine_pair dan x_imposite di X , dan y_genuine dan y_imposite di Y :

 size = 2 total_sample_size = 10000 def get_data(size, total_sample_size): #read the image image = read_image('data/orl_faces/s' + str(1) + '/' + str(1) + '.pgm', 'rw+') #reduce the size image = image[::size, ::size] #get the new size dim1 = image.shape[0] dim2 = image.shape[1] count = 0 #initialize the numpy array with the shape of [total_sample, no_of_pairs, dim1, dim2] x_geuine_pair = np.zeros([total_sample_size, 2, 1, dim1, dim2]) # 2 is for pairs y_genuine = np.zeros([total_sample_size, 1]) for i in range(40): for j in range(int(total_sample_size/40)): ind1 = 0 ind2 = 0 #read images from same directory (genuine pair) while ind1 == ind2: ind1 = np.random.randint(10) ind2 = np.random.randint(10) # read the two images img1 = read_image('data/orl_faces/s' + str(i+1) + '/' + str(ind1 + 1) + '.pgm', 'rw+') img2 = read_image('data/orl_faces/s' + str(i+1) + '/' + str(ind2 + 1) + '.pgm', 'rw+') #reduce the size img1 = img1[::size, ::size] img2 = img2[::size, ::size] #store the images to the initialized numpy array x_geuine_pair[count, 0, 0, :, :] = img1 x_geuine_pair[count, 1, 0, :, :] = img2 #as we are drawing images from the same directory we assign label as 1. (genuine pair) y_genuine[count] = 1 count += 1 count = 0 x_imposite_pair = np.zeros([total_sample_size, 2, 1, dim1, dim2]) y_imposite = np.zeros([total_sample_size, 1]) for i in range(int(total_sample_size/10)): for j in range(10): #read images from different directory (imposite pair) while True: ind1 = np.random.randint(40) ind2 = np.random.randint(40) if ind1 != ind2: break img1 = read_image('data/orl_faces/s' + str(ind1+1) + '/' + str(j + 1) + '.pgm', 'rw+') img2 = read_image('data/orl_faces/s' + str(ind2+1) + '/' + str(j + 1) + '.pgm', 'rw+') img1 = img1[::size, ::size] img2 = img2[::size, ::size] x_imposite_pair[count, 0, 0, :, :] = img1 x_imposite_pair[count, 1, 0, :, :] = img2 #as we are drawing images from the different directory we assign label as 0. (imposite pair) y_imposite[count] = 0 count += 1 #now, concatenate, genuine pairs and imposite pair to get the whole data X = np.concatenate([x_geuine_pair, x_imposite_pair], axis=0)/255 Y = np.concatenate([y_genuine, y_imposite], axis=0) return X, Y 

Sekarang kita akan menghasilkan data dan memeriksa ukurannya. Kami memiliki 20.000 foto, yang dikumpulkan dari 10.000 pasangan asli dan 10.000 pasangan palsu:

 X, Y = get_data(size, total_sample_size) X.shape (20000, 2, 1, 56, 46) Y.shape (20000, 1) 

Kami akan membagikan seluruh rangkaian informasi: 75% pasangan akan mengikuti pelatihan, dan 25% - untuk menguji:
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=.25)

Sekarang buat jaringan siam. Pertama kita mendefinisikan jaringan inti - itu akan menjadi jaringan saraf convolutional untuk mengekstraksi properti. Buat dua lapisan konvolusional menggunakan aktivasi ReLU dan lapisan dengan penyatuan maksimum setelah lapisan datar:

 def build_base_network(input_shape): seq = Sequential() nb_filter = [6, 12] kernel_size = 3 #convolutional layer 1 seq.add(Convolution2D(nb_filter[0], kernel_size, kernel_size, input_shape=input_shape, border_mode='valid', dim_ordering='th')) seq.add(Activation('relu')) seq.add(MaxPooling2D(pool_size=(2, 2))) seq.add(Dropout(.25)) #convolutional layer 2 seq.add(Convolution2D(nb_filter[1], kernel_size, kernel_size, border_mode='valid', dim_ordering='th')) seq.add(Activation('relu')) seq.add(MaxPooling2D(pool_size=(2, 2), dim_ordering='th')) seq.add(Dropout(.25)) #flatten seq.add(Flatten()) seq.add(Dense(128, activation='relu')) seq.add(Dropout(0.1)) seq.add(Dense(50, activation='relu')) return seq 


Kemudian kami akan mentransfer sepasang gambar dari jaringan inti, yang akan mengembalikan representasi vektor, mis. Vektor properti:

 input_dim = x_train.shape[2:] img_a = Input(shape=input_dim) img_b = Input(shape=input_dim) base_network = build_base_network(input_dim) feat_vecs_a = base_network(img_a) feat_vecs_b = base_network(img_b) 

feat_vecs_a dan feat_vecs_b adalah vektor properti dari sepasang gambar. Mari kita lewati fungsi energi mereka untuk menghitung jarak di antara mereka. Dan sebagai fungsi energi, kami menggunakan jarak Euclidean:

 def euclidean_distance(vects): x, y = vects return K.sqrt(K.sum(K.square(x - y), axis=1, keepdims=True)) def eucl_dist_output_shape(shapes): shape1, shape2 = shapes return (shape1[0], 1) distance = Lambda(euclidean_distance, output_shape=eucl_dist_output_shape)([feat_vecs_a, feat_vecs_b]) 

Kami menetapkan jumlah zaman ke 13, menerapkan properti RMS untuk optimisasi dan mendeklarasikan model:

 epochs = 13 rms = RMSprop() model = Model(input=[input_a, input_b], output=distance) 

Sekarang kita mendefinisikan fungsi function contrastive_loss kerugian dan mengkompilasi model:

 def contrastive_loss(y_true, y_pred): margin = 1 return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0))) model.compile(loss=contrastive_loss, optimizer=rms) 

Mari kita pelajari modelnya:

 img_1 = x_train[:, 0] img_2 = x_train[:, 1] model.fit([img_1, img_2], y_train, validation_split=.25, batch_size=128, verbose=2, nb_epoch=epochs) 

Anda melihat bagaimana penurunan kerugian seiring berlalunya waktu:

 Train on 11250 samples, validate on 3750 samples Epoch 1/13 - 60s - loss: 0.2179 - val_loss: 0.2156 Epoch 2/13 - 53s - loss: 0.1520 - val_loss: 0.2102 Epoch 3/13 - 53s - loss: 0.1190 - val_loss: 0.1545 Epoch 4/13 - 55s - loss: 0.0959 - val_loss: 0.1705 Epoch 5/13 - 52s - loss: 0.0801 - val_loss: 0.1181 Epoch 6/13 - 52s - loss: 0.0684 - val_loss: 0.0821 Epoch 7/13 - 52s - loss: 0.0591 - val_loss: 0.0762 Epoch 8/13 - 52s - loss: 0.0526 - val_loss: 0.0655 Epoch 9/13 - 52s - loss: 0.0475 - val_loss: 0.0662 Epoch 10/13 - 52s - loss: 0.0444 - val_loss: 0.0469 Epoch 11/13 - 52s - loss: 0.0408 - val_loss: 0.0478 Epoch 12/13 - 52s - loss: 0.0381 - val_loss: 0.0498 Epoch 13/13 - 54s - loss: 0.0356 - val_loss: 0.0363 

Dan sekarang mari kita uji model pada data uji:

 pred = model.predict([x_test[:, 0], x_test[:, 1]]) 

Tentukan fungsi untuk menghitung akurasi:

 def compute_accuracy(predictions, labels): return labels[predictions.ravel() 

Kami menghitung akurasinya:

 compute_accuracy(pred, y_test) 0.9779092702169625 

Kesimpulan


Dalam panduan ini, kami belajar cara membuat model pengenalan wajah berdasarkan jaringan Siam. Arsitektur jaringan tersebut terdiri dari dua jaringan saraf identik yang memiliki berat dan struktur yang sama, dan hasil pekerjaan mereka ditransfer ke fungsi energi tunggal - ini menentukan identitas data input. Untuk informasi lebih lanjut tentang meta-learning menggunakan Python, lihat Hands-On Meta-Learning with Python.

Komentar saya


Pengetahuan tentang jaringan Siam saat ini diperlukan saat bekerja dengan gambar. Ada banyak pendekatan untuk melatih jaringan dalam sampel kecil, pembuatan data baru, metode augmentasi. Metode ini memungkinkan relatif "murah" untuk mencapai hasil yang baik, berikut adalah contoh yang lebih klasik dari jaringan Siam di "Hello world" untuk jaringan saraf - dataset MNIST keras.io/examples/mnist_siamese

Source: https://habr.com/ru/post/id465279/


All Articles