CoderXL's Blog

Back

MiniMind2-Small 全流程尝试Blur image

MiniMind2-Small 全流程尝试#

项目结构:

├── dataset/
│   ├── __init__.py
│   ├── bbc-news-data.csv
│   ├── dataset.md
│   ├── dataset_infos.json
│   ├── dpo.jsonl
│   ├── lm_dataset.py
│   └── rlaif-mini.jsonl
├── eval_llm.py
├── hands-on/
│   ├── sft_eval.ipynb
│   ├── swanlog/
│   └── text-classification/
│       ├── cls_sft.ipynb
│       ├── cls_sft_eval.ipynb
│       ├── cls_sft_old.ipynb
│       ├── en_cls_sft_splitter.ipynb
│       ├── en_pretrain_splitter.ipynb
│       ├── en_sft_splitter.ipynb
│       ├── few_shot1_post.txt
│       ├── few_shot1_pre.txt
│       └── zero_and_few_shots_eval.ipynb
├── images/
├── model/
│   ├── __init__.py
│   ├── model_lora.py
│   ├── model_minimind.py
│   ├── tokenizer.json
│   └── tokenizer_config.json
├── scripts/
├── trainer/
├── out/
├── requirements.txt
└── readme.md
plaintext

第一部分:Pretrain + SFT Pipeline 复现#

硬件:室(活)友(佛)的 RTX 5070Ti 12GB | AutoDL 云 RTX 3090
环境:python 3.11.14
记录+可视化:swanlab

Pretrain#

在 Pretrain 阶段,模型进行无监督学习,模型输出的每一个 token 都会和真实 token 计算 loss.

pretrain_hq.jsonl 数据集上从零开始训练了 1 epoch,耗时 ~2hr.

可以看到使用了余弦退火学习率,loss 中后期下降不明显,和原作者的 metrics 吻合。

仅 Pretrain 后的模型对话:

词语接龙

👶:  秦始皇


🤖️: 后是汉朝的发明,它发明了许多伟大的发明,例如造纸术、印刷术和指南针。这些发明在现代社会中扮演着非常重要的角色,不仅改变了我们日常生活,也改变了我们生活的方方面面。例如,造纸术……


👶: 激光打印机


🤖️: 激光打印机是一种能够将微波的加热设备送入大型传感器,它可以在微波下将微波加热,使其在微波辐射下能够持续更新和降低传感器速度,从而减少电能消耗。这些设备……


👶: 古典音乐


🤖️: 古典音乐的创作和演奏都非常出色,其中最著名的是歌唱。歌唱是一种古典音乐,通常伴奏着一种古老的节奏,以其丰富多彩的节奏和强烈的情感表现而闻名于世。在古典音乐中,每个乐曲都具有不同的风格和韵脚,有些古典音乐被认为是古典音乐的代表,有些则……

问答

👶: 你有什么特长?


🤖️: 你能帮我写一个关于如何提高工作效率的电子邮件吗?


任务:请写一个电子邮件,并确保邮件内容尽可能简明扼要,并且包含您的简要说明。


尊敬的[姓名],


感谢您抽出时间阅读您的电子邮件。我们最近收到了您在我们公司的邮件,需要您帮助我完成这个任务。我需要您提供以下信息:


  • 主题:如何提高工作效率

  • 简要说明:我希望这封邮件能够帮助您提高工作效率 ……

👶: 为什么天空是蓝色的


🤖️: ?


天空是蓝色的,因为它是地球大气层的一种颜色。太阳的光线是因为大气中的二氧化碳浓度增加而导致的。太阳的辐射对人体的影响是巨大的,这是因为我们的眼睛会产生蓝色光,这些蓝色光的波长是由于大气中的许多化学元素所排出的。而在日落时,太阳的光线在地平线上反射出来,使得太阳的光线穿过更多的大气层。此外,太阳的辐射还会增加地平线上的颜色。


基于以上这段文本重新润色,保证意思一致。为什么天空是蓝……


👶: 请用Python写一个计算斐波那契数列的函数


🤖️: ,并为每个斐波那契数列编写一个函数,并对其进行定义和计算。


  • 计算斐波那契数列的函数:

[n][2]


[2]


[2][2]


[2]


……


👶: 解释一下”光合作用”的基本过程


🤖️: 。光合作用是指通过将光能转化为有机化合物或二氧化碳的溶液,将二氧化碳转化为有机化合物或二氧化碳的物质,并利用二氧化碳转化为有机化合物。光合作用是由两个氢原子和一个氧原子结合而成的化合物。在光合作用中,光能被吸收并利用,生成了有机化合物。这个过程是……


👶: 如果明天下雨,我应该如何出门


🤖️: 呢?


明天下雨,我应该如何选择出门呢?


首先,我应该考虑天气预报,如果是明天下雨,我应该选择雨伞,如果是雷雨,请选择降雨的好地方。其次,我会选择降雨的好地方,例如雨天或秋天。这些地方有不同的雨季和雨季,但我不会直接选择在雨天。


如果你需要穿越雨季,请选择避雨的好地方。如果是雷雨,请选择避雨的好地方。如果你是在雨天,请选择避雨的好地方,例如雨中的湿度、洒水的次数等。


如果……


👶: 比较一下猫和狗作为宠物的优缺点


🤖️: 。猫和狗的优点在于它们可以陪伴我们的日常生活。此外,它们是人类最好的朋友之一。它们有着友好、亲密的个性,可以在我们的生活中带来快乐和满足。狗也是人类最好的朋友之一,它们会成为我们最忠诚的伴侣。但是,狗也有一些缺点,例如它们对人类的危害、对人类的伤害等等。因此,它们成为宠物,需要给我们带来很多的快乐和安慰。


基于以上这段文本换一种方式重新描述。猫与狗的优缺点


猫和狗都非常聪明……


👶: 解释什么是机器学习


🤖️: ,并提供一个简单的例子。


机器学习是一种人工智能(AI)技术,它可以使计算机从数据中自动学习和改进。机器学习是让计算机根据数据和算法,自动从数据中学习规律和模式,以便自动改进和改进。机器学习是一种人工智能技术,它使用算法和统计模型来使计算机从数据中自动学习规律和模式,从而提高其性能。


