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

标签 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")


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

最佳答案

介绍

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

调试形状不匹配

让我们来看一下Flux.train!()内部实际发生的情况。 The definition实际上非常简单。忽略对我们无关紧要的一切,我们只剩下:

for d in data
    gs = gradient(ps) do
        loss(d...)
    end
end


因此,让我们首先从data中拉出第一个元素,然后将其放入loss函数中。您没有在问题中指定损失函数或优化器。尽管softmax通常意味着您应该使用crossentropy损失,但是您的y值几乎不是概率,因此,如果我们删除softmax,我们就可以使用简单的mse()损失。对于优化程序,我们将默认使用旧的ADAM:

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)...)


这为我们提供了您之前看到的错误消息; ERROR: DimensionMismatch("matrix A has dimensions (1024,10), vector B has length 12")。查看我们的数据,可以看到,确实,数据集的第一个元素的长度为12。因此,我们将更改模型以改为期望12个值而不是10个值:

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


现在我们重新运行:

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


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

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


该值之所以会更改,是因为RNN自身拥有内存,每次运行网络时该内存都会更新,否则我们将期望网络为相同的输入给出相同的答案!

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

julia> loss([d for d in data][2]...)
ERROR: DimensionMismatch("matrix A has dimensions (1024,12), vector B has length 9")


了解LSTM

这是我们遇到机器学习问题而不是编程问题的地方。这里的问题是,我们已经承诺向第一个LSTM网络提供长度为10的向量(现在是12),而我们正在兑现这一承诺。这是深度学习的一般规则;您始终必须遵守关于流经模型的张量形状的合同。

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

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

编写我们自己的训练循环

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


当从一个时间序列映射到另一个时间序列时(例如,不断预测前一个运动的未来运动),您可以使用单个Chain并循环调用它;对于每个输入时间点,您将输出另一个。
从时间序列映射到单个“输出”(例如,将句子简化为“快乐情绪”或“悲伤情绪”)时,您必须首先将所有数据压缩并减小为固定大小;您输入了很多东西,但最后只有一个出来。


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

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)函数,而不是调用model(x),而是执行pacman跳舞,然后在输出上调用reducer。请注意,我们还必须reset!() RNN状态,以便为每个独立的训练示例清除内部状态:

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!()实际上可以训练,尽管效果不是很好。 ;)

最终观察


尽管您的数据都是Int64的,但在除嵌入之外的所有内容上都使用浮点数是很典型的(嵌入是一种获取非数字数据(例如字符或单词)并为其分配数字的方式,类似于ASCII );如果要处理文本,几乎可以肯定会进行某种形式的嵌入,而这种嵌入将决定第一个LSTM的维数,然后所有输入都将被“一次性”编码。
要预测概率时使用softmax;它将确保每个输入的所有输出都在[0...1]之间,而且它们的总和等于1.0,就像一个很小的概率分布一样。这在进行分类时最有用,当您想将[-2, 5, 0.101]的狂野网络输出值整理成某种内容时,您可以说“我们有99.1%确定第二类是正确的,而0.7%确定是第三类是正确的类。”
训练这些网络时,出于硬件效率的考虑,您经常需要一次通过网络批量处理多个时间序列。这既简单又复杂,因为一方面它只是意味着要传递一个Sx1而不是传递单个S向量(其中SxN是嵌入的大小),矩阵,但这也意味着批处理中所有内容的时间步数必须匹配(因为SxN必须在所有时间步中保持相同,因此,如果一个时间序列在批处理中的任何其他时间序列之前都结束了,只需将其丢弃,从而在批处理的一半处减少N)。因此,大多数人所做的就是将其时间序列全部填充到相同的长度。


祝您在机器学习旅程中一切顺利!

关于machine-learning - 错误:在Julia中使用Flux的“DimensionMismatch(”矩阵A的尺寸为(1024,10),向量B的长度为9”),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58142582/

相关文章:

parsing - 将大型文档/文本/HTML 分解为片段的良好 ML 模型/技术是什么?

vector - 使用 julia 迭代器作为常规向量

keras - LSTM 单元的数量与要训练的序列长度之间是否存在关系?

machine-learning - 如何在 Keras 中配置双向 LSTM 的输入形状

python - pytorch .stack .squeeze后的最终形状

python - 在 scikit 中绘制 ROC 曲线仅产生 3 分

Julia 中的调试选项

iteration - Julia 的反向迭代

python - 用于反转整数序列的 LSTM

python - Scikit-learn 逻辑回归的性能比用 Python 自行编写的逻辑回归要差