在这里插入图片描述

《代码破晓:2014》——当穿越者遇到天才少女,他们用比喻改变AI史,让深度学习不再有门槛。


“如果你曾觉得Transformer高不可攀,这个故事将为你点燃第一束光”


核心亮点

  1. 硬核知识软着陆:每集一个核心概念,通过生活比喻+剧情冲突自然展现
  2. 真实技术演进:从3×3矩阵手工计算到完整模型训练,再现技术发展全路径
  3. 多元角色共鸣:天才研究者+实践工程师+跨界学习者,总有一个像你
  4. 历史与未来交织:在改变历史的伦理困境中,思考技术本质

适合人群

  • 🤔 对Transformer好奇但被公式吓退的初学者
  • 🔧 想深入理解模型本质的实践工程师
  • 🧠 热爱技术但讨厌枯燥讲解的跨界学习者
  • 📚 寻找教学灵感的AI教育者
  • 🚀 相信技术民主化的理想主义者

“最伟大的发明,是让复杂变得简单”——加入这场2014年的代码破晓,亲手点亮AI历史上的那个清晨。


现在,让我们回到第一集的开头,让故事正式开始……


本集专属旁白播放地址
本集播客播客地址

下面是我写主题曲
代码破晓:2014》主题曲(大家评选一下):
在这里插入图片描述

破晓的公式: 歌曲地址


在这里插入图片描述


第二集:从向量到矩阵——自注意力的诞生

开篇:昨夜火花的余温

清晨六点,实验室的门被推开。

苏小雨抱着一摞打印纸进来时,发现林枫已经在了。白板上的公式被仔细地重抄了一遍,旁边还多了一页潦草的Python代码。

“你……没回去?”苏小雨惊讶地看着林枫手边的空咖啡杯。

“回去了,又来了。”林枫揉了揉太阳穴,“昨晚那个基本公式,有三个致命问题。”

他指向白板:

“问题一:计算效率。按我们昨天的方法,一个n个词的句子,每个词要和其他n-1个词算相似度,总计算量是O(n²)。20个词要算400次点积,200个词就是4万次——这还没算softmax。”

“问题二:数值稳定性。向量点积如果维度高,结果可能极大或极小,softmax会出问题。”

“问题三:也是最关键的——”

林枫还没说完,老王提着工具箱进来了:“早啊两位……嚯,这白板,昨晚上演数学大战了?”


第一幕:老王的“灵魂拷问”

老王放下工具箱,盯着白板看了足足一分钟,然后指着那个求和符号:“小林,我昨天想了半宿。你那个‘每个词都要问所有词’的法子,听起来很美,但要是真用在咱们服务器上……”

他走到那台老旧的4路GTX 980服务器前,拍了拍机箱:“这老伙计,显存才4G×4。你要是处理一篇一千字的文章,就是1000×1000=一百万次点积计算,每个结果还要存下来算权重。内存够吗?时间等得起吗?”

苏小雨脸色一白——她只顾理论,完全忘了工程现实。

林枫却笑了:“王师傅问到了最关键的地方。但如果我们换一种思维方式——不把每个词当成独立的个体,而是把整个句子……”

他在白板上画了一个矩形:

“看作一个矩阵。”


第二幕:图书馆比喻的升级

“还记得昨天的图书馆比喻吗?”林枫说,“我们说,每个词像读者,去查所有书。”

“现在升级一下:整个图书馆同时为所有读者服务。”

他在白板上画了三个区域:

“1. 查询台(Query):每个读者把自己的问题写成标准格式的查询卡。”
“2. 目录索引(Key):每本书把自己的核心内容提炼成索引标签卡。”
“3. 书籍内容(Value):书本身的实际内容。”

老王眼睛一亮:“我有点懂了!读者不直接翻书,而是先查目录索引。这样……”

“这样效率高几个数量级。”苏小雨接话,“因为查询卡和索引卡可以标准化,用矩阵运算批量处理!”


第三幕:Query/Key/Value的诞生

林枫开始正式推导。

