MLAlgorithms RNN与LSTM深度解析:二进制加法与文本生成两大经典实现
2026/9/19 13:20:00 网站建设 项目流程

MLAlgorithms RNN与LSTM深度解析:二进制加法与文本生成两大经典实现

【免费下载链接】MLAlgorithmsMinimal and clean examples of machine learning algorithms implementations项目地址: https://gitcode.com/gh_mirrors/ml/MLAlgorithms

MLAlgorithms 是一个用极简、干净的 Python 代码从零实现机器学习算法的开源项目,其中 mla/neuralnet/layers/recurrent/ 下的 RNN 与 LSTM 层,配合 examples/nnet_rnn_binary_add.py 的二进制加法和 examples/nnet_rnn_text_generation.py 的文本生成两个示例,是理解循环神经网络内部机制的绝佳起点。本文带你读懂这两个经典实现的核心思想与代码结构。

为什么选择这个项目学习 RNN?

大多数深度学习框架(如 Keras、PyTorch)封装得很好用,但"黑盒"感也强。如果你想知道:

  • 隐藏状态h到底是怎么一步步更新的?
  • 梯度是怎么"沿时间反向传播"(BPTT)的?
  • LSTM 的门控公式在代码里长什么样?

MLAlgorithms 的答案就是:不依赖任何框架,只用 numpy + autograd 手写全部核心逻辑,代码量小到可以逐行读懂。RNN 相关代码集中在以下位置:

文件作用
mla/neuralnet/layers/recurrent/rnn.py原生 RNN 层(前向 + BPTT 反向传播)
mla/neuralnet/layers/recurrent/lstm.pyLSTM 层(四个门控的完整实现)
mla/neuralnet/nnet.py通用神经网络容器,串联各层完成训练

RNN 核心原理:一个状态矩阵记住过去

原生 RNN 的精髓可以用一句话概括:每个时间步的输出,都依赖上一个时间步的隐藏状态

在 rnn.py 的forward_pass(L55-L74)中,核心循环极其精炼:

for i in range(n_timesteps): states[:, i, :] = np.tanh( np.dot(X[:, i, :], p["W"]) # 当前输入的影响 + np.dot(states[:, i - 1, :], p["U"]) # 上一步隐藏状态的影响 + p["b"] )

其中W是"输入→隐藏"权重,U是"隐藏→隐藏"权重,tanh把状态压缩到 (-1, 1)。整段序列的隐藏状态被存进一个(样本数, 时间步数, 隐藏维度)的张量states

反向传播在backward_pass(L76-L104)中完成:沿时间轴倒序遍历每个时间步,把当前步的误差delta和来自未来的误差dh_next合并后回传,这就是经典的 BPTT。初学者可以对照这两个函数,亲手推一遍每个矩阵的维度变化——这是理解 RNN 最快的方式。

⚠️ 原生 RNN 的已知短板:梯度沿时间反向传播时容易消失或爆炸,难以学习长距离依赖。这正是 LSTM 登场的理由。

LSTM 核心原理:用"门"精细控制记忆流动

LSTM 引入了**细胞状态(cell)**这条"记忆高速公路",并用四个门来控制信息的写入、遗忘与读出。lstm.py 在setup(L47-L85)中清晰地定义了参数命名约定:

  • i(input gate,输入门)
  • f(forget gate,遗忘门)
  • o(output gate,输出门)
  • c(cell,细胞状态)

每个门各有一套W(输入权重)、U(隐藏权重)和b(偏置),共 12 个参数矩阵。

前向过程(L87-L131)的每一步只做了三件关键的事:

  1. 算门:三个门过sigmoid(输出 0~1 的"开关程度"),细胞候选值过tanh
  2. 更新记忆新状态 = 旧状态 × 遗忘门 + 输入门 × 细胞候选值(L117-L120)——这就是"有选择地记住或遗忘";
  3. 输出输出 = 输出门 × tanh(新状态)

正因为存在这条可以长期保持数值的细胞状态,LSTM 能学到 RNN 学不到的长距离依赖。反向传播(L133-L189)则分别对四个门求导,结构上比 RNN 多了一层"门控导数",但思路完全一致:倒序遍历时间步,累积各权重矩阵的梯度

实战一:二进制加法——让网络"学会算术"

这是循环网络最迷人的演示之一:不教网络任何数学知识,只给它成对的二进位数,它就能自己"悟出"进位规则。

示例代码在 nnet_rnn_binary_add.py:

  • 数据生成(L20-L53):随机取两个 8 位以内的数,转成二进制串并低位在前排好(加法从最低位开始,天然适合逐位顺序处理),形状为(样本数, 8, 2)
  • 模型结构(L56-L72):只有三层——LSTM(16)TimeDistributedDense(1)sigmoid,损失函数用均方误差mse,优化器用 Adam;
  • 评估技巧(L69-L72):预测值四舍五入后,用np.packbits把 0/1 位串打包回整数再比对,直接算出加法准确率。

值得注意的一个细节:注释掉的 RNN 版本(L76)特意给权重加了SmallNorm约束(见 mla/neuralnet/constraints.py)来抑制梯度爆炸,而 LSTM 版本直接用LSTM(16)就能稳定训练——这本身就是两个模型能力差异的直观证明。

实战二:文本生成——模仿尼采的写作风格

第二个示例 nnet_rnn_text_generation.py 让网络学习尼采著作的语料,然后自动"续写"出风格相近的文字(思路与 Keras 官方lstm_text_generation示例一致)。

1️⃣ 语料准备

mla/datasets/base.py 的load_nietzsche(L58-L78)读取 nietzsche.txt,把文本切成大量"40 个字符的句子 + 第 41 个字符作为标签"的滑动窗口对,并把字符 one-hot 编码成(句数, 40, 字符表大小)的矩阵。语料文件就在 mla/datasets/data/nietzsche.txt。

2️⃣ 模型与训练

结构为LSTM(128, return_sequences=False)Dense(字符数)softmax,损失用categorical_crossentropy,优化器 RMSprop(L44-L58)。外层用 for 循环反复训练 25 轮,每轮都从原文随机抽一句当"种子"现场生成,可以直观感受模型随训练逐步"像人话"的过程。

3️⃣ 温度采样

sample函数(L22-L29)是文本生成的灵魂:把预测概率取对数、除以温度temperature再 softmax,最后按概率抽样。示例中temperature=0.5让分布更"尖",生成的文字更确定;调大到 1.0 以上则更随机、更有"创造力"。

快速上手:三步跑通两个示例

git clone https://gitcode.com/gh_mirrors/ml/MLAlgorithms cd MLAlgorithms pip install scipy numpy && pip install -e .

然后任选其一运行(无需安装也可直接以模块方式运行):

python -m examples.nnet_rnn_binary_add python -m examples.nnet_rnn_text_generation

不想本地配置?项目还附带 Dockerfile,一行docker build -t mlalgorithms .即可在容器里跑全部示例。

两个示例对比与学习建议

维度二进制加法文本生成
任务类型逐位回归(0/1)字符级分类
输出层TimeDistributedDense+ sigmoidDense+ softmax
损失函数msecategorical_crossentropy
关键技巧低位在先的位序设计温度采样控制随机性

建议的学习路径:

  1. 先读 rnn.py:只约 110 行,逐行推一遍前向/反向的张量形状;
  2. 再读 lstm.py:对照四个门的命名注释理解参数结构;
  3. 跑通两个示例:把LSTM换成RNN、把temperature调来调去,观察训练曲线和生成质量的变化——亲手实验比看十篇博客都有效。

当你真正读懂了这两份代码,再回头看框架里"黑盒"的RNNLSTM模块时,你会发现它们不过就是这几行循环的封装罢了。🎯

【免费下载链接】MLAlgorithmsMinimal and clean examples of machine learning algorithms implementations项目地址: https://gitcode.com/gh_mirrors/ml/MLAlgorithms

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询