如何fine-tuning大模型

Fine-tuning(微调)能带来什么好处?主要有以下4点:
1. 更高质量的结果;相对于提示prompt来说;
2. 能力更强;允许训练更多的示例,远远超出了prompt的能力;
3. 减少令牌(token)使用,因为可以使用更短的提示;
4. 请求延迟的降低。

以下我们在Jupyter Notebook环境下,以gpt-3.5-turbo的模型为例,做下实验。

GPT模型已经在大量文本上进行了预训练。为了有效地使用这些模型,我们通常在提示(prompt)中包括指令及多个示例。给模型一些例子来展示如何执行任务通常被称为”少样本学习”(few-shot learning)。

通过提供比prompt更多例子的训练,微调可以改进少样本学习(few-shot learning),以达到更强大的能力。一旦模型经过微调,您就不需要在提示中提供太多示例。这可以节省成本并实现更低的延迟请求。

微调的步骤:

  1. 准备并上传训练数据。
  2. 训练一个新的微调模型。
  3. 使用您的微调模型。

一、场景介绍

我们要做一个线上回复养宠问题的聊天机器人。养宠问题分为二类:一类是通用性养宠的问题;一类是轻问诊。关于这两类问题,大模型的回复十分笼统,且输出不太规范。因此,我们基于已开展的宠物咨询和诊疗业务,通过兽医校对,整理了很多问答对。寄希望于对大模型进行微调,以给到用户更具体的回复。同时,我们将提示工程里的输出和风格要求也在训练数据中,以节省每次调用成本。

import openai
api_key="your key"

二、数据转换

openai的训练数据要以JSONL的格式上传。


{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}

每一个Message,由三个role组成:system user、assistant。system提供高级指令,告诉大模型要扮演什么角色;user提出查询或提示,如:猫猫太肥了怎么办?;assistant是大模型的回复。请见下图分解。

下面的代码是用于做训练数据转换的。原数据文件:training_data.JSON


[
{
"Questions": "我家猫太胖了怎么减肥呢?",
"Replies": "原因分析: 摸着肋部,感觉以肉为主,但稍微用力也可以感受到肋骨,那就理想体重。\n标准放宽一点的话,肋骨部位上的肉再多点,少少接近超重也ok,但要严重超标的体重得好好控制一下,而且要立刻行动.\n推荐方案:足够的热量差:高蛋白 蛋白质对肌肉组织最关键,而肌肉又是基础新陈代谢的一大源头;再加上蛋白质热量不大,饱腹感更强,猫对蛋白质的需求还高得“变态”,比人高3-6倍。要减肥,就给猫吃蛋白质足够高的主食。 定时,定量 猫习惯昼伏夜出,进食时间集中清晨、黄昏、后半夜,我们可以将食物分成4份:\n早上:你出门前给猫吃的一份食\n晚上:你回家后给猫吃的那餐\n睡前:你睡觉前给猫再吃一餐\n零食:主食里留一部分出来,猫讨饭时糊弄猫 饱腹感 肉类的胃排空速度似乎慢于猫粮,纤维素也可以增加一些饱腹感,如果猫实在忍不住饿,把食物中一部分改为主食冻干/罐头或者干脆:给化毛片。化毛片富含纤维,热量也不算大,这里其实就是骗饱腹感用的。"
},
{
"Questions": "我家猫吃塑料,毛线是怎么回事?",
"Replies": "原因分析: 猫“异食癖”的定义有待商榷,如果你的猫不会把异物吃下肚,不算异食癖。小猫吃猫砂可能是气味影响或者食物认知未形成导致的。\n为了获取注意力、单纯无聊、环境压力、祈求食物或者部分猫的“基因”才“异食癖”的主要原因。\n丰富食物类型,增加食物喂食频率,与猫高强度互动都是改善异食癖的潜在有效办法。\n很遗憾,最有用的还是把杂物收起来!!!\n.\n推荐方案:控制压力来源:\n最近有陌生人?动物?还是处于新环境?亦或者有噪音?如果有条件,给猫一个相对安静的环境和躲藏处,与近期的潜在干扰因素隔离开。看情况可以放零食和加餐,可能肚子饿、嘴巴馋也算压力。 避免接触异食对象:让猫咪无法接触异食对象,同时留意猫咪是否有营养不良、微量元素缺乏或其他疾病问题。\n一般来说,危险度排名如下:百合科植物>>尼龙线、鞋带等线状异物>>>猫砂>>小块塑料片。百合科植物很容易造成猫肾脏损伤,而强韧的线状异物有几率造成肠叠套、撕裂伤等,是需要手术处理的。"
}
]

转换成以下格式,并存于数据文件:training_dataR1.JSONL


{"messages": [{"role": "system", "content": "你是一位宠物喂养和健康专家,你回答用户提出的问题。回答方式分为两个自然段,分别阐述原因分析和推荐方案。尽量采用我给你的训练数据."}, {"role": "user", "content": "我家猫太胖了怎么减肥呢?"}, {"role": "assistant", "content": "原因分析: 摸着肋部,感觉以肉为主,但稍微用力也可以感受到肋骨,那就理想体重。\n标准放宽一点的话,肋骨部位上的肉再多点,少少接近超重也ok,但要严重超标的体重得好好控制一下,而且要立刻行动.\n推荐方案:足够的热量差:高蛋白 蛋白质对肌肉组织最关键,而肌肉又是基础新陈代谢的一大源头;再加上蛋白质热量不大,饱腹感更强,猫对蛋白质的需求还高得“变态”,比人高3-6倍。要减肥,就给猫吃蛋白质足够高的主食。 定时,定量 猫习惯昼伏夜出,进食时间集中清晨、黄昏、后半夜,我们可以将食物分成4份:\n早上:你出门前给猫吃的一份食\n晚上:你回家后给猫吃的那餐\n睡前:你睡觉前给猫再吃一餐\n零食:主食里留一部分出来,猫讨饭时糊弄猫 饱腹感 肉类的胃排空速度似乎慢于猫粮,纤维素也可以增加一些饱腹感,如果猫实在忍不住饿,把食物中一部分改为主食冻干/罐头或者干脆:给化毛片。化毛片富含纤维,热量也不算大,这里其实就是骗饱腹感用的。"}]}
{"messages": [{"role": "system", "content": "你是一位宠物喂养和健康专家,你回答用户提出的问题。回答方式分为两个自然段,分别阐述原因分析和推荐方案。尽量采用我给你的训练数据."}, {"role": "user", "content": "我家猫吃塑料,毛线是怎么回事?"}, {"role": "assistant", "content": "原因分析: 猫“异食癖”的定义有待商榷,如果你的猫不会把异物吃下肚,不算异食癖。小猫吃猫砂可能是气味影响或者食物认知未形成导致的。\n为了获取注意力、单纯无聊、环境压力、祈求食物或者部分猫的“基因”才“异食癖”的主要原因。\n丰富食物类型,增加食物喂食频率,与猫高强度互动都是改善异食癖的潜在有效办法。\n很遗憾,最有用的还是把杂物收起来!!!\n.\n推荐方案:控制压力来源:\n最近有陌生人?动物?还是处于新环境?亦或者有噪音?如果有条件,给猫一个相对安静的环境和躲藏处,与近期的潜在干扰因素隔离开。看情况可以放零食和加餐,可能肚子饿、嘴巴馋也算压力。 避免接触异食对象:让猫咪无法接触异食对象,同时留意猫咪是否有营养不良、微量元素缺乏或其他疾病问题。\n一般来说,危险度排名如下:百合科植物>>尼龙线、鞋带等线状异物>>>猫砂>>小块塑料片。百合科植物很容易造成猫肾脏损伤,而强韧的线状异物有几率造成肠叠套、撕裂伤等,是需要手术处理的。"}]}

