在Keras层中重置权重

58

我想要重置(随机化)我的Keras(深度学习)模型中所有层的权重。原因是我希望能够多次训练该模型,每次使用不同的数据划分,而无需每次都进行(缓慢的)模型重新编译。

这个讨论 的启发,我正在尝试以下代码:

# Reset weights
for layer in KModel.layers:
    if hasattr(layer,'init'):
        input_dim = layer.input_shape[1]
        new_weights = layer.init((input_dim, layer.output_dim),name='{}_W'.format(layer.name))
        layer.trainable_weights[0].set_value(new_weights.get_value())

然而,它只有部分起作用。

部分地是因为我已经检查了一些layer.get_weights()的值,它们似乎在改变。但当我重新启动训练时,成本值比第一次运行时的初始成本值低得多。这就像我已经成功重置了一些权重,但不是全部。

10个回答

72

在编译模型后但在训练模型之前,保存初始权重:

model.save_weights('model.h5')

在训练后,通过重新加载初始权重来“重置”模型:

model.load_weights('model.h5')

这提供了一个苹果对苹果的模型,用于比较不同的数据集,并且应该比重新编译整个模型更快。


15
我最终做了类似的事情。保存到磁盘并加载需要很多时间,所以我只是将权重保存在一个变量中: weights = model.get_weights()。在第一次训练之前,我使用这种方式获取初始权重。然后,在每一次后续的训练之前,我重新加载初始权重并运行jkleint的洗牌方法,就像我在发布的链接中提到的那样。看起来效果很顺利… - Tor
5
@Tor建议中完整代码片段的翻译如下:weights = model.get_weights()model.compile(args)model.fit(args)model.set_weights(weights) - BallpointBen
基于这个,我开始制作一个 lambda 函数,当我初始化我的模型时。我构建模型,然后做一些像 weights = model.get_weights() 这样的事情;reset_model = lambda model: model.set_weights(weights),这样我以后只需要调用 reset_model(model) 即可。 - Andrew
1
这里存在一个明显的问题,所有模型都将具有相同的起始权重。我们想要的是(我认为)再次随机初始化权重。 - J Agustin Barrachina
将模型架构和权重分别保存不是有效的选择吗?这种方法可能会带来什么问题吗?我没有看到有人建议这样做。 - haneulkim

23

通过检查初始化器重置所有层:

def reset_weights(model):
    import keras.backend as K
    session = K.get_session()
    for layer in model.layers: 
        if hasattr(layer, 'kernel_initializer'): 
            layer.kernel.initializer.run(session=session)
        if hasattr(layer, 'bias_initializer'):
            layer.bias.initializer.run(session=session)     

更新:kernel_initializer现在是kernel.initializer。


2
这是我认为最好的方法。 - SuperNES
5
过时了吗?现在 kernel_initializer 没有 run 属性。在我的情况下,kernel_initializer 是一个 VarianceScaling 对象。 - Xiaohong Deng
1
@XiaohongDeng 尝试使用 kernel.initializer.run(session=session)。我之前也遇到了同样的问题。 - tkchris
AttributeError: 模块 'tensorflow_core.keras.backend' 没有属性 'get_session',请使用 tensorflow.keras。 - Bersan
是的,在tf2中它不起作用,请看这里关于tf2的最后部分:https://github.com/keras-team/keras/issues/341 - Mendi Barel
@tkchris AttributeError: 'NoneType' object has no attribute 'run' - endolith

15

我找到了 clone_model 函数,它可以创建一个具有相同结构但新模型权重的克隆网络。

使用示例:

model_cloned = tensorflow.keras.models.clone_model(model_base)

比较重量:

original_weights = model_base.get_weights()
print("Original weights", original_weights)
print("========================================================")
print("========================================================")
print("========================================================")
model_cloned = tensorflow.keras.models.clone_model(model_base)
new_weights = model_cloned.get_weights()
print("New weights", new_weights)

如果您执行此代码多次,您将注意到克隆的模型每次都会收到新的权重。