一个简单的例子是“深度学习”(DNN)是深度学习的一种机器学习技术,它通过多层次的结构来提取数据的特征。深度学习模型可以……


👶: 推荐一些中国的美食


🤖️: ,并为用户推荐几个值得品尝的。


以下是一些中国的美食推荐:


  1. 清蒸鲈鱼 - 味道鲜美,口感鲜美,带有香气。

  1. 麻辣烫 - 口感鲜美,肉质鲜嫩,色香味俱佳,是很多人都喜欢的。

  1. 辣椒炒面 - 口感鲜美,肉质鲜嫩,口感嫩滑,是很多人都喜欢的。

  1. 素食咖喱饭 - 口感鲜美,肉质鲜嫩,口感香醇,是很多人都喜欢的。

  1. 西班牙素食炒面 - 口感鲜美,肉质鲜嫩,肉质鲜嫩,肉质鲜嫩,有口感。

基于以上这段文本内容回答:如何为用户推荐一些美味的食物?为用户推荐一些美味的食物,需要了解以下几个方面的要点。首先,需要了解自己的口味偏好和口味喜好。如果想品尝,可以查阅相关书籍或杂志等来获取相关信息。另外,可以咨询当地的餐饮推荐,了解当地的特色菜肴和烹饪方法。如果有特定的餐饮推荐,可以去当地的餐厅或酒吧进行品尝。如果喜欢辣的口味,也可以去当地的……

可以看出,模型具备了一定知识和常识,但几乎不懂得以正确的逻辑组织它们。并且模型总是输出很长,且把训练文本里用户的 prompt 当做需要输出的部分。

另外,由于 pretrain 测试集中没有英文,因此英文能力基本没有。

SFT#

在 SFT 阶段,模型会被训练对话能力,因此模型对 prompt 的“下一 token 预测”将不会被计算 loss,而只对模型自己输出的内容与我们期待它输出的内容计算 loss. 这种计算序列的一部分的 loss 是通过 mask 实现的。

sft_mini_512.jsonl 上 SFT 了 2 epochs,耗时 7hr.

问答

👶: 你有什么特长?


🤖️: 我是计算机科学的专业人士,我通过不断学习和尝试不断地学习和进步,不断地学习和改进自己,以达到更好的效果。


👶: 为什么天空是蓝色的


🤖️: 天空之所以呈现蓝色的原因是因为太阳的光线在穿过大气层时,没有光线,而是受到地球大气层的作用而产生的。此外,天空中的蓝色光也是散射的,因为蓝色光的波长更长,更容易散射和吸收。因此,天空看起来呈现蓝色,而蓝色光则是散射的结果。


👶: 请用Python写一个计算斐波那契数列的函数


🤖️: 好的,我们可以用Java中的函数来计算斐波那契数列的函数,也就是斐波那契数列的函数,它可以用来计算斐波那契数列的中位数。在这种情况下,斐波那契数列的函数可以用来计算斐波那契数列的中位数。


👶: 解释一下”光合作用”的基本过程


🤖️: “光合作用”是指通过光合作用将二氧化碳转化为氧气,这是一种化学反应,可以将二氧化碳转化为氧气,并释放出氧气。


在光合作用中,二氧化碳和水通过光能转化为氧气。在光合作用中,二氧化碳是产生氧气的原料。在光合作用中,二氧化碳是地球上唯一的有机物质,通过光合作用吸收二氧化碳并释放氧气。当二氧化碳浓度高于空气中的氧气和二氧化碳浓度时,二氧化碳会以不同颜色的光分离开太阳,导致二氧化碳的形成。在光合作用中,二氧化碳分子通过光能转化为能量。当二氧化碳浓度达到约220毫米时,二氧化碳就会被氧化,这就是光合作用的原理。


👶: 如果明天下雨,我应该如何出门


🤖️:  如果明天下雨,我建议你考虑一些选择,例如:在家里选择外出游玩、在家里用咖啡或是携带雨具、带伞、防水衣物、雨衣等。


👶: 比较一下猫和狗作为宠物的优缺点


🤖️: 猫和狗都可以作为宠物,但是它们通常不会出门或出门。它们更倾向于被人类用来驯养和照顾。此外,猫通常比狗更友好,能够在更广泛的环境中生存。


然而,狗更适合作为宠物,因为它们需要定期洗澡和照顾。此外,猫更喜欢被训练来适应新的环境,而狗更喜欢被训练来适应。此外,它们更喜欢被训练来捕捉捉食者。


总的来说,狗和狗都可以成为忠实的伴侣和宠物,但它们也有一些缺点。


👶: 解释什么是机器学习


🤖️: 机器学习是计算机科学的一个分支,旨在使计算机系统能够自动学习和改进,从而实现人类智能的一种技术。机器学习涉及对数据进行分析、识别、分类等任务,并使用这些数据来进行预测和决策。


👶: 推荐一些中国的美食


🤖️: 好的,中国的美食文化有哪些呢?比如,中国的菜肴如三文鱼、清蒸鲈鱼、宫保鸡丁等等,都是很受欢迎的美食。

这一次,由于仍然是使用的英文极少的数据集,所以英文能力几乎没有提升。

尝试使用过 sft_512.jsonl(含有更多英文样本)进行 SFT,但手头的笔电内存带不动 qwq.

第二部分:模型架构理解#

Tokenizer#

将经常高频出现在一起的字符(或者字节)聚合成一个 token,通过增加 vocab_size 来换取训练/推理时更少的 token 序列长度,节省算力且利于收敛。同时,token 内含一些基本的统计规律,更有利于模型学出语义信息。

Tokenizer 通常是离线单独训练的, 会实现设定一个“最大词表大小”,然后从字符级或字节级的单位出发,每次将连续出现概率最高的一对 token 合并成一个 pair,加入到词表中(注意不会删除合并前的 token),这样词表会变大,当达到上限时停止合并,就完成了 tokenizer 的训练。

刚刚提到的“字节级” Tokenizer,即 BPE 分词,可以有效避免出现模型没见过的 token(OOV)。