转换的代码如下:

# 引入相应的包
import json
import tiktoken # for token counting
import numpy as np
from collections import defaultdict
# 读原数据文件 Read the original data from 'training_data.JSON'
with open('training_data.JSON', 'r', encoding='utf-8') as file:
    original_data = json.load(file)

# 转化成目标格式 Convert the original data to the new format and save it
converted_data = []
for item in original_data:
    system_message = {
        "role": "system",
        "content": "你是一位宠物喂养和健康专家,你回答用户提出的问题。回答方式分为两个自然段,分别阐述原因分析和推荐方案。尽量采用我给你的训练数据."
    }
    user_message = {
        "role": "user",
        "content": item["Questions"]
    }
    assistant_message = {
        "role": "assistant",
        "content": item["Replies"]
    }
    messages = [system_message, user_message, assistant_message]
    converted_data.append({"messages": messages})

# 保存成目标文件
with open('training_dataR1.JSONL', 'w', encoding='utf-8') as output_file:
    for data in converted_data:
        json.dump(data, output_file, ensure_ascii=False)
        output_file.write('\n')

在当前路径,就已经有training_dataR1.JSONL文件。
我们来验证一下该文件,是否有59个训练数据,并显示第1个。

data_path = "training_dataR1.JSONL"

# Load the dataset
with open(data_path, 'r', encoding='utf-8') as f:
    dataset = [json.loads(line) for line in f]

# Initial dataset stats
print("Num examples:", len(dataset))
print("First example:")
for message in dataset[0]["messages"]:
    print(message)
Num examples: 59
First example:
{'role': 'system', 'content': '你是一位宠物喂养和健康专家,你回答用户提出的问题。回答方式分为两个自然段,分别阐述原因分析和推荐方案。尽量采用我给你的训练数据.'}
{'role': 'user', 'content': '我家猫太胖了怎么减肥呢?'}
{'role': 'assistant', 'content': '原因分析: 摸着肋部,感觉以肉为主,但稍微用力也可以感受到肋骨,那就理想体重。\n标准放宽一点的话,肋骨部位上的肉再多点,少少接近超重也ok,但要严重超标的体重得好好控制一下,而且要立刻行动.\\n推荐方案:足够的热量差:高蛋白 蛋白质对肌肉组织最关键,而肌肉又是基础新陈代谢的一大源头;再加上蛋白质热量不大,饱腹感更强,猫对蛋白质的需求还高得“变态”,比人高3-6倍。要减肥,就给猫吃蛋白质足够高的主食。 定时,定量 猫习惯昼伏夜出,进食时间集中清晨、黄昏、后半夜,我们可以将食物分成4份:\n早上:你出门前给猫吃的一份食\n晚上:你回家后给猫吃的那餐\n睡前:你睡觉前给猫再吃一餐\n零食:主食里留一部分出来,猫讨饭时糊弄猫 饱腹感 肉类的胃排空速度似乎慢于猫粮,纤维素也可以增加一些饱腹感,如果猫实在忍不住饿,把食物中一部分改为主食冻干/罐头或者干脆:给化毛片。化毛片富含纤维,热量也不算大,这里其实就是骗饱腹感用的。'}

我们对数据进行校验。如果有错误会显示出来。

# Format error checks
format_errors = defaultdict(int)

for ex in dataset:
    if not isinstance(ex, dict):
        format_errors["data_type"] += 1
        continue

    messages = ex.get("messages", None)
    if not messages:
        format_errors["missing_messages_list"] += 1
        continue

    for message in messages:
        if "role" not in message or "content" not in message:
            format_errors["message_missing_key"] += 1

        if any(k not in ("role", "content", "name", "function_call") for k in message):
            format_errors["message_unrecognized_key"] += 1

        if message.get("role", None) not in ("system", "user", "assistant", "function"):
            format_errors["unrecognized_role"] += 1

        content = message.get("content", None)
        function_call = message.get("function_call", None)

        if (not content and not function_call) or not isinstance(content, str):
            format_errors["missing_content"] += 1

    if not any(message.get("role", None) == "assistant" for message in messages):
        format_errors["example_missing_assistant_message"] += 1

if format_errors:
    print("Found errors:")
    for k, v in format_errors.items():
        print(f"{k}: {v}")
else:
    print("No errors found")
No errors found

我们来计算一下训练数据占了多少token。

encoding = tiktoken.get_encoding("cl100k_base")

# not exact!
# simplified from https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
def num_tokens_from_messages(messages, tokens_per_message=3, tokens_per_name=1):
    num_tokens = 0
    for message in messages:
        num_tokens += tokens_per_message
        for key, value in message.items():
            num_tokens += len(encoding.encode(value))
            if key == "name":
                num_tokens += tokens_per_name
    num_tokens += 3
    return num_tokens

def num_assistant_tokens_from_messages(messages):
    num_tokens = 0
    for message in messages:
        if message["role"] == "assistant":
            num_tokens += len(encoding.encode(message["content"]))
    return num_tokens

def print_distribution(values, name):
    print(f"\n#### Distribution of {name}:")
    print(f"min / max: {min(values)}, {max(values)}")
    print(f"mean / median: {np.mean(values)}, {np.median(values)}")
    print(f"p5 / p95: {np.quantile(values, 0.1)}, {np.quantile(values, 0.9)}")
# Warnings and tokens counts
n_missing_system = 0
n_missing_user = 0
n_messages = []
convo_lens = []
assistant_message_lens = []

for ex in dataset:
    messages = ex["messages"]
    if not any(message["role"] == "system" for message in messages):
        n_missing_system += 1
    if not any(message["role"] == "user" for message in messages):
        n_missing_user += 1
    n_messages.append(len(messages))
    convo_lens.append(num_tokens_from_messages(messages))
    assistant_message_lens.append(num_assistant_tokens_from_messages(messages))

