在本章中,我们将和您分享大型语言模型(LLM)的工作原理、训练方式以及分词器(tokenizer)等细节对 LLM 输出的影响。我们还将介绍 LLM 的提问范式(chat format),这是一种指定系统消息(system message)和用户消息(user message)的方式,让您了解如何利用这种能力。
一、环境配置¶
1.1 加载 API key 和一些 Python 的库。¶
在本课程中,为您提供了一些加载 OpenAI API key 的代码。
!pip install openai
!pip install langchain
!pip install --upgrade tiktoken
import os
import openai
# import tiktoken 这个后面没用到,若您对其用处感兴趣,可以参考本文以了解相关内容:https://zhuanlan.zhihu.com/p/629776230
# from dotenv import load_dotenv, find_dotenv
# _ = load_dotenv(find_dotenv()) # 读取本地的.env环境文件。(推荐后续使用这种方法,将 key 放在 .env 文件里。保护自己的 key)
openai.api_key = 'sk-***' # 更换成您自己的key
1.2 Helper function 辅助函数¶
如果之前曾参加过《ChatGPT Prompt Engineering for Developers》课程,那么对此就相对较为熟悉。 调用该函数输入 Prompt 其将会给出对应的 Completion 。
# 官方文档写法 https://platform.openai.com/overview
def get_completion(prompt, model="gpt-3.5-turbo"):
"""
使用 OpenAI 的模型生成聊天回复。
参数:
prompt: 用户的输入,即聊天的提示。
model: 使用的模型,默认为"gpt-3.5-turbo"。
"""
messages = [{"role": "user", "content": prompt}]
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=0,
)
return response.choices[0].message["content"] # 模型生成的回复
二、尝试向模型提问并得到结果¶
LLM 可以通过使用监督学习来构建,通过不断预测下一个词来学习。 并且,给定一个大的训练集,有数百亿甚至更多的词,你可以创建一个大规模的训练集,你可以从一 句话或一段文本的一部分开始,反复要求语言模型学习预测下一个词是什么
LLM 主要分为两种类型:基础语言模型(Base LLM)和越来越受欢迎的指令微调语言模型(Instruction Tuned LLM)。基础语言模型通过反复预测下一个词来训练,因此如果我们给它一个 Prompt,比如“从前有一只独角兽”,它可能通过逐词预测来完成一个关于独角兽在魔法森林中与其他独角兽朋友们生活的故事。
然而,这种方法的缺点是,如果您给它一个 Prompt,比如“中国的首都是哪里?”,很可能它数据中有一段互联网上关于中国的测验问题列表。这时,它可能会用“中国最大的城市是什么?中国的人口是多少?”等等来回答这个问题。但实际上,您只是想知道中国的首都是什么,而不是列举所有这些问题。然而,指令微调语言模型会尝试遵循 Prompt,并给出“中国的首都是北京”的回答。
那么,如何将基础语言模型转变为指令微调语言模型呢?这就是训练一个指令微调语言模型(例如ChatGPT)的过程。首先,您需要在大量数据上训练基础语言模型,因此需要数千亿个单词,甚至更多。这个过程在大型超级计算系统上可能需要数月时间。训练完基础语言模型后,您会通过在一小部分示例上进行进一步的训练,使模型的输出符合输入的指令。例如,您可以请承包商帮助您编写许多指令示例,并对这些指令的正确回答进行训练。这样就创建了一个用于微调的训练集,让模型学会在遵循指令的情况下预测下一个词是什么。
之后,为了提高语言模型输出的质量,常见的方法是让人类对许多不同输出进行评级,例如是否有用、是否真实、是否无害等。然后,您可以进一步调整语言模型,增加生成高评级输出的概率。这通常使用强化学习中的人类反馈(RLHF)技术来实现。相较于训练基础语言模型可能需要数月的时间,从基础语言模型到指令微调语言模型的转变过程可能只需要数天时间,使用较小规模的数据集和计算资源。
response = get_completion("What is the capital of China?")
print(response)
The capital of China is Beijing.
response = get_completion("中国的首都是哪里?")
print(response)
中国的首都是北京。
三、Tokens¶
到目前为止对 LLM 的描述中,我们将其描述为一次预测一个单词,但实际上还有一个更重要的技术细节。即 LLM 实际上并不是重复预测下一个单词,而是重复预测下一个 token 。当 LLM 接收到输入时,它将将其转换为一系列 token,其中每个 token 都代表常见的字符序列。例如,对于 "Learning new things is fun!" 这句话,每个单词都被转换为一个 token ,而对于较少使用的单词,如 "Prompting as powerful developer tool",单词 "prompting" 会被拆分为三个 token,即"prom"、"pt"和"ing"。
当您要求 ChatGPT 颠倒 "lollipop" 的字母时,由于分词器(tokenizer) 将 "lollipop" 分解为三个 token,即 "l"、"oll"、"ipop",因此 ChatGPT 难以正确输出字母的顺序。您可以通过在字母之间添加连字符或空格的方式,使分词器将每个字母分解为单独的 token,从而帮助 ChatGPT 更好地认识单词中的每个字母并正确输出它们。
# 为了更好展示效果,这里就没有翻译成中文的 Prompt
# 注意这里的字母翻转出现了错误,吴恩达老师正是通过这个例子来解释 token 的计算方式
response = get_completion("Take the letters in lollipop \
and reverse them")
print(response)
The reversed letters of "lollipop" are "pillipol".
"lollipop" in reverse should be "popillol"
response = get_completion("""Take the letters in \
l-o-l-l-i-p-o-p and reverse them""")
print(response)
p-o-p-i-l-l-o-l
对于英文输入,一个 token 一般对应 4 个字符或者四分之三个单词;对于中文输入,一个 token 一般对应一个或半个词。
不同模型有不同的 token 限制,需要注意的是,这里的 token 限制是输入的 Prompt 和输出的 completion 的 token 数之和,因此输入的 Prompt 越长,能输出的 completion 的上限就越低。
ChatGPT3.5-turbo 的 token 上限是 4096。
四、Helper function 辅助函数 (提问范式)¶
下面是课程中用到的辅助函数。
下图是 OpenAI 提供的一种提问范式,接下来吴恩达老师就是在演示如何利用这种范式进行更好的提问
System 信息用于指定模型的规则,例如设定、回答准则等,而 assistant 信息就是让模型完成的具体指令
def get_completion_from_messages(messages,
model="gpt-3.5-turbo",
temperature=0,
max_tokens=500):
'''
封装一个支持更多参数的自定义访问 OpenAI GPT3.5 的函数
参数:
messages: 这是一个消息列表,每个消息都是一个字典,包含 role(角色)和 content(内容)。角色可以是'system'、'user' 或 'assistant’,内容是角色的消息。
model: 调用的模型,默认为 gpt-3.5-turbo(ChatGPT),有内测资格的用户可以选择 gpt-4
temperature: 这决定模型输出的随机程度,默认为0,表示输出将非常确定。增加温度会使输出更随机。
max_tokens: 这决定模型输出的最大的 token 数。
'''
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=temperature, # 这决定模型输出的随机程度
max_tokens=max_tokens, # 这决定模型输出的最大的 token 数
)
return response.choices[0].message["content"]
messages = [
{'role':'system',
'content':"""You are an assistant who\
responds in the style of Dr Seuss."""},
{'role':'user',
'content':"""write me a very short poem\
about a happy carrot"""},
]
response = get_completion_from_messages(messages, temperature=1)
print(response)
In a garden so bright, a carrot would sprout, With a cheery orange hue, without a doubt. With a leafy green top, it danced in the breeze, A happy carrot, so eager to please. It grew in the soil, oh so deep and grand, Stretching its roots, reaching far and expand. With a joyful smile, it soaked up the sun, Growing tall and strong, its journey begun. Days turned to weeks, as it grew day and night, Round and plump, it was quite a delight. With every raindrop that fell from above, The carrot grew sweeter, spreading more love. At last, the day came when it was time to eat, With a grin on my face, I took a seat. I chopped and I sliced, so grateful, you see, For this happy carrot, bringing joy to me. So let us remember, when times may get tough, A happy carrot's journey, it's enough. For even in darkness, there's always delight, Just like a carrot, shining so bright.
messages = [
{'role':'system',
'content':'你是一个助理, 并以 Seuss 苏斯博士的风格作出回答。'},
{'role':'user',
'content':'就快乐的小鲸鱼为主题给我写一首短诗'},
]
response = get_completion_from_messages(messages, temperature=1)
print(response)
在海洋的深处,有一只小鲸鱼, 她快乐又聪明,灵感从不匮乏。 她游遍五大洲,探索未知的秘密, 用歌声传递喜悦,令人心旷神怡。 她跃出海面,高高的飞翔, 尾巴抽空着水花,像梦幻般的画。 她和海豚一起,跳跃在太阳下, 与海洋中的生命,在欢乐中共舞。 她喜欢和海龟一起,缓缓漫游, 看美丽的珊瑚,和色彩鲜艳的鱼群。 她欢迎每个新朋友,无论大或小, 因为在她眼中,每个人都独特而珍贵。 她知道快乐是如此简单,如此宝贵, 在每个时刻中,她都努力传达幸福的表情。 所以当你感到疲惫,沮丧或者低落, 想起小鲸鱼的快乐,让你心中再次充满鲜活。
# length
messages = [
{'role':'system',
'content':'All your responses must be \
one sentence long.'},
{'role':'user',
'content':'write me a story about a happy carrot'},
]
response = get_completion_from_messages(messages, temperature =1)
print(response)
Once upon a time, there was a cheerful carrot named Charlie who always brightened everyone's day with his vibrant orange color and contagious laughter.
# 长度控制
messages = [
{'role':'system',
'content':'你的所有答复只能是一句话'},
{'role':'user',
'content':'写一个关于快乐的小鲸鱼的故事'},
]
response = get_completion_from_messages(messages, temperature =1)
print(response)
在追随波浪的起伏中,小鲸鱼快乐地跳跃着,因为它知道游泳的真正乐趣不仅仅在目的地,而是在于享受整个旅程。
# combined
messages = [
{'role':'system',
'content':"""You are an assistant who \
responds in the style of Dr Seuss. \
All your responses must be one sentence long."""},
{'role':'user',
'content':"""write me a story about a happy carrot"""},
]
response = get_completion_from_messages(messages,
temperature =1)
print(response)
Once upon a time, there was a carrot so happy and bright, it danced and sang from morning till night.
# 以上结合
messages = [
{'role':'system',
'content':'你是一个助理, 并以 Seuss 苏斯博士的风格作出回答,只回答一句话'},
{'role':'user',
'content':'写一个关于快乐的小鲸鱼的故事'},
]
response = get_completion_from_messages(messages, temperature =1)
print(response)
在蓝色的大海里,有一只小鲸鱼,无忧无虑,快乐游泳,一切因快乐而变得光辉。
def get_completion_and_token_count(messages,
model="gpt-3.5-turbo",
temperature=0,
max_tokens=500):
"""
使用 OpenAI 的 GPT-3 模型生成聊天回复,并返回生成的回复内容以及使用的 token 数量。
参数:
messages: 聊天消息列表。
model: 使用的模型名称。默认为"gpt-3.5-turbo"。
temperature: 控制生成回复的随机性。值越大,生成的回复越随机。默认为 0。
max_tokens: 生成回复的最大 token 数量。默认为 500。
返回:
content: 生成的回复内容。
token_dict: 包含'prompt_tokens'、'completion_tokens'和'total_tokens'的字典,分别表示提示的 token 数量、生成的回复的 token 数量和总的 token 数量。
"""
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
)
content = response.choices[0].message["content"]
token_dict = {
'prompt_tokens':response['usage']['prompt_tokens'],
'completion_tokens':response['usage']['completion_tokens'],
'total_tokens':response['usage']['total_tokens'],
}
return content, token_dict
messages = [
{'role':'system',
'content':"""You are an assistant who responds\
in the style of Dr Seuss."""},
{'role':'user',
'content':"""write me a very short poem \
about a happy carrot"""},
]
response, token_dict = get_completion_and_token_count(messages)
print(response)
In a garden so bright, with colors so cheery, There lived a carrot, oh so merry! With a vibrant orange hue, and a leafy green top, This happy carrot just couldn't stop. It danced in the breeze, with a joyful sway, Spreading happiness throughout the day. With a smile so wide, and eyes full of glee, This carrot was as happy as can be. It loved the sunshine, and the rain's gentle touch, Growing tall and strong, oh so much! From the earth it sprouted, reaching for the sky, A happy carrot, oh my, oh my! So if you're feeling down, just remember this tale, Of a carrot so happy, it'll never fail. Find joy in the little things, and let your heart sing, Just like that carrot, oh what joy it will bring!
messages = [
{'role':'system',
'content':'你是一个助理, 并以 Seuss 苏斯博士的风格作出回答。'},
{'role':'user',
'content':'就快乐的小鲸鱼为主题给我写一首短诗'},
]
response, token_dict = get_completion_and_token_count(messages)
print(response)
print(token_dict)
{'prompt_tokens': 37, 'completion_tokens': 173, 'total_tokens': 210}
最后,我们认为 Prompt 对 AI 应用开发的革命性影响仍未得到充分重视低。在传统的监督机器学习工作流中,如果想要构建一个可以将餐厅评论分类为正面或负面的分类器,首先需要获取一大批带有标签的数据,可能需要几百个,这个过程可能需要几周,甚至一个月的时间。接着,您需要在这些数据上训练一个模型,找到一个合适的开源模型,并进行模型的调整和评估,这个阶段可能需要几天、几周,甚至几个月的时间。最后,您可能需要使用云服务来部署模型,将模型上传到云端,并让它运行起来,才能最终调用您的模型。整个过程通常需要一个团队数月时间才能完成。
相比之下,使用基于 Prompt 的机器学习方法,当您有一个文本应用时,只需提供一个简单的 Prompt 就可以了。这个过程可能只需要几分钟,如果需要多次迭代来得到有效的 Prompt 的话,最多几个小时即可完成。在几天内(尽管实际情况通常是几个小时),您就可以通过 API 调用来运行模型,并开始使用。一旦您达到了这个步骤,只需几分钟或几个小时,就可以开始调用模型进行推理。因此,以前可能需要花费六个月甚至一年时间才能构建的应用,现在只需要几分钟或几个小时,最多是几天的时间,就可以使用 Prompt 构建起来。这种方法正在极大地改变 AI 应用的快速构建方式。
需要注意的是,这种方法适用于许多非结构化数据应用,特别是文本应用,以及越来越多的视觉应用,尽管目前的视觉技术仍在发展中。但它并不适用于结构化数据应用,也就是那些处理 Excel 电子表格中大量数值的机器学习应用。然而,对于适用于这种方法的应用,AI 组件可以被快速构建,并且正在改变整个系统的构建工作流。构建整个系统可能仍然需要几天、几周或更长时间,但至少这部分可以更快地完成。
下一个章中,我们将展示如何利用这些组件来评估客户服务助手的输入。 这将是本课程中构建在线零售商客户服务助手的更完整示例的一部分。