全文共8500余字,预计阅读时间约17~30分钟 | 满满干货(附代码),建议收藏!
代码下载点这里
一、 Completions与Chat Completions基本概念
经过海量文本数据训练的大模型会在全量语义空间内学习语法关系和表达风格,并通过某些微调过程使得其能够更好的向人类意图对齐,模型在进行预测时,本质上是根据输入(也就是提示)来预测对应的文字输出,GPT模型作为以transformer为框架的生成式预训练大语言模型,这是它最基本的功能。
在大语言模型领域,这种根据提示来预测对应的文字输出的过程,被称为Completion,中文译为“补全”。能够完成Completion过程的模型,则被成为Completion模型,这也是OpenAI对于能够实现Completion功能的模型的命名。
比如text-davinci-003模型(在text-davinci-002模型基础上通过RLHF微调的模型),就是一个Completion模型。
对于大语言模型的应用而言,有一个非常重要的应用方向——对话机器人。尽管Completion模型也能很好的对文字进行补全,但这种补全有时很难准确理解人类意图,从而出现一些“幻觉”。例如下图为早期与text-davinci-002模型打招呼的对话,只输入了“你好”作为提示词,text-davinci-002自动补全了“您找谁”,并且返回了对应的英文翻译结果:
很明显,模型并没有很好的理解“意图”,当输入“你好”的时候,其实是想和模型打招呼,但模型只是根据已有知识,对“你好”之后可能出现的文字进行了补全。
基于此,为了更好的理解人类的对话意图,OpenAI在Completion模型模型基础上,进一步微调训练得到了对话类模型,也就是Chat Completion模型。相比Completion模型,Chat Completion模型在大量对话预料上进行了微调训练,并且在模型内部新增了system、user和assistant等参数,用于设置聊天语境及不同的聊天角色,从而使得模型能够更好的理解人类意图,从而更好的完成对话类任务。
例如gpt-3.5-turbo就是Chat Completion模型,同时也是ChatGPT普通版背后的大模型。这里我在gpt-3.5-turbo中输入你好,则返回结果如下:
能够看出,模型返回的结果符合人类预期,说明模型能够较好的理解人类对话意图,并返回更加合适的结果。由此也能看得出Completions模型与Chat Completions模型之间性能区别。
总的来说:Completions 和 Chat Completions 都是 OpenAI 提供的 API,它们都可以用来生成文本。Completions API 主要用于补全问题,用户输入一段提示文字,模型按照文字的提示给出对应的输出。而 Chat Completions API 则专门用于处理聊天任务,它在接口上显示定义了 system、user 和 assistant 三个角色。system 可以理解为聊天的语境,可有可无;user 和 assistant 可以理解为聊天的两个角色
二、 Completions与Chat Completions模型类
点击进入官网
截至目前,OpenAI发布的Completions模型与Chat Completions模型如下:
OpenAI发布的带有text标识的A、B、C、D四大类模型都是Completions模型(也就是gpt-3系列模型),而gpt-3.5和gpt4,则是Chat Completions模型。
从实际功能定位上来说,Completions模型是一类功能更加基础、同时配套功能更加齐全的模型(例如OpenAI只为Completions模型提供了微调的接口),而Chat Completions模型则是一类功能更加特异化、并且更加先进的一类模型。两类模型都有各自适用的场景
三、 Completion.create API
3.1 参数详解
在OpenAI提供的在线大模型中,所有的Completions模型都需要通过Completion.create进行调用,并在实际调用的过程中通过超参数设置对应的调用模型及模型相关参数。
看下官网的参数:
各参数解释如下:
3.2 代码测试
先引入依赖包和OPENAI_API_KEY
import os
import openaiopenai.api_key = os.getenv("OPENAI_API_KEY")
所有的Completions模型都需要通过Completion.create进行调用,在实际调用的过程中通过超参数设置对应的调用模型及模型相关参数。
response = openai.Completion.create(model="text-davinci-003",prompt="Say this is a test")
输出结果如下:
最简单的调用是:在函数中设置model和prompt参数,这两个是必填的参数。其中model表示调用的模型,而prompt则表示提示词。
返回结果response包含了text_completion id(completion任务编码),以及返回结果和模型参数。
response是一个OpenAIObject类型对象
获取元素就是常规的Python操作了,如:
- 提取response中具体返回的文本
- 提取text对象
生成式大模型的结果往往是无法复现的。尽管从理论上来说,当temperature取值为0的时候,模型在每个步骤中总是选择最可能的下一个token,这会导致生成的文本变得确定和可预测,但在实践过程中,由于模型内部的计算精度和舍入误差等因素,也可能无法完全保证结果的完全一致性。
除了temperature外,目前OpenAI并没有提供任何额外的可以用于控制结果随机性的参数。top_p尽管可以影响输出结果,但并不能完全控制模型随机性。此外,目前OpenAI的在线大模型也并没有类似机器学习领域的随机数种子的参数设置。因此,就目前的情况而言,控制大语言输出结果使其能够复现,几乎是不可能做到的。
3.3 参数调参实践
大语言模型是非常复杂的黑箱模型,导致很多时候参数设置导致的结果并不能完全按照理论情况来进行推演。
因此需要通过大量的实践,来积累不同参数的使用经验——即掌握不同参数在实际Completion过程中对输出结果的影响。
先来试一下 尝试调整Completion.create的核心参数来观察模型输出结果变化情况
3.3.1 参数n
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3)response
此时的返回就有3个completion,如下:
单独看某一个的话:
response["choices"][0]["text"].strip('?\n\n')
输出如下:
封装成函数:
import openaidef generate_response(n):response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=n)for i in range(n):print(response["choices"][i]["text"].strip('?\n\n'))# 测试函数
generate_response(3)
3.3.2 参数temperature
temperature参数是非常核心的用于控制模型输出结果随机性的参数。其取值范围在0-2之间,默认值为1,数值越大模型随机性越大。
先尝试设置temperature=1.5并观察返回结果:
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3,temperature=1.5)
看下结果:
对于“请问什么是机器学习”这一较为简单的提示(预训练文本中存在大量相关语料),返回的结果已经出现了非常严重的随机性,很多文本甚至不可读。这也说明temperature对文本输出结果的随机性影响非常大。
设置一个更小的temperature取值,并观察输出结果:
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3,temperature=0.8)
看下结果:
从结果上就能看出,当temperature参数数值下降时,模型返回的多个结果会更加“趋同”,整体来看答案严谨但缺乏创造力,更像是教科书式的定义。
关于temperature参数的设置技巧:
- 首先是关于参数推荐的取值范围,尽管temperature可以在[0,2]范围内进行调节,但temperature的数值对模型结果影响极大,往往小幅的数值调整就会导致非常大的影响,因此建议的调整范围在**[0.8,1.2]**之间;
- 其次是参数不同取值的实际影响,伴随着temperature取值逐渐增加,返回的文本也将从严谨表述逐渐变为“胡言乱语”,但temperature在某些取值时,返回的结果会呈现出具备一定“启发性”的特性。具体temperature参数取值如何设置,还需要根据实际情况来判断
更新一下函数:
import openaidef generate_response(n, temperature):response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=n,temperature=temperature)for i in range(n):print(response["choices"][i]["text"].strip('?\n\n'))# 测试函数
generate_response(3, 0.8)
3.3.3 参数presence_penalty
对文本输出质量有重要影响的另一个参数,是presence_penalty。
不同于temperature是用于控制输出结果是“严谨表述”还是“胡言乱语”,presence_penalty更多的是控制返回语句的精炼程度。presence_penalty参数在[-2,2]中取值,数值越大,返回结果越精炼,数值越小,返回结果越啰嗦。
仍然还是以“请问什么是机器学习”作为提示,观察presence_penalty取值不同时的输出结果:
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3,presence_penalty=2)
看下结果:
此时的输出结果是非常精炼的。而如果设置presence_penalty为-2:
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3,presence_penalty=-2)
结果如下:
此时结果则显得“啰嗦”了不少。
看起来presence_penalty参数对模型结果的影响并不如temperature那么显著,但在实际调用大模型进行Completion时,presence_penalty参数和temperature参数搭配进行使用,往往能达到非常好的效果。一般来说,如果希望模型更加具有创造力,则会增加temperature取值,并且适当降低presence_penalty取值
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3,presence_penalty=-1,temperature=1.2)
结果如下:
反之,如果希望结果更加精准,则会适当降低temperature,同时增加presence_penalty取值:
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=3,presence_penalty=2,temperature=0.8)
结果如下:
这也就是经常会看到一些大语言模型具备三种不同的模式,分别为精确模式、平衡模式和创造力模式(例如早期的Bing AI聊天),其功能的实现都是通过大模型的参数调整来完成的,而支持这些不同模式功能实现的最基础的两个参数就是presence_penalty参数和temperature参数。
更新一下函数:
import openaidef generate_response(n, temperature, presence_penalty):"""根据输入的n、temperature和presence_penalty生成相应的响应。n: 输出结果的数量。temperature: 控制输出的随机性。值越高,输出结果越随机;值越低,输出结果越确定。presence_penalty: 控制模型输出时对于当前已存在的内容的依赖程度。值越高,模型在生成文本时就越不会重复使用当前已有的内容;值越低,模型生成的文本就可能会有更多的重复。注意:如果希望模型更具有创造力,可以增加temperature的值,同时适当降低presence_penalty的值;如果希望结果更加精确,可以适当降低temperature的值,同时增加presence_penalty的值。"""response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=n,temperature=temperature,presence_penalty=presence_penalty)for i in range(n):print(response["choices"][i]["text"].strip('?\n\n'))# 测试函数
generate_response(3, 0.8, 2)
3.3 4 参数best_of
best_of能够让一个模型预测多组结果,并择优进行输出。
比如:如果设置best_of=5,则代表总共创建5个备选文本,再从中选取概率最大(得分最高)的一组进行输出:
response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,presence_penalty=-1,temperature=1.2,best_of=5)
结果如下:
-
在大多数情况下,随着best_of值增加,最终的输出结果质量也会随之提升;
-
best_of参数会消耗大量Token配额以及每分钟调用次数配额,同时也会消耗更多的资金;
best_of是自动筛选最优结果,参数n可以支持手动筛选最佳结果,有的时候可以同时使用这两个参数,以期获得更好的结果。如best_of=2、n=2时,总共发出四次请求,最终返回两个结果。
更新代码:
import openaidef generate_response(n, temperature, presence_penalty, best_of):"""根据输入的n、temperature、presence_penalty和best_of生成相应的响应。n: 输出结果的数量。temperature: 控制输出的随机性。值越高,输出结果越随机;值越低,输出结果越确定。presence_penalty: 控制模型输出时对于当前已存在的内容的依赖程度。值越高,模型在生成文本时就越不会重复使用当前已有的内容;值越低,模型生成的文本就可能会有更多的重复。best_of: 让模型预测多组结果,并择优进行输出。注意:如果希望模型更具有创造力,可以增加temperature的值,同时适当降低presence_penalty的值;如果希望结果更加精确,可以适当降低temperature的值,同时增加presence_penalty的值。"""response = openai.Completion.create(model="text-davinci-003",prompt="请问什么是机器学习",max_tokens=1000,n=n,temperature=temperature,presence_penalty=presence_penalty,best_of=best_of)for i in range(n):print(response["choices"][i]["text"].strip('?\n\n'))# 测试函数
generate_response(3, 1.2, -1, 5)
四、基于Completion.create函数的可调节对话风格的多轮对话机器人
在了解Completion.create函数功能之后,尝试编写一个基于Completion.create函数的可以在Jupyter内部实现多轮对话的聊天机器人,同时通过预设不同的参数,在函数内部预设精确模式、平衡模式和创造力模式,以满足不同使用场景。函数定义如下:
def chat_now(model='text-davinci-003',mode='balance'):"""基于Completion.create函数的多轮对话机器人:param model: 调用的大语言模型,默认为text-davinci-003:param mode: 聊天机器人预设模式,默认为平衡模式balance,可选precision(精确模式)和creativity(创造力模式)"""# 提示想终止聊天时输入"quit"print("if you want to stop the conversation, please input 'quit'") # 三种不同的模式及其对应的参数if mode == 'balance':temperature = 1presence_penalty = 0elif mode == 'precision':temperature = 0.8presence_penalty = 2elif mode == 'creativity':temperature = 1.2presence_penalty = -1 # 定义执行对话函数,方便后续反复调用def chat(prompt):try:# 不报错的情况下,返回Completion.create函数输出结果response = openai.Completion.create(model = model,prompt = prompt,max_tokens = 1000,temperature=temperature, presence_penalty=presence_penalty,stop = [" Human:", " AI:"])answer = response["choices"][0]["text"].strip()return answerexcept Exception as exc:# 报错时返回"broken"return "broken"# 对话执行函数,首先准备空容器text = "" turns = [] # 执行多轮对话,即多次调用chat函数while True: # 启动对话框question = input()# 首次开启对话框时提示请输入问题if len(question.strip()) == 0: print("please input your question")# 当输入为'quit'时,停止多轮对话,即停止while循环elif question == "quit": print("\nAI: See You Next Time!")breakelse:# 多轮对话时,将问题和此前对话结果都作为prompt输入prompt = text + "\nHuman: " + questionresult = chat(prompt)# 当一次请求失败时,再次发起请求while result == "broken": print("please wait...")result = chat(prompt) else:# 保留本次对话结果turns += [question] + [result]print(result)# 最多保留十次对话结果,超出次数则最开始的对话会被删除if len(turns)<=10: text = " ".join(turns)else:text = " ".join(turns[-10:])
测试一下,运行函数进行对话:
chat_now()
五、总结
文章详细阐述了OpenAI的Completions与Chat Completions模型的基本概念与模型类。接着,对Completion.create API进行了深度解析,包括参数详解、代码测试以及参数调参实践,特别是对n、temperature、presence_penalty、best_of等参数的详细讲解。最后,以Completion.create函数为基础,实现了具有可调节对话风格的多轮对话机器人,展现了AI对话系统的灵活性。掌握这些知识,对优化AI对话系统具有重要的指导意义。
感谢您阅读这篇文章!如果您觉得有所收获,别忘了点赞、收藏并关注我,这是我持续创作的动力。您有任何问题或建议,都可以在评论区留言,我会尽力回答并接受您的反馈。如果您希望了解某个特定主题,也欢迎告诉我,我会乐于创作与之相关的文章。谢谢您的支持,期待与您共同成长!
最后,给大家送上干货!建议大家点赞&收藏,Mark住别丢了。有高质量资料免费送!
1. 关于魔法,你需要知道的
2. 超全流程!OpenAI账户注册看这里!
3. ChatGPT Plus 升级指南