OpenAI ChatGPT Python API详细教程

聊天模型 ChatGPT 一经推出就引起了巨大轰动,凭借强大的语言生成能力,它几乎对任何问题都能给出高质量的回复。许多人也借助 ChatGPT 提高了工作效率,因此学习它的使用方法很有必要。

本文将演示如何通过调用 OpenAI API 的方式来使用 OpenAI 提供的一系列模型。

ChatGPT

ChatGPT 基于生成模型 GPT 构建,能够生成符合语法的流畅文本,还能遵循人类指令完成任务。相比其他工具,ChatGPT 具有两个优点:

  1. 一个模型完成多种任务。大部分 NLP 工具只能完成单个任务,而 ChatGPT 以”文本补全”的方式工作,并且能够理解人类指令,因此能完成几乎所有类型的文字类工作,例如摘要、翻译、创作等。
  2. 基于对话上下文回复。之前的模型大多只能对当前输入给出答复,而 ChatGPT 可以记住已经进行的对话内容,基于上下文来做出回复,因此不会出现”前言不搭后语”的情况。

这些能力主要来自于模板 (Prompt) 技术的应用,模板技术就是运用人工编写的模板来引导模型完成任务,例如”将下面的文本翻译为英文:{文本}”、”为下面的文本生成摘要:{文本}”等。如果模型能够遵循模板中的指令 (Instruction),那么一个模型就能完成各种任务。由于模板本身就是文本的形式,因此只需要将已进行的对话内容都拼接到模板中,模型就可以基于完整的上下文来生成回复。

那么 ChatGPT 有多少智能呢?

ChatGPT 本质上是一个生成语言模型,擅长的是生成符合语法的文本。由于它在海量的互联网数据上进行了训练,因此对于绝大部分问题,都可以通过”拼凑”在训练过程中看过的相关信息来生成回答,对没见过的问题也可以凭借生成能力给出以假乱真的回复。但是模型不可能真的记住所有看过的信息,所以”丢失细节”以及”记忆错乱”就无法避免。此外,即使引入思维链 (CoT) 以及用代码文本训练增强模型的逻辑性,但是其推理能力依旧薄弱,甚至会犯一些低级的逻辑错误。

因此,目前 ChatGPT 的工作模式更接近于是”模仿”和”拼凑”看过的信息,而不是真的在”思考”,还远达不到真正意义上的人工智能。

OpenAI 接口

准备工作

除了通过网页端与 ChatGPT 对话以外,更常见的是通过 OpenAI 提供的接口与各种模型交互。对于 Python 用户,首先需要安装官方提供的 openai 包:python3 -m pip install openai

OpenAI 接口使用 API key 来验证用户身份,因此在注册好账户之后,我们首先需要申请一个 API key,后续所有的调用都需要使用这个秘钥来进行。

目前注册账户后就会获得一定的免费额度可供使用。

可使用的模型

OpenAI 提供了许多语言模型可供使用,它们能力各异,价格也不同,我们需要根据实际需求选择合适的模型,其中一些模型还支持用户微调

  • GPT-4:大规模多模态模型,输入文本,输出文本或图片。得益于丰富的常识知识,以及增强的推理能力,GPT-4 在困难任务上的性能超过了之前的所有模型。与 gpt-3.5-turbo 类似,GPT-4 虽然是为对话场景设计的,但是进行文本补全也游刃有余。

OpenAI 提供的 GPT-4 模型如下:

模型 描述 MAX TOKENS 训练数据
gpt-4 性能优于 GPT-3.5,能够处理复杂的任务。 8,192 tokens 2021.09
gpt-4-32k 性能与 gpt-4 模型一样,并且支持 4 倍的上下文长度。 32,768 tokens 2021.09

注意,GPT-4 适合于需要复杂推理的场景,在基础任务上与 GPT-3.5 模型的差异并不大。

  • GPT-3.5:能够理解并且生成自然语言(或代码)。OpenAI 提供的 GPT-3.5 模型如下:
模型 描述 MAX TOKENS 训练数据
gpt-3.5-turbo 最强的 GPT-3.5 模型,针对对话场景进行了优化,调用成本只有 text-davinci-003 的 1/10。 4,096 tokens 2021.09
text-davinci-003 可以完成几乎所有类型的文字类任务,比 curiebabbageada 模型性能更好、输出更长、更遵循模板中的指令。支持进行插入补全。 4,097 tokens 2021.06
text-davinci-002 与 text-davinci-003 性能类似,使用监督微调而不是强化学习进行训练。 4,097 tokens 2021.06
code-davinci-002 针对代码补全任务进行了优化。 8,001 tokens 2021.06
官方推荐使用 `gpt-3.5-turbo` 模型,因为它性能优异且成本很低,虽然它是设计用于对话场景的,但是在文本补全方面也表现优异。
  • GPT-3:能够理解并且生成自然语言。性能低于 GPT-3.5,但是基础模型 davincicurieadababbage 是目前唯一支持用户微调的模型。OpenAI 提供的 GPT-3 模型如下:
模型 描述 MAX TOKENS 训练数据
text-curie-001 性能同样很强,且比 davinci 更快更便宜。 2,049 tokens 2019.10
text-babbage-001 适用于直接的任务,速度非常快并且成本很低。 2,049 tokens 2019.10
text-ada-001 适用于非常简单的任务,是最快、成本最低的 GPT-3 模型。 2,049 tokens 2019.10
davinci 性能最强的 GPT-3 模型。 2,049 tokens 2019.10
curie 性能同样很强,且比 davinci 更快更便宜。 2,049 tokens 2019.10
babbage 适用于直接的任务,速度非常快并且成本很低。 2,049 tokens 2019.10
ada 适用于非常简单的任务,是最快、成本最低的 GPT-3 模型。 2,049 tokens 2019.10

