黑盒蒸馏(技巧蒸馏)


文档摘要

黑盒蒸馏(技巧蒸馏) 黑盒蒸馏所用到的仅仅是教师模型的回答(有时也包括输出的概率分布,即软目标,但是不会用到logits)。 黑盒蒸馏意味着教师模型的输出是我们唯一能获取到的训练资源,因此,黑盒蒸馏整体的思路可以分成两步: 从教师模型收集问答数据 使用这些数据对学生模型进行微调 这份教程会详细介绍三种涌现能力的主流蒸馏方式以及改进方向。 值得一提的是,黑盒蒸馏和监督微调很相似,所以了解监督微调的概念将有利于本节的学习。不过不了解也没关系,只要知道一般模型训练的概念(损失函数、梯度下降等)也足够。 In-context learning 蒸馏 大模型的一大特有的能力是上下文学习(In-context learning, ICL),即在推理阶段进行少样本学习。

黑盒蒸馏(技巧蒸馏)

黑盒蒸馏所用到的仅仅是教师模型的回答(有时也包括输出的概率分布,即软目标,但是不会用到logits)。

黑盒蒸馏意味着教师模型的输出是我们唯一能获取到的训练资源,因此,黑盒蒸馏整体的思路可以分成两步:

  1. 从教师模型收集问答数据
  2. 使用这些数据对学生模型进行微调

这份教程会详细介绍三种涌现能力的主流蒸馏方式以及改进方向。

值得一提的是,黑盒蒸馏和监督微调很相似,所以了解监督微调的概念将有利于本节的学习。不过不了解也没关系,只要知道一般模型训练的概念(损失函数、梯度下降等)也足够。

1. In-context learning 蒸馏

大模型的一大特有的能力是上下文学习(In-context learning, ICL),即在推理阶段进行少样本学习。
上下文学习的能力是一种类似于“举一反三”的能力,我们可以在Prompt中先给出几个问-答示例,接着给出一个问题,
模型就能按照示例的格式和模式输出回答。

以下是一个简单的ICL例子:

alt_text

模型成功模仿了示例中的答题思路和答题格式。

然而,由于算力限制,不可能将具有ICL能力的大模型部署到实时推理系统上。
有没有一种技术能够将大模型的ICL能力迁移到一个小模型中呢?这就是ICL蒸馏要解决的问题。

1.1 ICL

首先,让我们对ICL做一个形式化的定义:ICL是指输入给模型以下格式的内容:

x_1, y_1; x_2, y_2; x_3,

模型会输出:

y_3

其中x表示问题,y表示回答。x,y是成对的。

示例的格式不一定在训练时见过,但是模型可以在推理时模仿其中的格式和模式。
即只要在prompt前加几个例子,模型就能学到其中的格式和逻辑,从而不用更新参数就能学习。

训练之前,我们会收集如下含有提示词和标签的数据:

alt_text

1.2 ICL 微调

这一部分将介绍一系列的用于ICL微调的损失函数,需要读者具备条件概率、熵和交叉熵相关的知识。

一句话概况,ICL微调就是让模型的输出y_3的概率尽量大。即在给定模型参数\theta, 示例1和2, 以及x_3时, 模型输出 y_3 的概率 p(y_3|x_3, (x_1, y_1, x_2, y_2), \theta) 越大越好。
为了计算稳定,可以转变为对数概率,即log \space p(y_3|x_3, (x_1, y_1, x_2, y_2), \theta) 越大越好. 损失函数一般是求最小化,所以再添加一个负号:

-log \space p(y_3|x_3, (x_1, y_1, x_2, y_2), \theta)

上面这个例子有两个示例问答对,所以可以叫做 two-shot, 实际中也可以使用 zero-shot, one-shot, ...。
假设示例问答对的数量是k, k-shot 的示例问答我们可以记作 S_k,正式的问题记作 x,期望的回答记作 y,最终的损失函数可以写成

-log \space p(y|x, S_k, \theta)

以上是一条微调数据的损失,一般微调需要几千到上万条数据,每一条数据都有各自的x, y, S^x_k,其中S^x_k代表对应于问题 x 的示例前缀。
所以微调时总的损失如下