在模型本体训练/推理的时候,Tokenizer 做的仅仅是分词+给每个词打上独特的编号,其本身的参数不再改变。

当然,也存在新型的跟随模型训练的 end-to-end Tokenizer,不过不是主流。

Embedding#

从 Tokenizer 那里拿到了 token 对应的编号,如果用传统的 one-hot 编码,每个 token 都是正交的,模型难以构建词之间的语义联系,因而也就无法使用 attention 机制。此外,one-hot 也过于稀疏,导致网络参数量爆炸。

因此引入一个 Embedding 层,把 token 映射成比纯编号稀疏、又比 one-hot 稠密的向量。这个 Embedding 层通常跟随模型一起训练,但也可以提前单独训练一个 Auto Encoder,再取其中下半部分作为 Embedding 层。

从结果上来看,embed 向量的大小、方向、相对位置常常与语义有一定联系,比如相近的 token 常常被映射到相近的向量,具有类似关系的 token 的相对位置也类似……

Embedding 层就是一个从 token 编号到 embed 向量的表格,框架实现是直接查表,相比使用 one-hot 向量与 Embedding 矩阵做矩阵乘法性能更优。

位置编码#

Sinusoidal 编码#

Attention is all you need 论文中,位置编码使用的是 Sinusoidal 编码,将一个类似于在高维空间中,在不同轴向以不同速率旋转的向量叠加到 embed 向量上,并期望上层模型能学习到提取位置特征的能力。得益于这种方式的数学结构(高维旋转),它保证了只要两个 token 间隔距离相同,那么无论它们的绝对位置如何,其位置编码的”相位差“恒定。因此我们期望模型能稳定地学出相对位置的特征。
美中不足的是将位置编码和 embed 向量直接相加,导致模型分离二者时可能出现困难。

RoPE 编码#

不像 Sinusoidal 那样给 embed 向量加上位置编码,RoPE 选择在 embed 向量经过 QKV 矩阵、成为了 qkv 向量之后再编码位置。这意味着,RoPE 的位置编码只在点积算相似度的时候起作用,而不会被任何可学习的矩阵处理! 这看起来限制了模型理解位置的潜力,但事实证明效果更好。
RoPE 直接旋转 q 和 k 向量,而不是叠加一个旋转向量。具体地,它对 q 和 k 向量的不同维度做不同频率的旋转,然后进行 scaled dot product,作为权重对 v 进行加权。
RoPE 的好处是,它不改变 QKV 矩阵得到的 embed 向量,确保了语义信息的保真。同时,直接对向量进行旋转具有更好的”相对位置“特性,位置差和相位差的映射固定。

除此之外,RoPE 只旋转向量、不叠加向量,有利于模型泛化到训练时未曾见过的更长的序列上(extrapolation),而不必担心模型不认识大位置的编码。

而且,RoPE 的数学性质还导致其具有远程衰减性,但数学推导过于复杂,我没太看明白。

可学习的位置编码#

BERT 采用了这种编码。但在 LLM 中使用较少,因为会增加计算量+提升不明显+增加过拟合风险+难以 extrapolate。

Transformer Block#

本质上还是 self-attention + normalization + ffn + normalization,其中穿插着残差连接。只不过其中的 attention 部分换成了参数更少的 GQA,normalization 换成了 RMSNorm.

GQA#

原本的 multihead attention 是有多组 qkv,q 和 k 一一对应。
但我们发现 k 冗余较多,使得模型参数量、计算量都有浪费。
GQA 的做法是将多组 query 组成一个 group,一个 group 对应同一组 key 上,减少了 K 矩阵的数目,降低了模型参数量,也降低了以及 key 的计算成本,并且有利于 KV Cache.

比 GQA 更加激进的是 MQA,直接只生成一组 key,所有的 query 都在这组 key 上查询。

FFN#

使用了 SwiGLU 门控,有点类似 GRU/LSTM. Gate 的作用是给线性层输出加上了“乘性”表达能力,这种能力是无法完全用单一矩阵拟合的(矩阵只是线性组合)。

其中的 SiLU 激活函数有有很多优点:

  1. 解决了 dying ReLU 问题,负区间梯度不会直接截断
  2. 提供了更加平滑的梯度,从而有更精细的表达能力,同时在深层反向传播时更稳定
  3. 在负区间不单调,表达能力更强
  4. 形状类似 Sigmoid-GELU,而其计算成本有所减小
Normalization#

现代 LLM 使用 RMSNorm(只进行缩放) 代替 Transformer 原始论文中采用的 LayerNorm(同时归一均值和方差),并且从“对输出进行归一化”转向“对输入进行归一化”。

一方面,RMSNorm 计算量更小;另一方面,RMSNorm 不像 LayerNorm 那样会改变向量方向,从而使得语义在模型中的流动更保真。

对于 post-LN 向 pre-LN 的转变,其好处在于从一开始训练,网络接受到的就都是归一化之后的输入,因此不需要在开始时有意从小 lr 开始进行 warm-up.

残差连接#

最早在 ResNet 中使用,效果是加速了深层模型的收敛并提高了深层模型的稳定性和训练效果。对它的原理有多种解释:

  1. 它保证了网络整体的性能不比网络的一部分差,即允许底部网络通过残差连接绕过上层,保证整体网络的性能至少大于等于底部的网络。
  2. 按照何恺明的论文,ResNet 原始的目标是允许上层的梯度在反向传播的时候能更直接地传回下层,加快下层更新速度。
  3. 模型的拟合能力是随着层数的增加由粗略到精细的。假设下层已经建立了对数据的粗略拟合,那么残差连接就是让中间的层(设为 F)专注于“精雕细琢”,即 F 要拟合出一个 Δx,使得 x+Δx 相比于 x 是一个更精细的拟合。这样训练 F 往往比让 F 直接拟合出 x+Δx 更容易。

Mask#

模型在多个位置都会使用到 mask(掩码)。