运行时错误:在训练/测试之前,您必须编译您的模型。请使用 model.compile(optimizer, loss) - isobretatel

14

如果你想真正重新随机化权重,而不仅仅是恢复初始权重,可以按照以下步骤进行。根据你是否使用TensorFlow或Theano,代码略有不同。

from keras.initializers import glorot_uniform  # Or your initializer of choice
import keras.backend as K

initial_weights = model.get_weights()

backend_name = K.backend()
if backend_name == 'tensorflow': 
    k_eval = lambda placeholder: placeholder.eval(session=K.get_session())
elif backend_name == 'theano': 
    k_eval = lambda placeholder: placeholder.eval()
else: 
    raise ValueError("Unsupported backend")

new_weights = [k_eval(glorot_uniform()(w.shape)) for w in initial_weights]

model.set_weights(new_weights)

很好的简单解决方案! - guillefix
Cannot evaluate tensor using `eval()`: No default session is registered. - Bersan
NotImplementedError: eval is not supported when eager execution is enabled - Sagi Mann
只需在倒数第二行更改为new_weights = [glorot_uniform()(w.shape) for w in initial_weights],并在急切模式下运行。对我来说完美地起作用了。 - tuzhucheng

7

Tensorflow 2的答案:

for ix, layer in enumerate(model.layers):
    if hasattr(model.layers[ix], 'kernel_initializer') and \
            hasattr(model.layers[ix], 'bias_initializer'):
        weight_initializer = model.layers[ix].kernel_initializer
        bias_initializer = model.layers[ix].bias_initializer

        old_weights, old_biases = model.layers[ix].get_weights()

        model.layers[ix].set_weights([
            weight_initializer(shape=old_weights.shape),
            bias_initializer(shape=old_biases.shape)])

原始权重:

model.layers[1].get_weights()[0][0]

array([ 0.4450057 , -0.13564804,  0.35884023,  0.41411972,  0.24866664,
        0.07641453,  0.45726687, -0.04410008,  0.33194816, -0.1965386 ,
       -0.38438258, -0.13263905, -0.23807487,  0.40130925, -0.07339832,
        0.20535922], dtype=float32)

新权重:
model.layers[1].get_weights()[0][0]

array([-0.4607593 , -0.13104361, -0.0372932 , -0.34242013,  0.12066692,
       -0.39146423,  0.3247317 ,  0.2635846 , -0.10496247, -0.40134245,
        0.19276887,  0.2652442 , -0.18802321, -0.18488845,  0.0826562 ,
       -0.23322225], dtype=float32)


不支持包含RNN的模型。 - Luca

4
K.get_session().close()
K.set_session(tf.Session())
K.get_session().run(tf.global_variables_initializer())

虽然不是很易于移植,但对于tensorflow后端效果很好! - bendl

3

试试使用 set_weights

例如:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import numpy as np
np.random.seed(1234)
from keras.layers import Input
from keras.layers.convolutional import Convolution2D
from keras.models import Model

print("Building Model...")
inp = Input(shape=(1,None,None))
x   = Convolution2D(1, 3, 3, border_mode='same', init='normal',bias=False)(inp)
output = Convolution2D(1, 3, 3, border_mode='same', init='normal',bias=False)(x)
model_network = Model(input=inp, output=output)

w = np.asarray([ 
    [[[
    [0,0,0],
    [0,2,0],
    [0,0,0]
    ]]]
    ])

for layer_i in range(len(model_network.layers)):
    print (model_network.layers[layer_i])

for layer_i in range(1,len(model_network.layers)):
    model_network.layers[layer_i].set_weights(w)



input_mat = np.asarray([ 
    [[
    [1.,2.,3.,10.],
    [4.,5.,6.,11.],
    [7.,8.,9.,12.]
    ]]
    ])

print("Input:")
print(input_mat)
print("Output:")
print(model_network.predict(input_mat))

w2 = np.asarray([ 
    [[[
    [0,0,0],
    [0,3,0],
    [0,0,0]
    ]]]
    ])