L_{ICL} = \Sigma_{(x,y)\in D} - log \space p(y|x, S^x_k, \theta)

这就是ICL微调的损失函数,但只是针对一种任务类型的。
一般我们会针对同一个类型的任务收集许多数据微调,所以一个任务\mathcal{T}会对应一个数据集 \mathcal{D_T}, 所以针对任务\mathcal{T}微调的损失函数如下:

L^{ICL}_\mathcal{T} = \Sigma_{(x,y)\in \mathcal{D_T}} - log \space p(y|x, S^x_k, \theta)

1.3 Meta-ICL Tuning 和 MultiTask-ICL Tuning

上面我们讲了针对任务\mathcal{T}进行ICL微调的损失函数,而实际中往往在多个任务上微调,以获得更好的泛化性能。
因此,对多个任务微调的总损失如下:

L^{ICL}_{train} = \Sigma_{\mathcal{T}\in \mathcal{T_{train}}} \Sigma_{(x,y)\in \mathcal{D_T}} - log \space p(y|x, S^x_k, \theta)

其中\mathcal{T_{train}}可以是\mathcal{T_{meta}}\mathcal{T_{target}}。 它们有什么区别呢?
如果使用\mathcal{T_{target}},训练时的任务就是我们的目标任务;
而使用Meta-ICL,则是训练时避免使用目标任务,希望模型能通过学习其他任务学习到“学习方法”,即学习上下文的能力,然后在我们的目标任务上应用它所学到的学习能力。了解Meta-ICL Tuning 和 MultiTask-ICL Tuning的损失函数将有利于理解ICL蒸馏的损失函数。

1.4 In-context Learning Distillation

ICL蒸馏同时使用软目标和硬目标作为损失函数:

\mathcal{L}_{KD} = \alpha(t)\mathcal{L}_{hard} + (1-\alpha(t))\mathcal{L}_{soft}

让我们分别认识软目标和硬目标。

软损失:

\mathcal{L}_{soft} = \mathcal{L}^{ICL}_{soft} + \beta\mathcal{L}^{LM}_{soft}

其中第一项是学生和老师的交叉熵损失:

\mathcal{L}^{ICL}_{soft} = - \Sigma_{\mathcal{T}\in \mathcal{T_{train}}} \Sigma_{(x,y)\in \mathcal{D_T}} \Sigma_{c \in C_{\mathcal{T}}} P(y=c | x; S^x_k; \theta^t)logP(y=c | x; S^x_k; \theta^s)

其中c是数据中的<lable>。后面的 概率*log概率 是计算针对某个可能的Label的交叉熵。

第二项是语言建模损失:

\mathcal{L}^{LM}_{soft} = \Sigma_{x\in \mathcal{D}_{LM}} P(x|\theta^t)logP(x|\theta^s)

这里的训练数据就是一般的网络文本:
alt text

硬损失

除了学习老师的经验,学生还要学习ground-truth,这就是硬损失的意义。

\mathcal{L}_{hard} = \mathcal{L}^{ICL}_{hard} + \beta\mathcal{L}^{LM}_{hard}
\mathcal{L}^{ICL}_{hard} = -\Sigma_{\mathcal{T}\in \mathcal{T_{train}}} \Sigma_{(x,y)\in \mathcal{D_T}} log P(y|x; S^x_k; \theta^s)
\mathcal{L}^{LM}_{hard} = -\Sigma_{x\in \mathcal{D}_{LM}} log P(x|\theta^s)

1.5 实践

参见code/ICL

1.6 改进方向

模型上下文学习的性能和上下文中的例子质量紧密相关,所以有人研究专门设计了一个例子检索器,检索高质量的示例[6]。

2. 指令跟随蒸馏

2.1 指令跟随

我们知道大模型的主流训练范式有三步,第一步是预训练,第二步是指令微调,第三步是RLHF。指令跟随的能力是在第二步获得的。

仅仅经过预训练的大模型没有指令微调的能力,它只能续写用户prompt。经过指令微调的模型具有执行prompt中的指令的能力,而不是续写用户的prompt。

