错误:在Julia中使用Flux的“ DimensionMismatch(“矩阵A的尺寸为(1024,10),向量B的长度为9”)“

时间:2019-09-28 00:54:23

标签: machine-learning julia lstm flux-machine-learning

我在Julia和机器学习方面还是一个新手,但是我非常渴望学习。在当前项目中,我正在处理尺寸不匹配的问题,无法解决该问题。

我有两个数组,如下所示:

x_array: 
9-element Array{Array{Int64,N} where N,1}:
 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 72, 73]
 [11, 12, 13, 14, 15, 16, 17, 72, 73]
 [18, 12, 19, 20, 21, 22, 72, 74]
 [23, 24, 12, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 72, 74]
 [36, 37, 38, 39, 40, 38, 41, 42, 72, 73]
 [43, 44, 45, 46, 47, 48, 72, 74]
 [49, 50, 51, 52, 14, 53, 72, 74]
 [54, 55, 41, 56, 57, 58, 59, 60, 61, 62, 63, 62, 64, 72, 74]
 [65, 66, 67, 68, 32, 69, 70, 71, 72, 74]


y_array:
9-element Array{Int64,1}
 75
 76
 77
 78
 79
 80
 81
 82
 83

以及使用Flux的下一个模型:

model = Chain(
    LSTM(10, 256),
    LSTM(256, 128),
    LSTM(128, 128),
    Dense(128, 9),
    softmax
)

我压缩两个数组,然后使用Flux.train将它们输入模型中!

data = zip(x_array, y_array)
Flux.train!(loss, Flux.params(model), data, opt)

并立即引发下一个错误:

ERROR: DimensionMismatch("matrix A has dimensions (1024,10), vector B has length 9")

现在,我知道矩阵A的第一维是隐藏层的总和(256 + 256 + 128 + 128 + 128 + 128),第二维是输入层,即10。我所做的就是将10更改为9,但随后只会引发错误:

ERROR: DimensionMismatch("dimensions must match")

有人可以向我解释不匹配的尺寸是什么,以及如何使它们匹配吗?

1 个答案:

答案 0 :(得分:2)

简介

首先,您应该知道,从体系结构的角度来看,您正在从网络中提出一些非常困难的问题; softmax将输出重新规范化为01之间的权重(像概率分布一样加权),这意味着要求您的网络输出77之类的值来匹配{{ 1}}将是不可能的。这不是导致尺寸不匹配的原因,但需要注意。我将在最后放下y,以给网络一个战斗的机会,尤其是因为它不是问题的根源。

调试形状不匹配

让我们逐步了解softmax()内部的实际情况。 The definition实际上非常简单。忽略对我们无关紧要的一切,我们只剩下:

Flux.train!()

因此,让我们首先从for d in data gs = gradient(ps) do loss(d...) end end 中提取第一个元素,然后将其放入data函数中。您没有在问题中指定损失函数或优化器。尽管loss通常意味着您应该使用softmax损失,但是您的crossentropy值几乎不是概率,因此,如果我们放弃y,我们可以使用简单的方法softmax损失。对于优化程序,我们将默认使用旧的ADAM:

mse()

