如何在tensorflow中制作Conv层中的一个/一些过滤器



我的意思是:假设有64个通道从Conv内核输出,我只想训练64个通道中的通道1。

这个问题来自于阅读这篇论文https://arxiv.org/abs/1911.09659.

在本文中,它建议我们可以冻结一些过滤器,并在迁移学习中训练其余的过滤器。

然而,我想知道如何在Tensorflow中实现它。

如果我们能够冻结一些层,那么很明显,只需迭代网络中的层,并使其可训练布尔值为False。

然而,当涉及到内核时,它会比我想象的更麻烦。

我发现了这个方法——在TensorFlow训练的模型中获取一些权重的值。

我试着把所有的重量都放成这样:

def get_weights_bias(model, layer_name):
"""
This function aims to extract kernel and its bias from the original weight
:param model: the model we want to extract weight from
:param layer_name: the name of the layer we want to extract the weight from
:return: kernel_list and bias_list of particular layer
"""
for layer in model.layers:
if layer_name == layer.name:
weights = layer.get_weights()
print(type(weights))
print(weights[0].shape, type(weights[0])) # weights
print(weights[1].shape, type(weights[1])) # biases
kernel_list = []
bias_list = weights[1]
print(type(bias_list))
for j in range(weights[0].shape[-1]):
name_weight = layer_name + "_weight_" + str(j)
kernel = tf.Variable(initial_value=weights[0][:, :, :, j], name=name_weight, trainable=True)
kernel = tf.expand_dims(kernel, -1)
kernel_list.append(kernel)
return kernel_list, bias_list

根据这个答案,我遇到了一些问题。我发现很难将它们恢复到conv层,因为layer.set_weights()只接受numpy数组,而不接受tf.Variable.

有什么建议吗??

据我所知,没有一种干净的方法可以冻结内核并使用keras API保持可训练的偏差。尽管如此,以下是使用私有属性的方法:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, Dense
# initialize the model
m = Sequential([Conv2D(1,(3,3), input_shape=(3,3,1)),Dense(1,)])
for layer in m.layers:
# pop the kernel from the trainable weights
kernel_w = layer._trainable_weights.pop(0)
# add it to the non trainable weights
layer._non_trainable_weights.append(kernel_w)

检查其是否工作

一个小实验,看看这个技巧是否奏效:让我们看看密集层的内核和偏差:

>>> m.layers[1].kernel
<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[0.586058]], dtype=float32)>
>>> m.layers[1].bias
<tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>

然后我们编译我们的模型,并在伪数据上训练它:

import numpy as np
# random data
X = np.random.random((100,3,3,1))
y = 5 + X[:,0,0,0] * 34 
# training
m.compile(loss='mse')

如果我们再次检查我们的重量:

>>> m.layers[1].kernel
<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[0.586058]], dtype=float32)>
>>> m.layers[1].bias
<tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([0.00957833], dtype=float32)>

偏见改变了,但内核没有改变!


注意事项

您必须小心使用此方法:内核可能并不总是位于层的索引0处。检查要使用的层及其内核的索引。就我所测试的而言,它适用于密集层和Conv2D层。

还要注意,此方法依赖于Layer类的私有属性。它目前有效,但有一天可能会坏掉。

相关内容

最新更新