重载后的Keras模型参数都是 "NaN"。

0 人关注

我使用Resnet50的转移学习。我从Keras提供的预训练模型("imagenet")中创建一个新模型。

训练完我的新模型后,我将其保存为以下内容。

# Save the Siamese Network architecture
siamese_model_json = siamese_network.to_json()
with open("saved_model/siamese_network_arch.json", "w") as json_file:
    json_file.write(siamese_model_json)
# save the Siamese Network model weights
siamese_network.save_weights('saved_model/siamese_model_weights.h5')

而后,我按以下方式重新加载,进行一些预测。

json_file = open('saved_model/siamese_network_arch.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
siamese_network = model_from_json(loaded_model_json)
# load weights into new model
siamese_network.load_weights('saved_model/siamese_model_weights.h5')

然后我检查权重是否合理,如下(从其中一个图层)。

print("bn3d_branch2c:\n",
      siamese_network.get_layer('model_1').get_layer('bn3d_branch2c').get_weights())

如果我只对我的网络进行1次训练,我看到了合理的数值。

但如果我训练我的模型18个epochs(这需要5-6个小时,因为我有一台非常慢的电脑),我只是看到NaN值,如下所示。

bn3d_branch2c:
 [array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,

这里有什么诀窍呢?

下面是我如何创建我的模型。

在这里,我有一个triplet_loss函数,我以后会需要它。

def triplet_loss(inputs, dist='euclidean', margin='maxplus'):
    anchor, positive, negative = inputs
    positive_distance = K.square(anchor - positive)
    negative_distance = K.square(anchor - negative)
    if dist == 'euclidean':
        positive_distance = K.sqrt(K.sum(positive_distance, axis=-1, keepdims=True))
        negative_distance = K.sqrt(K.sum(negative_distance, axis=-1, keepdims=True))
    elif dist == 'sqeuclidean':
        positive_distance = K.sum(positive_distance, axis=-1, keepdims=True)
        negative_distance = K.sum(negative_distance, axis=-1, keepdims=True)
    loss = positive_distance - negative_distance
    if margin == 'maxplus':
        loss = K.maximum(0.0, 2 + loss)
    elif margin == 'softplus':
        loss = K.log(1 + K.exp(loss))
    returned_loss = K.mean(loss)
    return returned_loss

这里是我如何从头到尾构建我的模型。我给出了完整的代码以提供准确的图片。

model = ResNet50(weights='imagenet')
# Remove the last layer (Needed to later be able to create the Siamese Network model)
model.layers.pop()
# First freeze all layers of ResNet50. Transfer Learning to be applied.
for layer in model.layers:
    layer.trainable = False
# All Batch Normalization layers still need to be trainable so that the "mean"
# and "standard deviation (std)" params can be updated with the new training data
model.get_layer('bn_conv1').trainable = True
model.get_layer('bn2a_branch2a').trainable = True
model.get_layer('bn2a_branch2b').trainable = True
model.get_layer('bn2a_branch2c').trainable = True
model.get_layer('bn2a_branch1').trainable = True
model.get_layer('bn2b_branch2a').trainable = True
model.get_layer('bn2b_branch2b').trainable = True
model.get_layer('bn2b_branch2c').trainable = True
model.get_layer('bn2c_branch2a').trainable = True
model.get_layer('bn2c_branch2b').trainable = True
model.get_layer('bn2c_branch2c').trainable = True
model.get_layer('bn3a_branch2a').trainable = True
model.get_layer('bn3a_branch2b').trainable = True
model.get_layer('bn3a_branch2c').trainable = True
model.get_layer('bn3a_branch1').trainable = True
model.get_layer('bn3b_branch2a').trainable = True
model.get_layer('bn3b_branch2b').trainable = True
model.get_layer('bn3b_branch2c').trainable = True
model.get_layer('bn3c_branch2a').trainable = True
model.get_layer('bn3c_branch2b').trainable = True
model.get_layer('bn3c_branch2c').trainable = True
model.get_layer('bn3d_branch2a').trainable = True
model.get_layer('bn3d_branch2b').trainable = True
model.get_layer('bn3d_branch2c').trainable = True
model.get_layer('bn4a_branch2a').trainable = True
model.get_layer('bn4a_branch2b').trainable = True
model.get_layer('bn4a_branch2c').trainable = True
model.get_layer('bn4a_branch1').trainable = True
model.get_layer('bn4b_branch2a').trainable = True
model.get_layer('bn4b_branch2b').trainable = True
model.get_layer('bn4b_branch2c').trainable = True
model.get_layer('bn4c_branch2a').trainable = True
model.get_layer('bn4c_branch2b').trainable = True
model.get_layer('bn4c_branch2c').trainable = True
model.get_layer('bn4d_branch2a').trainable = True
model.get_layer('bn4d_branch2b').trainable = True
model.get_layer('bn4d_branch2c').trainable = True
model.get_layer('bn4e_branch2a').trainable = True
model.get_layer('bn4e_branch2b').trainable = True
model.get_layer('bn4e_branch2c').trainable = True
model.get_layer('bn4f_branch2a').trainable = True
model.get_layer('bn4f_branch2b').trainable = True
model.get_layer('bn4f_branch2c').trainable = True
model.get_layer('bn5a_branch2a').trainable = True
model.get_layer('bn5a_branch2b').trainable = True
model.get_layer('bn5a_branch2c').trainable = True
model.get_layer('bn5a_branch1').trainable = True
model.get_layer('bn5b_branch2a').trainable = True
model.get_layer('bn5b_branch2b').trainable = True
model.get_layer('bn5b_branch2c').trainable = True
model.get_layer('bn5c_branch2a').trainable = True
model.get_layer('bn5c_branch2b').trainable = True
model.get_layer('bn5c_branch2c').trainable = True
# Used when compiling the siamese network
def identity_loss(y_true, y_pred):
    return K.mean(y_pred - 0 * y_true)  
# Create the siamese network
x = model.get_layer('flatten_1').output # layer 'flatten_1' is the last layer of the model
model_out = Dense(128, activation='relu',  name='model_out')(x)
model_out = Lambda(lambda  x: K.l2_normalize(x,axis=-1))(model_out)
new_model = Model(inputs=model.input, outputs=model_out)
anchor_input = Input(shape=(224, 224, 3), name='anchor_input')
pos_input = Input(shape=(224, 224, 3), name='pos_input')
neg_input = Input(shape=(224, 224, 3), name='neg_input')
encoding_anchor   = new_model(anchor_input)
encoding_pos      = new_model(pos_input)
encoding_neg      = new_model(neg_input)
loss = Lambda(triplet_loss)([encoding_anchor, encoding_pos, encoding_neg])
siamese_network = Model(inputs  = [anchor_input, pos_input, neg_input], 
                        outputs = loss) # Note that the output of the model is the 
                                        # return value from the triplet_loss function above
siamese_network.compile(optimizer=Adam(lr=.0001), loss=identity_loss)

有一点需要注意的是,我让所有的批处理规范化层都是 "可训练的",这样BN相关的参数就可以用我的训练数据来更新。这产生了很多行,但我找不到一个更短的解决方案。

5 个评论
Neil
你试过json库吗?json.dumps和json.load可以让你写和读json到一个文件或从一个文件中读。我不认为'写'会保留json数据结构,它把它看作是一个字符串。
edn
我可以试试。但要花几个小时才能看到结果。你认为你能提出一个更具体的解决方案吗。在使用json.dumps/load时,它到底应该是什么样子的?
Neil
试试这个: import json siamese_model_json = siamese_network.to_json() json_string = json.dumps(siamese_model_json) with open("said_model/siamese_network_arch.json", "w") as json_file: json_file.write(json_string) then later import json json_file = open('saved_model/siamese_network_arch.json', 'r') loaded_model_json = json.load(json_file.read()) 另外,看起来Keras有一些功能可以直接保存整个东西? keras.io/getting-started/faq/#how-can-i-save-a-keras-model
Neil
对不起,我已经很久没有使用过keras了,不能提供更多的帮助。但如果你的权重只是一个数组,那么我就不会通过重新训练整个模型来进行调试。只要做一个相同长度的假数组,看看它是否加载。
你有没有研究过其他保存模型的方法,比如 .save() 方法?还是你真的需要它是JSON格式的?另外,当你训练了18个epochs时,训练期间的损失值是多少?它是否会减少?还是在训练过程中的某个时刻,它也会变成 NaN
python
tensorflow
machine-learning
keras
transfer-learning
edn
edn
发布于 2018-07-12
1 个回答
edn
edn
发布于 2018-07-12
已采纳
0 人赞同

该解决方案的灵感来自@Gurmeet Singh的上述建议。

似乎在训练过程中,可训练层的权重在一段时间后变得很大,所有这些权重都被设置为NaN,这让我觉得我以错误的方式保存和重新加载我的模型,但问题是爆炸性梯度。

我在github的讨论中也看到一个类似的问题,可以在这里查看:github.com/keras-team/keras/issues/2378 在github的那个主题的底部,建议使用较低的学习率来避免这个问题。

In this link ( Keras ML库:梯度更新后如何进行权重剪裁?TensorFlow后端 ),讨论了2种解决方案。 - 使用 夹值 优化器中的参数,它简单地削减了配置的计算梯度值。但这并不是推荐的解决方案(在另一个主题中解释过)。 - 第二件事是使用clipnorm参数,当计算的梯度值超过用户给定的值时,它就会简单地剪切。

我也想过使用输入归一化(以避免爆炸性的梯度),但后来发现,它已经在 preprocess_input(..) 功能。 (查看此链接了解详情。 https://www.tensorflow.org/api_docs/python/tf/keras/applications/resnet50/preprocess_input ) It is though possible to set the mode 参数为 "tf" (set to "咖啡" 否则默认情况下,会有进一步的帮助(因为 mode="tf" 设置在-1和1之间缩放像素)但我没有尝试。

我总结了一下,我在编译将要训练的模型时改变了两件事。

被改变的一行是以下内容。

变化之前。

siamese_network.compile(optimizer=Adam(**lr=.0001**), 
                        loss=identity_loss)

变化之后。

siamese_network.compile(optimizer=Adam(**lr=.00004**, **clipnorm=1.**),
                        loss=identity_loss)

1) Used a smaller learning rate to make gradient updates a bit smaller