Maksim Sekretov 发布了一个微型的、纯 JavaScript 实现的 transformer,它可以在 Node.js 上运行,并在训练过程中展示每一个标量、权重和梯度。tiny-language-model-neuro-js 仓库让任何人都可以从一个随机模型开始,输入像 “can human read ?” 这样的提示词,看着它失败,然后运行单条命令进行训练,并亲眼见证答案变得正确。
为什么大多数 LLM 演示都隐藏了数学原理
典型的语言模型教程依赖于 TensorFlow 或 PyTorch。这些框架会在后台自动构建计算图并计算梯度,因此学习者只能看到高层代码和损失曲线。底层的操作——每个 token 如何变成嵌入(embedding)、注意力分数如何计算、梯度如何通过每个矩阵进行反向传播——都是不可见的。
这个 JavaScript 模型有何不同之处
Sekretov 的实现将一切简化为一个无依赖的 Node.js 脚本。训练流水线遵循一个直观的序列:
- 分词 (Tokenization)
- 嵌入 (Embeddings)
- 因果 Transformer 模块 (Causal transformer blocks)
- 多头自注意力机制 (Multi-head self-attention)
- 前馈网络 (FFN)
- 语言模型头和 softmax
- 交叉熵损失 (Cross-entropy loss)
- 反向传播 (Backpropagation)
运行 node src/train.js --generalize --adaptive-teach 即可启动完整的训练循环。因为代码是纯 JavaScript,每个张量(tensor)都是一个简单的数组,每次矩阵乘法都是一个显式的循环,每个梯度都紧挨着它的源权重。
从随机猜测到正确答案
当模型启动时,所有参数都是随机的。询问它 “can human read ?” 会得到一堆乱码。经过短暂的预训练阶段和一轮有监督微调 (SFT) 后,同样的提示词会返回一个合理的答案。教师信号(通过损失函数输入的正确 token 序列)会反向传播到嵌入层、注意力头和 FFN 层,逐一更新每个标量。
解决灾难性遗忘
早期的运行显示了一个经典问题:学习新知识会抹除之前学到的知识。Sekretov 通过“重演”(rehearsal)解决了这个问题——在引入新数据的同时,重新呈现旧的示例。只有当每个目标 token 的概率达到至少 95% 并且通过连续十次检查后,训练才会停止。这个简单的循环在没有任何外部库的情况下,展示了一个核心的研究挑战。
谁会受益,谁又可能被排除在外
该项目并不与运行在数十亿参数和 GPU 集群上的商业 LLM 竞争。这种性能差距使其不适合生产环境负载,但它将模型变成了一个透明的教学辅助工具。对于那些难以看透黑盒框架内部运作的学生、爱好者和研究人员来说,现在有了一个数学原理即是字面代码的沙盒。
缺失的部分以及项目的未来方向
由于实现刻意追求极简,它保持了代码的可读性,但也限制了可扩展性。
后续关注
该仓库已在 GitHub 上公开。
核心总结: 通过将 transformer 简化为纯 JavaScript,Sekretov 将这项出了名的晦涩难懂的技术变成了一个可读、可编辑的脚本。代价是速度,但收获是洞察力——现在任何人都可以逐个标量地观察语言模型如何学习、遗忘并重新学习。