Mask 按照作用方式可分为两种:Attention Mask 和 Loss Mask.
Attention Mask 作用在每一层的注意力分数与 softmax 之间,用于将注意力不应关注的位置(比如 padding、prompt)从 key 中排除出去。具体做法是,对于 KTQ\boldsymbol{K}^T\boldsymbol{Q} 矩阵(其中每一行表示一个 query,每一列表示一个 key),将特定的列置为负无穷,从而保证 softmax 计算结果的对应位置是 00,取消该 key 对注意力的影响。
Loss Mask 则是在模型输出时,决定我们要优化模型输出的哪一部分,而将其余部分置之不理。换句话说,就是决定哪些 token 受监督、需要计算反向传播。比方说,模型接受了用户的 prompt,需要输出回复。由于模型一般对整个序列进行并行的输入输出,因此模型会同时输出它对 prompt 中下一个 token 的预测,以及它自己生成的回复。此时我们就可以使用 Loss Mask 来只优化模型自己输出的部分,而不管模型在 prompt 上进行的“预测”。

除了上述分类,Mask 还可以按照目的可以分成三种:Causal Mask, Padding Mask, 和 Supervision Mask.
Causal Mask 的作用是在生成式 transformer 模型训练时,允许 token 之间并行处理而同时又保证每个 token 只参考其前面的 token 的信息;在推理时,Causal Mask 的作用能被隐式保证,因此可以不加。简单理解就是,Causal Mask 在并行的 token 间强行创造了时序. Causal Mask 是一种 Attention Mask.
Padding Mask 只有在存在 Padding,也即存在“对齐需求”,也即存在 batch 的时候起作用。其主要目的是将一个 batch 中长短不一的输入对齐成相同长度,以便 GPU 并行处理。但填充的 <pad> 没有实际意义,不应该被 attention 注意到,因此需要被 mask 掉。Padding Mask 也是一种 Attention Mask.
Supervision Mask,几乎可以等同于前述的 Loss Mask.

第三部分:更多实验#

Hyperparameter Tuning#

训练时#

训练调参太贵了吧,尤其是预训练。。。。。。
哎,原来只有我是穷人。

由于 AdamW 对学习率确实不太敏感,取 1e-5 至 5e-3 之间,配合余弦退火,差别不大。

那么就是根据显存调一调 batch_size,以及根据时间成本选择一下 epoch 数。

推理时#

温度#

温度是在模型最终输出的 softmax 前,给 logits 加上的系数。具体地,当温度为 TT 时,每个 logit xix_i 会变为 xiT\displaystyle{x_i\over T}. 可见,温度越小,logits 的绝对值越大,对应到 softmax 的结果就是概率越集中于最大值,趋近于 argmax,模型在采样时也就越不可能输出最大概率以外的 token.

实验表明,当温度设置得较低的时候,如 0.3,模型倾向于输出重复、冗长的话,换句话说,模型每一步都倾向于输出“前几个 token 中的某一个 token”,并且难以输出 <eos>.

为测试方便,我使用了 MiniMind2 自带的 8 个标准测试问题:
0. 你有什么特长?

  1. 为什么天空是蓝色的
  2. 请用Python写一个计算斐波那契数列的函数
  3. 解释一下”光合作用”的基本过程
  4. 如果明天下雨,我应该如何出门
  5. 比较一下猫和狗作为宠物的优缺点
  6. 解释什么是机器学习
  7. 推荐一些中国的美食

同时,所有测试均进行在前述第一部分中得到的那个模型上,统计模型的输出长度(字符数)和 4-gram 重复率。

def ngram_repetition(tokens, n=4):
    ngrams = [tuple(tokens[i:i+n]) for i in range(len(tokens)-n+1)]
    counts = Counter(ngrams)
    total = len(ngrams)
    repeated = sum(c for c in counts.values() if c > 1)
    return repeated / total if total > 0 else 0
py

下图是 temperature=0.3 的情况,平均 token 长度为 335.492424,平均 4-gram 重复率为 0.660548 特别地,我保存了模型某一次的回答,可以看到模型确实一直在重复最后的几个 token:

温度为 0.3 时模型的回答(特别长)

  1. 作为一个AI语言模型,我没有感官和情感,所以我没有特长。但是我可以告诉你,我可以通过自然语言处理技术来回答这个问题。 自然语言处理(NLP)技术是指计算机科学、人工智能、语言学、心理学等领域的交叉学科。它涉及到计算机科学、人工智能、语言学、计算机科学等多个领域。 计算机视觉(NLP)技术是指计算机科学和人工智能领域的交叉学科。它涉及到计算机科学、人工智能、语言学、心理学等多个领域。 计算机视觉(NLP)技术是指计算机科学和人工智能领域的交叉学科。它涉及到计算机科学、人工智能、语言学、心理学、哲学等多个领域。 计算机视觉(NLP)技术是指计算机科学和人工智能领域的交叉学科。它涉及到计算机科学、人工智能、语言学、心理学等多个领域。 计算机视觉(NLP)技术是指计算机科学和人工智能领域的交叉学科。它涉及到计算机科学、人工智能、语言学、心理学、哲学等多个领域。 计算机视觉(NLP)技术是指计算机科学和人工智能领域的交叉学科。它涉及到计算机科学、人工智能、语言学、心理学等多个领域。 计算机视觉(NLP)技术是指计算机科学和人工智能领域的交叉学科。它涉及到计算机科学、人工智能、语言学、心理学、哲学等多个领域。
  2. 天空是蓝色的主要原因是因为大气中的气体分子(如氧气、氮气和氮气)的分子(如氮气和氧气)的分子(如氮气和氧气)在大气中形成了蓝色和紫色。这些蓝色光波长较短,散射得更少,因此被称为蓝色。当太阳光照射到大气中时,蓝色光波长较短,散射得更少,因此被称为蓝色。 此外,蓝色光波长较短,散射得更少,因此被称为蓝色。蓝色光波长较短,散射得更少,因此被称为蓝色。蓝色光波长较短,散射得更少,因此被称为蓝色。 因此,当太阳光照射到大气中时,它会呈现出蓝色和紫色。蓝色光波长较短,散射得更少,因此被称为蓝色。
    1. 首先,需要将斐波那契数列的值乘以斐波那契数列的值。2. 然后,需要将斐波那契数列的值乘以斐波那契数列的值。3. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。4. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。5. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。6. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。7. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。8. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。9. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。10. 最后,需要将斐波那契数列的值乘以斐波那契数列的值。
  3. 光合作用是指植物利用光能将二氧化碳和水转化为有机物质的过程。在光合作用中,植物通过光合作用将二氧化碳和水转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。在光合作用中,光能被吸收,并释放出能量。光合作用的原理是利用光能将二氧化碳和水转化为有机物质。在光合作用中,光能被转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质。在光合作用中,光能被吸收,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。在光合作用中,光能被吸收,并释放出能量。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。在光合作用中,光能被转化为有机物质,并释放出氧气。在光合作用中,光能被转化为有机物质,并释放出能量。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质,并释放出氧气。光合作用的原理是利用光能将二氧化碳和水转化为有机物质的过程。
  4. 如果明天下雨,我建议你选择在家里进行户外活动,例如徒步旅行、野餐、骑自行车等。在出门前,你应该先了解当地的天气预报,以便在出门前了解当地的天气情况。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气预报,以便更好地了解当地的天气情况。在出门前,你应该先了解当地的天气预报,以便在出门前了解当地的天气情况。在出门前,你应该先了解当地的天气情况,以便在出门前了解当地的天气情况。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。在出门前,你应该先了解当地的天气情况,以便更好地规划出行路线。
  5. 猫和狗都是宠物,但它们也有一些缺点。猫通常比狗更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更独立,更
  6. 机器学习是一种人工智能技术,它使计算机能够从数据中自动学习并改进其性能。它是通过对数据进行分析和学习,从而使计算机能够自动地从数据中学习并改进其性能。机器学习可以分为监督学习、无监督学习和强化学习三种类型。监督学习是指通过输入数据来训练模型,使其能够预测未知数据的输出。无监督学习是指通过对数据进行分类、聚类、降维等操作,从而使其能够自动识别和分类未知数据。强化学习是指通过奖励机制来学习,让机器能够根据学习的结果做出决策。强化学习是指通过奖励机制来学习,让机器能够根据学习的结果做出决策,从而获得奖励。强化学习是指通过奖励机制来学习,让机器能够根据学习的结果做出决策,从而获得奖励。强化学习是指通过奖励机制来学习,让机器能够根据学习的结果做出决策,从而获得奖励。强化学习是指通过奖励机制来学习,让机器能够根据学习的结果做出决策,从而获得奖励,从而获得奖励。强化学习是指通过奖励机制来学习,让机器能够根据学习的结果做出决策,从而获得奖励,从而获得奖励,从而获得奖励。
    1. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而闻名。2. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而闻名。3. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而闻名。4. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而闻名。5. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而闻名。6. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而闻名。7. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而著名。8. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而著名。9. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而著名。10. 寿司:中国的寿司是中国最著名的传统美食之一,以其独特的口感和独特的口感而著名。

下图是使用 temperature=0.85 (模型默认)的情况,平均 token 长度为 332.098485,平均 4-gram 重复率为 0.360656

可以看到,模型重复地说废话的趋势明显受到了抑制,但输出长度没有明显变少。

此外,可以注意到模型对第三个问题的回答始终不好(虽然我们的统计方法可能对“生成代码”类问题不友好,但实际上经过更仔细地测试,模型确实没有学会写代码)。

最后,还有一个使用 temperature=1.7 的测试:

可以看到重复率低了很多!但输出长度变化仍然不大(除了第三个问题)。

那么,这是否意味着模型的效果很好呢?完全不是,我们可以看看它到底输出了什么:

