0%

在LLM兴起之后,很多工作都在如果更好的设计prompt的形式上下了很多苦功夫,设计提示词的这个学问就是prompt engineering。我之前一直觉得这是一个静态的过程,建议刚接触的同学先学习吴恩达和openai合作制作的prompt engineering的知道课程,另外也有中国热心的网友也翻译了自己的中文版本,还加入了一些自己的思考,推荐骆驼

我是从斯坦福的羊驼模型开始接触instruction tuning的,instruction tuning提供给了用户一种在特定数据集上低成本finetune模型的可能性,后来又接触到了斯坦福新出的paper:Demonstrate-Search-Predict: Composing retrieval and language models for knowledge-intensive NLP,这是一篇用信息抽取技术retrieval models来扩充query,从而使得LM能够得到更多context的技术,这才让我了解到其实除了最基础的我们知道的那些prompting的方式,比如zero-shot,few-shot,chain-of-thought方式,还有一些方式在这些方式上进行了创新。

lilian weng的博客在chiin-of-thought章节之后就介绍了自动prompt design和augmented language models。这些对于我来说都是新东西,新技术发展的很快,我学习的过程其实也是在织自己知识结构中的这个网,从接触GPT以来写了好几篇博客,零零散散的,很多博客都是只局限于其中的技术,时不时我需要停下里思考这些技术之间的关联,从而把整个逻辑理顺。技术的发展或者一个模型一个方法的出现都是有路径可循的,我希望能把这些路径穿起来,摸清楚发展脉络。

Augmented Language Models

本节参考review,我之前理解augmented llm有点狭隘,最基本的方法是从一个知识库或者搜索引擎中搜索query相关的内容,将这些内容加到prompt里,从而一定程度上解决大模型的幻觉问题,这种思路的典型应用见Internet-augmented language models through few-shot prompting for open-domain question answering. 但上面提到的这篇review把问题更细化一点,从更高的维度去分析现在的这些方法,第一个就是reasoning,也就是把复杂问题如何细化成LLM可以解决的简单问题,第二个是Tools,也就是我们上面介绍的应用的场景,tool帮我们抽取相关信息(document retriever)。关于更具体地解释可以阅读review地1.2节。

Useful Links

QA是什么?

直观上理解,QA就是用户给出一个question,系统(模型)给出一个answer。它和reading comprehension还是不一样的,在paper中对于这两个问题做了归纳:

Comparison of assumptions

reading comprehension问题,我们的任务是从一个paragraph中找出answer:comprehend a passage of text and answer questions about its content (P,Q)-> A,也就是我们事先不仅有question,还有一段paragraph,我们只需要找出answer在这个paragrapg中的位置(span),但是对于open-domain的QA来说,我们事先是不知道anwer在哪儿的,比如上图中unsupervised QA。那么对于这一类open-domaiin的问题如何解决?最常见的就是retriver-reader架构,就是我们先从一大堆语料库中挑选出我们的paragraph,然后对于这一些paragraph,我们用reading comprehension的技术再来定位answer在哪里。上面这篇paper就是应用的这种思想,提出了ORQA,这是19年的文章,想法很典型。

ORQA

上文提到的斯坦福的数据集SQuAD是使用的最广泛的reading comprehension数据集,它的组成形式是(passage, question,answer),每一个answer都是passgae里面的一个segment。

SQuAD example

这就有它的局限性,因为有一些问题的回答是不会在passage中找到的,所以它不能用于open-domain的task,它完全是一个监督性任务的数据集,目前在这个数据集上最好的模型的表现已经超越了人类,可以说是"almost solved"。

Reading Comprehension

首先我们可以先从比较简单的问题开始解决,reading comprehension可以说是QA的一个子问题。第一步搞明白问题定义:

problem formulation

2016年-2018年,主要用于解决RC的方法是LSTM+attention的模型架构,这些模型主要有15年的attentive reader、16,17年的stanford attentive reader、17年的match-LSTM、17年的BiDAF、17年的dynamic coattention network、17年的DrQA、17年的R-Net、17年的ReadoNet。2019年开始,主要是18年Bert出来之后,大家普遍开始采用finetune BERT来解决这个问题,值得一提的是在BERT的原文paper中下游任务也使用了SQuAD来做了实验,作者用于预测start和end的方式也设计的很精巧。