for layer_i in range(1,len(model_network.layers)):
    model_network.layers[layer_i].set_weights(w2)

print("Output:")
print(model_network.predict(input_mat))

建立一个模型,例如,使用两个卷积层

print("Building Model...")
inp = Input(shape=(1,None,None))
x   = Convolution2D(1, 3, 3, border_mode='same', init='normal',bias=False)(inp)
output = Convolution2D(1, 3, 3, border_mode='same', init='normal',bias=False)(x)
model_network = Model(input=inp, output=output)

然后定义您的权重(我使用简单的w,但如果您想要,可以使用np.random.uniform或任何其他方法)

w = np.asarray([ 
    [[[
    [0,0,0],
    [0,2,0],
    [0,0,0]
    ]]]
    ])

看一下模型内部的层是什么

for layer_i in range(len(model_network.layers)):
    print (model_network.layers[layer_i])

为每个卷积层设置权重(你会发现第一层实际上是输入层,你不想改变它,所以范围从1而不是0开始)。

for layer_i in range(1,len(model_network.layers)):
    model_network.layers[layer_i].set_weights(w)

生成一些测试输入并预测模型的输出

input_mat = np.asarray([ 
    [[
    [1.,2.,3.,10.],
    [4.,5.,6.,11.],
    [7.,8.,9.,12.]
    ]]
    ])

print("Output:")
print(model_network.predict(input_mat))

你可以再次更改它,然后检查输出:
w2 = np.asarray([ 
    [[[
    [0,0,0],
    [0,3,0],
    [0,0,0]
    ]]]
    ])

for layer_i in range(1,len(model_network.layers)):
    model_network.layers[layer_i].set_weights(w2)

print("Output:")
print(model_network.predict(input_mat))

示例输出:

Using Theano backend.
Building Model...
<keras.engine.topology.InputLayer object at 0x7fc0c619fd50>
<keras.layers.convolutional.Convolution2D object at 0x7fc0c6166250>
<keras.layers.convolutional.Convolution2D object at 0x7fc0c6150a10>
Weights after change:
[array([[[[ 0.,  0.,  0.],
         [ 0.,  2.,  0.],
         [ 0.,  0.,  0.]]]], dtype=float32)]
Input:
[[[[  1.   2.   3.  10.]
   [  4.   5.   6.  11.]
   [  7.   8.   9.  12.]]]]
Output:
[[[[  4.   8.  12.  40.]
   [ 16.  20.  24.  44.]
   [ 28.  32.  36.  48.]]]]
Output:
[[[[   9.   18.   27.   90.]
   [  36.   45.   54.   99.]
   [  63.   72.   81.  108.]]]]

通过查看.layers,您可以看到第一层是输入层,其他层是卷积层。


2

对于tf2,最简单的重置权重的方法是:

tf_model.set_weights(
    clone_model(tf_model).get_weights()
)
clone_model()是由@danielsaromo提到的,它返回一个新模型,可训练参数从头开始初始化,我们使用其权重重新初始化我们的模型,因此不需要模型编译(关于其损失或优化器的知识)。
但有两个注意事项,第一个在clone_model()的文档中提到: clone_model将不会保留模型内共享对象的唯一性(例如,附加到两个不同层的单个变量将恢复为两个单独的变量)。 另一个注意事项是对于大型模型,由于内存限制,克隆可能失败。

1
在TF 2.0(tf.keras)中,重新初始化编译但未经训练的模型的权重时,可以使用“随机”方法。
weights = [glorot_uniform(seed=random.randint(0, 1000))(w.shape) if w.ndim > 1 else w for w in model.get_weights()]

请注意 "if wdim > 1 else w"。您不希望重新初始化偏置(它们保持为0或1)。

0

使用keras.backend.clear_session()


1
这个问题已经有8个其他答案了。您能否解释一下您的答案,因为这样一个干巴巴的答案在这么晚的时间可能对其他读者没有太大帮助。 - Simas Joneliunas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接