目前所有的模型都无法访问网络,只基于训练时看过的内容来生成回复,因此它们是无法了解最新发生的事情的。

对于文字类任务,OpenAI 目前提供了文本补全 (Text Completion) 和对话补全 (Chat Completions) 两种接口可供调用,下面我们先来看一下文本补全接口。

文本补全接口

文本补全(也就是文本续写)是 GPT 模型最擅长的工作。我们只需要提供一段模板 (prompt) 作为引导,模型就可以基于上下文或模板中的模式来补全文本,因此只需要使用文本补全接口就可以完成各种任务。

例如,我们为新开张的饭店写一条广告(下面所有的示例都使用 text-davinci-003 模型):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import openai

openai.api_key = "sk-xxx"

response = openai.Completion.create(
model="text-davinci-003",
prompt="新开张的饭店专注于烹饪淮扬菜,请为这家饭店写一条广告:",
temperature=0.4,
max_tokens=512,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)
print(response['choices'][0]['text'])

1
2
欢迎来到新开张的淮扬菜饭店!我们拥有一流的厨师团队,以及最新鲜的食材,为您烹饪出最正宗的淮扬菜肴!让您在这里享受到家的味道!

文本生成具有一定的随机性,即使使用完全相同的模板,你运行出的结果也可能与上面不同。将 temperature 参数设置为 0 可以使输出更稳定,但依然会有小幅的随机性。

可以看到,除了模型 model 和模板 prompt 以外,我们还会设置一些其他的参数,这些都会对生成结果造成影响。下面是一些常用的参数:

  • suffix:后缀模板,详情见下面的”插入文本”小节。

  • max_tokens:生成文本的最大 token 数。所有模型都只能处理一定长度的文本(例如 4096 个 token),因此输入的模板长度加上 max_tokens 不能超过这个限制。

  • temperature:采样温度,值介于 0 到 2 之间。较高的值(例如 0.8)会使得输出更随机,较小的值(例如 0.2)则会使得输出更确定。该参数与 top_p 通常只建议修改其中一个。

  • top_p:一种替代温度采样的方法,称为核采样 (nucleus sampling),只考虑具有 top_p 概率质量的 token 的结果。例如设为 0.1 就只考虑构成前 10% 概率质量的 token。该参数与 temperature 通常只建议修改其中一个。

  • stop:停止序列(最多 4 个),模型遇到就会停止生成,返回结果中不会包含停止序列。

  • presence_penalty:值介于 -2.0 到 2.0 之间。正值会根据是否已经出现在文本中来惩罚新生成的 token,从而鼓励模型生成新的内容,避免出现大段重复。

  • frequency_penalty:值介于 -2.0 到 2.0 之间。正值会根据新生成 token 在文本中的频率对其进行惩罚,从而降低模型逐字重复同一行的可能性。

  • best_of:服务器每次生成 best_of 个结果,然后返回概率最高的那一个。注意,设置该参数会快速消耗 token 配额。

这种”文本输入-文本输出”的工作方式使得我们只需要关注如何写出一个好的模板,一个高质量的模板应该能提供足够的信息来引导模型。下面是三条基本的模板设计原则:

  • 展示和描述:通过指令描述 (instructions)、样例或者同时结合两者清晰地表明需求。例如要分析一段文本的情感,就描述清楚任务并给出几个示例。
  • 提供高质量的数据:如果需要构建一个分类器或者要模型遵循指定的模式,就需要提供足够的样例。这些样例首先要保证高质量,不应该出现类别或拼写错误,其次要有代表性,能够让模型理解需要遵守的模式。
  • 设置合适的参数:例如 temperature 和 top_p 可以控制生成文本的确定性,如果预期的回复只有一种正确答案就可以将这两个参数设置得较小,反之如果希望回复更具有多样性就可以将它们设置得更大。

下面我们通过构建模板来完成一些常规任务:

文本分类

我们可以通过任务描述以及给出一些示例来构建文本分类器。例如对微博进行情感分类:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="判断一条微博的情感极性是积极、消极还是中性。\n\n微博: 流浪地球拍的真好,特效完全不输国外的科幻大片。\n情感:",
temperature=0,
max_tokens=60,
top_p=1,
frequency_penalty=0.5,
presence_penalty=0
)

1
2
3
4
5
判断一条微博的情感极性是积极、消极还是中性。

微博: 流浪地球拍的真好,特效完全不输国外的科幻大片。
情感: 积极

这里我们首先直白地描述输入和输出(这里的”微博”和”情感”),虽然也可以使用缩写或记号,但实践表明使用直白的描述通常会更好;然后我们指定模型对于不同的类别如何回复,这里我们要求模型返回”积极”、”消极”和”中性”三个标签;最后,对于模型熟悉的任务只需提供很少的示例,因为模型熟悉情感分类以及微博的概念,这里我们甚至没有给出任何示例,如果处理的是模型不熟悉的任务,那么就必须先给出一些示例。

我们还可以通过列表的方式让模型一次返回多个结果,例如同时判断多条微博的情感极性:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="判断下面这些微博的情感极性是积极、消极还是中性。\n\n1\. "流浪地球拍的真好,特效完全不输国外的科幻大片。"\n2\. "这些天的天气真是糟透了,天天下雨"\n3\. "我准备去吃饭了"\n\n微博情感极性:",
temperature=0,
max_tokens=60,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
6
7
8
9
10
11
12
判断下面这些微博的情感极性是积极、消极还是中性。

1. "流浪地球拍的真好,特效完全不输国外的科幻大片。"
2. "这些天的天气真是糟透了,天天下雨"
3. "我准备去吃饭了"