stanford attentive reader

这篇文章在logistic regression的基础上(SQuAD数据集)有了巨大的进步,算是18年之前lstm+attention架构的集大成者。思想很简单,但里面很多细节,比如在编码passage的时候用了好多来源的vector进行拼接,然后再输入斤lstm中。但整体架构和机器翻译领域的发展历程是一样的,在transformer没有出现之前,大家都在lstm上做了很多创新,attention加入计算是其中一种,DrQA将多个来源的向量拼接也是一种,感兴趣的可以读一下原文:Reading Wikipedia to Answer Open-Domain Questions。作者是cs224n的QA这门课的讲师,也是一位华人小姐姐。其实我在看cs224n QA这一讲的ppt时感觉斯坦福的这门课着重讲解了stanford attentive reader这一系列的模型,不知道有没有私心。

DrQA

这之后还出现了bidirectional attention的,例如BiDAF,就是不仅计算了query2Context的attention weights,也计算了Context2Query的attention weights,将这两者和context原来的lstm值再输入进一个双层LSTM进行start,end的预测(很复杂就是了),虽然也不知道为啥这么做就会有一个不错的performance,模型架构越来越复杂,卷死了。

但不管怎么说,在transformer被大家普遍使用之前,rnn+attention的这种模式是最好的解决办法了,俗称SOAT。

Bert用作reading comprehension

自从18年开始,在reading comprehension这个任务上,谷歌出了bert,成就了新的历史,一下子F1从79.4(DrQA)升级到了91.8,人类在SQuAD上的表现才91.2,所以算是超越人类表现了。

BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 这篇文章的4.2节详细介绍了预训练完成之后的bert如何在SQuAD这个下游数据集上进行finetune的。

Bert QA

上面这幅图更好的解释了Bert在finetune的时候如何做RC这个任务的。首先我们将question和paragraph拼接在一起,中间使用[SEP]连接。然后将这一串字符串整个输入进预训练好的Bert。对应的bert输出了同样长度的一连串向量,这时我们只取paragrapgh那一部分对应的向量们。第二步加了两个独特的向量,这两个向量是下游任务新增的,需要通过finetune时更新参数,分别是start S和end E向量。为了得到paragrapgh中每一个token它时span-start的概率,采用如下公式:

image-20230508171252656

同样对于每一个token成为泡影span-end的概率,也对E采用同样的计算方式。到这一步我们就得到了paragrapgh中每一个token它们成为span-start和span-end的概率。第三步如何通过这些概率值得出我们的answer所在的span?

作者的做法是对每一个candidate span都计算一个分数: \[ S*T_i+E*T_j \] 选取得分最大的那个span作为预测值。计算loss时有一点不太一样,刚刚说的是predict,loss的计算是用的负log \[ L = - logP_(start^s) - logP_(end^e) \] 这里可能会有点迷惑。其实训练数据拿来时,对于paragraph中每一个token我们都会有一个label表示它是否是start还是end,是就是1,不是就是0.那么我们在做预测的时候,上面的第三步骤已经得到了每一个token的成为start还是end的概率,那么这时候我们就能用交叉墒来计算loss了。注意这里我们用的是softmax,所以我们应该是计算softmax的loss。另外这里只会计算到真正的start和end的那两个token上的loss,因为其他token的groudtruth label都是0。

SpanBERT

这篇是在Bert基础上改进的,主要改进点在于improve了Bert在pretrain时候的两个task:1. mask 2. next sentence prediction。同样是chen的文章:(Joshi & Chen et al., 2020): SpanBERT: Improving Pre-training by Representing and Predicting Spans

To finish

SpanBert在谷歌的bert基础上又将performance提高了许多。

open-domain question answering

这个问题在Speech and language processing的第三版的14章节进行了详细介绍,它也可以叫做是information-retrieval(IR) based QA。它首先要做的是从很大的语料库中搜索出相关的passage,然后第二步运动reading comprehension的算法从这些passage中找出answer(spans of text)。从一堆语料库里找到相关的passage,这个过程称之为information retrival。