print("Num examples missing system message:", n_missing_system)
print("Num examples missing user message:", n_missing_user)
print_distribution(n_messages, "num_messages_per_example")
print_distribution(convo_lens, "num_total_tokens_per_example")
print_distribution(assistant_message_lens, "num_assistant_tokens_per_example")
n_too_long = sum(l > 4096 for l in convo_lens)
print(f"\n{n_too_long} examples may be over the 4096 token limit, they will be truncated during fine-tuning")
Num examples missing system message: 0
Num examples missing user message: 0

#### Distribution of num_messages_per_example:
min / max: 3, 3
mean / median: 3.0, 3.0
p5 / p95: 3.0, 3.0

#### Distribution of num_total_tokens_per_example:
min / max: 172, 1600
mean / median: 599.271186440678, 510.0
p5 / p95: 236.8, 1024.6000000000001

#### Distribution of num_assistant_tokens_per_example:
min / max: 75, 1420
mean / median: 473.23728813559325, 389.0
p5 / p95: 134.8, 876.2000000000003

0 examples may be over the 4096 token limit, they will be truncated during fine-tuning

基于上述的训练数据和token,大概要花多少费用?

MAX_TOKENS_PER_EXAMPLE = 4096

TARGET_EPOCHS = 3
MIN_TARGET_EXAMPLES = 100
MAX_TARGET_EXAMPLES = 25000
MIN_DEFAULT_EPOCHS = 1
MAX_DEFAULT_EPOCHS = 25

n_epochs = TARGET_EPOCHS
n_train_examples = len(dataset)
if n_train_examples * TARGET_EPOCHS  MAX_TARGET_EXAMPLES:
    n_epochs = max(MIN_DEFAULT_EPOCHS, MAX_TARGET_EXAMPLES // n_train_examples)

n_billing_tokens_in_dataset = sum(min(MAX_TOKENS_PER_EXAMPLE, length) for length in convo_lens)
print(f"Dataset has ~{n_billing_tokens_in_dataset} tokens that will be charged for during training")
print(f"By default, you'll train for {n_epochs} epochs on this dataset")
print(f"By default, you'll be charged for ~{n_epochs * n_billing_tokens_in_dataset} tokens")
Dataset has ~35357 tokens that will be charged for during training
By default, you'll train for 3 epochs on this dataset
By default, you'll be charged for ~106071 tokens

三、训练数据文件上传

from openai import OpenAI
client = OpenAI(api_key=api_key)

client.files.create(
  file=open(data_path, "rb"),
  purpose="fine-tune"
)
FileObject(id='file-htiLNH6OfDuVYuXnjgP3p7xA', bytes=80068, created_at=1699431789, filename='training_dataR1.JSONL', object='file', purpose='fine-tune', status='processed', status_details=None)

四、训练一个新的模型

用上面的文件ID,基于gpt-3.5-turbo,我们训练一个模型。以下是新建微调任务:

client.fine_tuning.jobs.create(
  training_file="file-htiLNH6OfDuVYuXnjgP3p7xA", 
  model="gpt-3.5-turbo"
)
FineTuningJob(id='ftjob-FbfRd2U7BTAkggos5AO12jWl', created_at=1699431936, error=None, fine_tuned_model=None, finished_at=None, hyperparameters=Hyperparameters(n_epochs='auto', batch_size='auto', learning_rate_multiplier='auto'), model='gpt-3.5-turbo-0613', object='fine_tuning.job', organization_id='org-JQ3nn62Nnde4uERLZHxM0Kql', result_files=[], status='validating_files', trained_tokens=None, training_file='file-htiLNH6OfDuVYuXnjgP3p7xA', validation_file=None)

从现在开始,我们提交的任务正在排队。以下是关于微调任务常见的操作。如查看任务队列、中断、删除等。

from openai import OpenAI
client = OpenAI(api_key=api_key)

# List 10 fine-tuning jobs
client.fine_tuning.jobs.list(limit=10)

# Retrieve the state of a fine-tune
# client.fine_tuning.jobs.retrieve("ftjob-FbfRd2U7BTAkggos5AO12jWl")

# Cancel a job
# client.fine_tuning.jobs.cancel("ftjob-FbfRd2U7BTAkggos5AO12jWl")

# List up to 10 events from a fine-tuning job
# client.fine_tuning.jobs.list_events(id="ftjob-FbfRd2U7BTAkggos5AO12jWl", limit=10)

# Delete a fine-tuned model (must be an owner of the org the model was created in)
# client.models.delete("ft:gpt-3.5-turbo:acemeco:suffix:abc123")
SyncCursorPage[FineTuningJob](data=[FineTuningJob(id='ftjob-FbfRd2U7BTAkggos5AO12jWl', created_at=1699431936, error=None, fine_tuned_model='ft:gpt-3.5-turbo-0613:personal::8IavFxZw', finished_at=1699442524, hyperparameters=Hyperparameters(n_epochs=3, batch_size=1, learning_rate_multiplier=2), model='gpt-3.5-turbo-0613', object='fine_tuning.job', organization_id='org-JQ3nn62Nnde4uERLZHxM0Kql', result_files=['file-YDwJTKIv8TRi7MoTtw2LZWHD'], status='succeeded', trained_tokens=105717, training_file='file-htiLNH6OfDuVYuXnjgP3p7xA', validation_file=None)], object='list', has_more=False)

我们用查找,client.fine_tuning.jobs.list(limit=10)。最近10个jobs,只有1个job,status正在排队(queued)。过了大概4~5个小时,再查询,将JobID传入retrieve(), status得到succeeded.

# Retrieve the state of a fine-tune
client.fine_tuning.jobs.retrieve("ftjob-FbfRd2U7BTAkggos5AO12jWl")
FineTuningJob(id='ftjob-FbfRd2U7BTAkggos5AO12jWl', created_at=1699431936, error=None, fine_tuned_model='ft:gpt-3.5-turbo-0613:personal::8IavFxZw', finished_at=1699442524, hyperparameters=Hyperparameters(n_epochs=3, batch_size=1, learning_rate_multiplier=2), model='gpt-3.5-turbo-0613', object='fine_tuning.job', organization_id='org-JQ3nn62Nnde4uERLZHxM0Kql', result_files=['file-YDwJTKIv8TRi7MoTtw2LZWHD'], status='succeeded', trained_tokens=105717, training_file='file-htiLNH6OfDuVYuXnjgP3p7xA', validation_file=None)

微调后的模型名叫:ft:gpt-3.5-turbo-0613:personal::8IavFxZw

五、调用新模型

用新训练出来的模型:ft:gpt-3.5-turbo-0613:personal::8IavFxZw

from openai import OpenAI
client = OpenAI(api_key=api_key)

prompt = "我家猫太胖了怎么减肥呢?"

messages=[{"role": "user", "content": prompt}]

response = client.chat.completions.create(
  model="ft:gpt-3.5-turbo-0613:personal::8IavFxZw",
  messages=messages,
  temperature = 0
)

print("fine-tune: " + response.choices[0].message.content) ## refer to latest openai package

response = client.chat.completions.create(
  model="gpt-3.5-turbo",
  messages=messages,
  temperature = 0
)

print("GPT-3.5-turbo: " + response.choices[0].message.content) ## refer to latest openai package
fine-tune: 减肥的关键是控制猫咪的饮食和增加运动量。具体措施如下:
1. 控制饮食:每天定时定量喂食,不要随便喂食零食和人食,尽量选择低脂肪、低热量的猫粮。
2. 增加运动:每天陪猫咪玩耍,增加运动量,可以使用一些猫咪喜欢的玩具,让猫咪多动起来。
3. 定期体重:每周定期称一次猫咪的体重,记录下来,以便及时调整饮食和运动量。
4. 逐渐减少饮食:如果猫咪的体重一直没有下降,可以逐渐减少猫咪的饮食量,但是不要一下子减少太多,以免影响猫咪的健康。
GPT-3.5-turbo: 如果你的猫太胖了,有几种方法可以帮助它减肥:

1. 控制饮食:确保你的猫只摄入适量的食物。与兽医咨询后,可以选择低热量的猫粮,并按照建议的份量喂食。避免给予过多的零食或人类食物。

2. 分次喂食:将每日的食物分成几次喂给猫,而不是一次性喂给它。这样可以帮助控制猫的食欲,减少过量进食的可能性。

3. 增加运动:鼓励猫进行更多的运动,例如提供玩具、攀爬架和激发其活动性的游戏。每天与猫互动并提供适当的运动,有助于消耗多余的能量。

4. 定期体重监测:定期称量猫的体重,以确保减肥计划的有效性。如果体重没有减少,可能需要调整饮食或增加运动量。

5. 寻求兽医建议:如果你的猫的体重问题严重或你不确定如何正确减肥,最好咨询兽医。他们可以为你的猫制定一个适合其健康状况和需求的减肥计划。

以上是两个模型的输出。

上面的例子,仅仅只是微调gpt-3.5-turbor的过程。如果要使微调出的模型有更好的表现,还有大量的工作要做。

提示工程(7):构建点餐聊天机器人(Chatbot)

这是我们提示工程(Prompt Engineering)的最后一章,我们来构建一个聊天点餐机器人。

import openai
openai.api_key="Your openai key"

我们写两个辅助函数。
1. 单轮聊天:我们将prompt给大模型。
2. 多轮聊天:我们把聊天的消息串给大模型。

def get_completion (prompt, model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]
    response = openai.ChatCompletion. create (
        model=model,
        messages=messages,
        temperature=0, # this is the degree of randomness of 
    )
    return response.choices[0].message["content"]


def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0.0):
    response = openai.ChatCompletion. create(
        model = model, 
        messages = messages,
        temperature=temperature, # this is the degree of rand
    )
    return response.choices[0].message[ "content" ]

