التعرف على الوجوه باستخدام شبكات سيامي



تعتبر الشبكة العصبية السيامية واحدة من أبسط خوارزميات التعلم الفردية وأكثرها شعبية. الأساليب التي تؤخذ لكل فصل دراسة حالة واحدة فقط. وبالتالي ، عادةً ما يتم استخدام شبكة Siamese في التطبيقات التي لا توجد فيها وحدات بيانات كثيرة في كل فئة.

لنفترض أننا بحاجة إلى إنشاء نموذج للتعرف على الوجوه لمنظمة توظف حوالي 500 شخص. إذا قمت بإنشاء مثل هذا النموذج من نقطة الصفر بناءً على الشبكة العصبية التلافيفية (CNN) ، ثم لتدريب النموذج وتحقيق دقة جيدة في التعرف ، فسنحتاج إلى العديد من الصور لكل 500 شخص. لكن من الواضح أننا لا نستطيع جمع مجموعة البيانات هذه ، لذلك يجب ألا تصنع نموذجًا يستند إلى شبكة CNN أو أي خوارزمية أخرى للتعلم العميق إذا لم يكن لدينا بيانات كافية. في مثل هذه الحالات ، يمكنك استخدام خوارزمية التعلم المعقدة لمرة واحدة ، مثل شبكة Siamese ، والتي يمكن تدريبها على بيانات أقل.

في الواقع ، تتكون شبكات Siamese من شبكتين عصبيتين متماثلتين ، لهما نفس الأوزان والهندسة المعمارية ، التي تجمع في النهاية وتستخدم وظيفة الطاقة - E.
دعونا ننظر إلى شبكة سيامي ، وخلق نموذج التعرف على الوجوه على أساس ذلك. سوف نعلمها تحديد متى يكون وجهان متماثلان ومتى لا. وبالنسبة للمبتدئين ، سوف نستخدم مجموعة بيانات AT&T Database of Faces ، والتي يمكن تنزيلها من موقع مختبر الكمبيوتر بجامعة Cambridge .

قم بتنزيل المجلدات وإزالتها ورؤيتها من s1 إلى s40:



يحتوي كل مجلد على 10 صور مختلفة لشخص واحد مأخوذة من زوايا مختلفة. فيما يلي محتويات المجلد s1:



وإليك ما يوجد في مجلد s13:



تحتاج شبكات Siamese إلى إدخال قيم مقترنة بعلامات ، لذلك دعونا ننشئ مثل هذه المجموعات. التقط صورتين عشوائيتين من نفس المجلد وقم بتمييزهما كزوج "أصلي". ثم نلتقط صورتين من مجلدات مختلفة ونضع علامة عليها كزوج "خطأ" (غير صحيح):



بعد توزيع جميع الصور على أزواج ملحوظة ، سنقوم بدراسة الشبكة. من كل زوج ، سننقل صورة واحدة إلى الشبكة A ، والثانية إلى الشبكة B. كلا الشبكتين تستخرج متجهات الخصائص فقط. للقيام بذلك ، نستخدم طبقتين تلافيفيين مع تنشيط الوحدة الخطية المصححة (ReLU). بعد دراسة الخصائص ، نقوم بنقل المتجهات الناتجة عن كلتا الشبكتين إلى دالة طاقة تقدر التشابه. نستخدم المسافة الإقليدية كدالة.

الآن النظر في كل هذه الخطوات بمزيد من التفصيل.


أولاً ، قم باستيراد المكتبات الضرورية:

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 

الآن نحدد وظيفة لقراءة الصور المدخلة. تقوم وظيفة read_image بالتقاط صورة وإرجاع صفيف 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) 

على سبيل المثال ، افتح هذه الصورة:

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



نقوم read_image الدالة read_image والحصول على مجموعة NumPy:

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

الآن نحدد وظيفة get_data التي ستنشئ البيانات. اسمحوا لي أن أذكركم بأن شبكات Siamese تحتاج إلى تقديم أزواج من البيانات (أصلية وغير دقيقة) مع وضع علامة ثنائية.

أولاً ، اقرأ الصور ( img1 ، img2 ) من دليل واحد ، واحفظها في صفيف x_genuine_pair, اضبط y_genuine على 1 . ثم نقرأ الصور ( img1 ، img2 ) من أدلة مختلفة ، x_imposite, زوج x_imposite, y_imposite على 0 .

لسَلسَلة x_genuine_pair و x_imposite في X ، و y_genuine و y_imposite في 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 

الآن سنقوم بإنشاء البيانات والتحقق من حجمها. لدينا 20.000 صورة ، تم جمع 10000 منها من خلال 10000 زوج أصلي:

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

سوف نشارك مجموعة كاملة من المعلومات: 75 ٪ من أزواج سوف تذهب إلى التدريب ، و 25 ٪ - لاختبار:
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=.25)

الآن إنشاء شبكة سيامي. أولاً نحدد الشبكة الأساسية - ستكون شبكة عصبية تلافيفية لاستخراج الخصائص. قم بإنشاء طبقتين تلافيفيًا باستخدام تنشيطات ReLU وطبقة ذات تجمع أقصى بعد طبقة مسطحة:

 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 


بعد ذلك ، سننقل زوجًا من صور الشبكة الأساسية ، والتي ستُرجع تمثيلات المتجهات ، أي متجهات الممتلكات:

 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 و feat_vecs_b هما feat_vecs_b لخاصية زوج من الصور. لنقم بتمرير وظائف الطاقة الخاصة بهم لحساب المسافة بينهما. وكدالة للطاقة ، نستخدم المسافة الإقليدية:

 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]) 

قمنا بتعيين عدد الحلقات إلى 13 ، ونطبق خاصية RMS لتحسينها ونعلن النموذج:

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

الآن نقوم بتعريف دالة الخسارة دالة contrastive_loss وتجميع النموذج:

 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) 

دعنا ندرس النموذج:

 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) 

ترى كيف تنخفض الخسائر مع مرور العصور:

 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 

والآن لنختبر النموذج على بيانات الاختبار:

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

حدد وظيفة لحساب الدقة:

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

نحسب دقة:

 compute_accuracy(pred, y_test) 0.9779092702169625 

النتائج


في هذا الدليل ، تعلمنا كيفية إنشاء نماذج للتعرف على الوجوه استنادًا إلى شبكات Siamese. تتكون بنية هذه الشبكات من شبكتين عصبيتين متطابقتين لهما نفس الوزن والهيكل ، ويتم نقل نتائج عملها إلى وظيفة طاقة واحدة - وهذا يحدد هوية بيانات الإدخال. لمزيد من المعلومات حول التعليم التلوي باستخدام Python ، راجع التدريب العملي على Meta-Learning مع Python.

تعليقي


مطلوب حاليا معرفة شبكات سيامي عند العمل مع الصور. هناك العديد من الأساليب لشبكات التدريب في عينات صغيرة ، وتوليد البيانات الجديدة ، وطرق التعزيز. تتيح هذه الطريقة "رخيصة" نسبيًا لتحقيق نتائج جيدة ، وإليك مثال كلاسيكي على شبكة سيامي على "Hello world" للشبكات العصبية - dataset MNIST keras.io/examples/mnist_siamese

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


All Articles