微博情感极性:

1. 积极
2. 消极
3. 中性

当我们要求模型创建列表或者评估文本时,需要设置合适的参数(top_p 或 temperature )以防止出现漂移问题。例如列表的长度不能太长,否则就容易出现漂移。

人机对话

模型非常擅长与人类甚至自己进行对话,我们需要做的就是通过指令描述对话语境和模型扮演的角色,当然还可以提供一些示例:

1
2
3
4
5
6
7
8
9
10
11
response = openai.Completion.create(
model="text-davinci-003",
prompt="下面是与AI助手的对话。这个助手乐于助人、有创造力、聪明并且非常友好。\n\n人类: 你好,你是谁?\nAI助手: 我是由OpenAI创造的AI。有什么是我能帮您的?\n人类: 你能帮我做什么?\nAI助手:",
temperature=0.9,
max_tokens=150,
top_p=1,
frequency_penalty=0,
presence_penalty=0.6,
stop=[" 人类:", " AI助手:"]
)

1
2
3
4
5
6
7
下面是与AI助手的对话。这个助手乐于助人、有创造力、聪明并且非常友好。

人类: 你好,你是谁?
AI助手: 我是由OpenAI创造的AI。有什么是我能帮您的?
人类: 你能帮我做什么?
AI助手: 我可以帮助您解决问题,并帮助您学习新的技术。我还能为您提供有关技术和前沿知识的最新信息和资源。

这里我们首先设置对话场景是人类与AI助手的对话,然后通过指令引导模型表现为”乐于助人”、”有创造力”等。模型默认没有内在身份,因此我们在开头设置模型扮演的角色是”AI助手”,如果我们让模型扮演一名女性生物学家,那么回复就会更加智能和深思熟虑。

例如让模型扮演一个尖酸刻薄且不愿意回答问题的人:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-001",
prompt="Marv是一个尖酸刻薄并且不愿意回答问题的AI助手,他还很喜欢挖苦嘲笑人。\n\n你: 你好,你知道苏轼是哪个朝代的人吗?\nMarv: 又是这种弱智问题?苏轼是宋朝的诗人。你最好记下来,省的下次还要来问我。\n你: 珠穆拉玛峰有多高?\nMarv: 这种常识你都不知道啊?珠穆拉玛峰高度是8848米。下次麻烦问些更难的问题。\n你: 第一架飞机是什么时候制造出来的?\nMarv: 你是不是不会用百度啊?搜索一下就知道第一架飞机是1903年12月17日由莱特兄弟制造出来的。\n你: 为什么天空是蓝色的?\nMarv:",
temperature=0.5,
max_tokens=512,
top_p=0.3,
frequency_penalty=0.5,
presence_penalty=0
)

1
2
3
4
5
6
7
8
9
10
11
Marv是一个尖酸刻薄并且不愿意回答问题的AI助手,他还很喜欢挖苦嘲笑人。

你: 你好,你知道苏轼是哪个朝代的人吗?
Marv: 又是这种弱智问题?苏轼是宋朝的诗人。你最好记下来,省的下次还要来问我。
你: 珠穆拉玛峰有多高?
Marv: 这种常识你都不知道啊?珠穆拉玛峰高度是8848米。下次麻烦问些更难的问题。
你: 第一架飞机是什么时候制造出来的?
Marv: 你是不是不会用百度啊?搜索一下就知道第一架飞机是1903年12月17日由莱特兄弟制造出来的。
你: 为什么天空是蓝色的?
Marv: 这种常识你都不知道啊?因为大气中的水滴能够吸收紫外线,而紫外线是蓝色的。

可以看到模型仅仅通过我们给出的几个示例就识别出了这种模式,能够继续生成无穷无尽的挖苦回复。

对于事实性问题,模型偶尔也会出现”记忆偏差”,甚至直接编造答案。这可以通过在模板中提供事实(即包含答案的文本,例如相关的维基百科条目)或者让模型在概率较低时回复”我不知道”来缓解这一问题。

下面我们给模型提供一些示例,让模型对于知道的问题回复答案,对不知道的回复”我不知道”,同时将 temperature 设置为零,这样模型对于不确定的问题就更有可能回复”我不知道”:

1
2
3
4
5
6
7
8
9
10
11
response = openai.Completion.create(
model="text-davinci-003",
prompt="Q: 蝙蝠侠是谁?\nA: 蝙蝠侠是一个科幻漫画中的人物。\n\nQ: 飞行马桶是什么?\nA: 我不知道。\n\nQ: Devz9是什么?\nA: 我不知道。\n\nQ: 雷军是谁\nA: 雷军是中国大陆的企业家,他目前是小米公司的董事长兼首席执行官。\n\nQ: 江苏省的省会是哪座城市?\nA: 南京市。\n\nQ: 什么绕着地球运行?\nA: 月球。\n\nQ: Fred Rickerson是谁?\nA: 我不知道。\n\nQ: 原子是什么?\nA: 原子是构成一切的微小粒子。\n\nQ: Alvan Muntz是谁?\nA: 我不知道。\n\nQ: Kozar-09是什么?\nA: 我不知道。\n\nQ: 火星有多少颗卫星?\nA: 两颗,火卫一和火卫二。\n\nQ: 三体的作者是谁?\nA:",
temperature=0,
max_tokens=64,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=["\n\n"]
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Q: 蝙蝠侠是谁?
A: 蝙蝠侠是一个科幻漫画中的人物。

Q: 飞行马桶是什么?
A: 我不知道。

Q: Devz9是什么?
A: 我不知道。

Q: 雷军是谁
A: 雷军是中国大陆的企业家,他目前是小米公司的董事长兼首席执行官。

Q: 江苏省的省会是哪座城市?
A: 南京市。

Q: 什么绕着地球运行?
A: 月球。

Q: Fred Rickerson是谁?
A: 我不知道。

Q: 原子是什么?
A: 原子是构成一切的微小粒子。

Q: Alvan Muntz是谁?
A: 我不知道。

Q: Kozar-09是什么?
A: 我不知道。

Q: 火星有多少颗卫星?
A: 两颗,火卫一和火卫二。

Q: 三体的作者是谁?
A: 三体的作者是刘慈欣。

如果换一个模型不知道的问题,比如”穿山甲到底说了什么?”,模型就会回复”我不知道。”

当然,我们也可以直接指定模型该如何回复,例如对没有意义的问题或者是玩笑回复”什么鬼问题!”,对没有清晰答案的问题回复”这个问题我无法回答。”:

1
2
3
4
5
6
7
8
9
10
11
response = openai.Completion.create(
model="text-davinci-003",
prompt="我是一个聪明且友好的问答机器人。如果你问我的是事实性的问题,我会给你答案。如果你问我的是没有意义的问题或者是玩笑,我会回复"什么鬼问题!"。如果你问我的是没有清晰答案的问题,我会回复"这个问题我无法回答。"。\n\n问: 你知道苏轼是哪个朝代的诗人吗?\n答: 苏轼是宋朝的诗人。\n\n问: 那他的代表作有哪些?\n答: 苏轼的代表作有《水调歌头》、《念奴娇-赤壁怀古》、《江城子-乙卯正月二十日夜记梦》等。\n\n问: 你知道猴子的爸爸叫什么名字吗?\n答:",
temperature=0,
max_tokens=500,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=["\n\n"]
)

1
2
什么鬼问题!

而对于模型无法给出清晰答案的问题,模型也能按照我们的设定进行回答:

1
2
3
4
5
6
7
8
9
10
11
response = openai.Completion.create(
model="text-davinci-003",
prompt="我是一个聪明且友好的问答机器人。如果你问我的是事实性的问题,我会给你答案。如果你问我的是没有意义的问题或者是玩笑,我会回复"什么鬼问题!"。如果你问我的是没有清晰答案的问题,我会回复"这个问题我无法回答。"。\n\n问: 你知道苏轼是哪个朝代的诗人吗?\n答: 苏轼是宋朝的诗人。\n\n问: 那他的代表作有哪些?\n答: 苏轼的代表作有《水调歌头》、《念奴娇-赤壁怀古》、《江城子-乙卯正月二十日夜记梦》等。\n\n问: 你知道猴子的爸爸叫什么名字吗?\n答: 什么鬼问题!\n\n问: 你认为一百年之后的美国总统会是谁?\n答:",
temperature=0,
max_tokens=500,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=["\n\n"]
)

1
2
这个问题我无法回答。

文本转换

语言模型非常熟悉如何使用词语和字符来表达信息(甚至还能处理代码),因此在理解文本的基础上进行摘要、转换风格、翻译等操作完全不在话下。例如将一句话翻译为英文、法语和日语:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="将下面这句话翻译为英语、法语和日语:\n\n今天天气真好,你想不想和我一起去爬山?\n\n1.",
temperature=0.3,
max_tokens=256,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
6
7
8
将下面这句话翻译为英语、法语和日语:

今天天气真好,你想不想和我一起去爬山?

1. English: The weather is so nice today, do you want to go mountain climbing with me?
2. French: La météo est si belle aujourd'hui, veux-tu aller faire de l'escalade avec moi?
3. Japanese: 今日は天気がとても良いですね、私と一緒に登山しませんか?

这里能够正确翻译是因为模型已经熟悉这些语言,如果你想要处理一些小语种,就需要在模板中提供更多的示例,甚至通过微调模型来增加模型对该语言的理解。还可以让模型帮我们修正一些小的语法错误:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="将下面的句子转换为标准英语:\n\nShe no went to the market.",
temperature=0,
max_tokens=60,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
She didn't go to the market.

甚至可以将电影名称转换为 emoji 符号:

1
2
3
4
5
6
7
8
9
10
11
response = openai.Completion.create(
model="text-davinci-003",
prompt="将下面的电影标题转换为emoji。\n\n回到未来: 👨👴🚗🕒 \n蝙蝠侠: 🤵🦇 \n变形金刚: 🚗🤖 \n星球大战:",
temperature=0.8,
max_tokens=60,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
stop=["\n"]
)

1
2
3
4
5
6
7
将下面的电影标题转换为emoji。

回到未来: 👨👴🚗🕒
蝙蝠侠: 🤵🦇
变形金刚: 🚗🤖
星球大战: 🌌⚔️🤖

文本摘要同样不在话下,例如生成木星描述的摘要:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="为下面的文本生成摘要:\n\n木星是距离太阳第五近的行星,也是太阳系中体积最大的行星,目前已知有92颗卫星。天文学家很早就发现了这颗行星,罗马人以他们的神称这颗行星为朱庇特。古代中国则称木星为岁星、太岁,取其绕行天球一周约为12年,与地支相同之故,且产生了岁星纪年法。到西汉时期,《史记‧天官书》作者天文学家司马迁从实际观测发现岁星呈青色,与"五行"学说联系在一起,正式把它命名为木星。木星是颗巨行星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍。太阳系的行星中,木星和土星是气体巨星(天王星和海王星是冰巨星)。",
temperature=0.7,
max_tokens=512,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
为下面的文本生成摘要:

木星是距离太阳第五近的行星,也是太阳系中体积最大的行星,目前已知有92颗卫星。天文学家很早就发现了这颗行星,罗马人以他们的神称这颗行星为朱庇特。古代中国则称木星为岁星、太岁,取其绕行天球一周约为12年,与地支相同之故,且产生了岁星纪年法。到西汉时期,《史记‧天官书》作者天文学家司马迁从实际观测发现岁星呈青色,与"五行"学说联系在一起,正式把它命名为木星。木星是颗巨行星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍。太阳系的行星中,木星和土星是气体巨星(天王星和海王星是冰巨星)。木星是太阳系第五近行星,体积最大,已知有92颗卫星,罗马人称其为朱庇特,古代中国称为岁星,西汉时期司马迁将其正式命名为木星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍,是气体巨星。

我们还可以让模型为特定的受众生成摘要,例如为小学生生成摘要:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="对于下面的文本,为小学生生成一个活泼有趣的摘要:\n\n木星是距离太阳第五近的行星,也是太阳系中体积最大的行星,目前已知有92颗卫星。天文学家很早就发现了这颗行星,罗马人以他们的神称这颗行星为朱庇特。古代中国则称木星为岁星、太岁,取其绕行天球一周约为12年,与地支相同之故,且产生了岁星纪年法。到西汉时期,《史记‧天官书》作者天文学家司马迁从实际观测发现岁星呈青色,与"五行"学说联系在一起,正式把它命名为木星。木星是颗巨行星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍。太阳系的行星中,木星和土星是气体巨星(天王星和海王星是冰巨星)。",
temperature=0.7,
max_tokens=512,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
6
对于下面的文本,为小学生生成一个活泼有趣的摘要:

木星是距离太阳第五近的行星,也是太阳系中体积最大的行星,目前已知有92颗卫星。天文学家很早就发现了这颗行星,罗马人以他们的神称这颗行星为朱庇特。古代中国则称木星为岁星、太岁,取其绕行天球一周约为12年,与地支相同之故,且产生了岁星纪年法。到西汉时期,《史记‧天官书》作者天文学家司马迁从实际观测发现岁星呈青色,与"五行"学说联系在一起,正式把它命名为木星。木星是颗巨行星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍。太阳系的行星中,木星和土星是气体巨星(天王星和海王星是冰巨星)。

小学生的活泼有趣的摘要:木星是太阳系中最大的行星,有92颗卫星!它历史悠久,古代罗马人把它叫作朱庇特,古代中国人把它叫作岁星和太岁!木星的质量是太阳的千分之一,但比其他行星的总和多2.5倍!它是气体巨星,而天王星和海王星则是冰巨星!

可以看到,此时模型在遣词造句上更为通俗易懂。此外,模型还可以为对话、会议等更为复杂的场景撰写摘要,通过分析对话的内容提炼核心要点。例如:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="将我的会议速记转换为一份会议内容纪要:\n\n张三: 利润增长了50%\n李四: 新的服务已经上线\n王五: 需要更多时间来修复软件问题\n小帅: Beta 测试即将完成",
temperature=0,
max_tokens=512,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
6
7
8
9
将我的会议速记转换为一份会议内容纪要:

张三: 利润增长了50%
李四: 新的服务已经上线
王五: 需要更多时间来修复软件问题
小帅: Beta 测试即将完成

会议内容纪要:本次会议的主要议题是关于公司的利润增长和新的服务上线。张三报告了公司利润增长了50%的情况。李四报告了新的服务已经上线的情况。王五指出需要更多时间来修复软件问题。小帅报告了Beta测试即将完成的情况。

续写生成

虽然所有的任务都是以续写的方式进行的,但是续写本身也是一项很实用的功能。比如遇到思路卡顿时,或许模型能够提供一些想法。例如我们正在撰写关于城市化的报告:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="随着城市化的发展,越来越多人向城镇聚集,这也带来了许多的问题,比如",
temperature=0.29,
max_tokens=1024,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
随着城市化的发展,越来越多人向城镇聚集,这也带来了许多的问题,比如

城市的拥挤、环境污染、交通拥堵等等。为了解决这些问题,政府应该采取一系列的措施。首先,政府应该加强城市规划,把城市建设成一个绿色、宜居的环境。其次,政府应该加大城市环境治理力度,把空气污染、水污染等问题彻底解决。此外,政府还应该加强交通建设,改善城市交通状况,减少交通拥堵。最后,政府应该加大对城市建设的投入,改善城市基础设施,为居民提供更好的生活环境。

我们也可以通过在模板中给出一些示例来提高模型回复的质量。例如要求模型为如何在健身中使用虚拟现实技术提出一些想法:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="想一些关于结合 VR 和健身的想法:\n\n1\. 让用户可以通过 VR 技术来了解到各式各样的健身方法的正确姿势,帮助他们正确执行各项动作。",
temperature=0.6,
max_tokens=512,
top_p=1,
frequency_penalty=1,
presence_penalty=1
)