指令微调数据包含指令、输入、输出,但是指令和输入没有明确界限,把输入当成指令也可以, 而输入部分可以是空的。比如这是一份指令微调数据. Self-Instruct生成的数据输入部分也可能是空的。

如何将指令跟随能力蒸馏到小模型中呢?容易想到的思路是从大模型收集若干<指令-输入-输出>数据,然后用这些数据来微调小模型。
那么如何从大模型收集<指令-输入-输出>数据呢?有没有成本低,效率高的方法?答案是Self-Instruct。

2.2 Self-Instruct 范式

alt text

要使用Self-Instruct生成数据,我们只需要推理一个经过预训练的大模型(不要经过指令微调哦)就可以了。这是因为预训练的大预言模型具有续写的能力,利用这个能力可以收集新的数据。

首先,人工设计若干任务,针对每个任务,撰写一个初始数据。这形成了初始的数据池。

然后,随机从数据池中抽取8条指令,让大模型续写更多的指令。可以使用如下的模板:
alt text
生成指令,直到遇到“Task 16”字符串或达到最大生成字数或模型自动停止生成。

生成指令之后,下一步,就应该生成输入和输出了。但是在此之前,先让大模型自己判断这个生成的每一个指令是否是分类问题。这里还是利用了续写能力。
alt text

对于是分类问题的任务,先生成输出,在基于输出生成输入;而对于非分类问题,先生成输入,基于输入生成输出。
alt text
alt text

2.3 指令跟随蒸馏实践

参见 code/InsructFollowing.

2.4 对抗蒸馏

对抗蒸馏(adversarial distillation)提出除了可以让知识单向地从教师注入学生,学生也可以产生“反馈”,
即通过对比学生的回答和老师的回答,老师可以发现困难的知识,并加生成更多困难知识的供学生学习。具体的细节可以查看参考文献[7]。

3. 思维链蒸馏

和之前说的一样,我们可以从大模型收集<问题-回答>对,然后用这些数据来微调小模型。
但是这时数据收集要注意数据的质量。回答中要包含正确的推理,并且最好是对于一个问题有多样的推理路径。

以下是一个训练gpt2进行时间推理的例子。

步骤1:收集数据

从教师模型收集推理样本,作为训练数据。在问题后面,我们加上一句"Let's think step by step."

USER: Q: <Question> A: Let's think step by step.

ROBOT: <Reasoning>

USER: Q: A: Let's think step by step. <Reasoning>\nTherefore, the answer is

ROBOT: <Answer>

其中第三行的针对一个问题多次运行后可能有不同的推理路径。

步骤2:数据格式化

我们把以上收集的数据整理成某种格式化的数据,让他们具有统一的格式,方便训练。比如可以使用如下的格式:

input_format = "<Question> ###" label_format = "<Reasoning> --> <Answer>"

步骤3:实践

正式的微调Loss函数是交叉熵损失。

参见code/CoT.

4. 扩展和总结

实际上,除了以上三种涌现能力的蒸馏,只要是从教授模型收集某种类型的数据,然后用这些数据微调学生模型,都是黑盒蒸馏的应用范围。
因此,对于一些特定领域和特定需求的任务,也可以使用类似的方法达到希望的效果。比如随着OpenAI 强大的复杂推理模型O1的发布,对推理能力进行蒸馏也可以套用上面的方法。近期上海交通大学的O1复现论文就是一个很好的对教师模型的推理能力进行蒸馏的例子。

但是也有研究[5]指出黑盒蒸馏导致仅模仿但不理解的问题,要提高学习质量,还需学生有良好的天赋(base 模型的能力)。

参考资料

  1. In-context Learning Distillation: Transferring Few-shot Learning Ability of Pre-trained Language Models
  2. MetaICL: Learning to Learn In Context
  3. 交叉熵:https://blog.csdn.net/tsyccnh/article/details/79163834
  4. Model Compression and Efficient Inference for Large Language Models: A Survey
  5. The false promise of imitating proprietary llms
  6. Learning to Retrieve In-Context Examples for Large Language Models
  7. Lion: Adversarial Distillation of Proprietary Large Language Models

作者与出处
原作者: Datawhale
来源:datawhalechina
许可证:CC BY-SA 4.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: Datawhale 转发
评论区 (0)
U