Prompt Distillation for Efficient LLM-based Recommendation
本文发表在CIKM’2023上,主要针对大模型应用在推荐系统时采用离散提示(discrete prompt)出现的两个问题提出解决方案并进行了实验证明。
- 问题1:用户与物品的ID embedding 在推荐系统中用户对物品的偏好以及用户和物品的相似度,而离散提示的模板中的词嵌入通常捕捉的是词之间的上下文关系。在大模型训练时,通常需要经过大量的微调才能弥补id和模板词之间的gap。对此提出了 (Prompt Distillation)提示蒸馏的方法,将离散提示提炼为了连续向量的提示,是model-free的。
- 问题2:训练效率问题,大模型推荐通常是经过预训练之后适应到不同的下游任务。如本论文主要针对的是TopN推荐,序列推荐和可解释性推荐三种推荐。而不同的推荐对应的输入\输出以及提示模板的长度可能不同(常用的解决方法:填充到相同长度),如果将这些任务的训练样本混合在一起直接进行训练虽然是可行的,但是效率大打折扣。为此提出了任务交替训练策略,就是按照预先设定的顺序依次重复训练三种任务。
方法论
三种典型推荐介绍
U \mathcal U U:用户集合, I \mathcal I I:物品集合, u , i u,i u,i:具体用户和物品
- 序列推荐: 每个用户有自己的历史交互序列,根据给定的用户和物品交互序列,预测该用户下一项的点击物品。
- TopN推荐:给定用户以及一个候选物品列表,向给定的用户推荐一个项目列表,该列表内包含的是候选列表中N个最有可能交互的且用户未曾交互的物品。
- 解释性推荐:给定一个用户-物品对(u,i),生成一个解释来证明为什么要向该用户推荐该物品(比如:价格合理)。
离散提示
在推荐系统中,用户和物品的id是用来区分它与其他个体的关键,大多数过往的工作采用与id相关的文本段(如用户或者物品标题)来作为替代,填充在预定义的模板中。举个例子: Generate an expalanation for user_1234 about item_5678。这种类型的文本输入就被作者定义为离散提示,由一系列的离散token组成。由于user_1234会被分词为user,_,12,34。作者设计了一个额外的Whole-word Embedding来连接ID的token,具体形式在蒸馏中会解释。
提示蒸馏
提示蒸馏:不牺牲大模型的性能的前提下,缩短提示长度。经过提炼的简短提示符可以是自由文本也可以是向量
作者认为离散提示可能不能够有效的指引大模型。当两个不同的任务的输入数据格式十分相似的时候,大模型可能没法区分该任务是哪个任务,例如序列推荐和TopN推荐的输入都是一个用户和一堆物品。另一个问题就是当模板较长时,可能会掩盖关键的信息,这也会花费较长的时间来进行训练和微调。为此提出了POD提示蒸馏的方法,将离散的提示模板提炼成了多个的连续向量。一个具体的提示蒸馏样例如下。
整体连续向量提示的提炼过程如下图所示:
具体的推理过程如下,省略了提示模板和单词分词。
大致的流程:
X
=
[
x
1
,
.
.
.
,
x
∣
X
∣
]
X = [x_{1},...,x_{|X|}]
X=[x1,...,x∣X∣]是输入序列,
Y
=
[
y
1
,
.
.
.
,
y
∣
Y
∣
]
Y = [y_{1},...,y_{|Y|}]
Y=[y1,...,y∣Y∣]输出序列(都是标量),输入X经过embedding层之后,可以得到一个token表示,
[
x
1
,
.
.
.
,
x
∣
X
∣
]
[\mathbf{x}_1,...,\mathbf{x}_{|X|}]
[x1,...,x∣X∣](矢量),将其添加在K个连续的提示向量
[
p
1
,
.
.
.
,
p
k
]
[\mathbf{p}_1,...,\mathbf{p}_{k}]
[p1,...,pk]后,形成
[
p
1
,
.
.
.
,
p
k
,
x
1
,
.
.
.
,
x
∣
X
∣
]
[\mathbf{p}_1,...,\mathbf{p}_{k},\mathbf{x}_1,...,\mathbf{x}_{|X|}]
[p1,...,pk,x1,...,x∣X∣],这个整体作为全词表示
[
w
1
,
.
.
.
,
w
∣
X
∣
+
k
]
[\mathbf{w}_1,...,\mathbf{w}_{|X|+k}]
[w1,...,w∣X∣+k],记为
S
=
[
s
1
,
.
.
.
,
s
∣
S
∣
]
\mathbf{S} = [\mathbf{s}_1,...,\mathbf{s}_{|S|}]
S=[s1,...,s∣S∣]。将其通过LLM的双向编码器得到一个隐向量
H
=
[
h
1
,
.
.
.
,
h
∣
S
∣
]
\mathbf{H} = [\mathbf{h}_1,...,\mathbf{h}_{|S|}]
H=[h1,...,h∣S∣],在此基础上LLM解码器可以进行自回归生成。生成的过程可以理解为总共有|Y|步,每一步生成一个单词。具体来说,对于每个时间步t,解码器在词汇表上输出一个概率分布
p
(
y
t
∣
Y
<
t
,
X
)
p(y_t|Y_{<t},X)
p(yt∣Y<t,X),然后采取一定的策略选择出合适单词。可以参考下图(图是自己找的):
整体过程类似自然语言生成问题,因此最终损失函数采用负对数似然函数:
L
Θ
=
1
∣
D
∣
∑
(
X
,
Y
)
∈
D
1
∣
Y
∣
∑
t
=
1
∣
Y
∣
−
log
p
(
y
t
∣
Y
<
t
,
X
)
(1)
\mathcal{L}_{\Theta}=\frac{1}{|\mathcal{D}|}\sum_{(X,Y)\in\mathcal{D}}\frac{1}{|Y|}\sum_{t=1}^{|Y|}-\log p(y_t|Y_{<t},X)\tag{1}
LΘ=∣D∣1(X,Y)∈D∑∣Y∣1t=1∑∣Y∣−logp(yt∣Y<t,X)(1)
其中
D
\mathcal{D}
D是所有输入输出对(X,Y)组成的训练集。
任务交替训练
由于不同推荐任务的输入输出长度不同,将它们填充到相同的长度会耗费大量内存,使批处理规模变小进而导致更多的迭代和更长的训练时间,因此使用不同任务的混合样本进行训练会导致LLM效率降低。作者提出任务交替训练策略:交替地使用来自一个任务的一批样本更新模型参数,然后再使用来自另一个任务的一批样本,以此类推。一般情况下,每个任务的数据格式都是相同的,因此该策略可以节省大量内存,提高训练效率。
集束搜索生成
Beam search详情参考Beam search,个人理解就是如果将beam的大小设置为b,在结果生成的每个时间步长中,都从整个候选列表中选择b个具有最大似然估计的序列。对于b为2,具体例子如下:
实验
实验部分不过多介绍了,感觉和以往的差不多。值得一提有两点。
- 在消融实验的时候验证了“连续提示+离散提示”与单用连续提示相比推荐性能和解释性能变化不大,但是去除离散提示的效率会大大提高。
- 连续提示向量的数量问题,结果表明随着数量的增加(K = [1,3,5,10,20,50]),序列推荐和TopN推荐的性能会普遍增长,但是解释性推荐的解释性能在数量为1或者3时达到了最佳。