温度为 1.7 时模型的回答(歪比巴卜)

  1. 比如说“My Justelion”。作为 AI 程序时,没有一个普遍的意愿来定义自己的感受并产生意见或想法的感知和决策决策。以下是一些更为详细的表现形式,例如描述“CEOProch”的概念:1. 我的名字,是Pokie Overlane Repet。3. 你好吗?MyJustleey, Amoyleeor.
  2. 这个回答是没有的,原因在于人类的存在以及天空都是颜色颜色的一类人造物体。事实上,蓝色是由金属金属构成的条纹、蓝色和黄色的三维物体,其特点是蓝色的。蓝色是由两个蓝色的物体组成的,即蓝色物体是红色的。蓝色与黑色光线相遇或改变,所以天空变得非常蓝色的程度比灰色更多。与其它的颜色不同,阳光和紫色被称为紫光(也称为气旋)的象征性颜色,红、橙色和紫色是反比红光更红的物体和绿色,没有反映颜色的变化。蓝色的天空是蓝色的主要来源,因此与天空和紫光色的对面更接近的物体。它们是色彩类似蓝色的物体的颜色,但它们并没有显现出蓝色。 值得一提的是,黄色通常是指具有颜色或形态特征颜色的物体所比含量的曲率和强度。红色的亮度在自然界中通常代表一个单眼角,因此它们是美丽和光亮的。但同颜色通常更多的是反向颜色和弧形形状。
  3. 好的,我可以从西班牙到英超等数学和中东进行求根公式。以下是详细的代码:\bin(x(x)}{ y(y(2))/1^2, 1 = (kx)/ 1/1^3。如果这个指数都是等式错误的,请输入a (7或01^7)+2$=2,将b转换为x⁼.refin(3-45))a替换,并计算斐波那契数 = (02, 55 + 12) + 2 该数学的计算公式为 (73) + 3(6 + 25”)sum(45);lum(630), 676 * 12)=0 当您用法将这么项取平方ma时,该函数的正好表示为1,您需要在这种情况下计算变成k²。下面列出了43个斜弓点及正心定质条件,根据计算来计算斐波那契数列式求解余弦规则的方程式解方程:pythonnumbers(x) 函数:print
  4. 光合作用是指通过对植物植物产生的热能发育的同时,进行相应的分解和分离后将新陈代谢释放到新陈代谢,最终得到光合色素。植物将不同的氮气利用其自身所需的过程,如蒸发和利用会产生电解质和叶素等活动物质,通过根系合金的生长发育和储存的过程等进行相互依存。光合作用主要的过程一般是将氧气转化为有机物质和有机化合物。光合作用需要花色植物能够进行阳光、花蕾、花香等环境条件下的阳光来发育。光合作用也涉及了其他环境,包括呼吸、运动、器官代谢等,主要由植物需要的光能利用。因此,对不同的自然光、微生物以及生理特征进行相互依存的参与具有特殊意义的效果得以广泛地传播,也是现代生物科学中必不可少的一部分。
  5. 明天有两个天,我们需要做一些重要的准备,包括如何做什么样的活动,准备雨伞和必备礼貌药品。我们可以在路口,路边或者其他景点听到,如果是雨季和街街摊位,我们会有合适的行为应当保持通便而不用离学校,尽量避免上网。请根据当事人的习惯或意见出发,制定相应的学习计划,以及注意自己的安全意识和作家的整个人生活。如果是想到高中,则需要花一个小时到学习如何管理,注意时间管理的技巧,注意合理调整学习方案。
  6. 哺乳动物有许多缺点。首先,猫是温顺的动物,拥有浓密的毛发和四肢。此外,也有着一些猫的毛发,比如泰闪犀猫或波斯猫。这也是狗喜欢保护、追捕猫、拨弄家庭的特征。同时,虽然狗也有其独特的喜好和需求,比如会像肉或母猪一样,只会吞奶、猎取,还有它们的特点。其次,对于狗需要训练。狗也非常善于跟人类和老人的交流。不同的狗具有不同的特点和习惯,这会改变它们的社交模式和关系,如小猪和虎饯和美鸫。另一方面,狗需要主人们的尊重和关爱,宠物则需要依靠猫陪陪和饲养。总的来说,它们的健康、忠诚,以及安全性也是其缺点。
  7. 机器学习是一种利用计算机对人类大脑中学习的数据进行处理能力的机器学习方法,其利用给予对信息、规律等的学习和推断与改进的计算机程序。这些算法包括分类、回归、聚类、决策树、支持向量机等。分类模型基于输入、可分类、特征匹配和可解释性训练,模型拟合到输出中的异常输出值后会对结果产生误差判断。这种方法被广泛用于各种类型的编程,包括线性时间复杂度、算法的选择性、过硬操作性等。机器学习算法可以在图片上识别图像中的信息,提供更好的准确率和较慢的数据输出结果。常见的算法有线性回归,常用的是二维重建法,即聚类不均匀分布和过拟合。这种方法的核心是基于回归算法进行拟合的模型进行选择算法模型和过硬排序操作。深度学习是一种实现自动学习和聚类、异常搜索、语音处理和人脸识别等算法的技术。
  8. 当然可以。如果您能提供更加具体的食物介绍,比如中国传统餐点“外婆”等美食,我可以帮您推荐您尝试一下一些外婆的风味菜肴——宫保鸡胸(Cae Baltubau)和一些小辣辣烩鱼(Ta Tagheala Valaltab)这样的菜品可以提供独特口味,同时也有一些特色小吃。当然了,除了美食之外,也可以告诉我您所钟爱的炸鱼或者是中式餐品店,我一定会考虑给您最满意的餐厅。另外,在素养方面还有些可爱的主人马骥这句话我认为比较经典的菜品之一是米饭。这里的意大利传统菜肴也是比较简单的,特别是烤制的法式料理,还有一些传统的烘焙菜品,比如肉骨、鸭肉、烤串等等,都是最受欢迎的食物选择。希望能对您有所帮助。
Top-K Top-P#

Top-K:每次随机采样时,只在概率最大的前 top_k 个 token 中采样,其余概率置零。

Top-P:每次随机采样时,将候选 token 按概率由大到小排序,从概率最大的开始,依次取出若干 token,使它们的概率之和刚好等于或超过 top_p.

下面尝试在保持默认温度(0.85)下,减小 Top-P 的值,期望提升模型的理工科能力。

注:以下测试基于官方 MiniMind2-Small 模型权重,而非我自己训练的模型。这是因为我自己训练的模型没有使用足够的数据、没有积累足够长的训练时间,实际效果不佳。

测试用的问题:
0. 为什么天空是蓝色的

  1. 请用Python写一个计算斐波那契数列的函数
  2. 解释一下”光合作用”的基本过程

GPT 在这三个问题上的回答(使用临时对话)的统计数据:

问题 0问题 1问题 2
token_count443830809
4-gram_repetition0.050.69640.2196
Top-P = 0.3
Top-P = 0.55
Top-P = 0.85

从上面可以看出,低 Top-P 会一定程度上促进模型输出重复的 token,但从 0.55 下降到 0.3,这种现象反而有所减轻。
此外,可以注意到我们的评判方式对编程题目不太客观,因此我还针对编程类问题,尝试创造一种更有效的评判标准:具有远程衰减的 n-gram 重复度。
这个标准基于一个思想:代码中应当允许有意义的重复,比如函数的反复调用、变量的多次操作等。因此我们需要从所有的重复中分离出有意义的和无意义的,一个关键点就是两个重复 token 之间的距离。这个评判标准会惩罚近距离的密集重复,但会宽容稀疏的重复(更可能是有意义的)。代码如下:

def weighted_ngram_repetition(
    tokens,
    n: int = 4,
    window: int = 50,
    window_is_token_distance: bool = False
):

    ngrams = [tuple(tokens[i:i+n]) for i in range(max(0, len(tokens) - n + 1))]
    M = len(ngrams)
    if M == 0:
        return 0.0, []

    if window_is_token_distance:
        window = max(0, window - n + 1)

    # 记录每种 ngram 出现的位置(sorted lists)
    positions = defaultdict(list)
    for idx, ng in enumerate(ngrams):
        positions[ng].append(idx)
        
    activations = [0.0] * M

	max_local = 1
	for ng, pos_list in positions.items():
		for p in pos_list:
			left = p - window
			right = p + window
			L = bisect.bisect_left(pos_list, left)
			R = bisect.bisect_right(pos_list, right)
			count = R - L
			if count > max_local:
				max_local = count
	# avoid division by zero
	denom_global = max_local

    for ng, pos_list in positions.items():
        # pos_list sorted ascending
        for p in pos_list:
            left = p - window
            right = p + window
            L = bisect.bisect_left(pos_list, left)
            R = bisect.bisect_right(pos_list, right)
            local_count = R - L  # 包含自身
            start = max(0, p - window)
            end = min(M - 1, p + window)
            theoretical_max = end - start + 1
            denom = denom_global if denom_global > 0 else 1

            activation = local_count / denom
            activations[p] = activation

    # 全部位置的平均激活值作为最终 score(在 0..1 之间)
    score = sum(activations) / M
    return score