“假设我们有n个词,每个词向量维度是d。传统做法是n个循环,每个循环里做(n-1)次点积。”

“但如果引入三个矩阵:”

  • Q(Query矩阵):n×d,每个词想知道什么
  • K(Key矩阵):n×d,每个词能提供什么
  • V(Value矩阵):n×d,每个词的实际内容

“注意力分数矩阵 = Q × Kᵀ ”林枫写下公式,“这是一个n×n的矩阵,每个元素(i,j)就是词i对词j的注意力原始分数。”

苏小雨立即跟上:“然后对这个n×n矩阵的每一行做softmax,得到权重矩阵W。最后输出 = W × V!”

老王盯着公式:“等等……Q×Kᵀ,这意思是……”

“意思是用一次矩阵乘法,代替了n²次循环。”林枫说,“GPU最擅长干这个。昨天你要算400次的20个词,现在只需要做一次20×d和d×20的矩阵乘法。”

实验室突然安静了。

然后老王猛地一拍桌子:“我靠!这……这才是正经路子!”


第四幕:第一次矩阵实战

苏小雨已经在电脑上敲代码了。

import numpy as np

# 模拟数据:3个词,每个词向量维度4
X = np.array([[1, 0, 0.5, 0.2],   # 猫
              [0, 1, 0,   0.3],   # 追  
              [0.5, 0, 1, 0.1]])  # 老鼠

# 先简单假设Q、K、V就是X本身(实际会有线性变换)
Q = X
K = X  
V = X

# 计算注意力分数:Q * K^T
scores = Q @ K.T  # 3x4 @ 4x3 = 3x3
print("原始分数矩阵:")
print(scores)

输出:

[[1.29 0.3  1.05]
 [0.3  1.09 0.3 ]
 [1.05 0.3  1.26]]

“看这里!”苏小雨指着第二行,“‘追’对‘猫’的分数是0.3,对‘自己’是1.09,对‘老鼠’是0.3。和昨天手工算的趋势一致!”

老王凑过来:“但这数字……好像比昨天大?”


第五幕:缩放点积的数学必然

“这就是第二个问题。”林枫说,“当维度d变大时,点积的方差也会增大。假设向量各维度是独立随机变量,均值为0,方差为1,那么点积的方差就是d。”

他在白板上推导:

“数学上,如果q和k的每个分量都是独立正态分布N(0,1),那么q·k的方差就是d。当d=512时,标准差就是√512≈22.6。这意味着……”

“意味着softmax会出问题!”苏小雨反应过来,“如果有些分数特别大,softmax后权重会接近1,其他接近0——梯度消失!”

林枫点头:“所以需要缩放:分数 / √d。”

苏小雨修改代码:

d = 4  # 向量维度
scaled_scores = scores / np.sqrt(d)

# softmax
def softmax(x):
    exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))  # 数值稳定
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

weights = softmax(scaled_scores)
print("\n缩放后的注意力权重:")
print(weights)

输出:

[[0.36 0.27 0.37]
 [0.28 0.44 0.28]
 [0.36 0.27 0.37]]

老王指着结果:“这个合理多了!‘追’关注自己最多,但也给‘猫’和‘老鼠’留了余地。”


第六幕:硬件天才的逆袭

“但还有实际问题。”老王皱着眉头,“Q×Kᵀ产生n×n矩阵。如果n=1000,就是100万个数,每个float32占4字节,这就4MB。然后softmax需要存中间结果,W×V又是……”

他快速在纸上计算:“显存占用可能超了。”

苏小雨有些沮丧:“那还是不行吗?”

“等等。”老王眼睛突然眯起来,“小林,你刚才说这个计算可以分块?Q×Kᵀ可以拆成子矩阵乘法?”

林枫点头:“理论上可以,但需要仔细设计内存访问模式……”

老王已经打开工具箱,拿出螺丝刀开始拆服务器侧板:“咱们这四张卡,每张4G。如果能把计算拆成四块,每张卡算一部分,最后再汇总……”

