暹罗网络的Keras模型不学习并且总是预测相同的输出

时间:2019-12-22 09:09:08

标签: python tensorflow keras neural-network

我正在尝试使用Keras训练暹罗神经网络,目的是识别2张图像是否属于同一类别。我的数据被打乱了,正例和负例的数量相等。我的模型没有学习任何东西,并且总是在预测相同的输出。我每次都得到相同的损失,验证准确性和验证损失。

Training Output

def convert(row):
    return imread(row)

def contrastive_loss(y_true, y_pred):
    margin = 1
    square_pred = K.square(y_pred)
    margin_square = K.square(K.maximum(margin - y_pred, 0))
    return K.mean(y_true * square_pred + (1 - y_true) * margin_square)

def SiameseNetwork(input_shape):
    top_input = Input(input_shape)

    bottom_input = Input(input_shape)

    # Network
    model = Sequential()
    model.add(Conv2D(96,(7,7),activation='relu',input_shape=input_shape))
    model.add(MaxPooling2D())
    model.add(Conv2D(256,(5,5),activation='relu',input_shape=input_shape))
    model.add(MaxPooling2D())
    model.add(Conv2D(256,(5,5),activation='relu',input_shape=input_shape))
    model.add(MaxPooling2D())
    model.add(Flatten())
    model.add(Dense(4096,activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(1024,activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(512,activation='relu'))
    model.add(Dropout(0.5))

    encoded_top = model(top_input)
    encoded_bottom = model(bottom_input)

    L1_layer = Lambda(lambda tensors:K.abs(tensors[0] - tensors[1]))    
    L1_distance = L1_layer([encoded_top, encoded_bottom])

    prediction = Dense(1,activation='sigmoid')(L1_distance)
    siamesenet = Model(inputs=[top_input,bottom_input],outputs=prediction)
    return siamesenet

data = pd.read_csv('shuffleddata.csv')
print('Converting X1....')

X1 = [convert(x) for x in data['X1']]

print('Converting X2....')

X2 = [convert(x) for x in data['X2']]

print('Converting Y.....')
Y = [0 if data['Y'][i] == 'Negative' else 1 for i in range(len(data['Y']))]

input_shape = (53,121,3,)
model = SiameseNetwork(input_shape)
model.compile(loss=contrastive_loss,optimizer='sgd',metrics=['accuracy'])
print(model.summary())
model.fit(X1,Y,batch_size=32,epochs=20,shuffle=True,validation_split = 0.2)
model.save('Siamese.h5')

3 个答案:

答案 0 :(得分:1)

为了社区的利益,在本节中提到该问题的解决方案(即使它在“评论”部分中也有提及)。

由于模型可以与其他标准数据集配合使用,因此解决方案是使用更多数据。模型不是学习型的,因为它的训练数据较少。

答案 1 :(得分:0)

如评论和Tensorflow支持中的答案所述,该模型可以很好地处理更多数据。稍微调整模型也可以。将第二和第三卷积层中的过滤器数量从256更改为64将使可训练参数的数量大量减少,因此模型开始学习。

答案 2 :(得分:0)

我想在这里提几件可能对其他人有用的事情:

1) 数据分层/随机抽样

当您使用 validation_split 时,Keras 使用最后 x% 的数据作为验证数据。这意味着如果数据是按类排序的,例如因为“对”或“三胞胎”是按顺序生成的,验证数据将仅来自包含在最后 x% 数据中的类(或类)。在这种情况下,验证集将没有用。因此,必须对输入数据进行混洗,以确保验证集包含来自每个类的随机样本。

validation_split 的文档说:

<块引用>

在 0 和 1 之间浮动。要用作的训练数据的分数 验证数据。该模型将把这部分 训练数据,不会对其进行训练,并将评估损失和 每个时期结束时有关此数据的任何模型指标。这 验证数据选自 x 和 y 数据中的最后一个样本 提供,洗牌前

2) 优化器的选择

model.compile() 中,选择 optimizer='sgd' 可能不是最好的方法,因为 sgd 可能会陷入局部最小值等。Adam (see docs) 似乎是一个不错的选择,因为它...

<块引用>

[...] 结合了 [...] AdaGrad 的优点来处理稀疏 梯度,以及 RMSProp 处理非平稳的能力 目标。

根据 Kingma and Ba(2014 年,第 10 页)。

from keras.optimizers import Adam
...
model.compile(loss=contrastive_loss, optimizer=keras.optimizers.Adam(lr=0.0001))

3) 提前停止/学习率

在训练期间使用 early stoppingadjusting the learning rate 也可能对获得良好结果非常有用。所以模型可以训练直到不再成功(在这种情况下自动停止)。

from keras.callbacks import EarlyStopping
from keras.callbacks import ReduceLROnPlateau
...
early_stopping = EarlyStopping(monitor='val_loss', patience=50, mode='auto', restore_best_weights=True)
reduce_on_plateau = ReduceLROnPlateau(monitor="val_loss", factor=0.8, patience=15, cooldown=5, verbose=0)
...
hist = model.fit([img_1, img_2], y, 
            validation_split=.2, 
            batch_size=128, 
            verbose=1, 
            epochs=9999,
            callbacks=[early_stopping])

4) 内核初始化

内核初始化(带有小 SD)也可能有帮助。

# Layer 1
    seq.add(Conv2D(8, (5,5), input_shape=input_shape, 
        kernel_initializer=keras.initializers.TruncatedNormal(mean=0.0, stddev=0.01, seed=None), 
        data_format="channels_first"))
    seq.add(Activation('relu'))
    seq.add(MaxPooling2D(pool_size=(2, 2))) 
    seq.add(Dropout(0.1))

5) 过拟合

我注意到,与其使用 dropout 来对抗过拟合,不如添加一些噪声会很有帮助。在这种情况下,只需在网络顶部添加一些 GaussianNoise