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