他突然停下,抬头看着两人:“给我两天时间。我写个CUDA核试试。”

苏小雨震惊:“王师傅,你还会写CUDA?”

老王咧嘴一笑:“大专怎么了?我这些年给学院维护集群,显卡驱动、并行计算,摸得比谁都熟。理论我不行,但怎么让硬件跑出最快速度——这是我的地盘。”

这一刻,老王身上有种不一样的光。


第七幕:第一次完整前向传播

傍晚,他们完成了第一个完整的最小版自注意力模块。

class SelfAttention_v0:
    def __init__(self, d_model=4):
        self.d_model = d_model
        
    def forward(self, X):  # X: (n, d_model)
        n = X.shape[0]
        
        # 1. Q, K, V (目前还是简单假设)
        Q = X
        K = X
        V = X
        
        # 2. 计算缩放点积注意力
        scores = Q @ K.T  # (n, n)
        scores = scores / np.sqrt(self.d_model)
        
        # 3. softmax得到权重
        weights = softmax(scores)  # (n, n)
        
        # 4. 加权求和
        output = weights @ V  # (n, d_model)
        
        return output, weights

# 测试
attn = SelfAttention_v0()
output, weights = attn.forward(X)

print("输入X:")
print(X)
print("\n注意力权重:")
print(weights)  
print("\n输出:")
print(output)

运行结果让三人都屏住了呼吸。

“看‘追’的输出!”苏小雨指着第二行,“[0.28, 0.44, 0.28]的权重分配下,它的新向量是[0.37, 0.44, 0.37, 0.20]。”

林枫分析:“中间维度(原特征1)被强化了,其他维度融合了‘猫’和‘老鼠’的特征。这比我们昨天手工算的结果更合理。”

老王突然说:“我有个问题。现在每个词的新表示,都是所有词的混合。但有些词可能该关注语法,有些该关注语义……能不能让它们同时关注不同方面?”

林枫和苏小雨对视一眼——老王无意中触及了多头注意力的核心思想。


第八幕:破晓时分的顿悟

晚上九点,实验室再次亮着灯。

老王在另一台电脑上敲着CUDA C代码,屏幕上滚动着复杂的线程调度逻辑。

苏小雨在白板上推导矩阵求导公式——她在思考反向传播该如何实现。

林枫则盯着输出结果,突然说:“我们犯了个概念错误。”

两人转过头。

“我们一直说‘自注意力’,但今天的实现……”林枫缓缓道,“其实是矩阵化的注意力机制。真正的‘自’体现在:同一个输入序列,既产生Query,也产生Key和Value。”

“就像一个人自我反思。”苏小雨理解得很快,“你既是提问者,也是回答者。你从自己的记忆中提取信息,重新组织认知。”

老王从代码中抬头:“这不就是‘吾日三省吾身’吗?用今天的自己,问昨天的自己问题。”

这个比喻如此精准,三人都愣住了。


第九幕:命名时刻

“所以该叫它什么?”苏小雨问,“矩阵注意力?缩放点积注意力?”

林枫想起历史上的名字:“我觉得老王说的对——自注意力(Self-Attention)。因为它让序列自己关注自己。”

他写下完整公式:

在这里插入图片描述

“但这是单头版本。”林枫继续说,“老王刚才提到的‘同时关注不同方面’,需要把Q、K、V先投影到不同的子空间,这就是**多头注意力(Multi-Head Attention)**的雏形。”

苏小雨立即在白板上画出分拆投影的示意图。

老王看着那些矩阵分块,突然兴奋:“如果是多头,计算可以更并行!每个头可以分到不同的GPU核心上!”


第十幕:未完成的革命

深夜十一点,三人站在实验室窗前。

窗外是2014年的校园,远处宿舍楼星星点点的灯光,那些学生中,也许有人正在为LSTM的梯度问题苦恼,有人觉得序列建模已经触顶。

“我们有了矩阵化的自注意力。”林枫总结今天进展:

  1. 从循环到矩阵:O(n²)次点积→一次矩阵乘法
  2. 引入Q/K/V:结构化信息提取
  3. 缩放点积:解决数值稳定性问题
  4. 硬件并行思想:为多头机制铺路

