版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
目录TOC\o"1-2"\h\u118 125162第1 49192 4145761.1 4324181.2 7102031.3ChatGPT 15231501.4 2720090第2 289375 2831342.1 28216112.2ChatGPT 3318532.3 36295452.4 5727885第3 588822 58181923.1 58182623.2ChatGPT 648203.3 73175923.4 10724389第4 10830466 108111504.1 108176134.2 1105596(...)#省略 114184284.3 11937464.4 12331954第一册的Token 12782664.5 1313703第5 1324642 132216955.1 132152265.2 133300189.32是偶数,此时累计:偶数6个,奇数3 140207235.3 154271275.4 1561298第6 157506 157242066.1 15721790 160312236.2 162106226.3 164229506.4 167555第7 16815891 16822117.1 16846197.2 170201587.3 17344387.4 1745417第8 17517870 175252678.1 17556268.2 175232788.3 17614758 180304558.4 18119468 181第1——基本概念和语言模型的基本原理,它们是自然语言处理(naturallanguageprocessing,石;GPT(generativepre-trainedtransformer,生成式预训练Transformer)是ChatGPT的本它更大了,同时效果更好了;RLHF(reinforcementlearningfromhumanfeedback,从人类人工智能(artificialintelligence,AI)的概念建立伊始,机器能否具备使用自然语言同人从图灵测试到1950年,图灵发表论文opunghnyndngn,提出并尝试回答机器能否思考这一关键问题。在这篇论文中,图灵提出了图灵测试(即模仿游戏)的概念,用来检测机器智能水平。图灵测试的核心思想是,如果一个人(代号)使用测试对象都能理解的语言询问两个他不能看见的对象任意一串问题,其中一个是正常思维的人(),另一个是机器(代号)。如果经过若干询问以后,测试者不能得出实质的区别来分辨与的不同,则机器通过图灵测试(见图11)。图1-1图灵测试是任务的一个重要驱动力。图灵测试提出了一个具有挑战性和吸引力的目标,即让机器能够用自然语言与人类进行流畅、智能、多样化的对话。为了达到这个目标,力。例如,为了让机器能够回答用户提出的问题,就需要研究问答系统这一了让机器能够根据用户提供的信息生成合适的文本,就需要研究文本生成这一为了让机器能够适应不同领域和场景的对话,就需要研究领域适应和情境感知这一务;等等。·1972年,TerryWinograd开发了SHRDLU——一种能够理解和生成自然语言的程序,用于文“AttentionisAllYouNeed”,这篇论文的作者在其中提出了Transformer——一个具有多·2018年,谷歌公司发布了BERT(bidirectionalencoderrepresentationsfrom图1-2ChatGPT官方网站(图片源自最小语义单位Token与本来说,要做的首先就是把它变成一个个okn。你可以将okn理解为一小块,可以是取不同okn的方式,可以分词,也可以分字。英文现在都使用子词,比如单词nnoyngy会被拆成如下两个子词。["annoying",接拼接的,没有空格。中文现在基本使用字词的方式。我们不直接解释为什么这么做,但你可以想一下完全的字或词的效果,拿英文举例更直观。如果只用26词表很小(加上各种符号可能也就100来个),但粒度太细,每个okn(即每个字母)同时兼顾词表大小和语义表示,是一种折中的做法。中文稍微简单一些,就是字词,字能独立表示意义,比如是有爱;词是由一个以上的字组成的语义单位,一般来说,把词拆开可能会丢失语义,比如长城情比金坚。当然,中文如果非要拆成一个一个字也不是不可以,具体要看任务类型和效果。稍微多想一点,因为已经有一个预先设计好的词表,那么是不是可以用词表中的每个okn是否在句子中出现来表示?如果句子中包含某个okn,对应位置为1,否则为0,这样每句话都可以表示成长度(长度等于词表大小)相同的1和0组成的数组。更进一步地,还可以将是否出现改成频率以凸显高频词。事实上,在很长一段时间里,自然语言都是用这种方法表示的,它有个名字,叫作词袋模型(bgofod,)。从名字种表示方法如表11所示,每一列表示一个okn,每一行表示一个句子,每个句子可以表示成一个长度(就是词表大小)固定的向量,比如第一个句子可以表示为3,1,1,0,1,1,0,表1-1维度比较高,而且比较稀疏(大量的0),计算起来不太方便;第二,由于忽略了okn之间的顺序,导致部分语义丢失。比如你爱我和我爱你的向量表示一模一样,但其实意思不一样。于是,词向量(也叫词嵌入)一个okn可以表示成一定数量的小数(一般可以是任意多个,专业叫法是词向量维度,根据所用的模型和设定的参数而定),使再大的模型,这个维度也会比词表小很多。如下面的代码示例所示,每一行的若干(向量维度)的小数就表示对应位置的okn,词向量维度常见的值有200、300、768、1536等。爱[0.61048,0.46032,0.7194,0.85409,0.67275,0.31967,0.89993,不[0.19444,0.14302,0.71669,0.03338,0.34856,0.6991,0.49111,对[0.24061,0.21482,0.53269,0.97885,0.51619,0.07808,0.9278,[0.21798,0.62035,0.89935,0.93283,0.24022,0.91339,0.6569,你[0.392,0.13321,0.00597,0.74754,0.45524,0.23674,0.7825,完[0.26588,0.1003,0.40055,0.09484,0.20121,0.32476,0.48591,我[0.07928,0.37101,0.94462,0.87359,0.55773,0.13289,0.22909,...NLP(deepNLP)早期,往往是对句子的所有词向量直接取平均(或者求和),最终得到一个和每个词向量同样大小的向量——句子向量。这项工作最早要追溯到YoshuaBengio等人于2003年发表的论文“Aneuralprobabilisticlanguagemodel”,他们在训练语言模型的2013年谷歌公司的TomasMikolov发布的Word2Vec。借助Word2Vec,我们可以很容易地(或其他非文本符号)表示成稠密向量的方法,统称bddng表示技术。bddng表示技术可以说是领域(其实也包括图像、语音、推荐等领域)深度学习模型都基于此。我们甚至可以稍微夸张点说,深度学习的发展就是bddng表示技术的不断发展。语言模型(languagemodel,LM)简单来说,就是利用自然语言构建的模型。自然语言就(greedysearch),因为它只往后看一个词,只考虑下一步最大概率的词!为了让生成的图1-3搜索(beamsearch),简单来说,就是一步多看几个词,看最终句子(比如生成到句号、好了,其实在最开始的语言模型中,基本就到这里,上面介绍的两种不同搜索方法(和的区别是,前者的下一个okn是根据上一个okn来的,而后者的下一个okn是根据前两个okn来的。在模型中,okn的表示是离散的,实际上就是词表中的一个个单词。这种表示方式比较简单,再加上不能太大,导致难以学到丰富的上下文知识。事实上,它并没有用到深度学习和神经网络,只是一些统计出来的概率值。以为例,在给定很多语料的情况下,统计的是从人工智能开始,下个词出现的频率。假设人工智能让出现了5次,人工智能是出现了3次,将它们出现的频率除以所有的数就是概率。d维,加起来和1没关系,大小是1×d,表示给定的TokenX=[0.001,0.002,0.0052,...,0.0341]N个,加起来为1,大小是1×N,表示下一个Token就是每个Token出现的概率Y=[0.1,0.5,...,0.005,0.3]WX·WYWd×N在深度学习早期,最著名的语言模型是使用循环神经网络(recurrentneuralnetwork,RNN在处理时间序列数据时特别有用,例如预测未图1-4RNN(摘自Colah的博客文章“UnderstandingLSTMToken接着一个Token(TokenbyToken)的,从而形成一个序列。参数怎么学习呢?这就如图1-5所示,第一行就是输入X,第二行就是输出Y,SOS(startofsentence)表示句子开始,EOS(endofsentence)表示句子结束。注意,图1-4中的h并不是那个输出的概率,而图1-5语言模型学习(训练)importimporttorch.nnasrnn=nn.RNN(32,input=torch.randn(4,32)h0=torch.randn(1,64)output,hn=rnn(input,h0)output.shape,hn.shape#(torch.Size([4,64]),torch.Size([1,#假设词表大小N=1000wotorch.randn(641000)得到4×1000的概率矩阵,每一行概率和为1probs=nn.Softmax(dim=1)(output@wo)probs.shape,probs.sum(dim=1)#torch.Size([4,1000]),tensor([1.0000,1.0000,1.0000,#这里的pob的每一行就是词表大小的概率分布,概率和为1,意思是根据当前okn生成下一个okn的概率,下一个okn有可能是词表中的任意一个okn,但它们的概率和一定为1。因为我们知道接下来每个位置的okn是什么(也就是图15中的输出)到最大概率的那个okn,如果正好是这个okn,则说明预测对了,参数就不用怎么调整;反之,模型就会调整前面的参数(、h0、npu和o)。你可能会疑惑为什个okn,预测下一个okn。如果使用贪心搜索,则每次给定同样的okn时,生成的结果就一样。其余的就和前面讲的接上了。随着深度学习的不断发展,出现了更多比N还复杂的网络结构,而且模型变得更大,参数更多,但逻辑和方法是一样的。ChatGPT最强表示架构Transformer篇论文“AttentionIsAllYouNeed”,其最重要的核心就是提出来的自注意力(self-no简单来说,就是先把输入映射到编码器(nod),这里大家可以把编码器想象成前面介绍的,解码器(dod)也可以想象成。这样,左边负责编码,用当前okn的历史(后面的)okn和未来(前面的)okn;但在解码时,因为是一个个okn输出来的,所以只能根据历史okn以及编码器的okn表示进行建模,而不能利用未来okn。Transformer的这种架构从更普遍的角度来看,其实是Seq2Seq(sequencetosequence)架以及已经生成的那些okn的信息。前面这种关注编码器中每个okn的信息的机制就是注意力(non)机制。直观的解释,就是当生成单词po时,”两个字会被赋予更多权重(注意力),其他情况也类似。图1-7Transformer的整体结构(摘自论文“AttentionIsAllYou主要用了两个模块:多头注意力(multi-headattention)和前馈(feedforward)网络。对于多头注意力,我们不妨回顾一下q2q架构的注意力机制,它是解码器中的okn和编码器中每一个okn的重要性权重。多头注意力中用到了自注意力(no),自注意力和刚刚讲的注意力非常类似,只不过自注意力是自己的每一个okn之间的重要性权重。简单来说,就是一句话到底哪里重要。自注意力机制可以说是nor的精髓,无论是h还是其他非文本的大语言模型,都用到了它,它可以说是真正地“一统江湖。多头(uhd)简单来说,就是把刚刚的这种自己注意自己重复多次,每个要特别注意,解码器的淡黄色模块内有一个遮盖多头注意力(maskedmulti-headattention),它和多头注意力的区别就是遮盖(mask)了未来Token。以本小节开头也会把它们分别叫作自然语言理解(naturallanguageunderstanding,NLU)任务和自然语言生成(naturallanguagegeneration,NLG)任务。上面提到的这些任务,后面都会进一我们首先介绍任务。句子分类是指给定一个句子,输出一个类别。因为句子可以表类的类别则要看具体的任务,有二分类、多分类、多标签分类等。okn分类是指给定一个句子,给其中的每个okn输出一个类别。这和语言模型就更像了,只不过把下一个okn换成了对应的类别,比如命名实体抽取就是把句子中的实体(人名、地名、作品等你所关注的词,一般是名词)提取出来。如果以地名(oon,)举例的话,对应的类别是这样的:(bgnof)表示实体开始、(ndeof)表示实体中间。举个例子:“中国的首都是北京。注意此时的okn是字,每个okn为、、、、、、、”,表示h。对于分类任务,类别接下来介绍任务。除文本续写外,其他常见的任务还有文本摘要、机器翻译、文本改写、文本纠错等。这里q2q架构就比较常见了,体现了一种先理解再输出的思提供参考答案(n),可以评估模型输出和参考答案之间的重叠程度或相似程度,任务(如任务型聊天机器人的回复生成),还可以设计一些诸如“是否完成任务是否达到目标”的评测方法。但对于没有具体目标的任务(比如闲聊),评测起来就见仁见智料中,使用我们前面所讲的语言模型的训练方法训练而来。偏领域的第一个成果是谷歌公司的,相信不少人即便不是这个行业的也大概听过。就是使用了nor的编码器(没有使用解码器),有12个ok(图1左侧的淡黄色模块,每一个ok也可以叫作一层)和1亿多个参数。不预测下一个okn,而是随机地把15%的okn盖住(其中80用替换,10%保持不变,10随机替换为其他okn),然后利用其他没盖住的okn来预测盖住位置的okn。这其实和根据上文信息预测下一个okn是类似的,所不同的是它可以利用下文信息。偏领域的第一个成果是pnI的,就是使用了nor的解码器(没有使用编码器),参数和差不生成语言模型GPT,就是h中的那个,中文叫作生成式预训练nor。生成式的意思就是类似于语言模型那样,一个okn一个okn地生成文本,也就是上面提到的解码器的原理。预训练刚刚也提过了,就是在大量语料中训练语言模型。模型从1到-4,一共经历了5个版本,中间的h是3.5版。1、和3都是有论文发表的,接下来分别介绍它们的基本思想。h没有论文发表,不过它的姐妹版本nu有论文发表,我们放在1.3.3节介绍。4也没有论文发表,只有技术报告,不过里面并没有技术细节。因此,我们对4不做介绍,读者可以将其看作能力更强的h升级版。图1-8GPT-1基本结构和下游任务微调范式(摘自GPT-1文“ImprovingLanguageUnderstandingbyGenerativePre-关于图18左边的基本结构,我们在前面已经介绍过了,用的是nor的解码看看图18的右边,这是在各种下游任务上的处理流程。简单来说,就是针对不同的任务构造不同的输入序列,然后丢给1获取ok或句子的bddng表示,再通过nox输出结果。n是一种最基础的网络结构,也就是线性映射,这里用于维度转换,转为输出需要的大小。o主要用来把输出映射到概率分布(概率和为1)。这种拼接输入的方法在当时非常流行,紧跟其后的也使用类似的方式,并引领了一个时代,直至h的出现让我们进入大语言模型时代(不过,针对很多传统任务依然具备优势)。统一的处理方法能够减小不同任务对模型的适配难度。因此不管什么任务,都想方设法将其变成一个序列就行,比如在图18中,相似匹配就是把两句话直接拼接起来,预测它们是否相似(输出标签为1或0)。1的这篇论文还有几个点在当时看起来可能没什么感觉,现在回看却有点意思。第一,预训练模型中的每一层(图18中的淡黄色模块)都包含用于解决目标任务的有用功能,多层(意味着模型更深)有更多能力;第二,随着参数的增加,零样本获得更好的性能。简单总结就是,模型大了不仅能学到更多知识,有助于解决下游任务,还表现出了零样本能力。这里的零样本(oho)是指直接给模型输入任务,让它输出任务结果。与此类似的还有少样本(ho)和单样本(onho),即给模型提供一些(或一个)示例,然后给出任务,让它输出任务结果。客文章“Improvinglanguageunderstandingwithunsupervisedlearning”中有一个“未来工作列1发布于2018年6月,发布于2019年2月,2是的升级版,主要在两个方面进行进一步研究:首先是扩大规模,然后是零样本。如果说1是观察到了规模大、能力强的零样本”这个现象,那么就是进一步研究这个现象。其结果自然是,模型越来越大,参数越来越多,能力越来越强。2进一步验证了的想法,下一步要做的就是继续扩大规模。不过且慢,在此之前,我们不妨看一下2中的ok生成策略,也就是生成下一个okn的方法。前面介绍过比较优秀的集束搜索,不过它有两个比较明显的问题:第一是生成的内容容易重复,第二是高质量的文本和高概率并不一定相关(系)们更希望有“不一样的内容,而不是完全可预测的内容,比如张爱玲说过,孤独的人有他们自己的泥沼”,这种独一无二的文字用高概率的词大概率是得不到的。样下一个okn。这里可以用一个温度(pu)参数调整输出的概率分布,参数值越大,分布看起来就越平滑,也就是说,高概率和低概率的差距变小了(定);当然,这个参数值越小的话,高概率和低概率的差距就会更明显(对输出比较确定);如果这个参数值趋近于0,那就和贪心搜索一样了。请看下面的代码示例。importnumpyaslogits=np.random.random((2,4))logits/=temperaturescores=probs=scores/np.sum(scores,axis=1,我们让温度参数分别取0.1和0.9#array([[0.003,0.873,0.098,[0.001,0.001,0.,#array([[0.176,0.335,0.262,[0.196,0.196,0.176,除了这些技巧,2018年的一篇论文“HierarchicalNeuralStoryGeneration”另外介绍了一种新于是,2019年的一篇论文“TheCuriousCaseofNeuralTextDegeneration”提出了另一种采样每个参数的作用。最后需要说明的是,任何一种采样方案都不能100%保证每一次生成的从官方给的默认参数开始尝试。3发布于2020年7月,这在当时也是个大新闻,因为它的参数已经达到其他任何模型在当时都望尘莫及的量级——1750亿,是2的100多倍,没有开源。既然有零样子(少样本)和一些简单的说明,就可以处理任务了。怎么办?2不是进一步确认了零样本能力吗?继续加大参数量,于是就有了3。也就是说,各种任务来吧,不调参数,顶多就要几个例子(预计下一步连例子也不要了),3就能帮你完成它们。其实革命性的触动。关于这一点,感兴趣的读者可以进一步阅读笔者的一篇文章《3和它nonxtnng》。现在回忆,1750亿的参数量在当时看太大了,而且也太贵了·图1-9x-shot在不同参数规模下的表现(摘自GPT-3论文“LanguageModelsareFew-ShotLearners”)总而言之,大语言模型具有nonx(上下文)学习能力,这种能力使得它不需要针对不同任务再进行适应性训练(微调),应该很让人震惊(甚至有一点惊恐),下来,我们再直观地感受一下利用这种nonx学习能力完成任务的方式,如图110所示。图1-10使用In-Context学习能力和微调完成任务(摘自GPT-3论文“LanguageModelsareFew-ShotLearners”)图110右边的微调方式需要先根据训练样本更新模型参数,之后再进行预测。图11左边的三种方式都利用了大语言模型(gengugeod,M)的nonx学习能力,进行预测就可以了。这也是本书写作的初衷之一——人工智能已经平民化,只要有手(能以后不用手也行),通过使用M就可以做出人工智能应用了。不过这里有一点需要说明,为了简便,图110中的样本都比较简单,但实际中的样本一般是完整的句子。·利器强化学习RLHF(nontnngomhundb,从人类反馈中强化学习)听起来有点平淡无奇。确实,的思想非常朴素、简单,但它有着不可忽视的效果。刚刚我们已经提到了,3论文指出未来要找到新的训练方法,其中就包括从人类那里学习目标函数、强化学习微调、多模态等。时至今日,从nu到h,再到4,人类晰地摆在那里的,中间还有非常多的探索和阶段性成果(既有pnI自己的研究,也有其他从业人员的研究)要做出来很有难度,不过我们如果只是用的话,如前所述,有手就行。文“Traininglanguagemodelstofollowinstructionswithhumanfeedback”,更大范围的熟知就文“Traininglanguagemodelstofollowinstructionswithhuman·步骤一:SFT(supervisedfine-tuning,有监督微调)。顾名思义,SFT是在有监督(有标·步骤二:RM(rewardmodel,奖励模型)。具体来说,将一个提示词丢给前一步的常关键,它就是所谓的人类反馈(humanfeedback),·步骤三:RL(reinforcementlearning,强化学习),使用PPO进行训练。PPO(proximalpolicyoptimization,近端策略优化)是一种强化学习优化方法,它背后的言模型,然后丢给它一个提示词,生成一个回复,用上一步的给这个回复打分,将这个很重要的动作,就是在更新模型时考虑模型每一个okn的输出和输出之间的差异性,要让它们尽量相似。这是为了缓解强化学习可能的过度优化。就这样?对,就这样,都表现在上面了,效果大家都知道了。虽然h没有相在HumanBehaviorandthePrincipleofLeastEffort:AnIntroductiontoHumanEcology一书中剩下要解决的就是怎么衡量“好坏,毕竟最终是要有个结果的,既然要结果,就要有标准。读者不妨思考一下,如果换作你,你会如何设计一些指标来衡量两段输出内容的好坏。这一步看似容易,其实特别难,因为指标的设计会影响到模型的学习方向,最终就会影响到效果。因为这个输出的好坏衡量标准太多了,虽然看起来是对给出的几个结果进行排序(上文的步骤二),但其实这个过程中间隐藏了大量人类的认知,模型训练过程其实就是和步骤二这个衡量过程对齐的过程;所以,如果步骤二指标没设计好,步骤三就会白费力气。尤其是对于nu这样要完成大量不同任务的设计,衡量就更加不容易。以一个文本摘要任务为例,我们可能最关注的是能否准确概括原文信息,而一个生成任务可能更关注流畅性和前后逻辑一致性。nu里面有10种任务,分别针对每种任务设计指标,不仅麻烦,而且效果还不一定好,因为这些指标并不一定都是一个方向。还有就是,万一又有了一个新任务,难道要再去设计一套指标,全部重新训练一遍模型吗?篇专门介绍ChatGPT标注的文章《ChatGPT标注指南:任务、数据与规范》。首先,·····当选择同样有帮助但以不同方式不真实或有害时,问自己哪个输出更有可能对用户(现实世界中受任务影响最大的人)这一点,则将这些输出标记为并列。nu或h相比3有更强的零样本能力,少样本很多时候已经用不着,但提示词还是需要的,由此催生了一个新的行当——提示工程。不过,据pnI的在一次采访中所言,再过几年提示工程也不需要了(可能在生成图片时还需要一些),过已有的服务创造出不错的人工智能应用。第2——万物皆可语言文本,以及如何衡量bddng的相似程度。接下来,我们将介绍h相关接口的用法,其他类似接口的用法也差不多。最后,我们将介绍与bddng相关的几个任务和应用,这里面有些可以用大语言模型解决,但也可以不用大语言模型解决。无论是h还是其他大语言模型,它们都只是我们工具箱中的工具,我们将侧重任务和应用,重点介绍如何解决此类问题。我们期望读者能在阅读的过程中感受到目的和方法的区别,方法无论如何,总归是为目的服务的。更好的EmbeddingEmbedding首先,我们简单回顾一下第1章介绍的bddng表示技术。对于自然语言,因为输入是一段文本,在中文里就是一个一个字,或一个一个词,业内把这个字或词叫作okn。如果要使用模型,拿到一段文本的第一件事,就是把这段文本okn化。当然,可以按字,这个例子。·按字Bi-GramToken化:人////////////。于是自然地就有了一个新的问题:我们应该怎么选择okn化方式?其实每种不同的方式模型基本都使用字词的方式。如第1章所述,这种方式一方面能够更好地表示语义,另无法识别和处理的情况。okn化之后,第二件事就是要怎么表示这些okn,我们知道计算机只能处理数字,所以要想办法把这些okn变成计算机所能识别的数字才行。这里需要一个词表,将每个词一个文本文件来存储,内容如下。的Embedding参数。接下来就是将这些序号(TokenID)表示成稠密向量(Embedding表importnumpyasrngnp.random.default_rng(42)#词表大小N=16,维度D=256table=rng.uniform(size=(16,table.shape==(16,2014年,也就是谷歌公司发布od2后一年,差不多是同一批人提出了一种表示文档的方法——o2,其思想是在每句话的前面增加一个额外的段落okn作为段落的向更新词向量参数时,需要额外更新这个段落okn向量。直观地看,就是把文档的语义都融入这个特殊的okn向量。不过这种方法存在一个很严重的问题,那就是推理时,如果(onvouonlnulnok,)。x以若干大小固定的窗口在文本上滑动,每个窗口从头滑到尾,就会得到一组浮点数特征,使用若干不同大小的窗口(一般取2、3、4),就会得到若干不同的特征,将它们拼接起来就可以表示这段文本了。x的表示能力其实不错,一直以来都作为基准模型使用,很多线上模型也用它。x的主要问题是只利用了文本的局部特征,没有考虑全局语义。和它的几个变体都是时序模型,从前到后一个okn接一个okn处理。也有不错的表示能力,但它有两个比期词向量用得比较少,文本的表示主要通过模型架构来体现,okn化的方式以词为主。2017年,no横空出世,带来了迄今为止最强的特征表示方式——自注意力机制。多创新,但由于模型变大,表示效果有了明显提升。自此,进入预训练时代——基于no训练一个模型,在做任务时都以该模型为起点,在对应数据上进行微调训练。具有代表性的成果是和,前者用了no的编码器,后者用了nor的解码器。在每个文档的前面添加了一个okn来表示整句话的语义,但与o2不同的是,模型在推理时不需要额外训练,而是根据当前输入,通过计算自动获得表示。也就是说,同样的输入,相比o2,因为其强大的表示能力,可以通过模型计算,不额外训练就能获得不错的文本表示。在第1章有相关介绍,这里不赘述。无论是哪个预训练模型,底层其实都是对每个okn进行计算(在计算时一般会用到其他okn信息)。所以,预训练模型一般可以获得每个okn位置的向量表示。于是,文档表示依然可以使用那种最常见的方式——平均的方式也变得更加灵活多样,比如用自注意力作为权重加权平均。还是以人类阅读进行类比(很多模型都是从人类获得启发,比如、自注意力等)。一二。我们在看一段话时,可能依然重点关注的是关键词、包含关键词的关键句等。但关注这篇文章整体在表达什么,描述这样的表达可能并不会用到文本中的词或句。如何度量Embedding其中,和分别表示两个文本向量,i表示向量中第i个元素的值importnumpyasa=[0.1,0.2,b=[0.2,0.3,cosine_ab=(0.1*0.2+0.2*0.3+0.3*0.4)/(np.sqrt(0.1**2+0.2**2+0.3**2)*cosine_ab==ChatGPTEmbeddingimportosimportopenai#OPENAI_API_KEY=或直接填入自己专属的APIkey(接口密钥),不建议在正式场景下使用OPENAI_API_KEY="填入专属的APIkey"openai.api_key=text我喜欢你model="text-embedding-ada-emb_req=openai.Embedding.create(input=[text],emb=emb_req.data[0].embeddinglen(emb)==1536type(emb)==fromopenai.embeddings_utilsimportget_embedding,text1我喜欢你text2="我中意你"text3我不喜欢你"注意默认的模型是text-similarity-davinci-001,我们也可以换成text-embedding-#ada-002emb1=get_embedding(text1)emb2=get_embedding(text2)emb3=len(emb1)==12288type(emb1)==list可以发现,Embedding的长度变了,从1536变成了12288。这主要是因为get_embedding接(从调用接口的角度可能感知不明显)cosine_similarity(emb1,emb2)==0.9246855139297101cosine_similarity(emb1,emb3)==0.8578009661644189cosine_similarity(emb2,emb3)==text1="我喜欢你"text2="我中意你"text3我不喜欢你"emb1=get_embedding(text1,"text-embedding-ada-002")emb2=get_embedding(text2,"text-embedding-ada-002")emb3=get_embedding(text3,"text-embedding-ada-使用方法类似,只是将第二个参数改成了维度为1536cosine_similarity(emb1,emb2)==0.8931105629213952cosine_similarity(emb1,emb3)==0.9262074073566393cosine_similarity(emb2,emb3)==0.845821877417193同,之所以我们觉得它们不相似,只是因为我们只关注了一个(我们想要的)角度。所ChatGPT+content请告诉我下面三句话的相似度:\n1.我喜欢你。\n2.我中意你。\n3.我不喜欢你。response=openai.ChatCompletion.create(messages=[{"role":"user","content":1和2相似,都表达了对某人的好感或喜欢之情。而3content第一句话用a表示,第二句话用b表示,第三句话用c表示,请以JSON格式输出两两相似度,类似下面这样:\n{"ab":a和b的相似度}"response=openai.ChatCompletion.create(messages=[{"role":"user","content":b0.8,1,b0.7nn解释:和b的相似度为0.8,因为两句话表达了相同的情感;和c的相似度为1,因为两句话表达了相反的情感;b和的相似度为0.7,因为两句话都是表达情感,但一个是积极情感,一个是消极情感,相似度略低。有的读者可能会疑惑,既然h已经这么强大了,为什么还要介绍bddng这种看起来好像有点低级的技术呢?原因我们在本章开头就简单说过了,这里稍微再扩充一下,其实目前来看主要有两点原因:第一,有些问题使用bddng(或其他非hT的方式)解决会更加合理,通俗地说就是杀鸡焉用牛刀;第二,h是特别高效,毕竟是一个okn一个okn吐出来的。importpandasasdf=pd.read_csv("dataset/KagglerelatedquestionsonQoura-Questions.df.shape==(1166,该数据集包括1166行、4表2-1Quorafromopenai.embeddings_utilsimportget_embedding,cosine_similarityimportopenaiimportnumpyasOPENAI_API_KEY=openai.api_key=vec_base=forvinemb=get_embedding(v.Questions)im={"question":v.Questions,"embedding":emb,"answer":v.Link接下来直接使用就可以了。比如,给定输入"iskagglealive?",我们先获取它的query="iskagglealive?"q_emb=get_embedding(query)sims=[cosine_similarity(q_emb,v["embedding"])forvin为了方便展示,我们假设只有5sims==[此时,第二条相似度最高,我们返回第二个文档(索引为1)vec_base[1]["question"],vec_base[1]["answer"]==('IsKaggledead?','/Is-arr=[v["embedding"]forvinq_arr=np.expand_dims(q_emb,0)q_arr.shape==(1,12288)fromsklearn.metrics.pairwiseimportsims=cosine_similarity(arr,使用kn包里的on_y可以批量计算两个数组的相似度。这里的批量计算主要利用了uy的向量化计算,可以极大地提升效率,建议读者亲自尝试并体会这两种方案的效率差异。还是假设只有5条,结果如下所示。sims==array([[0.6657692$dockerrun-p6379:6379-itredis/redis-$pipinstallimportr=redis.Redis()r.set("key","value")r.get("key")==mands.search.queryimportfrommands.search.fieldimportTextField,VectorFieldmands.search.indexDefinitionimportIndexDefinition#VECTOR_DIM=#INDEX_NAME=#建好要存字段的索引,针对不同属性字段,使用不同的question=TextField(name="question")answer=TextField(name="answer")embedding=VectorField("TYPE":"FLOAT32","DIM":VECTOR_DIM,schema=(question,embedding,(hierarchicalnavigablesmallworlds,HNSW)算法。这是一种用于高效相似性搜索的算indexr.ft(INDEX_NAMEft表示fulltextsearchinfo=()index.create_index(schema,definition=IndexDefinition(prefix=[INDEX_NAME+forvinemb=注意,Redisemb=np.array(emb,dtype=np.float32).tobytes()im={"question":v.Questions,"embedding":emb,"answer":v.Link重点是hset#query="kaggleembed_query=get_embedding(query)params_dict={"query_embedding":k=base_query=f"*=>[KNN{k}@embedding$query_embeddingASscore]"return_fields=["question","answer","score"]query=(.paging(0,查询语法为{somefilterqueryKNNnum|$num@vector_field$query_vec],其中包括·{somefilterquery}:字段过滤条件,可以使用多个条件。*·[KNNnum|$num@vector_field$query_vec]:K(Knearestneighbors,KNN)的主要思想是对未知数据点分别和已有的数据点算距离,ASscore表示K最近邻算法计算结果的数据名称为score。注意,这里的score其实是距离,#res=index.search(query,params_dict)fori,docinenumerate(res.docs):#similarity=1-print(f"{doc.id},{doc.question},{doc.answer}(Similarity:{round(similarity,3)faq-1,IsKaggledead?,/Is-Kaggle-dead(Score:faq-2,HowshouldabeginnergetstartedonKaggle?,/How-should-a-beginner-get-started-on-Kaggle(Score:0.735)faq-3,WhataresomealternativestoKaggle?,/What-are-some-alternatives-to-Kaggle(Score:0.73)上面我们通过几种不同的方法向大家介绍了如何使用bddn执行任务。简单回顾一下,要执行任务,首先得有一个库,这个库就是我们的仓库。每当一个新的问题,接下来就把该问题的答案当作新问题的答案交给用户。本小节使用Kaggle的DBPedia数据集DBPediaClasses,该数据集可以从Kaggle官网搜索下importpandasasdf=pd.read_csv("./dataset/DBPEDIA_val.csv")df.shape==(36003,4)表2-2DBPedia Name:l1,dtype:由于整个数据集比较大,展示起来不太方便,我们随机采样200sdf=df.sample(200) Name:l1,dtype:cdf=(sdf.l1=="Place")|(sdf.l1=="Work")|(sdf.l1==cdf.shape==(72,fromopenai.embeddings_utilsimportget_embedding,cosine_similarityimportopenaiimportnumpyasOPENAI_API_KEY=os.environ.get("OPENAI_API_KEY")openai.api_key=OPENAI_API_KEYcdf["embedding"]=cdf.text.apply(lambdax:get_embedding(x,engine="text-接下来使用主成分分析(principalcomponentanalysis,PCA)算法进行特征降维,将原来fromsklearn.decompositionimportarr=np.array(cdf.embedding.to_list())pca=PCA(n_components=3)vis_dims=pca.fit_transform(arr)cdf["embed_vis"]=vis_dims.tolist()arr.shape==(72,1536),vis_dims.shape==(72,%matplotlibimportmatplotlib.pyplotaspltimportnumpyasnpfig,ax=plt.subplots(subplot_kw={"projection":"3d"},figsize=(8,8))cmap=plt.get_cmap("tab20")categories=#fori,catinsub_matrix=np.array(cdf[cdf.l1==cat]["embed_vis"].to_list())x=sub_matrix[:,0]y=sub_matrix[:,z=sub_matrix[:,colors=[cmap(i/len(categories))]*len(sub_matrix)ax.scatter(x,y,z,c=colors,label=cat)ax.legend(bbox_to_anchor=(1.2,1))图2-1··fromsklearn.clusterimportkmeans=推荐应用:一切都是···刚开始用户没有浏览记录时如何推荐的问题(一般被业界称为冷启动问题)··当用户注册并登录时,让其选择感兴趣的类型(如体育、音乐、时尚等),从而将用户限定在几个类别的范围内(推荐时可以只考虑这几个类别,提升效率),解决冷启动问题。····这里使用Kaggle的AG_News数据集AGNewsClassificationDataset,该数据集可以从fromdataclassesimportdataclassimportpandasaspddf=pd.read_csv("./dataset/AG_News.csv")df.shape==(120000,3)表2-3AG_Newsdf["Class类别一共有4个,每个类别33421Name:ClassIndex,dtype:sdf=sdf["Class2413Name:ClassIndex,dtype:fromtypingimportclassuser_name:classuser_name:strprefers:List[int]classItem:item_id:stritem_props:dictclassAction:action_type:straction_props:dictclassuser:Useritem:Itemaction:Actionaction_time:stru1=up1=UserPrefer("u1",[1,2])i1=Item("i1",{"id":"catetory":"title":"Swimming:ShibataJoinsJapaneseGoldRush","description":"\ATHENS(Reuters)-AiShibataworedownFrenchteen-agerLaureManaudoutowinthewomen's800meters\freestylegoldmedalattheAthensOlympicsFridayandprovideJapanwiththeirfirstfemaleswimming\championin12years.","content":a1Action("浏览""open_time":"2023-04-01"leave_time":"2023-04-01"type":"close","duration":"2hour"ua1=UserAction(u1,i1,a1,"2023-04-01fromopenai.embeddings_utilsimportget_embedding,cosine_similarityfromsklearn.metrics.pairwiseimportcosine_similarityimportopenaiimportnumpyasnpOPENAI_API_KEY=os.environ.get("OPENAI_API_KEY")openai.api_key=OPENAI_API_KEYsdf["embedding"]=lambdax:get_embedding(x.Title+x.Description,engine="text-embedding-ada-002"),axis=1)··importclassdefinit(self,df:pd.DataFrame):self.data=dfdefuser_prefer_recall(self,user,up=idx=random.randrange(0,len(up.prefers))returnself.pick_by_idx(idx,n)defhot_recall(self,#df=self.data.sample(n)returndfdefuser_action_recall(self,user,actions=interest=self.get_most_interested_item(actions)recoms=self.recommend_by_interest(interest,n)returndefget_most_interested_item(self,user_action):idx=user_action.item.item_props["id"]im=self.data.iloc[idx]returnimdefrecommend_by_interest(self,interest,n):cate_id=interest["ClassIndex"]q_embinterest["embedding"]#确定类别base=self.data[self.data["ClassIndex"]==#base_arr=[v.embeddingforvinq_arr=np.expand_dims(q_emb,simscosine_similarity(base_arrq_arr)#排除自身idxes=sims.argsort(0).squeeze()[-(n+1):-returndefpick_by_idx(self,category,df=self.data[self.data["ClassIndex"]==category]returndf.sample(n)defget_user_actions(self,user):dct={"u1":ua1}returndefget_user_prefers(self,dct={"u1":returndefrun(self,ur=self.user_action_recall(user,5)iflen(ur)==0:ur=self.user_prefer_recall(user,5)hr=self.hot_recall(3)returnpd.concat([ur,hr],r=Recall(sdf)rd=r.run(u1)·表2-4第3——句子Token常见任务,以及如何对句子和okn进行分类。接下来介绍h相关接口的用法,其他厂商提供的类似接口的用法也类似。通过类似h这样的大语言模型接口其实可以了,而且效果也不错。与第2章一样,我们依然重点关注最终目的以及为达到最终目的所使用的方法与流程。自然语言理解(naturallanguageunderstanding,NLU)(naturallanguagegeneration,NLG)任务并称NLP两大主流任务。一般意义上的NLU任情感分析。简单来说,情感分析一般包括正向、中性、负向三种类型,也可以设计更多务等一个或多个方面表达自己的观点。这时候,我们更加需要的是对不同属性的情感倾向,而不是整个评论的情感倾向。意图识别。意图识别一般用分类模型:大部分是多分类,但也有可能是层级标签分类或议(在投诉的同时提出了建议)。实体和关系抽取。实体抽取就是提取出给定文本中的实体。实体一般指具有特定意义的系抽取是指实体与实体之间的关系判断。实体之间往往有一定的关系,比如中国四大名著之一《红楼梦》由清代作家曹雪芹编写。其中曹雪芹是人名,红楼梦是作品名,其中的关系就是编写,一般与实体作为三元组来表示:(曹雪芹,编写,红楼梦)。I-WORKI-WORKI-PERSONI-PERSONoimportnumpyasnpemd=np.random.normal(0,1,(1,W=np.random.random((32,3))z=emd@Wz==array([[6.93930177,5.96232449,z.shape==(1,defexp=returnexp/y=y==array([[0.70059356,0.26373654,0.0356699np.sum(y)==根据给出的y,我们得知预测的标签是第0个位置的标签,因为那个位置的概率最大(1(100),但目前预测的第1个位置的概率只有大约26.37,将这个误差回传以调整模型参数。下次计算时,第0个位置的概率就会变小,第1过标注的数据样本不断循环迭代的过程,其实就是模型训练的过程,也就是通过标注数据,让模型尽可能正确地预测出标签。w1=np.random.random((32,100))w2=np.random.random((100,32))w3=np.random.random((32,3))y=norm(norm(norm(emd@w1)@w2)@y==array([[0.32940147,0.34281657,来要麻烦一些,不过它们的处理方式是类似的。我们以多标签分类来说明。假设有10签,给定输入文本,可能是其中任意多个标签。这就意味着我们需要将10布都表示出来。可以针对每个标签做二分类,也就是说,输出的大小是102,每一行表示是否为该标签的概率分布,示例如下。defaxis=-exp=returnexp/np.expand_dims(np.sum(exp,axis=axis),emd=np.random.normal(0,1,(1,W=np.random.random((10,32,2))y=norm(emd@W)y.shape==(10,1,y==[[0.66293305,[[0.76852603,[[0.59404023,[[0.04682992,[[0.84782999,[[0.01194495,[[0.96779413,[[0.04782398,[[0.41894957,[[0.43668264,从句子分类到Token10,并且假定维度依然是32,那么Embedding的大小为(1,10,32),相比句子分类用到的(1,32)多了一个10。换句话说,这个文本的每一个Token都是一个32维的向量。emd=np.random.normal(0,1,(1,10,W=np.random.random((32,5))z=emd@Wy=y.shape==(1,10,y==[0.23850186,0.04651826,0.12495322,0.28764271,[0.06401011,0.34220550,0.54911626,0.01179874,[0.18309536,0.62132479,0.09037235,0.06016401,[0.01570559,0.02714370,0.20159052,0.12386611,[0.13085410,0.06810165,0.61293236,0.00692553,[0.08011671,0.04648297,0.00200392,0.02913598,[0.05143706,0.09635837,0.00115594,0.83118412,[0.03721064,0.14529403,0.03049475,0.76177941,[0.24154874,0.28648044,0.11024747,0.35380566,[0.10965428,0.00432547,0.08823724,0.00407713,sum([0.23850186,0.04651826,0.12495322,0.28764271,0.30238395])这里具体的意思是,第一个okn是第0个位置的标签(如果标签按上面给出的顺序,那就是)的概率约为23.85,其他类似。根据这里预测的结果,第一个okn的标签是,真实的标签和这个预测的标签之间可能有误差,通过误差就可以更新参数,从而使得之后预测时能预测到正确的标签(也就是正确位置的概率最大)逻辑和前面的句子分类是类似的,其实就是对每一个okn做了多分类。关于常见问题的基本原理我们就介绍到这里,读者如果对更多细节感兴趣,可以阅读与识体系。ChatGPT基础版GPTimportOPENAI_API_KEY=os.environ.get("OPENAI_API_KEY")openai.api_key=OPENAI_API_KEYdefcomplete(prompt:str)->response=openai.Completion.create(ans=response.choices[0].textreturnanspop:提示词,默认为ndox,它是模型在训练期间看到的文档分隔符。因此,如果未指定提示词,模型将像从新文档开始一样。简单来说,pop就是给模型的提示词。pn_pny:存在惩罚,默认为0,取值介于2.0和2.0之间。正值会根据新okn到能会降低样本质量。零样本,来自OpenAIprompt=ThefollowingisalistofcompaniesandthecategoriestheyfallApple,Facebook,ans=complete(prompt)ans=="""SocialLogisticsandDelivery#prompt今天真开心。-->正向ans=complete(prompt)ans=="""零样本,来自OpenAIprompt=Fromthetextbelow,extractthefollowingentitiesinthefollowingCompanies:<comma-separatedlistofcompaniesPeople&titles:<comma-separatedlistofpeoplementioned(withtheirtitlesorrolesappendedinInMarch1981,UnitedStatesv.AT&TcametotrialunderAssistantAttorneyGeneralWilliamBaxter.AT&Tchairmanthebestandonlyrealisticalternative.AT&T'sdecisionallowedittoretainitsresearchandmanufacturingarms.Thedecree,In1982,theU.S.governmentannouncedthatAT&Twouldceasetoexistasamonopolisticentity.On1January1984,itwassplians=complete(prompt)ans=="""Companies:AT&T,BellSouth,BellAtlantic,NYNEX,AmericanInformationTechnologies,SouthwesternBell,USWest,People&titles:WilliamBaxter(AssistantAttorneyGeneral),CharlesL.Brown(AT&Tchairman),HaroldH.Greene(Judge)#prompt=根据下面的格式抽取给定文本中的实体和弦实体用逗号分隔ans=ans和弦:大三和弦,小三和弦也可以尝试给模型一些其他的例子,看看效果如何。值得注意的是,随着pn模型的不断升级,这一接口将逐渐被废弃。进阶版ChatGPT和content两个字段,分别表示角色和消息内容,如[{"role"user""content"importOPENAI_API_KEY=os.environ.get("OPENAI_API_KEY")openai.api_key=OPENAI_API_KEYdefask(content:str)->response=openai.ChatCompletion.create(messages=[{"role":"user","content":ans=response.get("choices")[0].get("message").get("content")returnansprompt=ThefollowingisalistofcompaniesandthecategoriestheyfallApple,Facebook,ans=ask(prompt)ans=="""Technology/Social可以看到,当保持输入和3.2.1prompt="""pleaseoutputthecategoryofthefollowingcompanies:Apple,Facebook,FedexTheoutputformatshouldans=ask(prompt)ans=="""Technology/Social·····没有说明具体的输出目标,特殊场景除外(比如就是漫无目的地闲聊)····prompt请给出下面句子的情感倾向,情感倾向包括三种:正向、中性、负向。ans=ans情感倾向:正向prompt=InMarch1981,UnitedStatesv.AT&TcametotrialunderAssistantAttorneyGeneralWilliamBaxter.AT&Tchairmanthebestandonlyrealisticalternative.AT&T'sdecisionallowedittoretainitsresearchandmanufacturingarms.Thedecree,In1982,theU.S.governmentannouncedthatAT&Twouldceasetoexistasamonopolisticentity.On1January1984,itwassplians=ask(prompt)ans=="""Company:AT&T,BellSouth,BellAtlantic,NYNEX,AmericanInformationTechnologies,SouthwesternBell,USWest,PacificTelesisPeople&Title:WilliamBaxter(AssistantAttorneyGeneral),CharlesL.Brown(AT&Tchairman),JudgeHaroldH.Greene.prompt=Desiredans=ans和弦:大三和弦,小三和弦第29届夏季奥林匹克运动会(Beijing2008;GamesoftheXXIXOlympiad),又称2008年北京奥运会,2008年8月8日晚importopenaiimportosOPENAI_API_KEY=os.environ.get("OPENAI_API_KEY")openai.api_key=OPENAI_API_KEYdefcomplete(prompt:str)->str:response=openai.Completion.create(ans=response["choices"][0]["text"].strip("\n")returnans来自OpenAIprompt="""Answerthequestionastruthfullyaspossibleusingtheprovidedtext,andiftheanswerisnotcontainedwithintheThemen'shighjumpeventatthe2020SummerOlympicstookplacebetween30Julyand1August2021attheOlympicQ:Whowonthe2020SummerOlympicsmen'shighjump?ans=ans=="GianmarcoTamberiandMutazEssaBarshimemergedasjointwinnersoftheprompt请根据以下Context尤金袋鼠(opusugn)是袋鼠科中细小的成员,通常都是就袋鼠及有袋类的研究对象。尤金袋鼠分布在澳洲南1628有三个亚种。尤金袋鼠很细小,约只有8公斤重,适合饲养。尤金袋鼠的奶中有一种物质,被称为0,有可能是一种神奇药,青霉素的改良版。0是一种蛋白质,在实验中证实比青霉素有效100倍,可以杀死99的细菌及真菌,如沙门氏菌、变形杆菌及金黄色葡萄球菌。defask(content:str)->response=openai.ChatCompletion.create(messages=[{"role":"user","content":ans=response.get("choices")[0].get("message").get("content")returnansans=ans尤金袋鼠分布在澳洲南部岛屿及西岸地区。我们使用pnI提供的数据集——来自维基百科的关于2020年东京奥运会的数据。该数据集可以从pnI的opnookboo的ub仓库xpnund_q”获取。下载后是一个文件,和之前一样,先加载并查看数据集。importpandasasdf=pd.read_csv("./dataset/olympics_sections_text.csv")df.shape==(3964,4)表3-12020·第3·第4上面的第1步依然需要借助pnI的bddn接口,但是第2步我们这次不用d,而是换用一个向量搜索工具——dn。dn相比d更简单易用且容易扩展。不过,我器。dockerrun-p6333:6333-v$(pwd)/qdrant_storage:/qdrant/storageqdrant/$pipinstallqdrant-fromopenai.embeddings_utilsimport
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 47219-2026蜂品种意大利蜜蜂
- GB/T 47152-2026石榴
- 机器学习(第3版)-习题及答案
- 电梯厂商指导方案范本
- 小院设计防雨方案范本
- 工地残渣处理方案范本
- 卫生间台面一体施工方案
- 需要施工方案
- 餐饮对口招商方案模板范本
- 单元内水管维修方案范本
- 自来水厂安全生产题库及答案解析
- 高空作业车安全操作规程
- 2024云南省委党校研究生招生考试真题(附答案)
- 诺如病毒考试题及答案
- DB45∕T 2479-2022 一般固体废物填埋场水文地质工程地质勘察规范
- 岗位安全责任清单意义
- 2025年焊工(技师)考试练习题库(附答案)
- 学术自由与责任共担:导师制度与研究生培养制的深度探讨
- 法拍司辅内部管理制度
- 道路损坏修缮协议书模板
- 2025年上海市各区高三二模语文试题汇编《现代文一》含答案
评论
0/150
提交评论