AK大神用200行代码,让我终于看懂ChatGPT原理了!
AK大神用200行代码,让我终于看懂ChatGPT原理了!
AK大神用200行代码,让我终于看懂ChatGPT原理了! AK大神用200行代码,让我终于看懂ChatGPT原理了! Modified March 2 这些数字一开始是随机的小数,训练过程中,模型会自己调整。 训练后,行为相似的token(比如元音字母)往往有相似的向量。 模型从零学习这些表示,不需要预先知道什么是元音 。 就像小孩学说话,没人告诉他元音和辅音的区别,但他自己能总结出规律。 Token之间怎么交流:注意力机制 这是transformer的核心。 每个位置需要从之前的位置收集信息。 想象你在读一个句子,读到"她"的时候,你会回头看看前面提到的女性角色是谁,这就是注意力在做的事。 注意力机制(Attention) :让模型决定在处理当前位置时,应该重点关注之前哪些位置的信息。就像你读书时,视线会在不同词语之间跳转。 每个token产生三个东西: • Query("我在找什么?") • Key("我包含什么?") • Value("如果被选中,我提供什么信息?") 你可以这样理解:Query是搜索关键词,Key是每个位置的标签,Value是实际内容。 模型用Query去匹配所有之前位置的Key,找到最相关的,然后把对应的Value拿过来。 有个重要限制: 每个位置只能看过去,不能看未来 。 位置2不能关注位置4,因为位置4还没发生。 这让模型成为自回归的,符合我们生成文本的方式。 多头注意力(Multi Head Attention) :同时运行多个注意力机制,每个关注不同的模式。就像同时用多个视角看同一个问题。 不同的注意力头学习不同模式: • 一个头可能强烈关注最近的token • 另一个可能聚焦开头的BOS token(记住"我们在生成名字") • 第三个可能寻找元音 四个头并行工作,最后把结果拼起来。 完整流程:信息怎么在模型里流动 把每个token想象成一个包裹,在流水线上经过多个工序: 1. 嵌入 :给包裹贴上标签(token性格 + 位置信息) 2. 归一化 :把包裹整理成标准大小 3. 注意力 :包裹之间互相交换信息 4. 加残差 :保留原始包裹的一部分 5. 归一化 :再次整理 6. MLP :每个包裹独立加工 7. 加残差 :再次保留原始信息 8. 输出 :得到27个分数,对应下一个可能的字符 MLP(多层感知机) :一个简单的神经网络,负责对每个位置的信息做独立处理。如果说注意力是"交流",MLP就是"独立思考"。 残差连接(Residual Connection) :在处理信息时保留原始输入的一部分。就像做菜时保留一些原材料的原味,不要全部加工掉。 残差连接特别关键,想象信息在网络里传递就像打电话,每传一层就会损失一些。 残差连接相当于给信息开了一条高速通道,直接跳过某些层,保证重要信息不会丢失。 没有它,深度网络根本训练不起来。 RMSNorm(均方根归一化) :把每个向量缩放到标准大小。就像把不同大小的照片统一调整成同样尺寸,方便处理。 学习过程:模型怎么变聪明 训练循环重复1000次,每次做这些事: 1. 随机挑一个名字 2. 把它变成数字序列 3. 在每个位置运行模型,预测下一个字符 4. 计算预测有多离谱(损失) 5. 反向传播,找出每个参数的责任 6. 调整参数,让下次预测更准 Adam优化器 :一个聪明的参数更新策略。它会记住每个参数最近的表现,对稳定的参数走大步,对摇摆不定的参数走小步。 损失从约3.3开始(完全随机猜,27个选项中瞎蒙),慢慢降到2.37左右。 生成的名字也从乱码演变成合理的: • 刚开始:xqbzjfmwplk(完全乱码) • 训练中期:kamon, karai(有点意思了) • 训练后期:anna, anton(完全像真名字) 你能看到模型在"开窍"。 它逐渐理解了什么是合理的字母组合,什么样的音节听起来像名字。 生成新名字:让模型自由发挥 训练完成后,生成很直接: 1. 从BOS开始 2. 让模型预测下一个字符 3. 得到27个概率 4. 随机选一个(按概率) 5. 把选中的字符喂回去 6. 重复,直到模型说"我完成了"(输出BOS) 温度(Temperature) :控制生成的随机性。低温度让模型更保守(总选最可能的),高温度让模型更大胆(更多尝试不常见的选择)。 你可以这样理解温度: • 温度0.5:模型很谨慎,倾向于生成常见的、安全的名字 • 温度1.0:模型按它学到的真实概率生成 • 温度2.0:模型很大胆,会尝试不寻常的组合 温度太低,生成的东西很无聊,总是那几个最常见的名字。 温度太高,生成的东西可能是胡话。 对于名字,最佳点在0.5左右 。 既有创意,又不会太离谱。 其他都是效率问题 这个200行脚本包含完整算法。 从这个算法到ChatGPT的完整实现, 核心思想没变 。 变的是什么?规模和效率: • 训练数据:从32000个名字到整个互联网的文本 • 词汇表:从27个字符到10万个子词 • 参数:从4192个到数千亿个 • 层数:从1层到几百层 • 硬件:从你的笔记本到数千个GPU集群 • 训练时间:从几分钟到几个月 但 循环是一样的 : 分词 → 嵌入 → 注意力 → 计算 → 预测下一个token → 衡量错误 → 反向传播 → 调整参数 → 重复 就这么简单,也就这么复杂。 你现在知道了,ChatGPT并不神秘。 它就是一个超大规模的"猜下一个词"游戏,玩了无数次之后,变得出奇地聪明。 这些数字一开始是随机的小数,训练过程中,模型会自己调整。 训练后,行为相似的token(比如元音字母)往往有相似的向量。 模型从零学习这些表示,不需要预先知道什么是元音 。 就像小孩学说话,没人告诉他元音和辅音的区别,但他自己能总结出规律。 Token之间怎么交流:注意力机制 这是transformer的核心。 每个位置需要从之前的位置收集信息。 想象你在读一个句子,读到"她"的时候,你会回头看看前面提到的女性角色是谁,这就是注意力在做的事。 注意力机制(Attention) :让模型决定在处理当前位置时,应该重点关注之前哪些位置的信息。就像你读书时,视线会在不同词语之间跳转。 每个token产生三个东西: • Query("我在找什么?") • Key("我包含什么?") • Value("如果被选中,我提供什么信息?") 你可以这样理解:Query是搜索关键词,Key是每个位置的标签,Value是实际内容。 模型用Query去匹配所有之前位置的Key,找到最相关的,然后把对应的Value拿过来。 有个重要限制: 每个位置只能看过去,不能看未来 。 位置2不能关注位置4,因为位置4还没发生。 这让模型成为自回归的,符合我们生成文本的方式。 多头注意力(Multi Head Attention) :同时运行多个注意力机制,每个关注不同的模式。就像同时用多个视角看同一个问题。 不同的注意力头学习不同模式: • 一个头可能强烈关注最近的token • 另一个可能聚焦开头的BOS token(记住"我们在生成名字") • 第三个可能寻找元音 四个头并行工作,最后把结果拼起来。 完整流程:信息怎么在模型里流动 把每个token想象成一个包裹,在流水线上经过多个工序: 1. 嵌入 :给包裹贴上标签(token性格 + 位置信息) 2. 归一化 :把包裹整理成标准大小 3. 注意力 :包裹之间互相交换信息 4. 加残差 :保留原始包裹的一部分 5. 归一化 :再次整理 6. MLP :每个包裹独立加工 7. 加残差 :再次保留原始信息 8. 输出 :得到27个分数,对应下一个可能的字符 MLP(多层感知机) :一个简单的神经网络,负责对每个位置的信息做独立处理。如果说注意力是"交流",MLP就是"独立思考"。 残差连接(Residual Connection) :在处理信息时保留原始输入的一部分。就像做菜时保留一些原材料的原味,不要全部加工掉。 残差连接特别关键,想象信息在网络里传递就像打电话,每传一层就会损失一些。 残差连接相当于给信息开了一条高速通道,直接跳过某些层,保证重要信息不会丢失。 没有它,深度网络根本训练不起来。 RMSNorm(均方根归一化) :把每个向量缩放到标准大小。就像把不同大小的照片统一调整成同样尺寸,方便处理。 学习过程:模型怎么变聪明 训练循环重复1000次,每次做这些事: 1. 随机挑一个名字 2. 把它变成数字序列 3. 在每个位置运行模型,预测下一个字符 4. 计算预测有多离谱(损失) 5. 反向传播,找出每个参数的责任 6. 调整参数,让下次预测更准 Adam优化器 :一个聪明的参数更新策略。它会记住每个参数最近的表现,对稳定的参数走大步,对摇摆不定的参数走小步。 损失从约3.3开始(完全随机猜,27个选项中瞎蒙),慢慢降到2.37左右。 生成的名字也从乱码演变成合理的: • 刚开始:xqbzjfmwplk(完全乱码) • 训练中期:kamon, karai(有点意思了) • 训练后期:anna, anton(完全像真名字) 你能看到模型在"开窍"。 它逐渐理解了什么是合理的字母组合,什么样的音节听起来像名字。 生成新名字:让模型自由发挥 训练完成后,生成很直接: 1. 从BOS开始 2. 让模型预测下一个字符 3. 得到27个概率 4. 随机选一个(按概率) 5. 把选中的字符喂回去 6. 重复,直到模型说"我完成了"(输出BOS) 温度(Temperature) :控制生成的随机性。低温度让模型更保守(总选最可能的),高温度让模型更大胆(更多尝试不常见的选择)。 你可以这样理解温度: • 温度0.5:模型很谨慎,倾向于生成常见的、安全的名字 • 温度1.0:模型按它学到的真实概率生成 • 温度2.0:模型很大胆,会尝试不寻常的组合 温度太低,生成的东西很无聊,总是那几个最常见的名字。 温度太高,生成的东西可能是胡话。 对于名字,最佳点在0.5左右 。 既有创意,又不会太离谱。 其他都是效率问题 这个200行脚本包含完整算法。 从这个算法到ChatGPT的完整实现, 核心思想没变 。 变的是什么?规模和效率: • 训练数据:从32000个名字到整个互联网的文本 • 词汇表:从27个字符到10万个子词 • 参数:从4192个到数千亿个 • 层数:从1层到几百层 • 硬件:从你的笔记本到数千个GPU集群 • 训练时间:从几分钟到几个月 但 循环是一样的 : 分词 → 嵌入 → 注意力 → 计算 → 预测下一个token → 衡量错误 → 反向传播 → 调整参数 → 重复 就这么简单,也就这么复杂。 你现在知道了,ChatGPT并不神秘。 它就是一个超大规模的"猜下一个词"游戏,玩了无数次之后,变得出奇地聪明。 🔗 原文链接: https://mp.weixin.qq.com/s/9BTLe2O2... https://mp.weixin.qq.com/s/9BTLe2O2... 原创 向阳乔木 向阳乔木 AI不插电2026年3月2日 13:56 天津 AK大神情人节前(2.12)写了篇博客: https://karpathy.github.io/2026/02/12/microgpt/ 分享如何用200行Python代码从零写一个类ChatGPT算法,用于生成人名。 项目叫MicroGPT,已开源到Github: https://gist.github.com/karpathy/8627fe009c40f57531cb18360106ce95 自己不懂开发,没怎么看懂。 最近有好心人用更通俗的语言和图表写了篇解读: https://growingswe.com/blog/microgpt 发现还是有点难懂,让AI重写一遍,去公式,加名词解释,终于好懂些了! 训练数据:32000个名字 模型要学什么? 32000个人名,一行一个:emma、olivia、ava、isabella、sophia... 任务很简单 :学会这些名字的统计规律,然后生成新的、听起来像真名字的词。 训练完之后,模型能输出"kamon"、"karai"、"anna"、"anton"这样的名字。 它学会了哪些字母容易跟在哪些后面,哪些音节常出现在开头或结尾,一个名字通常多长。 从ChatGPT的视角看,你跟它的对话就是一个文档。 你输入提示词,它的回应就是 统计意义上的文档续写 。 第一步:把文字变成数字 神经网络只认数字,不认字母。 所以得有个转换方式,最简单的做法是这样的: 给每个独特字符分配一个整数。 26个小写字母得到0到25的编号,再加一个特殊标记BOS(Beginning of Sequence,序列开始),编号26,用来标记名字的起止。 Tokenizer(分词器) :把文本转换成数字序列的工具。就像给每个字符发一张身份证,上面写着专属编号。 比如"emma"变成: BOS(26) → e(4) → m(12) → m(12) → a(0) → BOS(26) 这些整数本身没有大小关系。 4不比2"更大",它们只是不同的符号,就像给每个字母涂上不同颜色。 生产环境的tokenizer(比如GPT 4用的tiktoken)会按字符块切分,词汇表有10万个token,但原理一样。 预测游戏:猜下一个字 核心任务来了: 给定目前看到的token,预测下一个是什么 。 我们一个位置一个位置往前滑。 在位置0,模型只看到BOS,要预测第一个字母。 在位置1,它看到BOS和第一个字母,要预测第二个字母。以此类推。 对于"emma"这个名字,会产生5个训练样本: • 看到[BOS] → 预测"e" • 看到[BOS, e] → 预测"m" • 看到[BOS, e, m] → 预测"m" • 看到[BOS, e, m, m] → 预测"a" • 看到[BOS, e, m, m, a] → 预测BOS(结束) 这个滑动窗口,就是所有语言模型的训练方式,ChatGPT也一样。 从分数到概率:让模型给出确定答案 每个位置,模型输出27个原始分数,对应27个可能的下一个token。 这些分数可以是任何值:正的、负的、大的、小的。 Logits(原始分数) :模型最初输出的未经处理的数字,就像考试的原始卷面分,还没有转换成等级。 我们需要把它们转成概率:正数,加起来等于1,Softmax就干这个事。 你可以这样理解:假设模型给5个候选字母打分是[1.2, 2.8, 0.5, 1.8, 0.3]。 Softmax做两件事: 1. 把每个分数变成正数(通过指数运算) 2. 让它们加起来等于100% 结果就是一个概率分布,比如"e"有60%的可能,"a"有22%的可能。 Softmax :把任意数字转换成概率分布的函数。就像把考试分数转换成排名百分比。 注意一个大的分数会主导整个分布,因为指数运算会放大差异。 这也是为什么模型能"确信"某个答案。 代码里有个小技巧:先减去最大值再做指数。 数学上结果一样,但能防止计算溢出。 衡量错误:模型猜得有多离谱 预测有多离谱? 我们需要一个数字来表示"模型觉得正确答案有多不可能"。 交叉熵损失(Cross Entropy Loss) :衡量预测和真实答案之间差距的指标。模型越确信错误答案,惩罚越重。 举个例子: • 如果模型给正确答案90%的概率,损失很小(0.1) • 如果只给1%的概率,损失很大(4.6) • 如果完全确信正确答案(100%),损失是0 模型不仅要答对,还要"有信心地答对"。 如果它给正确答案只分配了很小的概率,即使蒙对了,损失也会很大。 训练就是让这个数字不断变小 。 反向传播:找到问题出在哪 模型有4192个参数(可以理解为4192个旋钮)。 要改进,需要知道:每个旋钮往上调一点点,损失会上升还是下降? 反向传播(Backpropagation) :倒着追踪计算过程,找出每个参数对最终错误的贡献。就像出了事故,倒推每个环节的责任。 想象一下多米诺骨牌。 前向计算是推倒第一张牌,一路传导到最后。 反向传播是从最后一张倒推回去,看每张牌对最终结果的影响有多大。 每个数学操作(加、乘、指数、对数)记住自己的输入。 反向传播从损失开始,沿着计算路径往回走,给每个参数算出一个"责任分数"。 梯度(Gradient) :告诉你参数该往哪个方向调整,以及调整的力度。就像指南针,指向"让错误变小"的方向。 举个例子,假设参数a在两个地方被用到。 那它的总责任是两条路径的责任之和。 这就是为什么有些参数的梯度特别大,它们在多个地方影响了最终结果。 PyTorch的loss.backward()跑的就是这个算法,只不过操作的是大批量数据而不是单个数字。 从ID到意义:给每个字符一个性格 原始token ID(比如4)只是个索引,模型没法直接用整数做数学运算。 所以每个token会对应一个16个数字的列表。 可以理解为每个token有个16维的"性格档案",训练时可以调整。 嵌入(Embedding) :把离散的符号(比如字符)转换成连续的数字向量。就像给每个人建立一份多维度的性格档案。 位置也重要,位置0的"a"和位置4的"a"作用不同,所以有第二份档案,按位置索引。 两份档案相加,形成输入到网络的向量。