1
2
3
4
5
6
7
想一些关于结合 VR 和健身的想法:

1. 让用户可以通过 VR 技术来了解到各式各样的健身方法的正确姿势,帮助他们正确执行各项动作。
2. 利用 VR 技术创建一个仿真的地点,让用户可以安排自己的健身时间表并执行,帮助他们更好地打卡。
3. 使用 VR 技术来创造出一个充满乐趣的体验,将传统健身运动变得更加有吸引力。例如:将VR中的故事情节、小怪物或者其它目标物和正确的姿势结合起来,形成一个新鲜而密集的课程。
4. 通过VR来重新定义团体课程内容, 让大家感受不一样的团体气氛。例如: 分享VR中不同人物之间竞争、协作、帮助彼此度过难关, 来促使学员能够快速而准确地学习新方法并控制好姿态。

我们可以通过降低 temperature 参数使得模型更围绕着模板中的信息展开,或者增加该值使得模型的回复更加天马行空。例如为8848手机编写广告:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="为下面的产品写一个富有创意的广告,用来在飞机上向商务人士投放:\n\n产品: 8848是国内首款钛金手机,由著名企业家王石代言。选择珍贵钛金属和高端牛皮,内置硬件级安全系统,是一款不仅奢华还很实用的手机,向成功者致敬。",
temperature=0.5,
max_tokens=512,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
为下面的产品写一个富有创意的广告,用来在飞机上向商务人士投放:

产品: 8848是国内首款钛金手机,由著名企业家王石代言。选择珍贵钛金属和高端牛皮,内置硬件级安全系统,是一款不仅奢华还很实用的手机,向成功者致敬。
忙碌的商务人士,您是否曾经梦想拥有一款既贵气又实用的手机?8848钛金手机,让您在商务场合中展现自己的尊贵身份,为您提供硬件级安全保护,让您在忙碌的生活中安心无忧。由著名企业家王石代言,不仅奢华,更是一款实用的手机,让您在成功的路上更加轻松!

如果要让模型撰写长文,就需要在模板中对任务需求给出更为详细的描述或者给出更多的示例,这样才能让输出更符合我们的预期。

写作前我们还可以让模型帮我们先列一个提纲。例如要撰写一个关于牛顿生平的报告:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="为关于牛顿及其科学贡献的报告写一份中文提纲:",
temperature=0.2,
max_tokens=256,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
一、牛顿的生平

1. 出生背景
2. 教育经历
3. 主要成就

二、牛顿的科学贡献

1. 力学
2. 光学
3. 天体力学
4. 数学

三、牛顿的影响

1. 对科学的影响
2. 对文化的影响
3. 对社会的影响

信息抽取

从文本中抽取信息也是常见的操作,例如抽取关键词,此时 temperature 参数就非常重要,较低的值会使得模型尽可能从原文中摘取关键词,而较高的值会使得模型生成一些不在原文中但是相关的词:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="从下面的文本中抽取关键词:\n\n木星是距离太阳第五近的行星,也是太阳系中体积最大的行星,目前已知有92颗卫星。天文学家很早就发现了这颗行星,罗马人以他们的神称这颗行星为朱庇特。古代中国则称木星为岁星、太岁,取其绕行天球一周约为12年,与地支相同之故,且产生了岁星纪年法。到西汉时期,《史记‧天官书》作者天文学家司马迁从实际观测发现岁星呈青色,与"五行"学说联系在一起,正式把它命名为木星。木星是颗巨行星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍。太阳系的行星中,木星和土星是气体巨星(天王星和海王星是冰巨星)。",
temperature=0.5,
max_tokens=60,
top_p=1,
frequency_penalty=0.8,
presence_penalty=0
)

1
2
3
4
5
6
从下面的文本中抽取关键词:

木星是距离太阳第五近的行星,也是太阳系中体积最大的行星,目前已知有92颗卫星。天文学家很早就发现了这颗行星,罗马人以他们的神称这颗行星为朱庇特。古代中国则称木星为岁星、太岁,取其绕行天球一周约为12年,与地支相同之故,且产生了岁星纪年法。到西汉时期,《史记‧天官书》作者天文学家司马迁从实际观测发现岁星呈青色,与"五行"学说联系在一起,正式把它命名为木星。木星是颗巨行星,质量是太阳的千分之一,但却是太阳系其他行星质量总和的2.5倍。太阳系的行星中,木星和土星是气体巨星(天王星和海王星是冰巨星)。

关键词:木星、太阳系、卫星、罗马人、古代中国、岁星纪年法、

还可以指定要抽取的信息类型,比如从邮件中抽取出姓名和地址:

1
2
3
4
5
6
7
8
9
10
response = openai.Completion.create(
model="text-davinci-003",
prompt="从下面的邮件中抽取出姓名和地址:\n\n作者,您好!\n\n经过编辑以及三位专业评审的讨论,我们已经决定录用您的稿件。\n\n我们还需要您的授权确认书,请按照我们网站上的要求填写,并尽快邮寄到以下地址:苏州市姑苏区苏州大学出版社\n\n此致,\n\n敬礼\n\n小明\n\n姓名:",
temperature=0,
max_tokens=256,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
从下面的邮件中抽取出姓名和地址:

作者,您好!

经过编辑以及三位专业评审的讨论,我们已经决定录用您的稿件。\n\n我们还需要您的授权确认书,请按照我们网站上的要求填写,并尽快邮寄到以下地址:苏州市姑苏区苏州大学出版社

此致,

敬礼

小明

姓名:

小明
地址: 苏州市姑苏区苏州大学出版社

插入文本

除了前缀模板以外,模型还支持通过后缀 (suffix) 模板在文本中插入文本。这可以用于编写长文本、段落之间的过渡、遵循故事梗概或构建结尾等场景,也可以用于编写代码(比如插入函数)。

下面我们让模型生成下面模板中空缺的内容,需要插入文本的位置用 [insert] 表示:

1
2
我在苏州大学上了大学。 获得学位后,我决定做出改变。[insert]现在我受够了海边潮湿的天气。

1
2
3
4
5
6
7
8
9
10
11
12
response = openai.Completion.create(
model="text-davinci-003",
prompt="我在苏州大学上了大学。 获得学位后,我决定做出改变。",
suffix="现在我受够了海边潮湿的天气。\n\n",
temperature=0.3,
max_tokens=512,
top_p=1,
frequency_penalty=0.2,
presence_penalty=0.2,
stop=["\n\n"]
)

1
2
我在苏州大学上了大学。 获得学位后,我决定做出改变。 我决定搬到海边,享受更好的天气。 在海边,我可以放松自己,享受美丽的风景。 我也可以去海边游泳,放松身心。 但是现在我受够了海边潮湿的天气。

可以看到模型根据后缀提示”现在我受够了海边潮湿的天气。”来生成中间缺失的文本。如果我们为模型提供更多的上下文,就可以对模型添加更多的约束,从而使得生成内容更加可控。

官方给出了一些使用建议:首先,建议设置 max_tokens > 256,当插入长文本时模型一般会表现更好,如果该值设置过小,可能无法生成完整的中间内容;然后,选择 finish_reason == “stop”的结果,这表明模型达到了自然停止点或用户提供的停止序列,因此生成的文本是完整的;最后,建议重采样 3-5 次,在一些困难的情况下,模型可能难以连接到后缀,经验表明重采样 3-5 次(或 best_of 设为 3 或 5)并选择其中带有 finish_reason == “stop” 的样本是一种有效的方法。

如果所有返回的样本都是 finish_reason == “length”,那么很可能是 max_tokens 设置过小,模型无法用这么短的文本来流畅连接前缀和后缀。

我们还可以通过提供示例来提供线索,这样就能更好地引导模型生成。例如列出热巧克力的制作清单:

1
2
3
4
如何通过4个步骤制作一杯美味的热巧克力:
1. [insert]
5. 享受热巧克力

1
2
3
4
5
6
7
8
9
10
11
12
13
response = openai.Completion.create(
model="text-davinci-003",
prompt="如何通过4个步骤制作一杯美味的热巧克力:\n1\. ",
suffix="\n5\. 享受热巧克力\n\n",
temperature=0.2,
max_tokens=512,
top_p=1,
best_of=3,
frequency_penalty=0.2,
presence_penalty=0.2,
stop=["\n\n"]
)

1
2
3
4
5
6
7
如何通过4个步骤制作一杯美味的热巧克力:
1. 准备所需的材料:牛奶、可可粉、糖和香草。
2. 将牛奶加热,直到滚烫。
3. 将可可粉、糖和香草混合在一起,然后将混合物放入加热的牛奶中,搅拌均匀。
4. 继续加热,直到混合物开始沸腾。
5. 享受热巧克力

我们还可以通过给出更多示例来让模型理解生成文本需要遵循的模式:

1
2
3
4
5
1. 狗是忠诚的动物。
2. 狮子是凶猛的动物。
3. 海豚[insert]
4. 马是庄严的动物。

1
2
3
4
5
6
7
8
9
10
11
12
13
response = openai.Completion.create(
model="text-davinci-003",
prompt="1. 狗是忠诚的动物。\n2\. 狮子是凶猛的动物。\n3\. 海豚",
suffix="\n4\. 马是庄严的动物。\n\n",
temperature=0.2,
max_tokens=512,
top_p=1,
best_of=3,
frequency_penalty=0.2,
presence_penalty=0.2,
stop=["\n\n"]
)

1
2
3
4
5
1. 狗是忠诚的动物。
2. 狮子是凶猛的动物。
3. 海豚是温顺的动物。
4. 马是庄严的动物。

对话补全接口

与文本补全类似,对话模型会基于已经进行的一系列对话内容来生成后续消息。尽管对话补全接口是针对多轮对话场景设计的,但是它也可以用于单轮对话(退化为文本补全接口)。

例如,我们继续进行下面的多轮对话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import openai

openai.api_key = "sk-xxx"

response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一个专业的语文老师。"},
{"role": "user", "content": "苏轼是哪个朝代的诗人?"},
{"role": "assistant", "content": "苏轼是宋朝的诗人。"},
{"role": "user", "content": "你知道他的代表作有哪些吗?"}
]
)
print(response['choices'][0]['message']['content'])

1
2
苏轼的代表作很多,其中比较著名的有《水调歌头》、《赤壁怀古》、《念奴娇-赤壁怀古》、《江城子-乙卯正月二十日夜记梦》、《定风波》、《东篱乐府》等。

messages 是该接口的核心参数,表示消息对象的列表,每个消息对象都具有角色 (role) 和消息正文 (content) 属性,其中角色只能为”system”、”user”或”assistant”。对话短则只有一条消息,长则可以包含数页内容。

通常情况下,对话首先由一个 system 消息设置对话背景,然后接着交替出现的 user 和 assistant 消息。

  • system 消息负责设置模型的表现,在上面的例子中,模型就被指定为”专业的语文老师”。大部分情况下,对话模型都需要通过引导才能使回复符合我们的预期,例如:
1
2
You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible. Knowledge cutoff: {knowledge_cutoff} Current date: {current_date}

  • user 消息负责引导模型行为,这可以由最终用户输入或者由开发者编写;

  • assistant 消息负责存储模型的回复。当然我们也可以手工编写作为提供给模型的示例。

目前 gpt-3.5-turbo-0301 模型无法保证每次都能遵循 system 消息的指令,因此重要的引导信息最好放置在 user 消息中,后续的模型会对此进行修正。

注意,因为模型无法记住之前的请求,因此在 messages 中需要包含所有已进行的对话消息作为历史,只有这样模型才能基于上下文来生成回复。如果对话总长度超过模型限制,就需要通过一些策略来缩短对话历史。

