有没有办法删除模型内的特定神经元?
例如,我有一个包含 512 个神经元的密集层的模型。有没有办法删除在 list_indeces
内有索引的所有神经元?
当然,删除神经元会影响下一层,甚至前一层。
示例:
我在多篇论文中都有这个通用模型:
data_format = 'channels_last'
input_shape = [28, 28, 1]
max_pool = functools.partial(
tf.keras.layers.MaxPooling2D,
pool_size=(2, 2),
padding='same',
data_format=data_format)
conv2d = functools.partial(
tf.keras.layers.Conv2D,
kernel_size=5,
padding='same',
data_format=data_format,
activation=tf.nn.relu)
model = tf.keras.models.Sequential([
conv2d(filters=32, input_shape=input_shape),
max_pool(),
conv2d(filters=64),
max_pool(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(512, activation=tf.nn.relu),
tf.keras.layers.Dense(10 if only_digits else 62),
])
return model
假设我想从层 tf.keras.layers.Dense(512,activation=tf.nn.relu)
中删除 100 个神经元,基本上将它们关闭。
当然,我将有一个新模型,其中层为tf.keras.layers.Dense(412,activation=tf.nn.relu)
而不是tf.keras.layers.Dense (512,activation=tf.nn.relu)
但是这种修改也应该传播到下一层的权重,因为从密集层的神经元到下一层的连接也被删除了。
有关于如何执行此操作的任何意见吗?我可以通过执行以下操作手动执行此操作:
如果我正确理解的话,模型形状就是这个:[5, 5, 1, 32], [32], [5, 5, 32, 64], [64], [3136, 512 ], [512], [512, 62], [62]
所以我可以做这样的事情:
- 生成我需要的所有索引并在
list_indices
中将它们相同 - 访问图层
tf.keras.layers.Dense(512,activation=tf.nn.relu)
的权重,并使用list_indices内的所有权重创建一个张量
- 将新的权重张量分配给子模型的
tf.keras.layers.Dense(412,activation=tf.nn.relu)
层
问题是我不知道如何获得下一层权重的正确权重,该权重与我刚刚创建的权重索引以及我应该分配给子模型下一层的权重索引相对应。我希望我已经清楚地解释了自己。
谢谢, 莱拉。
最佳答案
您的操作在文献中被称为选择性丢失
,实际上并不需要每次都创建不同的模型,您只需要将所选神经元的输出乘以 0,这样下一层的输入不会考虑这些激活。
请注意,如果您“关闭”Ln
层中的一个神经元,它不会完全“关闭”Ln+1
层中的任何神经元,假设两者都是全连接层(密集):Ln+1
层中的每个神经元都连接到前一层中的所有 神经元。换句话说,删除全连接(密集)层中的神经元不会影响下一个神经元的维度。
您可以简单地使用 Multiply Layer
(Keras) 来实现此操作。缺点是你需要学习如何使用Keras functional API 。还有其他方法,但比这更复杂(例如自定义层),而且函数式 API 在很多方面都非常有用和强大,非常建议阅读!
你的模型会变成这样:
data_format = 'channels_last'
input_shape = [28, 28, 1]
max_pool = ...
conv2d = ...
# convert a list of indexes to a weight tensor
def make_index_weights(indexes):
# converting indexes to a list of weights
indexes = [ float(i not in indexes) for i in range(units) ]
# converting indexes from list/numpy to tensor
indexes = tf.convert_to_tensor(indexes)
# reshaping to the correct format
indexes = tf.reshape(indexes, (1, units))
# ensuring it is a float tensor
indexes = tf.cast(indexes, 'float32')
return indexes
# layer builder utility
def selective_dropout(units, indexes, **kwargs):
indexes = make_index_weights(indexes)
dense = tf.keras.layers.Dense(units, **kwargs)
mul = tf.keras.layers.Multiply()
# return the tensor builder
return lambda inputs: mul([ dense(inputs), indexes ])
input_layer = tf.keras.layers.Input(input_shape)
conv_1 = conv2d(filters=32, input_shape=input_shape)(input_layer)
maxp_1 = max_pool()(conv_1)
conv_2 = conv2d(filters=64)(maxp_1)
maxp_2 = max_pool()(conv_2)
flat = tf.keras.layers.Flatten()(maxp_2)
sel_drop_1 = selective_dropout(512, INDEXES, activation=tf.nn.relu)(flat)
dense_2 = tf.keras.layers.Dense(10 if only_digits else 62)(sel_drop_1)
output_layer = dense2
model = tf.keras.models.Model([ input_layer ], [ output_layer ])
return model
现在您只需根据需要删除的神经元的索引构建您的 INDEXES
列表即可。
在您的例子中,张量的形状为 1x512
,因为密集层中有 512 个权重(单位/神经元),因此您需要为索引提供尽可能多的权重。 selective_dropout
函数允许传递要丢弃的索引列表,并自动构建所需的张量。
例如,如果您想删除神经元 1, 10, 12,您只需将列表 [1, 10, 12]
传递给函数,它将生成一个 1x512
code> 张量在这些位置上为 0.0
,在所有其他位置上为 1.0
。
编辑:
正如您所提到的,您严格需要减少模型中参数的大小。
每个密集层由关系y = Wx + B
描述,其中W
是内核(或权重矩阵),B
是偏置向量。 W
是 INPUTxOUTPUT
维度的矩阵,其中 INPUT
是最后一层输出形状,OUTPUT
是层中的神经元/单元/权重; B
只是维度 1xOUTPUT
的向量(但我们对此不感兴趣)。
现在的问题是,您在 Ln
层中删除了 N
个神经元,这导致了 NxOUTPUT
层中权重的下降Ln+1
。让我们用一些数字来实践一下。在您的情况下(假设 only_digits
为 true),您可以从以下内容开始:
Nx512 -> 512x10(5120 权重)
丢掉 100 个神经元后(意味着丢掉 100*10=1000 个权重)
Nx412 -> 412x10(4120 权重)
现在,W
矩阵的每一列描述一个神经元(作为权重向量,其维度等于前一层输出维度,在我们的例子中为 512 或 412)。矩阵的行代表前一层中的单个神经元。
W[0,0]
表示第n
层第一个神经元与第n+1
层第一个神经元之间的关系。
W[0,0] -> 第一个 n,第一个 n+1
W[0,1] -> 第二个 n,第一个 n+1
W[1,0] -> 第一个 n,第二个 n+1
等等。因此,您可以从该矩阵中删除与您删除的神经元索引相关的所有行:index 0 -> row 0
。
您可以使用dense.kernel
从密集层访问W
矩阵作为张量
关于python - 如何删除 Model Tensorflow Keras 中的特定神经元,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69842494/