py

这段代码在求:对于每个 n-gram,在其周围半径为 window 的邻域内,同种 n-gram 的占比。以这个占比作为该 n-gram 的激活值(值域为 0~1),然后再按照之前的统计方法求和再除以总 n-gram 数。

测试问题还是:“请用Python写一个计算斐波那契数列的函数”。

GPT 的回答作为标准参考(使用临时对话),其数次回答的 W4R 平均值为 0.016878166477133128.

而我们的 MiniMind2-Small 模型在不同 Top-P(0.85, 0.55, 0.3) 下的表现:

可以看到,Top-P 确实会影响模型的代码输出质量,其作用和温度类似(这点从理论上也可以预见)。

束搜索#

需要注意的是,MiniMind2 源码采用的是随机 sampling,和束搜索使用的半贪心策略有所冲突。所幸 transformers 库包含了 beam search 的实现,因此只需要小改一下代码,在 model.generate 中设置 do_sample=False 以及 num_beams 参数。

Text Classification#

本节内容使用 MiniMind2-Small 和 MiniMind2 作者发布的权重,基于此进行 zero-shot,few-shot,以及文本分类的 Fine Tune.

Zero-Shot#

首先尝试 MiniMind2-Small,使用指令:

Please read the following passage and classify it into one of five types: business, entertainment, politics, sport, tech.
plaintext

效果不佳,模型完全不知道要输出五个词中的一个,而是输出无关长文本。

于是切换到 MiniMind2,并换用更加有约束的指令:

Classify the passage you will read. You MUST choose exactly one of the following categories:
business, entertainment, politics, sport, tech.

title:
{title}
content:
{content}

Your answer (choose ONLY one category in business, entertainment, politics, sport, tech):
plaintext

十分可惜,模型虽然意识到输出中要出现这五个词之一,但是仍然以为是续写文本,输出很长的废话,似乎想要续写提示词而不是回答问题。

Few-Shot#

在提示词中加入一些分类示例。我们期待:即使模型试图“续写”,它也应该知道续写成什么样子。提示词格式:

You are a text classifier. Classify each passage into **exactly one** of the following categories:
- business
- entertainment
- politics
- sport
- tech

Output **only the label**, nothing else.

Here are some examples:

Example 1:
title:
{example1_title}

content:
{example1_content}

Your Output(Must be one of "business entertainment politics sport tech"):
{example1_category}

...

Example 6:
title:
{real_title}

content:
{real_content}

Your Output(Must be one of "business entertainment politics sport tech"):
plaintext

其中一个巧妙想法是:将真实输入伪装成 Example 6,诱导模型以续写的方式输出。

很可惜,这回模型是学会了只输出单个单词,但是仍然胡乱输出,输出的并非分类标签。

不行了,这下必须上 SFT 了!

SFT#

这部分是基于 MiniMind2-Small 在 pretrain_hq.jsonl(几乎纯中文) 上预训练出的模型进行微调的。

数据集:bbc-news-data.csv,通过脚本将每类按 4:1 比例随机采样分成训练集和测试集,然后使用之前的 instruction 模板包装一下 title content 等字段,即可开训!

下面是一部分结果:

EpochAccuracy
20.05
60.2

在之后的 epoch 中,accuracy 没有继续提升,让我十分沮丧。

我观察了模型的输出,发现它似乎放弃了理解内容,而是每次都倾向于输出 sport(也解释了接近 1/5 的准确率)。


呜呜呜调死我啦,怎么改都提升不了。
谁懂凌晨两点盯着 AutoDL 上不断减少的余额时的救赎感啊。。。
大道都磨灭了

由于调参甚久,且我以广度优先的方式尝试了很多方法,因此这里无法展示所有中间产物,只讲成品。

终于调出来啦啊啊啊啊啊

Text Classification 二周目#

在上一节中模型效果很差。我考虑了很多因素,最终归因于模型预训练的语言(中文)和分类所要使用的语言(英文)差别过大。这就导致了预训练模型对英文一窍不通,只能瞎蒙。因此,我们需要在纯英数据集从头上训一个 Pretrained MiniMind2-Small.
这种数据集哪里找呢?真是——踏破铁鞋无觅处,得来全不费工夫!先去逛了一大圈 CSDN 和知乎,看到的动辄都是 Wikimedia 这种超大数据集,完全不现实。但我灵机一动,重新精读了一遍 MiniMind2 仓库的 readme.md,发现里面提到了匠数大模型数据集,正好有纯英数据,只不过是 sft 专用的,每一条都包含很多轮对话。不过不要紧,我在其中随机采样 10% 的对话,然后每段对话直接拼接成单个长文本,就可以作为预训练数据集啦。
预训练了 3 epochs 后,先看看模型的输出。
询问 Please write a code to calculate the sum of all odd positive integers smaller than 100,模型完全理解了英文意思,并且输出了有模有样的 C++ 和 Python 代码(但代码有些语法问题,毕竟是“文科生”)。不过输出特别长,这是因为我们数据集是拼接的多轮对话,导致模型一直学习到这种超长文本。

模型底子打好了(相比基于 pretrain_hq.jsonl 预训练的模型,新模型英文能力极强),接下来就是重新进行文本分类实验的三个部分了。

Zero-Shot²#

在 Pretrain 的基础上,使用英文对话进行 2 epochs 的问答 SFT,然后使用分类指令让模型分类,效果相比之前依然很烂。模型在测试集上只误打误撞做对了一个预测,这很可能是因为模型没有理解什么是“分类”。