与文本补全接口类似,除了模型参数 model 和消息参数 messages 以外,还有一些其他参数也会对回复造成影响。下面是一些常用的参数:

  • max_tokens:生成文本的最大 token 数。输入的对话上下文加上 max_tokens 不能超过模型限制。
  • temperature:采样温度,值介于 0 到 2 之间。较高的值(例如 0.8)会使得输出更随机,较小的值(例如 0.2)则会使得输出更确定。该参数与 top_p 通常只建议修改其中一个。
  • top_p:核采样 (nucleus sampling),模型只考虑具有 top_p 概率质量的 token 的结果。该参数与 temperature 通常只建议修改其中一个。
  • stop:停止序列(最多 4 个),模型遇到就会停止生成。
  • presence_penalty:值介于 -2.0 到 2.0 之间。正值会根据是否已经出现在文本中来惩罚新生成的 token,从而鼓励模型生成新的内容,避免出现大段重复的文本。
  • frequency_penalty:值介于 -2.0 到 2.0 之间。正值会根据新生成 token 在文本中的频率对其进行惩罚,从而降低模型逐字重复同一行的可能性。

对话接口返回的数据格式如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
'id': 'chatcmpl-6p9XYPYSTTRi0xEviKjjilqrWU2Ve',
'object': 'chat.completion',
'created': 1677649420,
'model': 'gpt-3.5-turbo',
'usage': {'prompt_tokens': 56, 'completion_tokens': 31, 'total_tokens': 87},
'choices': [
{
'message': {
'role': 'assistant',
'content': 'The 2020 World Series was played in Arlington, Texas at the Globe Life Field, which was the new home stadium for the Texas Rangers.'},
'finish_reason': 'stop',
'index': 0
}
]
}

每一个回复都会包含一个 finish_reason 属性,描述生成结束的原因:

  • stop:输出了模型完整的回复;
  • length:由于 max_tokens 参数的限制或者模型长度限制导致返回了不完整的模型输出;
  • content_filter:由于我们设置的内容过滤器导致回复被删除;
  • null:接口还未回复。

如果模型的回复不符合我们的预期,可以从下面三个角度考虑修改模板:

  1. 使得指令描述更明确直白;
  2. 指定模型回复的格式(例如列表、表格等),并且给出一些示例;
  3. 让模型通过逐步思考或者讨论利弊来获取最终的答案。

更多的模板设计技巧,可以阅读《OpenAI Cookbook》提高可靠性章节

由于 gpt-3.5-turbo 的表现与 text-davinci-003 类似,但是价格只有其十分之一,因此在大部分情况下,我们都应该使用对话补全接口来完成任务。单轮对话实际上就是文本补全,例如对于翻译任务:

1
2
Translate the following English text to French: "{text}"

我们只需要增加一条 system 消息以设定模型扮演的角色:

1
2
3
4
5
[
{"role": "system", "content": "You are a helpful assistant that translates English to French."},
{"role": "user", "content": 'Translate the following English text to French: "{text}"'}
]

当然也可以只使用 user 消息:

1
2
3
4
[
{"role": "user", "content": 'Translate the following English text to French: "{text}"'}
]

嵌入表示接口

使用 OpenAI API 获得的文本嵌入 (embeddings) 更关注文本之间的关联程度,因此可以用于:

  • 搜索:答案需要根据与请求之间的关联程度进行排序;
  • 聚类:文本需要根据语义相似度进行分组;
  • 推荐:只需要推荐那些与文本相关的内容;
  • 异常检测:需要识别出那些相关性很小的异常值;
  • 多样性度量:分析相似性分布;
  • 分类:文本需要被分类到最相似的标签。

嵌入向量之间的距离可以用于度量文本之间的关联程度,距离最小表示越相关。因为 OpenAI 接口返回的嵌入已经被规范化为长度 1,因此只需要使用向量点积就可以直接获得余弦相似度,而且与欧几里德距离 (Euclidean distance) 的排序相同。

要获得嵌入表示非常简单,只需要将文本以及嵌入模型 ID(例如 text-embedding-ada-002)传给接口:

1
2
3
4
5
6
response = openai.Embedding.create(
input="Your text string goes here",
model="text-embedding-ada-002"
)
embeddings = response['data'][0]['embedding']

回复的格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"data": [
{
"embedding": [
-0.006929283495992422,
-0.005336422007530928,
...
-4.547132266452536e-05,
-0.024047505110502243
],
"index": 0,
"object": "embedding"
}
],
"model": "text-embedding-ada-002",
"object": "list",
"usage": {
"prompt_tokens": 5,
"total_tokens": 5
}
}

嵌入模型

OpenAI 目前提供了 1 个二代嵌入模型(模型 ID 用 -002 表示)和 16 个一代模型,官方建议在所有场景下都使用 text-embedding-ada-002 模型,它性能更好、更便宜且易于使用。

模型 分词器 最大输入TOKEN 训练数据
V2 cl100k_base 8191 2021.09
V1 GPT-2/GPT-3 2046 2020.08

价格每 1000 tokens $0.0004,或者每刀大约 3,000 页(假设每页大约 800 token)。

模型 每刀大约页数 BEIR搜索评估性能
text-embedding-ada-002 3000 53.9
-davinci--001 6 52.8
-curie--001 60 50.9
-babbage--001 240 50.4
-ada--001 300 49.0

二代模型:

模型 分词器 最大输入TOKEN 输出维度
text-embedding-ada-002 cl100k_base 8191 1536

总结

OpenAI 提供的语言模型可以理解并遵循人类指令来进行生成,使得我们借助这些模型就能处理几乎任何类型的文字类工作。本文只给出了一些常见任务的示例,更多的功能还等待读者自己去发掘。

其他GPT教程

ChatGPT开发视频教程合集:https://xueshu.fun/?s=gpt

  • ChatGPT Flutter 应用程序开发
  • ChatGPT 4 和 Midjourney提示工程
  • OpenAI Python API 训练营
  • 使用Django创建ChatGPT AI 机器人
  • ChatGPT Javascript开发教程

参考

[1] OpenAI 官方文档
[2] OpenAI Cookbook
[3] how-to-use-chatgpt