В сиамской сети потери не уменьшатся - PullRequest
1 голос
/ 11 июня 2019

Я очень плохо знаком с машинным обучением и начал внедрять сиамскую сеть, чтобы проверять уровень сходства рукописных цифр, тренируюсь с набором данных MNIST, но у меня серьезные проблемы с потерями.

Сетевая модель

import keras
from keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Lambda
from keras.models import Sequential, Model
from keras.optimizers import Adam
import keras.backend as K
import cv2
from keras.datasets import mnist
import numpy as np
import random

def siameseNet(input_shape):
    input1 = Input(input_shape)
    input2 = Input(input_shape)

    model = Sequential()
    model.add(Conv2D(50, (5,5), activation='relu', input_shape=input_shape))
    model.add(MaxPooling2D())
    model.add(Conv2D(100, (3,3), activation='relu'))
    model.add(MaxPooling2D())
    model.add(Conv2D(100, (3,3), activation='relu'))

    model.add(Flatten())
    model.add(Dense(2048, activation='sigmoid'))

    input_model_1 = model(input1)
    input_model_2 = model(input2)
    distance_func = Lambda(lambda t: K.abs(t[0]-t[1]))
    distance_layer = distance_func([input_model_1, input_model_2])

    prediction = Dense(1,activation='sigmoid')(distance_layer)

    network = Model(inputs=[input1,input2],outputs=prediction)

    return network

Обучающие данные

Мой pairs объект представляет собой массив numpy с двумя массивами, содержащими изображения с одинаковым индексом, первая половина массива является изображением той же категории,вторая половина другой категории.

category объект представляет собой простой массив, содержащий такое же количество выборок из обучающего набора, первая половина которого равна 0, чтобы указать значение Y для того же изображения, ивторая половина установлена ​​на 1.

И pairs, и category заполняются следующей функцией:

INPUT_SHAPE = (28,28,1)

def loadData():
    (X_train, Y_train), _ = mnist.load_data()
    n_samples = 20000
    arrPairs = [np.zeros((n_samples, INPUT_SHAPE[0], INPUT_SHAPE[1],INPUT_SHAPE[2])) for i in range(2)]
    category = np.zeros((n_samples))
    category[n_samples//2:] = 1
    for i in range(n_samples): 
        if i%1000==0:
            print(i)

        cur_category = Y_train[i]

        img = random.choice(X_train[Y_train==cur_category]).reshape(28,28,1)
        _, img = cv2.threshold(img, .8, 1, cv2.THRESH_BINARY)
        arrPairs[0][i] = img.reshape(28,28,1)

        if category[i] == 1:
            img = random.choice(X_train[Y_train!=cur_category])
        else:
            img = random.choice(X_train[Y_train==cur_category])
        _, img = cv2.threshold(img, .8, 1, cv2.THRESH_BINARY)
        arrPairs[1][i] = img.reshape(28,28,1)
    arrPairs[0] = arrPairs[0]/255
    return arrPairs, category

Результаты обучения

pairs, category = loadData()
model = siameseNet(INPUT_SHAPE)
model.compile(optimizer=Adam(lr=0.0005),loss="binary_crossentropy")
model.fit(pairs, category,  epochs=5, verbose=1, validation_split=0.2)

Train on 16000 samples, validate on 4000 samples
Epoch 1/5
16000/16000 [==============================] - 6s 353us/step - loss: 0.6660 - val_loss: 0.9474
Epoch 2/5
16000/16000 [==============================] - 5s 287us/step - loss: 0.6628 - val_loss: 0.9335
Epoch 3/5
16000/16000 [==============================] - 5s 287us/step - loss: 0.6627 - val_loss: 0.8487
Epoch 4/5
16000/16000 [==============================] - 5s 287us/step - loss: 0.6625 - val_loss: 0.9954
Epoch 5/5
16000/16000 [==============================] - 5s 288us/step - loss: 0.6616 - val_loss: 0.9133

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

Я пытался изменить активации, увеличивая и уменьшая сложность сети (добавляя иудаление слоев, а также увеличение и уменьшение параметров Conv2D), но ничего из этого не сработало, поэтому я предполагаю, что это архитектурная проблема, которую мне не хватает

Обновление: Линии, использованные для тестирования:

test_pairs = [np.zeros((2, INPUT_SHAPE[0], INPUT_SHAPE[1],INPUT_SHAPE[2])) for i in range(2)]
test_pairs[0][0] = cv2.cvtColor(cv2.imread('test1_samenumber.png'), cv2.COLOR_BGR2GRAY).reshape(28,28,1); 
test_pairs[1][0] = cv2.cvtColor(cv2.imread('test2_samenumber.png'), cv2.COLOR_BGR2GRAY).reshape(28,28,1);

pred = model.predict(test_pairs)
print(pred)

Какие из них:

[[0.32230237]
 [0.44603676]]

1 Ответ

1 голос
/ 12 июня 2019

У вас есть ненужная нормализация при загрузке ваших данных.В частности, для первой пары изображений вы делите на 255, когда это не требуется.После порогового значения, равного cv2.threshold, выходные значения по своей природе равны 0 или 1, поэтому дальнейшее деление на 255 делает динамический диапазон меньшим, чем у второй пары изображений, что может вызвать проблемы при обучении различению двух изображений.Я удалил эту нормализацию, закомментировав оператор arrPairs[0] = arrPairs[0] / 255.

После того, как я обучил вашу сеть, я пробежал по каждой из пар и проверил выходной прогноз.По сути, если категория равна 1, а прогноз, сгенерированный сетью (ваш сигмовидный слой), больше 0,5, я считаю это правильным прогнозом.Точно так же, когда я вижу, что категория равна 0, а сгенерированный прогноз меньше 0,5, это тоже правильно.

correct = 0
for i in range(len(pairs[0])):
    output = model.predict([pairs[0][i][None], pairs[1][i][None]])[0][0]
    if (category[i] == 1 and output >= 0.5) or (category[i] == 0 and output < 0.5):
        correct += 1

print(correct / len(pairs[0]))

Я получаю здесь точность 99,26%, то есть 0,74% из 20000 выборок,или около 148 образцов были неправильно классифицированы.Я бы сказал, что это хороший результат.

Воспроизводимый блокнот Google Colab можно найти здесь: https://colab.research.google.com/drive/10Q6rjuiytRSump2nulW5UhXY_PJh1eor

...