Первая версия от 5 марта 2017 г.

Несколько месяцев назад я начал экспериментировать с различными инструментами глубокого обучения. Что касается производительности, я был очень впечатлен Керасом. Под производительностью я подразумеваю, что я редко трачу много времени на ошибку.

Этот пост показывает, как легко перенести модель в Keras.

Я буду использовать модель VGG-Face в качестве примера. Модель объяснена в этой статье (Deep Face Recognition, Visual Geometry Group), а подогнанные веса доступны как MatConvNet здесь.

Вкратце, модель VGG-Face - это та же архитектура NeuralNet, что и модель VGG16, используемая для идентификации 1000 классов объектов в конкурсе ImageNet. Название VGG16 просто указывает, что модель произошла из Группы визуальной геометрии и что это 16 обучаемых слоев. Основное различие между моделями VGG16-ImageNet и VGG-Face - это набор калиброванных весов, поскольку обучающие наборы были разными.

Полный код доступен в этой папке GitHub.

Архитектура модели

Архитектура модели (см. Стр. 6, таблица 3) представляет собой линейную последовательность преобразований слоев следующих типов:

  • Активации Convolution + ReLU
  • MaxPooling
  • softmax

Поскольку в последовательности есть повторяющийся образец слоев, мы факторизуем его как функцию строительного блока:

# building block of the VGG model :
def convblock(cdim, nb, bits=3):
    L = []
    
    for k in range(1,bits+1):
        convname = 'conv'+str(nb)+'_'+str(k)
        L.append( Convolution2D(cdim, 3, 3,
                                border_mode='same',
                                activation='relu',
                                name=convname) )
    
    L.append( MaxPooling2D((2, 2), strides=(2, 2)) )
    
    return L

Мы называем слой свертки модели, чтобы мы могли легко получить к нему доступ при загрузке весов.

Инициализация модели

from keras.models import Sequential
mdl = Sequential()
# Trick :
# dummy-permutation = identity to specify input shape
# index starts at 1 as 0 is the sample dimension
mdl.add( Permute((1,2,3), input_shape=(3,224,224)) ) 

Модель тела

for l in convblock(64, 1, bits=2):
    mdl.add(l)

for l in convblock(128, 2, bits=2):
    mdl.add(l)
        
for l in convblock(256, 3, bits=3):
    mdl.add(l)
            
for l in convblock(512, 4, bits=3):
    mdl.add(l)
            
for l in convblock(512, 5, bits=3):
    mdl.add(l)

Модель головы

mdl.add( Convolution2D(4096, 7, 7, activation='relu', name='fc6') )
mdl.add( Convolution2D(4096, 1, 1, activation='relu', name='fc7') )
mdl.add( Convolution2D(2622, 1, 1, name='fc8') )
mdl.add( Flatten() )
mdl.add( Activation('softmax') )

Кормление модельных гирь

После того, как модель построена, мы можем установить веса слоев на значения, обученные на более крупном наборе данных. Обучение модели с нуля действительно представляет собой сложность в глубоком обучении. В идеале мы можем найти веса для Keras напрямую, но часто это не так. Для модели VGG веса, которые я нашел, взяты из реализации MatConvNet, то есть в формате файла Matlab.

К счастью, функция scipy.io.loadmat позволяет загрузить такой файл в Python.

from scipy.io import loadmat
data = loadmat('vgg_face_matconvnet/data/vgg_face.mat',
               matlab_compatible=False,
               struct_as_record=False)
net = data['net'][0,0]
l = net.layers
description = net.classes[0,0].description

Извлечение данных Matlab в правильном формате немного утомительно, но весь смысл работы с записной книжкой Jupyter заключается в том, что мы можем взаимодействовать с данными. После 15 минут работы с массивами я нашел следующий рецепт преобразования веса, в котором основная уловка состоит в том, чтобы «перевернуть» ось, соответствующую окнам ядра свертки:

prmt = (3,2,0,1)
l_weights = matlab_layer.weights[0,0]
f_l_weights = l_weights.transpose(prmt)
f_l_weights = np.flip(f_l_weights, 2) # reverse axis ordering
f_l_weights = np.flip(f_l_weights, 3) # reverse axis ordering
l_bias = matlab_layer.weights[0,1]
keras_layer.set_weights([f_l_weights, l_bias[:,0]])

Использование модели

После того, как модель будет полностью определена (архитектура и значение веса), мы можем использовать ее для идентификации лиц с помощью метода прогноз.

Поскольку веса VGG были обучены на квадратных / центрированных изображениях лиц, мы тестируем их на изображении, которое было вручную обрезано:

imarr = np.array(cropped_image).astype(np.float32)
# move color dimension on first axis :
imarr = imarr.transpose((2,0,1)) 
# turn image into a 1-element batch :
imarr = np.expand_dims(imarr, axis=0)
# prediction probability vector :
out = model.predict(imarr)

# most probable item :
best_index = np.argmax(out, axis=1)[0]

В этом случае модель правильно определяет index = 2 (метка «Aamir_Khan») как наиболее вероятный с 99%.

Найдите лица на больших изображениях с помощью OpenCV

Чтобы быть полезным, инструмент идентификации лиц должен иметь возможность работать с изображениями любого измерения, содержащими несколько объектов: людей, улиц, автомобилей и т. Д.

Поскольку модель VGG-Face была оптимизирована для центрированных лиц, мы добавим этап предварительной обработки, который извлекает лица из изображений. Хотя эту задачу можно выполнить с помощью NeuralNets (например, эта статья), здесь мы используем реализацию классификатора Cascade из библиотеки OpenCV (Быстрое обнаружение объектов с использованием усиленного каскада простых функций, П. Виола и М. Джонс).

Помимо предварительной обработки изображений, классификатор OpenCV Cascade является очень удобным инструментом, если вы хотите создать набор данных лиц; вам просто нужно объединить веб-парсер с классификатором, чтобы создать набор данных о лицах! Этот набор данных, скорее всего, будет немаркированным, но неконтролируемое и полу-контролируемое обучение также весьма полезно.

Загрузка каскад-классификатора

import cv2
cascade_file_src = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascade_file_src)

Использование каскадного классификатора

# load image on gray scale :
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# detect faces in the image :
faces = faceCascade.detectMultiScale(gray, 1.2, 5)

# draw rectangles around the faces :
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)

plt.imshow(image)

# crop image and scale to 224x224 :
crpim = im.crop(box).resize((224,224))
plt.imshow(np.asarray(crpim))

Обобщение модели на «всех»

Чтобы иметь дело с лицами людей, которые не были частью обучающей выборки модели (2622 знаменитости), мы можем получить сокращенную модель из обученной модели VGG. Это легко сделать с помощью функционального API Keras: мы указываем вход и выход.

В нашем случае мы можем с уверенностью предположить, что признаков, закодированных в модельных весах, чтобы различать 2622 знаменитостей, достаточно, чтобы точно описать «любое» лицо.

Мы строим модель из вероятностных входов Softmax, то есть «логитов».

Векторная модель лица

from keras.models import Model
# output the 2nd last layer :
featuremodel = Model( input = facemodel.layers[0].input,
                      output = facemodel.layers[-2].output )

Косинусное сходство

from scipy.spatial.distance import cosine as dcos
fvec1 = featuremodel.predict(imarr1)[0,:]
fvec2 = featuremodel.predict(imarr2)[0,:]
dcos_1_2 = dcos(fvec1, fvec2)

1 последнее слово

Если вам понравилось читать эту статью, вы можете улучшить ее видимость с помощью маленькой зеленой кнопки в виде сердечка ниже. Спасибо!