Распознавание лиц с помощью сиамских сетей

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Сиамская нейросеть — один из простейших и наиболее популярных алгоритмов однократного обучения. Методики, при которой для каждого класса берётся лишь по одному учебному примеру. Таким образом, сиамская сеть обычно используется в приложениях, где в каждом классе есть не так много единиц данных.
Допустим, нам нужно сделать модель распознавания лиц для организации, в которой работает около 500 человек. Если делать такую модель с нуля на основе свёрточной нейросети (Convolutional Neural Network (CNN)), то для обучения модели и достижения хорошей точности распознавания нам понадобится много изображений каждого из этих 500 человек. Но очевидно, что такой датасет нам не собрать, поэтому не стоит делать модель на основе CNN или иного алгоритма глубокого обучения, если у нас нет достаточного количества данных. В подобных случаях можно воспользоваться сложным алгоритмом однократного обучения, наподобие сиамской сети, которая может обучаться на меньшем количестве данных. По сути, сиамские сети состоят из двух симметричных нейросетей, с одинаковыми весами и архитектурой, которые в конце объединяются и используют функцию энергии — E. Давайте разберёмся в сиамской сети, создав на её основе модель распознавания лиц. Мы научим её определять, когда два лица одинаковы, а когда нет. И для начала воспользуемся датасетом AT&T Database of Faces, который можно скачать с сайта компьютерной лаборатории Кембриджского Университета. Скачали, распаковали и видим папки от s1 до s40:
В каждой папке лежит 10 разных фотографий какого-то одного человека, снятых с разных углов. Вот содержимое папки s1:
А вот что находится в папке s13:
Сиамским сетям нужно подавать на вход парные значения с маркировками, поэтому давайте создадим такие наборы. Возьмём из одной папки две случайные фотографии и пометим как «подлинную» пару (genuine). Затем возьмем две фотографии из разных папок и пометим как «ложную» пару (imposite):
Распределив все фотографии по маркированным парам, займемся обучением сети. Из каждой пары мы одну фотографию передадим сети А, а вторую — сети Б. Обе сети всего лишь извлекают векторы свойств. Для этого воспользуемся двумя свёрточными слоями с активациями блоков линейной ректификации (rectified linear unit (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"(^P5s(?:s*#.*[  ])*"         b"(d+)s(?:s*#.*[  ])*"         b"(d+)s(?:s*#.*[  ])*"         b"(d+)s(?:s*#.*[  ]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 и получим NumPy-массив:

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

Теперь определим функцию get_data, которая будет генерировать данные. Напомню, что сиамским сетям нужно подавать пары данных (genuine и imposite) с двоичной маркировкой.

Сначала прочитаем изображения (img1, img2) из одной директории, сохраним их в массиве x_genuine_pair, присвоим y_genuine значение 1. Затем прочитаем изображения (img1, img2) из разных директорий, сохраним их в паре 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 фотографий, из которых собрано 10 000 подлинных и 10 000 ложных пар:

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-активаций и слоя с определением максимального значения (max pooling) после flat-слоя:

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 — это векторы свойств пары изображений. Давайте передадим их функции энергии для вычисления дистанции между ними. А в качестве функции энергии воспользуемся евклидовым расстоянием:

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 function и скомпилируем модель:

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 

Выводы

В этом руководстве мы научились создавать модели распознавания лиц на основе сиамских сетей. Архитектура таких сетей состоит из двух одинаковых нейросетей, имеющих один вес и структуру, а результаты их работы передаются в одну функцию энергии — таким образом определяется одинаковость входных данных. Подробнее о метаобучении с помощью Python читайте в книге Hands-On Meta-Learning with Python.

Мой комментарий

Знание сиамский сетей на данный момент необходимо при работе с изображениями. Существует много подходов к обучению сетей в условиях небольших выборок, генерация новых данных, методы аугментации. Данный способ позволяет относительно «дешево» достичь неплохих результатов, вот более классический пример сиамской сети на «Hello world» для нейронных сетей — датасете MNIST keras.io/examples/mnist_siamese


Источник: habr.com

Комментарии: