【问题标题】:Loss won't decrease on Siamese NetworkSiamese Network 的损失不会减少
【发布时间】:2019-10-26 07:22:24
【问题描述】:

我对机器学习非常陌生,我开始实施连体网络来检查手写数字的相似度,使用 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 .

pairscategory 都填充在以下函数中:

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

【问题讨论】:

  • 感谢您指出@rayryeng,我确实忘记了。我应用了归一化,损失从 0.60 变为 0.02。丢失问题是由它引起的。但是由于一些奇怪的原因,网络仍然错误地对图像进行了分类,因为我拍了两张相同类别的图像,蜜蜂相同的概率是 33%,而两张不同的图像得到相同的概率是 99%,任何知道为什么?
  • 我查看了您数据中的唯一值。只有 0 和 1,所以实际上我们需要删除标准化。这可能是由于您对数字进行了阈值处理,因此只有 0 和 1 的值。我有一个 colab 实例正在运行,我将在回复您之前对其进行测试。
  • 好的,所以我已经删除了不必要的标准化,看起来损失正在下降。我要等到它完成训练后再测试预测。我还冒昧地让您的代码可重现。缺少某些函数调用和包会阻止任何人使用它。

标签: python opencv keras deep-learning neural-network


【解决方案1】:

在加载数据时,您有不必要的规范化。具体来说,对于第一对图像,您在不需要时除以 255。使用cv2.threshold 设置阈值后,输出值本质上为 0 或 1,因此进一步除以 255 会使动态范围小于第二对图像,这可能会导致学习如何区分两个图像时出现问题。我已通过注释掉 arrPairs[0] = arrPairs[0] / 255 语句来删除此规范化。

在训练完您的网络后,我遍历了每一对并检查了输出预测。本质上,如果类别为 1 并且网络(您的 sigmoid 层)生成的预测大于 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% 的准确率,这意味着 20000 个样本中有 0.74% 或大约 148 个样本被错误分类。我会说这是一个很好的结果。

可在此处找到可重现的 Google Colab 笔记本:https://colab.research.google.com/drive/10Q6rjuiytRSump2nulW5UhXY_PJh1eor

【讨论】:

  • 谢谢,损失确实下降了。作为旁注,我尝试手动写入两位数字并进行比较,结果不正确,这意味着泛化效果不佳。有什么改进的建议吗?
  • @RodolfoDonãHosp 啊。有两种方法。 (1) 在您的训练数据集中引入更多的缩放、旋转和平移,并在这些数据上进行训练。希望有更多在比例、旋转和平移方面不同的示例,我们可以更好地概括。 (2) 将你写作的图像引入数据集中。它如此糟糕的原因是因为你的写作与 MNIST 数据集有很大不同,尤其是在数字的比例和方向方面。根据您认为更可行的方法,我可以为您提供进一步的建议。
  • @RodolfoDonãHosp ...而且最重要的是,您必须对输入图像进行完全相同的预处理,就像在网络中进行推理之前对 MNIST 所做的那样。你这样做吗?
  • 好的。这两个秤可能是关闭的。请记住,MNIST 数字占据了整个图像。可能存在一些轻微的规模问题。尝试使用 ImageGenerator 并输入一些缩放、旋转和平移来概括模型。很高兴我能帮助你!如果您还需要什么,请告诉我。
  • 当您生成对时,它是一个包含两个元素的列表,其中列表中的每个元素都包含一个大小为 20000 x 28 x 28 x 1 的 NumPy 数组。 20000 将是样本数。因此,您创建了一个包含两个元素的列表,每个元素的大小为1 x 28 x 28 x 1,因此您代表一对图像进行测试。您所要做的就是将 np.zeros 声明的第一个维度从 2 更改为 1(即 [np.zeros((1, INPUT_SHAPE[0], INPUT_SHAPE[1],INPUT_SHAPE[2])) for i in range(2)]。它会生成两个概率,因为您提供的第二个图像对对于两个图像都是空白的。
猜你喜欢
  • 1970-01-01
  • 2020-06-06
  • 2021-06-01
  • 1970-01-01
  • 2019-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-08
相关资源
最近更新 更多