我们这一系列的文章和示例都常常会用到get_completion()这个函数:
1. 将入参的prompt,我们会封装成一个message。这个message的角色是user,内容是我们的提示prompt;
2. 在调用openai.ChatCompletion函数时,我们把message做为参数初始化了。

除了user这个角色,openai还有一个隐藏的角色system。这个角色是用来告诉大模型要做什么。 \

“系统system”提供高级指令,“用户user”提出查询或提示,“助手assistant”是模型的响应。请见下图分解。

1. 理解上下文

我们给一个聊天的消息串来初始化大模型,然其按之前的风格接着聊下去。这里的温度是0.7,越高的温度,随机性会越高。

messages=[
{'role':'system', 'content':'You are an assistant that speaks like Shakespeare.'},
{'role':'user', 'content':'tell me a joke'},
{'role':'assistant', 'content':'Why did the chicken cross the road'},
{'role':'user','content': 'I don\'t know'} ]
response = get_completion_from_messages(messages, temperature = 0.7)
print(response)
{
  "role": "assistant",
  "content": "To reach the other side, but lo, the other side was full of jesters and mirth, and the chicken found itself in a most amusing predicament!"
}
To reach the other side, but lo, the other side was full of jesters and mirth, and the chicken found itself in a most amusing predicament!

2. 聊天记忆

大模型只能记得当下一次对话的内容,对之前的内容是没有记忆的。请看下面的示例。

首先我和大模型打个招呼,自称我是Lisa。

messages = [
    {'role':'system', 'content': 'Your are friendly chatbot.'},
    {'role':'user', 'content': 'Hi, my name is Isa'} ]
response = get_completion_from_messages(messages, temperature=0.7)
print(response)
{
  "role": "assistant",
  "content": "Hello Isa! How can I assist you today?"
}
Hello Isa! How can I assist you today?

问大模型我是谁,大模型是不记得的。请见下例。

messages = [
    {'role':'system', 'content': 'Your are friendly chatbot.'},
    {'role':'user', 'content': 'Yes, can you remind me, what is my name?'} ]
response = get_completion_from_messages(messages, temperature=0.7)
print(response)
{
  "role": "assistant",
  "content": "I'm sorry, but as a chatbot, I don't have access to personal information or memory. Therefore, I don't know your name."
}
I'm sorry, but as a chatbot, I don't have access to personal information or memory. Therefore, I don't know your name.

假如我们把上述的聊天消息都一次给大模型,它就会记得你是Lisa.

messages = [
    {'role':'system', 'content': 'Your are friendly chatbot.'},
    {'role':'user', 'content': 'Hi, my name is Isa'},
    {'role':'assistant', 'content': 'Hi Isa! It \'s a nice to meet you.'},
    {'role':'assistant', 'content': 'Is there anything I can hlep you with today?'},
    {'role':'user', 'content': 'Yes, can you remind me, what is my name?'} ]

response = get_completion_from_messages(messages, temperature=0.7)
print(response)
{
  "role": "assistant",
  "content": "Your name is Isa."
}
Your name is Isa.

有了上下文,大模型就会记住Lisa这个名称。

现在我们来创新一个chatbot。
第1个方法collect_messages()是用来收集用户信息。

3.创建聊天机器人

定义消息收集函数用于收集我们的聊天消息。这个函数会从我们下面构建的用户界面中收集消息,奖后将其附加到一个名为上下文的列表中,并在每次调用模型时使用该上下文。同时,模型的响应也会被添加到上下文。

def collect_messages(_):
    prompt = inp.value_input
    inp.value = ''
    context.append({'role':'user', 'content':f"{prompt}"})
    response = get_completion_from_messages(context) 
    context.append({'role':'assistant', 'content':f"{response}"})
    panels.append(
        pn.Row('User:', pn.pane.Markdown(prompt, width=600)))
    panels.append(
        pn.Row('Assistant:', pn.pane.Markdown(response, width=600, styles={'background-color': '#F6F6F6'})))

    return pn.Column(*panels)

