Sutskever 30 #04:记住了,然后呢?

上一篇留下的问题 上一篇里,LSTM 解决了 vanilla RNN 记不远的问题——加法通道让梯度不再衰减,记忆可以跨越几十步传下去。 但我们一直在做同一件事:给定一个序列,预测下一个 token。输入和输出共享同一个时间线——第 1 步输入产生第 1 步输出,第 2 步输入产生第 2 步输出。 现实世界的很多任务不是这样的。翻译:一句中文进去,一句英文出来,长度不同,语序不同。摘要:一段话进去,一句话出来。对话:一个问题进去,一个回答出来。 输入是一个序列,输出是另一个序列。长度可以不同,结构可以不同。怎么办? 两个 LSTM,一根管子 2014 年,Sutskever、Vinyals 和 Le 提出了一个极简的方案:用两个 LSTM,中间接一根管子。 Encoder:一个 LSTM,把输入序列从头读到尾。读完之后,最后一步的 hidden state 就是整个输入的"总结"——一个固定长度的向量。 Decoder:另一个 LSTM,拿到这个向量作为初始状态,一个 token 一个 token 地生成输出。 就这么简单。输入和输出可以不同长度,因为 encoder 把任意长度的输入压缩成一个固定长度的向量,decoder 从这个向量展开成任意长度的输出。 代码的核心就几行(这里用简化的 RNN 展示结构,实际论文用的是 LSTM——gate 的细节上一篇讲过了): # Encoder:读完整个输入,拿到最后的 hidden state h = np.zeros((hidden_size, 1)) for x in input_sequence: concat = np.vstack([x, h]) h = np.tanh(np.dot(W_enc, concat) + b_enc) context = h # 这就是"总结"——整个输入压缩成一个向量 # Decoder:从 context 开始,逐步生成输出 h = context for t in range(max_output_length): concat = np.vstack([prev_output, h]) h = np.tanh(np.dot(W_dec, concat) + b_dec) output = np.dot(W_out, h) + b_out prev_output = output 这个架构有个名字——encoder-decoder,也叫 seq2seq(sequence to sequence)。它是 Sutskever 自己的工作,发表时直接打破了法语-英语翻译的最佳纪录。 ...

April 17, 2026