Machine learning 时间分布(稠密)与Keras中的稠密-相同数量的参数

Machine learning 时间分布(稠密)与Keras中的稠密-相同数量的参数,machine-learning,neural-network,keras,recurrent-neural-network,keras-layer,Machine Learning,Neural Network,Keras,Recurrent Neural Network,Keras Layer,我正在构建一个模型,该模型使用递归层(GRU)将一个字符串转换为另一个字符串。我试过将稠密层和时间分布(稠密)层作为最后一层,但当使用return_sequences=True时,我不理解这两个层之间的区别,特别是因为它们似乎具有相同数量的参数 我的简化模型如下: InputSize = 15 MaxLen = 64 HiddenSize = 16 inputs = keras.layers.Input(shape=(MaxLen, InputSize)) x = keras.layers.r

我正在构建一个模型,该模型使用递归层(GRU)将一个字符串转换为另一个字符串。我试过将稠密层和时间分布(稠密)层作为最后一层,但当使用return_sequences=True时,我不理解这两个层之间的区别,特别是因为它们似乎具有相同数量的参数

我的简化模型如下:

InputSize = 15
MaxLen = 64
HiddenSize = 16

inputs = keras.layers.Input(shape=(MaxLen, InputSize))
x = keras.layers.recurrent.GRU(HiddenSize, return_sequences=True)(inputs)
x = keras.layers.TimeDistributed(keras.layers.Dense(InputSize))(x)
predictions = keras.layers.Activation('softmax')(x)
该网络概述如下:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 64, 15)            0         
_________________________________________________________________
gru_1 (GRU)                  (None, 64, 16)            1536      
_________________________________________________________________
time_distributed_1 (TimeDist (None, 64, 15)            255       
_________________________________________________________________
activation_1 (Activation)    (None, 64, 15)            0         
=================================================================
这对我来说很有意义,因为我对TimeDistributed的理解是,它在所有时间点应用相同的层,因此密集层有16*15+15=255个参数(权重+偏差)

但是,如果切换到简单的密集层:

inputs = keras.layers.Input(shape=(MaxLen, InputSize))
x = keras.layers.recurrent.GRU(HiddenSize, return_sequences=True)(inputs)
x = keras.layers.Dense(InputSize)(x)
predictions = keras.layers.Activation('softmax')(x)
我仍然只有255个参数:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 64, 15)            0         
_________________________________________________________________
gru_1 (GRU)                  (None, 64, 16)            1536      
_________________________________________________________________
dense_1 (Dense)              (None, 64, 15)            255       
_________________________________________________________________
activation_1 (Activation)    (None, 64, 15)            0         
=================================================================
我想知道这是否是因为Dense()将只使用形状中的最后一个维度,并有效地将所有其他维度视为类似于批处理的维度。但是我不再确定稠密和时间分布(稠密)之间的区别

更新看一看,Dense似乎只使用最后一个维度来调整自身大小:

def构建(自身,输入形状):
断言len(输入_形状)>=2
输入尺寸=输入形状[-1]
self.kernel=self.add\u weight(形状=(输入尺寸,self.units),
它还使用keras.dot应用权重:

def调用(自身,输入):
输出=K.dot(输入,self.kernel)

keras.dot的文档暗示它在n维张量上工作得很好。我想知道它的确切行为是否意味着每个时间步实际上都会调用稠密()。如果是这样,问题仍然是时间分布是什么()在这种情况下实现。

TimeDistributedDense
在GRU/LSTM单元展开期间对每个时间步应用相同的密度。因此,误差函数将在预测标签序列和实际标签序列之间。(这通常是序列到序列标签问题的要求)

但是,对于
return\u sequences=False
densite
层仅在最后一个单元格应用一次。这通常是RNN用于分类问题时的情况。如果
return\u sequences=True
densite
层将应用于每个时间步,就像
timedistributedense
一样

因此,根据您的模型,这两个模型都是相同的,但是如果您将第二个模型更改为
return\u sequences=False
,那么
densite
将仅应用于最后一个单元格。尝试更改它,模型将作为错误抛出,因为
Y
的大小将
[批处理大小,输入大小]
,这不再是一个序列到序列的问题,而是一个完整的序列到标签的问题

from keras.models import Sequential
from keras.layers import Dense, Activation, TimeDistributed
from keras.layers.recurrent import GRU
import numpy as np

InputSize = 15
MaxLen = 64
HiddenSize = 16

OutputSize = 8
n_samples = 1000

model1 = Sequential()
model1.add(GRU(HiddenSize, return_sequences=True, input_shape=(MaxLen, InputSize)))
model1.add(TimeDistributed(Dense(OutputSize)))
model1.add(Activation('softmax'))
model1.compile(loss='categorical_crossentropy', optimizer='rmsprop')


model2 = Sequential()
model2.add(GRU(HiddenSize, return_sequences=True, input_shape=(MaxLen, InputSize)))
model2.add(Dense(OutputSize))
model2.add(Activation('softmax'))
model2.compile(loss='categorical_crossentropy', optimizer='rmsprop')

model3 = Sequential()
model3.add(GRU(HiddenSize, return_sequences=False, input_shape=(MaxLen, InputSize)))
model3.add(Dense(OutputSize))
model3.add(Activation('softmax'))
model3.compile(loss='categorical_crossentropy', optimizer='rmsprop')

X = np.random.random([n_samples,MaxLen,InputSize])
Y1 = np.random.random([n_samples,MaxLen,OutputSize])
Y2 = np.random.random([n_samples, OutputSize])

model1.fit(X, Y1, batch_size=128, nb_epoch=1)
model2.fit(X, Y1, batch_size=128, nb_epoch=1)
model3.fit(X, Y2, batch_size=128, nb_epoch=1)

print(model1.summary())
print(model2.summary())
print(model3.summary())

在上面的示例架构中,
model1
model2
是示例(序列到序列模型),而
model3
是标记模型的完整序列。

这里有一段代码验证
时间分布(密集(X))
密集(X)
相同:

(2,4,3)

(3,5)

(2,4,5)

(2,?,5)

区别在于:

[[[0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]]]

谢谢你的回答。我不确定我是否能理解,因为我知道这两种情况下的输出都是一个序列。在这两种情况下,递归层的return\u sequences=True,并且两种情况下的输出形状都是3D的,并且完全相同(batch\u size,64,15)。因此,在我看来,密集层也适用于每个时间步。我已更新了我的答案,并提供了更好的解释,希望它能帮助您。谢谢。为避免疑问,当您说“根据您的模型,两者都是相同的,但如果您将第二个模型更改为“return_sequences=True”,则密集层将仅适用于最后一个单元格。”-你的意思是如果我将return\u序列更改为False吗?你的回答似乎暗示如果return\u序列为True,则Dense()和TimeDistributed(Dense())做完全相同的事情。你能确认这一点吗?这是有道理的,但为什么Keras需要TimeDistributed()?再次感谢。是的,我同意一个人需要TimeDistributed()对于其他层类型。在我看来,在返回序列的循环层之后使用一个简单的Dense()确实有效,但更多的是出于偶然,而不是出于设计。从旧的Keras示例中,我认为曾经有一个TimeDistributedDense()-对于我来说,如果Dense()为什么需要它仍然是一个谜无论如何都会奏效。嗨@thon,我也得出了同样的结论。这也很奇怪,因为Dense()应该在大于2的情况下展平输入维度,如文档中引用的:“注意:如果层的输入的秩大于2,那么它在带内核的初始点积之前展平”.你找到答案了吗?让我补充一下,这两个模型在培训期间的表现几乎完全相同。我也一直在想这个问题。所以你确认了稠密()和时间分布(稠密())在您的情况下是否具有相同的性能?我认为更好的API设计应该允许用户设置一个参数,是在时间步长上使用相同的密集层,还是在每个时间步长上使用单独的密集层。在您的情况下,密集和时间分布(密集)根据您的更新具有相同的结果(从更新的角度来看,Dense似乎只使用最后一个维度来调整自身的大小)。这是错误的;会话图结果并不等同于完整的模型图结果-后者涉及渐变和权重更新。从,“如果层的输入具有大于2的秩,那么它在与内核的初始点积之前被展平”——与之形成对比的是
timedistributedense
,它不会展平。
dense_weights = np.array([[0.1, 0.2, 0.3, 0.4, 0.5],
                          [0.2, 0.7, 0.9, 0.1, 0.2],
                          [0.1, 0.8, 0.6, 0.2, 0.4]])
bias = np.array([0.1, 0.3, 0.7, 0.8, 0.4])
print(dense_weights.shape)
dense = Dense(input_dim=3, units=5, weights=[dense_weights, bias])
input_tensor = tf.Variable(X, name='inputX')
output_tensor1 = dense(input_tensor)
output_tensor2 = TimeDistributed(dense)(input_tensor)
print(output_tensor1.shape)
print(output_tensor2.shape)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    output1 = sess.run(output_tensor1)
    output2 = sess.run(output_tensor2)

print(output1 - output2)
[[[0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]]

 [[0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0.]]]