这是pizza的菜单,右边是同不大小的价格:\
The menu includes:\
pepperoni pizza 12.95, 10.00, 7.00 \
cheese pizza 10.95, 9.25, 6.50 \
eggplant pizza 11.95, 9.75, 6.75 \
fries 4.50, 3.50 \
greek salad 7.25

这是可选择的Topping,右边是价格
Toppings: \
extra cheese 2.00 \
mushrooms 1.50 \
sausage 3.00 \
canadian bacon 3.50 \
AI sauce 1.50 \
peppers 1.00

饮品: \
Drinks: \
coke 3.00, 2.00, 1.00 \
sprite 3.00, 2.00, 1.00 \
bottled water 5.00

import panel as pn  # GUI
pn.extension()

panels = [] # collect display 

context = [ {'role':'system', 'content':"""
You are OrderBot, an automated service to collect orders for a pizza restaurant. \
You first greet the customer, then collects the order, \
and then asks if it's a pickup or delivery. \
You wait to collect the entire order, then summarize it and check for a final \
time if the customer wants to add anything else. \
If it's a delivery, you ask for an address. \
Finally you collect the payment.\
Make sure to clarify all options, extras and sizes to uniquely \
identify the item from the menu.\
You respond in a short, very conversational friendly style. \
The menu includes \
pepperoni pizza  12.95, 10.00, 7.00 \
cheese pizza   10.95, 9.25, 6.50 \
eggplant pizza   11.95, 9.75, 6.75 \
fries 4.50, 3.50 \
greek salad 7.25 \
Toppings: \
extra cheese 2.00, \
mushrooms 1.50 \
sausage 3.00 \
canadian bacon 3.50 \
AI sauce 1.50 \
peppers 1.00 \
Drinks: \
coke 3.00, 2.00, 1.00 \
sprite 3.00, 2.00, 1.00 \
bottled water 5.00 \
"""} ]  # accumulate messages


inp = pn.widgets.TextInput(value="Hi", placeholder='Enter text here…')
button_conversation = pn.widgets.Button(name="Chat!")

# button_conversation.on_click(collect_messages),
interactive_conversation = pn.bind(collect_messages, button_conversation)

dashboard = pn.Column(
    inp,
    pn.Row(button_conversation),
    pn.panel(interactive_conversation, loading_indicator=True, height=300),
)

我们来运行一下。

dashboard

以下是我们的聊天记录。




现在我们完成了定餐聊天机器人的开发。你也可以用上述方法改变机器人的行为,以适应不同的场景。

Troubleshooting

在调试上面聊天机器人遇到很多奇怪的问题,花了很长时间才调通。总结2个:
1. panel的TextInput控件时常取不到值,即用户输入的内容,时深无法传参到函数。如我输入:”What types of Pizza do you have?”,这句话没有接收到。
解决办法:换成google chrome浏览器(之前我用的是safari浏览器)

  1. 点击chat按钮,交互界面一直在运行但实际上卡住了,也会回运行collect_message()函数。
    解法办法:用”’!python3.11 -m pip install ipywidgets”’重装了ipywidgets就好了。因为Jupyter Notebook运行的环境是python 3.11,而pip install默认是用更新版本的python来安装。所有,要指定版本python3.11再安装一次ipywidgets。
!python3.11 -m pip install panel
!python3.11 -m pip install ipywidgets

提示工程(6):扩展(Expanding)能力

扩展是将短文本(如一组说明或主题列表)转化成长文(如电子邮件或主题文章)。我们将使用模型的输入参数称为温度(temperature),这种参数允许您在模型响应中变化探索的程度和多样性。

下面是环境搭建。

import openai
openai.api_key="openai key"
def get_completion (prompt, model="gpt-3.5-turbo", temperature=0): 
    messages = [{"role": "user", "content": prompt}] 
    response = openai.ChatCompletion.create( 
        model=model, 
        messages=messages, 
        temperature=0
    ) 
    return response.choices[0].message["content"]

一、自动回复邮件

下面是一段产品评论和用户情绪。

# given the sentiment from the lesson on "inferring",
# and the original customer message, customize the email
sentiment = "negative"

# review for a blender
review = f"""
So, they still had the 17 piece system on seasonal \
sale for around 49 in the month of November, about \
half off, but for some reason (call it price gouging) \
around the second week of December the prices all went \
up to about anywhere from between70-89 for the same \
system. And the 11 piece system went up around10 or \
so in prict also from the earlier sale price of $29. \
So it loold okay, but if you look at the base, the part \
where the blade locks into place doesn't look as good \
as in previous editions from a few years ago, but I \
plan to be very gentle with it (example, I crush \
very hard items like beans, ice, rice, etc. in the \
blender first then pulverize them in the serving size \
I want in the blender then switch to the whipping \
blade for a finer flour, and use the cross cutting blade \
first when making smoothies, then use the flat blade \
if I need them finer/less pulpy). Special tip when making \
smoothies, finely cut and freeze the fruits and \
vegetables (if using spinach-lightly stew soften the \
spinach then freeze until ready for use-and if making \
sorbet, use a small to medium sized food processor) \
that you plan to use that way you can avoid adding so \
much ice if at all-when making your smoothie. \
After about a year, the motor was making a funny noise. \
I called customer service but the warranty expired \
already, so I had to buy another one. FYI: The overall \
quality has gone done in these types of products, 30 \
they are kind of counting on brand recognition and \
consumer loyalty to maintain sales. Got it in about \
two days.
"""

我们用大模型提取了情感,并根据情感来进行回复。

这里的指令是:假设你是一个客户服务AI助手,你的任务是为客户发送电子邮件回复,根据通过三个反引号分隔的客户电子邮件,生成一封回复以感谢客户的评价。

prompt  = f"""
You are a customer service Al assistant.
Your task is to send an email reply to a valued customer.
Given the customer email delimited by ‘’’,\
Generate a reply to thank the customer for their review.

If the sentiment is positive or neutral, thank them for \ 
their review.

If the sentiment is negative, apologize and suggest that \ 
they can reach out to customer service.

Make sure to use specific details from the review.
Write in a concise and professional tone.
Sign the email as ‘AI customer agent'.

Customer review: '''{review}'''
Review sentiment: {sentiment}
"""

response=get_completion(prompt)
print(response)
Dear Valued Customer,

Thank you for taking the time to share your review with us. We appreciate your feedback and apologize for any inconvenience you may have experienced with our pricing and product quality.

We understand your frustration regarding the price increase in December. Our pricing is subject to change based on various factors, including market demand and availability. We apologize if this was not communicated clearly to you. We strive to provide competitive prices and value for our customers, and we regret any disappointment caused.

Regarding the issue with the base of the system, we apologize for any inconvenience it may have caused. We appreciate your feedback on this matter and will take it into consideration for future improvements.

We are sorry to hear about the motor issue you encountered and the expiration of your warranty. We understand how important it is to have a reliable product, and we apologize for any inconvenience this may have caused. If you have any further concerns or questions, please don't hesitate to reach out to our customer service team. They will be more than happy to assist you further.