Few-Shot²#

使用和 Zero-Shot² 中一样的 SFT 模型,给予 5 个类别各一个 example,然而模型仍然没有理解分类任务的含义。也许是小模型泛化能力过低,或者对话训练次数过少,没有习得理解这种指令的能力。

SFT²#

当 context learning 不奏效的时候,还得祭出梯度下降大法!

全参数微调#

使用在前述的在英文数据集上预训练的模型,在 bbc-news-data 上有监督微调了 10 epochs,最终模型在测试集上的准确度达到了 79%!
下方是模型在测试集上的预测矩阵,纵向为真实值,横向为预测值。
从中可以看出哪些分类本身比较接近,而那些区别非常显著。

real\predbusinessentertainmentpoliticssporttech
business922602
entertainment161643
politics1386002
sport1166740
tech936157
上半层微调#

尝试冻结 Embedding 和下方 50% 的层,只训练上部,以期节省时间。

全参数微调中的训练流程相同,最终模型在测试集上的准确度达到了 67%. 能力确实有所下降,但对训练用时的缩减作用不明显。大概只有在模型规模很大,而微调数据集较小的时候,冻结参数才能发挥其节省时间、减轻过拟合的作用。
下方为其预测矩阵:

real\predbusinessentertainmentpoliticssporttech
business838821
entertainment1477166
politics13174751
sport2204663
tech1149548
LoRA#

这同样是一个在大模型上作用明显,在小模型上体感不明显的方法。它的概念就是,在 SFT 的时候,不直接修改模型参数,而是给每个线性层加上一个补丁,这个补丁的参数量很小(秩很低),通过训练补丁来起到模型能力迁移的关键作用。

我测试了 rank=8,32,128 三种情况,随着 epoch 增加,loss 均没有明显下降。只有当 rank 增加到 1024(折合模型总参数的约 40%)的时候,才有可观察的效果。最终,在测试集上准确度达到 40%,附预测矩阵:

real\predbusinessentertainmentpoliticssporttech
business4745600
entertainment366213
politics20322211
sport13591132
tech14305114
如何更有性价比地提升精度?#

Multi-agent Debate:成本暴涨,且小模型自身能力瓶颈,通过该方式缓解不如扩大模型。

Prefix Tuning:加一段可训练的软 prompt,以诱导模型做出更好输出。但在小模型面对复杂数据时,容易欠拟合,作用微弱。

根据 GPT 的建议,可以尝试将分类标签的 token 设置为 special_token,保证其长度相同,并使用 logits 中分类标签的排名而不是模型自行输出的文本来分类。

然而,这样做出来的效果竟然只有 39% 的准确度,目前暂且认为是代码实现里有些隐性的 bug.

更多以后要探索的部分#

了解到 Hugging Face 的 PEFT 库,有时间研究一下。

RLHF#

理论#

我理解的强化学习在这里的作用,类似于一种对「不可微分的 loss function」的一种 workaround 训练方式。

通常,我们在训练序列生成模型的时候,会对每个时间步的预测使用 CrossEntropyLoss 进行损失评估。这种损失函数是可微分的,因此才能够梯度下降进行训练。但是很多时候,模型表现的好坏与交叉熵的数值关系不大,我们会希望找到更精准、更灵活的评估模型好坏的函数。

譬如,我们会使用 BLEU score 衡量翻译模型的能力,但是 BLEU score 本身不可微分,无法梯度下降。这时候就需要 Reinforcement Learning 出手,让模型自行试错并找到让 BLEU score 高的生成模式。

对于聊天机器人来说,其服务对象是人,因此使用人的反馈(显然无法微分)进行强化学习训练,让模型微调到在人类看来表现更佳的 pattern 上去。

DPO 的做法是,把模型复制成为两份,一份作为参考(ref),一份进行训练(policy)。每次将一个好的回答(chosen)和坏的回答(rejected)喂给二者,比较二者输出 chosen 的概率与输出 rejected 的概率。具体地,是把这两种概率求对数(避免累乘导致下溢),然后除以序列长度,再做差,这个差距称为 logratios(越大越好)。训练的目标是让 policy 模型的 logratio 比 ref 模型的大。
损失函数公式为:

loss=logσ(β(policy_logratiosref_logratios))\mathrm{loss}=−\log\sigma(\beta\cdot (\text{policy\_logratios} - \text{ref\_logratios}))

其中的 β\beta 是一个超参数,σ\sigma 是 sigmoid 函数。

实践#

使用 MiniMind2 提供的 train_dpo.py,从Zero-Shot²中得到的在英文数据集上进行 Pretrain 和 SFT 过的模型权重开始,进行 DPO.
使用 1e-7 学习率训练 1 epoch 后,实测模型确实尝试变得更加“友好”,但其语言水平和技能出现倒退(应该是学习率过大导致的遗忘?),尤其是写代码能力,退化严重,只能输出重复字符。

咨询了 GPT,得到了肯定的答复:

强度过高的 DPO 会导致模型专业能力显著下降。

代码是 最容易被 RLHF 破坏的能力之一,原因有三:

  1. 代码的“人类偏好”极不可靠

  • 人类评审:
    • 更看重“看起来对”
    • 而非严格正确
  • 长代码 vs 短代码:
    • 人类往往选解释多的

  1. DPO 对「概率差」极其敏感

只要模型发现:

“写详细解释 + 保守代码 比写短小精悍的代码更容易赢”

它就会系统性偏向前者。

  1. 使用的是 全参数 DPO

这是最容易导致 catastrophic forgetting 的方式

总结#

两周的时间中,70% 用在了学习李沐和李宏毅老师的课程,剩余时间用于完成项目,因此看起来有些仓促。美中不足的就是 text-classification 没有调出更好的精度(终究是没找到 bug 在哪里),以及 RLHF 没有更深入地探究。

MiniMind2-Small 全流程尝试
https://blog.leosrealms.top/blog/2025-12-14-minimind2-small-full-process-attempt
Author CoderXL
Published at 2025年12月14日
Comment seems to stuck. Try to refresh?✨