现在,为了模拟model = Chain( LSTM(10, 256), LSTM(256, 128), LSTM(128, 128), Dense(128, 9), #softmax, # commented out for now ) loss(x, y) = Flux.mse(model(x), y) opt = ADAM(0.001) data = zip(x_array, y_array) 的第一次运行,我们将Flux.train!()并将其吐入first(data)

loss()

这给了我们您以前看过的错误信息; loss(first(data)...) 。查看我们的数据,可以看到,确实,数据集的第一个元素的长度为12。因此,我们将更改模型以改为期望12个值而不是10个值:

ERROR: DimensionMismatch("matrix A has dimensions (1024,10), vector B has length 12")

现在我们重新运行:

model = Chain(
    LSTM(12, 256),
    LSTM(256, 128),
    LSTM(128, 128),
    Dense(128, 9),
)

Huzzah!有效!我们可以再次运行:

julia> loss(first(data)...)
       50595.52542674723 (tracked)

该值会发生变化,因为RNN自身会保留内存,每次运行网络时该内存都会更新,否则我们将期望网络为相同的输入给出相同的答案!

但是,当我们尝试通过我们的网络运行第二个训练实例时,问题就来了

julia> loss(first(data)...)
        50578.01417593167 (tracked)

了解LSTM

这是我们遇到机器学习问题而不是编程问题的原因;这里的问题是,我们已经承诺向第一个julia> loss([d for d in data][2]...) ERROR: DimensionMismatch("matrix A has dimensions (1024,12), vector B has length 9") 网络提供长度为LSTM的向量(现在是10),而我们正在兑现这一承诺。这是深度学习的一般规则;您始终必须遵守所签署的关于流经模型的张量形状的合同。

现在,您完全使用LSTM的原因可能是因为您想要输入参差不齐的数据,对其进行细化,然后对结果进行一些处理。也许您正在处理长度可变的句子,并且想要进行情感分析,等等。 LSTM之类的循环体系结构的优点在于,它们能够将信息从一次执行传递到另一次执行,因此当应用于另一个时间点时,它们能够构建序列的内部表示。

在Flux中构建12层时,因此不是在声明要输入的序列的长度,而是在声明每个时间点的维数。假设您的加速度计读数长了1000点,并在每个时间点给出了X,Y,Z值;要了解这一点,您将创建一个LSTM,它的维数为LSTM,然后将其3喂入几次。

编写我们自己的训练循环

我发现编写我们自己的训练循环和模型执行功能非常有启发性,因此我们可以完全控制所有内容。在处理时间序列时,通常很容易混淆如何调用LSTM和Dense层以及诸如此类的东西,因此我提供了以下简单的经验法则:

  • 当从一个时间序列映射到另一个时间序列时(例如,不断地根据先前的运动预测未来的运动),您可以使用单个1000并循环调用;对于每个输入时间点,您将输出另一个。

  • 当从时间序列映射到单个“输出”时(例如,将句子简化为“快乐情绪”或“悲伤情绪”),您必须首先将所有数据压缩并减小为固定大小;您输入了很多东西,但最后只有一个出来。

我们将把模型重新构造为两部分:首先是递归的“ pacman”部分,在这里我们将可变长度的时间序列切成预定长度的内部状态向量,然后是前馈部分,将内部状态向量缩减为单个输出:

Chain

之所以将其分成两部分是因为问题陈述希望我们将可变长度输入序列减少为一个数字;我们在上面的第二个要点。因此,我们的代码自然必须考虑到这一点;我们将编写pacman = Chain( LSTM(1, 128), # map from timepoint size 1 to 128 LSTM(128, 256), # blow it up even larger to 256 LSTM(256, 128), # bottleneck back down to 128 ) reducer = Chain( Dense(128, 9), #softmax, # keep this commented out for now ) 函数,而不是调用loss(x, y),而是执行pacman舞,然后在输出上调用reducer。请注意,我们还必须model(x)的RNN状态,以便为每个独立的训练示例清除内部状态:

reset!()

将其输入function loss(x, y) # Reset internal RNN state so that it doesn't "carry over" from # the previous invocation of `loss()`. Flux.reset!(pacman) # Iterate over every timepoint in `x` for x_t in x y_hat = pacman(x_t) end # Take the very last output from the recurrent section, reduce it y_hat = reducer(y_hat) # Calculate reduced output difference against `y` return Flux.mse(y_hat, y) end 实际上可以训练,尽管效果不是很好。 ;)

最终观察结果

  • 尽管您的数据都是Flux.train!()的数据,但很典型的情况是将浮点数用于除嵌入之外的所有内容(嵌入是一种获取非数字数据(例如字符或单词)并进行赋值的方法给他们的数字,有点像ASCII);如果要处理文本,几乎可以肯定会进行某种形式的嵌入,而这种嵌入将决定第一个LSTM的维数,然后所有输入都将被“一次性”编码。

  • Int64用于预测概率;它将确保对于每个输入,输出都在softmax之间,而且它们的总和为[0...1],就像一个很小的概率分布一样。这在进行分类时非常有用,当您要将1.0的狂野网络输出值整理成某种东西时,您可以说“我们[-2, 5, 0.101]确定第二类是正确的,而{{1 }}确定是第三类。“

  • 在训练这些网络时,出于硬件效率的原因,您通常经常希望一次通过网络批处理多个时间序列。这既简单又复杂,因为一方面它意味着不传递单个99.1%向量(其中0.7%是嵌入的大小),而是传递Sx1矩阵,但这也意味着批处理中所有内容的时间步数必须匹配(因为S在所有时间步中必须保持相同,因此如果一个时间序列在任何时间步长之前结束您不能将其丢弃,从而减少SxN到批次的一半)。因此,大多数人要做的就是将时间序列全部填充到相同的长度。

在您的机器学习之旅中祝您好运!