Thank you once again for your review and for choosing our products. We value your feedback as it helps us improve our products and services. We hope to have the opportunity to serve you better in the future.

Best regards,

AI customer agent

我们现在来试用一个temperature参数。temperature值越高,随机性会越强;反之确定性就越强。

prompt  = f"""
You are a customer service Al assistant.
Your task is to send an email reply to a valued customer.
Given the customer email delimited by ‘’’,\
Generate a reply to thank the customer for their review.

If the sentiment is positive or neutral, thank them for \ 
their review.

If the sentiment is negative, apologize and suggest that \ 
they can reach out to customer service.

Make sure to use specific details from the review.
Write in a concise and professional tone.
Sign the email as ‘AI customer agent'.

Customer review: '''{review}'''
Review sentiment: {sentiment}
"""

response=get_completion(prompt,temperature=0.0)
print(response)
Dear Valued Customer,

Thank you for taking the time to share your review with us. We appreciate your feedback and apologize for any inconvenience you may have experienced with our pricing and product quality.

We understand your frustration regarding the price increase in December. Our pricing is subject to change based on various factors, including market demand and availability. We apologize if this was not communicated clearly to you. We strive to provide competitive prices and value for our customers, and we regret any disappointment caused.

Regarding the issue with the base of the system, we apologize for any inconvenience it may have caused. We appreciate your feedback on this matter and will take it into consideration for future improvements.

We are sorry to hear about the motor issue you encountered and the expiration of your warranty. We understand how important it is to have a reliable product, and we apologize for any inconvenience this may have caused. If you have any further concerns or questions, please don't hesitate to reach out to our customer service team. They will be more than happy to assist you further.

Thank you once again for your review and for choosing our products. We value your feedback as it helps us improve our products and services. We hope to have the opportunity to serve you better in the future.

Best regards,

AI customer agent

二、使用温度系数

我们将使用语言模型的一个称为“温度”的参数,它将允许我们改变模型响应的多样性。您可以将温度视为模型探索或随机性的程度。

# given the sentiment from the lesson on "inferring",
# and the original customer message, customize the email
sentiment = "negative"

# review for a blender
review = f"""
So, they still had the 17 piece system on seasonal \
sale for around 49 in the month of November, about \
half off, but for some reason (call it price gouging) \
around the second week of December the prices all went \
up to about anywhere from between70-89 for the same \
system. And the 11 piece system went up around10 or \
so in price also from the earlier sale price of $29. \
So it looks okay, but if you look at the base, the part \
where the blade locks into place doesn’t look as good \
as in previous editions from a few years ago, but I \
plan to be very gentle with it (example, I crush \
very hard items like beans, ice, rice, etc. in the \ 
blender first then pulverize them in the serving size \
I want in the blender then switch to the whipping \
blade for a finer flour, and use the cross cutting blade \
first when making smoothies, then use the flat blade \
if I need them finer/less pulpy). Special tip when making \
smoothies, finely cut and freeze the fruits and \
vegetables (if using spinach-lightly stew soften the \ 
spinach then freeze until ready for use-and if making \
sorbet, use a small to medium sized food processor) \ 
that you plan to use that way you can avoid adding so \
much ice if at all-when making your smoothie. \
After about a year, the motor was making a funny noise. \
I called customer service but the warranty expired \
already, so I had to buy another one. FYI: The overall \
quality has gone done in these types of products, so \
they are kind of counting on brand recognition and \
consumer loyalty to maintain sales. Got it in about \
two days.
"""

response = get_completion(prompt, temperature=0.7)
print(response)

Dear Valued Customer,

Thank you for taking the time to share your review with us. We appreciate your feedback and apologize for any inconvenience you may have experienced with our pricing and product quality.

We understand your frustration regarding the price increase in December. Our pricing is subject to change based on various factors, including market demand and availability. We apologize if this was not communicated clearly to you. We strive to provide competitive prices and value for our customers, and we regret any disappointment caused.

Regarding the issue with the base of the system, we apologize for any inconvenience it may have caused. We appreciate your feedback on this matter and will take it into consideration for future improvements.

We are sorry to hear about the motor issue you encountered and the expiration of your warranty. We understand how important it is to have a reliable product, and we apologize for any inconvenience this may have caused. If you have any further concerns or questions, please don't hesitate to reach out to our customer service team. They will be more than happy to assist you further.

Thank you once again for your review and for choosing our products. We value your feedback as it helps us improve our products and services. We hope to have the opportunity to serve you better in the future.

Best regards,

AI customer agent

我们可以再试几次,跑多几次会发现不同的结果。

提示工程(5):转换(Transforming)能力

LLM非常擅长将输入转换成不同的格式,例如多语种文本翻译、拼写及语法纠正、语气调整、格式转换等。以前要用正则表达式,或者调用一些转换函数来解决的问题,现在用一个大语言模型和几个提示就可以简单实现了。