IR能想到的最最简单的做法就是将query和语料库中的每一个passage都编码成一个向量,然后计算这些向量之间的相似度,也就是score,分数越高的就是跟query越相似,那么就可以得出语料库中和query最相关的那些passage了。那么将query和passage们编码成向量有很多种方法,最简单的方法就是TFIDF,还有tfidf的变种BM25。这些方法现在已经不再采用,更多的是用Bert,俗称dense vectors,这是和tfidf这种稀疏向量相对应的叫法。

具体做法是:

ORQA

分别用两个不同的bert分别编码query和passage们,然后将query的向量[CLS]和passage的向量[CLS]点积,这个点积的结果就作为query和passage的相似度得分。上面这张图片是将retriver和reader一起训练的,当然也可以单独用query和answer训练retriver

用生成模型来做QA

由于LLM的兴起,大家开始发现用生成模型来做QA更能回答复杂的问题。比如现在的GPT系列模型。它完全摈弃了抽取信息和从passage中寻找answer所在位置的环节,有点黑科技,就好像模型将所有的知识都记到脑子里去了。但也带来了新的问题,用户没有办法知道模型是从哪里找到的答案。

在Neural Language Generation的任务中,如何在每一个时间步产生一个token称为decoding method.最常见的decoding方法就是用softmax激活函数计算概率分布之后,将概率最大的那个token列为当前的预测值(most likely string)。这种argmax的方式在machine translation等non-open-ended的任务中表现还可以,但是如果是在纯粹的开放性的任务中,比如写一首诗歌。这种方式会造成重复性的输出,并且输出的句子有时候连续性也比较差。

这时候很多研究工作就对decoding strategic展开了研究,比如beam search,基本原理很简单,就是从取概率排名前k个token作为当前预测值。但是这还是会有一个问题,就是当我们概率分布均衡的时候,这个方法可以,但如果概率分布不均衡,也就是softmax计算出来的概率值只有几个token的概率比较大,其他概率都非常小,比如零点几,那这个时候其实我们不太需要考虑k个单词,只需要考虑概率比较大的那些tokens就够了。

Top-p(Nucleus Sampling)

所以这时候就有人提出了Top-p(nucleus) sampling的方法The Curious Case of Neural Text Degeneration

具体做法就是维护一个动态的k值,这个k值随着softmax的输出概率分布决定,

image-20230421134555223

作者的思路就是当概率分布比较flat的时候,我们应该把sample的池子定的大一点。但是当概率分布比较陡峭,也就是上图中的第三种情况时,我们就需要把这个sample的池子变得小一点。具体是如何操作的呢?

image-20230421134938176
image-20230421134952769

看公式可能会比较复杂,具体做法就是提前预设一个阈值p,然后对于某个时间步的概率分布P,我们寻找一个最小的top-p的一个token池子,这个池子里所有的token的概率值加起来要大于p,注意这里是找一个smallest set,也就是我们在找这个token池子时,从概率最高的token往下捋,一直到概率和大于p。找到这个池子之后,我们将原来经过softmax函数计算之后的概率分布按照上图中(3)的公式重新计算得出一个新的概率分布。最终我们得到的概率分布不属于我们之前找的token池子里的token的概率全部置为0,至于在这个token池子里的token的概率值会除以这个池子里所有token概率值的。然后我们从这个分布中去sample我们的预测token。

Sampling with Temperature

Temperature这个概念在GPT中也有,不同的温度值,你会得到不同的结果。温度值越低,它会对自己的输出结果更自信,而温度值越高,它会降低模型的确信值,也就会返回更多的结果给你。Blog对上面提到的The Curious Case of Neural Text Degeneration文章进行了解答,但我发现有一点和paper中不一样的是:

image-20230421144452222

temperature值t并不是取值是[0,1),stanford224n的课件以及博客里对t的取值是可以大于1的:

image-20230421144619190

在博客中我们可以看到作者对于t值大于1和小于1画出的图的区别:

image-20230421144711636