“但还缺什么?”苏小雨问。

林枫列出清单:

  1. 位置编码(模型还不知道词序)
  2. 多头机制的具体实现
  3. 前馈网络、残差连接、层归一化
  4. 编码器-解码器框架
  5. 最关键的——如何向世界解释这一切

老王收拾工具准备下班,走到门口时回头:“对了,你们知道最好的解释是什么吗?”

“是什么?”

让它工作。”老王说,“做出一个真正能翻译长句子的系统,然后告诉大家:看,这就是用那个‘图书馆查书’的办法做出来的。”

门关上后,苏小雨轻声说:“王师傅今天……不一样。”

“每个人都有自己擅长的地方。”林枫看着白板上密密麻麻的公式,“理论家的优雅推导,工程师的暴力破解,还有……”

“还有什么?”

“还有把复杂事物翻译成人类语言的能力。”林枫说,“这才是我们真正在发明的——不仅是新算法,更是新的解释方式。”


第二集知识点总结

1. 从向量到矩阵的核心跨越

  • 循环计算问题:原始注意力需要O(n²)次点积,无法并行
  • 矩阵化解决方案:将整个序列视为矩阵,用矩阵乘法一次性计算所有注意力分数
  • 计算复杂度不变但并行性质变:仍是O(n²)但可完全并行

2. Query/Key/Value三元组

  • Query (Q):想要查询的信息(如“当前词想知道什么”)
  • Key (K):能够提供的信息标签(如“每个词的语义标签”)
  • Value (V):实际的信息内容(如“每个词的具体表示”)
  • 比喻:图书馆系统中,读者填写查询卡(Q)、书籍有目录卡(K)、书籍本身是内容(V)

3. 缩放点积注意力公式

注意力分数 = softmax(Q·Kᵀ / √d_k) · V
  • Q·Kᵀ:计算所有查询-键对的相似度(n×n矩阵)
  • 除以√d_k:缩放因子,防止点积方差随维度增大而爆炸
  • softmax:将分数转为概率分布(每行和为1)
  • 乘以V:用权重对值进行加权平均

4. 数值稳定性与softmax技巧

  • 实际实现使用softmax(x) = exp(x-max(x))/sum(exp(x-max(x)))防止溢出
  • 缩放确保softmax输入数值在合理范围内,避免梯度消失

5. 硬件与计算优化思想

  • 矩阵乘法的高度并行性:GPU可高效处理
  • 内存访问模式优化:分块计算适应显存限制
  • 为多头机制埋下伏笔:不同注意力头可分配到不同计算单元

6. 自注意力的“自我”本质

  • 与传统注意力不同:Q、K、V来自同一个输入序列的不同线性变换
  • 实现序列内部的信息重组与提炼
  • 如老王比喻:“吾日三省吾身”——自己提问,自己回答,自己整合

下集预告:位置信息缺失的困境凸显——“猫追老鼠”与“老鼠追猫”仍无法区分。苏小雨从傅里叶变换获得灵感,林枫引入正弦位置编码,而老王发现了一个影响训练稳定性的深层问题……当序列获得“位置感”时,模型第一次真正理解了顺序的重量。


在这里插入图片描述

片尾曲:
矩阵升起时A版 :播放地址
矩阵升起时B版: 播放地址


版权声明
代码破晓:2014和主题曲和片尾曲以及相关封面图片等 © [李林] [2025]

本作品采用 知识共享 署名-非商业性使用 4.0 国际许可协议 进行授权。
这意味着您可以:

  • 注明原作者附上原文链接的前提下,免费分享、复制本文档与设计。
  • 个人学习、研究或非营利项目中基于此进行再创作。

这意味着您不可以:

  • 将本作品或衍生作品用于任何商业目的,包括企业培训、商业产品开发、宣传性质等。

如需商业用途或宣传性质授权,请务必事先联系作者。
作者联系方式:[1357759132@qq.com]

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