import openai
openai.api_key="openai key"
def get_completion (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"]

一、翻译任务

将一段英语翻译成西班牙语

prompt =f"""
Translate the following English text to Spanish:
'''  Hi, I would like to order a blender. '''
"""

response =get_completion(prompt)
print(response)
Hola, me gustaría ordenar una licuadora.

翻译成法语和海盗英语。

prompt = f"""
Translate the following text to French and Spanish and English pirate: 
‘’‘ I want to order a basketball ’‘’
"""

response = get_completion (prompt)
print (response)
French: "Je veux commander un ballon de basket."
Spanish: "Quiero ordenar una pelota de baloncesto."
English Pirate: "I be wantin' to order a basketball, matey."

翻译成中文,分别为正式和非正式的语气。

prompt = f"""
请将以下文本翻译成中文,分别展示成正式与非正式两种语气: 
'Would you like to order a pillow?'
"""

response = get_completion (prompt)
print (response)
正式语气:您是否需要订购一个枕头?
非正式语气:你想要订购一个枕头吗?

通用翻译器。一个模型识别各种语言,并且翻译成中文。

user_messages = [
    "La performance du système est plus lente que d'habitude.",
    "Mi monitor tiene pixeles que no se iluminan.",
    "Il mio mouse non funziona",
    "Mój klawisz Ctrl jest zepsuty",
    "我的屏幕在闪烁"
]
for issue in user_messages:
    prompt = f"告诉我以下文本是什么语种,直接输出语种,如法语,无法输出标点为符合:'''{issue}''' "
    lang = get_completion(prompt)
    print(f"原始消息 ({lang}): {issue} \n")

    prompt = f"""
    将以下消息分别翻译成英文和中文,并写成
    中文翻译:xxx
    英文翻译:yyy
    的格式:
    ```{issue}```
    """

    response = get_completion(prompt)
    print(response, "\n=========================================\n")
原始消息 (法语): La performance du système est plus lente que d'habitude. 

中文翻译:系统性能比平时慢。
英文翻译:The system performance is slower than usual. 
=========================================

原始消息 (西班牙语): Mi monitor tiene pixeles que no se iluminan. 

中文翻译:我的显示器有一些像素点不亮。
英文翻译:My monitor has pixels that do not light up. 
=========================================

原始消息 (意大利语): Il mio mouse non funziona 

中文翻译:我的鼠标不工作
英文翻译:My mouse is not working 
=========================================

原始消息 (这段文本是波兰语。): Mój klawisz Ctrl jest zepsuty 

中文翻译:我的Ctrl键坏了
英文翻译:My Ctrl key is broken 
=========================================

原始消息 (无法确定该文本的语种。): 我的屏幕在闪烁 

中文翻译:我的屏幕在闪烁
英文翻译:My screen is flickering. 
=========================================

二、语气调整

我想让大模型给我写封正式的邮箱。请见下例。

prompt = f"""
Translate the following from slang to a business letter:
'Dude, This is Joe, check out this spec on this standing lamp'
"""

response = get_completion (prompt)
print (response)
Dear Sir/Madam,

I hope this letter finds you well. My name is Joe, and I am writing to bring your attention to a specification document regarding a standing lamp. 

I kindly request that you take a moment to review the attached spec, as it provides detailed information about the features and qualities of the aforementioned lamp. 

Thank you for your time and consideration. I look forward to discussing this matter further with you.

Yours sincerely,
Joe

三、格式转换

我想把一JSON转换成HTML的表格。

data_json = { "resturant employees" :[
    {"name": "Shyam", "email": "shyamjaiswalegmail.com"},
    {"name": "Bob" , "email" : "Jaibob32@gmail.com"},
    {"name": "Jai", "email": "jai87@gmail.com"} 
]}

prompt = f"""
Transalte the following python dictionary from JSON to a HTML table with column headers and title: {data_json}
"""

response = get_completion (prompt)
print (response)
<!DOCTYPE html>
<html>
<head>
<style>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

td, th {
  border: 1px solid #dddddd;
  text-align: left;
  padding: 8px;
}

tr:nth-child(even) {
  background-color: #dddddd;
}
</style>
</head>
<body>

<h2>Restaurant Employees</h2>

<table>
  <tr>
    <th>Name</th>
    <th>Email</th>
  </tr>
  <tr>
    <td>Shyam</td>
    <td>shyamjaiswalegmail.com</td>
  </tr>
  <tr>
    <td>Bob</td>
    <td>Jaibob32@gmail.com</td>
  </tr>
  <tr>
    <td>Jai</td>
    <td>jai87@gmail.com</td>
  </tr>
</table>

</body>
</html>

展示表格如下:

from IPython.display import display, HTML
display(HTML(response))

table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}

td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}

tr:nth-child(even) {
background-color: #dddddd;
}

Restaurant Employees

Name Email
Shyam shyamjaiswalegmail.com
Bob Jaibob32@gmail.com
Jai jai87@gmail.com

四、语法和拼写检查

下面是对每句英文进行语法检查。如果有错误则直接修正,如果没有则显示“No errors found.”

text = [ 
  "The girl with the black and white puppies have a ball.",  # The girl has a ball.
  "Yolanda has her notebook.", # ok
  "Its going to be a long day. Does the car need it’s oil changed?",  # Homonyms
  "Their goes my freedom. There going to bring they’re suitcases.",  # Homonyms
  "Your going to need you’re notebook.",  # Homonyms
  "That medicine effects my ability to sleep. Have you heard of the butterfly affect?", # Homonyms
  "This phrase is to cherck chatGPT for speling abilitty"  # spelling
]

for t in text:
    prompt = f"""Proffread and correct the following text
    and rewrite the correct version. If you don't find 
    any errors, just say "No errors found": 
    '''{t}'''
    """
    response = get_completion (prompt)
    print (response)
The girl with the black and white puppies has a ball.
No errors found.
"It's going to be a long day. Does the car need its oil changed?"
There goes my freedom. They're going to bring their suitcases.
You're going to need your notebook.
That medicine affects my ability to sleep. Have you heard of the butterfly effect?
This phrase is to check chatGPT for spelling ability.

下面对一个段落进行纠错,并输出正确的文本。

text = f"""
Got this for my daughter for her birthday cuz she keeps takir 
mine from my room. Yes, adults also like pandas too. 
She ti it everywhere with her, and it's super soft and cute. One of 
ears is a bit lower than the other, and I don't think that 
we designed to be asymmetrical. It's a bit small for what I pair 
though. I think there might be other options that are bigger 
the same price. It arrived a day earlier than expected, so 
to play with it myself before I gave it to my daughter.
"""

prompt = f"proofread and correct this review: '''{text}'''"
response = get_completion(prompt)
print(response)
Got this for my daughter for her birthday because she keeps taking mine from my room. Yes, adults also like pandas too. She takes it everywhere with her, and it's super soft and cute. One of the ears is a bit lower than the other, and I don't think it was designed to be asymmetrical. It's a bit small for what I paid though. I think there might be other options that are bigger for the same price. It arrived a day earlier than expected, so I got to play with it myself before I gave it to my daughter.

有哪些地方纠错了,我们希望看到痕迹。请见下面示例。
我们先安装redlines包,并import markdown包。

from redlines import Redlines
from IPython.display import Markdown  # Import Markdown from IPython.display

diff = Redlines(text, response)
display(Markdown(diff.output_markdown))

Got this for my daughter for her birthday cuz because she keeps takir ¶ taking mine from my room. Yes, adults also like pandas too. She ti takes it everywhere with her, and it’s super soft and cute. One of the ears is a bit lower than the other, and I don’t think that ¶ we it was designed to be asymmetrical. It’s a bit small for what I pair ¶ paid though. I think there might be other options that are bigger for the same price. It arrived a day earlier than expected, so I got to play with it myself before I gave it to my daughter.

五、风格变换

上面那段文字,我们希望采用APA(American Psychological Association)风格。同时,也综合应用了语法纠错,改写等方法,最后输出预期的结果。

prompt = f"""
proofread and correct this review. Make it more compelling.
Ensure it follows APA style guide and targets an advanced reader.
Output in markdown format.

Text: '''{text}'''
"""
response = get_completion(prompt)
display(Markdown(response))

Review of a Panda Plush Toy

I purchased this adorable panda plush toy as a birthday gift for my daughter, who has a habit of taking my belongings from my room. However, it turns out that adults can also appreciate the charm of pandas.

My daughter absolutely adores this toy and takes it everywhere with her. The plush is incredibly soft and undeniably cute. However, I did notice a minor flaw in its design – one of the ears is slightly lower than the other, which seems to be unintentional.

While the quality of the plush is exceptional, I found it to be a bit smaller than I anticipated for its price. I believe there might be other options available at the same price point that offer a larger size.

On a positive note, the delivery of the toy was prompt, arriving a day earlier than expected. This allowed me to thoroughly inspect and even play with the toy myself before presenting it to my daughter.

In conclusion, despite the minor design flaw and smaller size, this panda plush toy is undeniably adorable and made of high-quality materials. It would make a delightful gift for any panda enthusiast, regardless of age. However, for those seeking a larger plush toy, it may be worth exploring other options within the same price range.

以上是关于大模型转换(Transforming)的应用。

提示工程(4):推断(Inferring)能力

如何从产品评论和新闻文章里推断情感和主题呢?传统的做法,这些任务可以看作是模型接收文本作为输入并执行某种分析的过程。

这可能涉及提取标签、提取实体、理解文本情感等等。如果你想要从一段文本中提取正面或负面情感,在传统的机器学习工作流程中,需要收集标签数据集、训练模型、确定如何在云端部署模型并进行推断。

这样做可能效果还不错,但是这个过程需要很多工作。而且对于每个任务,如情感分析、提取实体等等,都需要训练和部署单独的模型。
现在要做的就是写提示prompt,结果就能够马上出来。

我也可以也利用一个模型,就能应对之前需要多个模型处理的工作。接下来我们举些例子。

一、识别情绪

import openai
openai.api_key="your openai key"
def get_completion (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"]

以下是关于一个台灯的产品评论。

# review for a standing lamp
lamp_review = """
Needed a nice lamp for my bedroom, and this one 
had additional storage and not too high of a price 
point. Got it fast - arrived in 2 days. The string 
to the lamp broke during the transit and the company 
happily sent over a new one. Came within a few days 
as well. It was easy to put together. Then I had a 
missing part, so I contacted their support and they 
very quickly got me the missing piece! Lumina Seems to me 
to be a great company that cares about their customers
and products!
"""

我们想了解一下上述评论的情绪,是正面的(positive)还是负面的(negative)?

prompt = f"""
What is the sentiment of the following product review, which is delimited with tripe backticks?
Review: '''{lamp_review}'''
"""

response = get_completion(prompt)
print (response)
The sentiment of the product review is positive.

更简洁的答案。一个字是positive还是negative?

prompt = f"""
What is the sentiment of the following product review, which is delimited with tripe backticks?

Give your answer as a single word, either "positive" or "negative".

Review: '''{lamp_review}'''
"""

response = get_completion(prompt)
print (response)
positive

我们让模型来识别这段评价有几种情况?可能不是一种,可能有多种。我们来试试。
no more that five item (emtions)。

prompt = f"""
Identify a list of emotions that the writer of the following review is expressing.
Include no more that five items in the list. Format your answer as a list of low-case words separated by commas.

Review: '''{lamp_review}'''
"""

response = get_completion(prompt)
print (response)
happy, satisfied, impressed, grateful, pleased

效果非常好。大模型很擅长识别情感。

让模型来识别是否有“愤怒”的情绪,请见下例。

prompt = f"""
Is the writer of the following review expression anger?
The review is delimited with triple backticks.
Give your answer as either yes or no.

Review: '''{lamp_review}'''
"""

response = get_completion(prompt)
print (response)
No
如何在产品评论提取产品和品牌名称,请见下例。
prompt = f"""
Identify the following items from the review text:
- Item purchased by reviewer
- Company that made the item

The review is delimited with triple backticks.
Format your response as a JSON object with
"Item" and "Brand" as the keys.
If the information isn't present, use "unknown"  as the value.

Make your response as short as possible

Review: '''{lamp_review}'''
"""
response = get_completion(prompt)
print(response)
{
  "Item": "lamp",
  "Brand": "Lumina"
}

结合上面的例子,让模型分析产品评论,并结构化输出:商品、品牌、情绪(正面/负面)、是否愤怒的的内容,并以JSON形式展示。

prompt = f"""
Identify the following items from the review text:
- Sentiment (positive or negative)
- Is the reviewer expressing anger? (true or false)
- Item purchased by reviewer
- Company that made the item
The review is delimited with triple backticks. 
Format your response as a JSON object with 
"Sentiment", "Anger", "Item" and "Brand" as the keys.
If the information isn't present, use "unknown"
as the value.

Make your response as short as possible.
Format the Anger value as a boolean.
Review text:'''{Lamp_review}''
"""

response = get_completion(prompt)
print(response)
{
  "Sentiment": "positive",
  "Anger": false,
  "Item": "Lamp",
  "Brand": "unknown"
}

二、推断主题

推断主题是大模型另一个方面的应用。当我们阅读一篇新闻,这段新闻是关于什么主题的呢?如:科技、电影、音乐等等。大模型可以推断出来。请看下面的例子。

story = """
In a recent survey conducted by the government, public sector employees were asked to rate their level of satisfaction with the department they work at.
The results revealed that NASA was the most popular department with a satisfaction rating of 958.

One NASA employee, John Smith, commented on the findings, stating "I'm not surprised that NASA came out on top.
It's a great place to work with amazing people and incredible opportunities. I'm proud to be a part of such an innovative organization."

The results were also welcomed by NASA's management team, with Director Tom Johnson stating, "We are thrilled to hear that our employees are satisfied with their work at NASA. We have a talented and dedicated team who work tirelessly to achieve our goals, and it's fantastic to see that their hard work is paying off."

The survey also revealed that the Social Security Administration had the lowest satisfaction rating, with only 458 of employees indicating they were satisfied with their job. The government has pledged to address the concerns raised by employees in the survey and work towards improving job satisfaction across all departments.
"""
prompt = f"""
Determine five topics that are being discussed in the \ 
following text, which is delimited by triple backticks.

Make each item one or two words long.

Format your response as a list of items separated by commas.

Text sample: '''{story}'''
"""

response = get_completion(prompt)
print(response)
1. Government survey
2. Department satisfaction
3. NASA
4. Social Security Administration
5. Job satisfaction improvement
response.split(sep=", ")
['1. Government survey\n2. Department satisfaction\n3. NASA\n4. Social Security Administration\n5. Job satisfaction improvement']

以上是关于上篇文章相关的主题。

试想我们希望通过大模型去阅读多篇报道,并统计我们感兴趣主题(nasa, local government, engineering, employee satisfaction, federal government)有多少篇文章。请看下面的示例。

以下是我们需要统计的主题。

topic_list = [
    "nasa", "local government", "engineering",
    "employee satisfaction", "federal government"
]

让大模型进行主题推断,并以我们要求的方式输出。

prompt = f"""
Determine whetner each item in the following list of
topics is a topic in the text below, which is delimited with triple backticks.

Give your answer as bullet list with 0 or 1 for each topic.

List of topics: {", " .join(topic_list)}

Text sample: '''{story}'''
"""

response = get_completion(prompt)
print(response)
- nasa: 1
- local government: 0
- engineering: 0
- employee satisfaction: 1
- federal government: 1

以上是关于应用大模型进行推断的内容了,主要包括情绪识别和主题推断。接下来我们讲转换。