18|流式生成与模型微调,打造极致的对话体验

在之前介绍 llama-index 和 LangChain 的几讲里面,我们学习了如何将大语言模型和你自己的知识库组合到一起来解决问题。这个方法中,我们不需要对我们使用的模型做任何调整,而是通过将我们的数据用 Embedding 向量索引起来,然后在使用的时候查询索引来解决问题。

不过,其实我们也完全可以利用我们自己的数据,创建一个新的模型来回答问题。这个方法,就是 OpenAI 提供的模型微调(Fine-tune)功能。这也是我们要探讨的大语言模型的最后一个主题。

如何进行模型微调?

模型微调,是因为无论是 ChatGPT 还是 GPT-4 都不是全知全能的 AI。在很多垂直的领域,它的回答还是常常会出错。其中很大一部分原因,是它也缺少特定领域的训练数据。而如果我们有比较丰富的垂直领域的数据,那么就可以利用这些数据来“微调”一个特别擅长这个垂直领域的模型。在这个模型“微调”完成之后,我们就可以直接向模型提问了。而不用再像之前使用 llama-index 或者 LangChain 那样,先通过 Embedding 来查询相关资料,然后把查找到的资料也一并提交给 OpenAI 来获得所需要的答案。

OpenAI 模型微调的过程,并不复杂。你只需要把数据提供给 OpenAI 就好了,对应的整个微调的过程是在云端的“黑盒子”里进行的。需要提供的数据格式是一个文本文件,每一行都是一个 Prompt,以及对应这个 Prompt 的 Completion 接口会生成的内容。

就像下面的示例:

{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
...

模型微调的过程,就是根据输入的内容,在原来的基础模型上训练。这个基础模型,就是我们第 8 讲介绍过的 Ada、Babbage、Curie 和 Davinci 其中的一个。每一个示例,都会导致基础模型原有参数发生变化。整个微调过程结束之后,变化后的参数就会被固定下来,变成一个只有你可以使用的新模型。

如果你提供了很多医疗行业的文本内容,那么微调出来的新模型就会拥有更多医疗领域的知识,以及对话的风格。而如果你给的是笑话大全,那么微调出来的模型就更擅长讲笑话。而且要注意,微调之后的模型,不仅有你用来微调的数据的相关知识,原先基础模型里面的绝大部分知识和能力它也还都保留着。

来一个擅长写“历史英雄人物和奥特曼一起打怪兽”的 AI

那今天我们来微调一个什么样的模型呢?周围有不少朋友家里都有孩子,都特别迷恋奥特曼打怪兽的故事。他们就提过一个需求,说能不能利用 ChatGPT 来做一个专门讲奥特曼打怪兽故事的应用。可以是可以,不过,为了让这个故事既能精彩一点,又有点教育意义,我们就再找一些历史上的英雄人物,赋予他们一些超能力,来和奥特曼一起打怪兽。而对应的故事数据,我们也用 ChatGPT 的模型来帮我们生成。

import os,openai,backoff
import pandas as pd

openai.api_key = os.getenv("OPENAI_API_KEY")
dynasties= ['唐', '宋', '元', '明', '清', '汉', '魏', '晋', '南北朝']
super_powers = ['隐形', '飞行', '读心术', '瞬间移动', '不死之身', '喷火']
story_types = ['轻松', '努力', '艰难']

@backoff.on_exception(backoff.expo, openai.error.RateLimitError)
def gpt35(prompt, max_tokens=2048, temperature=0.5, top_p=1, frequency_penalty=0, presence_penalty=0):
    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=prompt,
        max_tokens=max_tokens,
        temperature=temperature,
        top_p=top_p,
        frequency_penalty=frequency_penalty,
        presence_penalty=presence_penalty)
    return response["choices"][0]["text"]

def prepare_stories(dynasties, super_powers, story_types, output_file="data/ultraman_stories.csv"):
    df = pd.DataFrame()
    repeat = 3
    for dynasty in dynasties:
        for super_power in super_powers:
            for story_type in story_types:
                   for i in range(repeat):
                        prompt = f"""请你用中文写一段300字的故事,情节跌宕起伏,讲述一位{dynasty}朝时期的英雄人物,穿越到现代,拥有了{super_power}这样的超能力,通过{story_type}的战斗,帮助奥特曼一起打败了怪兽的故事。"""
                        story = gpt35(prompt)
                        row = {"dynasty": dynasty, "super_power": super_power, "story_type": story_type, "story": story}
                        row = pd.DataFrame([row])
                        df = pd.concat([df, row], axis=0, ignore_index=True)

    df.to_csv("data/ultraman_stories.csv")

prepare_stories(dynasties, super_powers, story_types)

这部分代码非常简单,我们定义了一系列朝代、超能力和故事的类型。然后通过三重循环,让 AI 根据这三者的组合来生成一系列故事。这些生成出来的故事,也就构成了我们用来微调模型的训练数据。因为数据量不大,我就直接用 CSV 把它存下来了。在这个过程中,数据是一条条生成的,比较慢,也比较消耗 Token,你可以不用运行,直接拿我运行后生成的结果数据就好。

拿到了这些数据,我们就可以来微调模型了。我们之前已经通过 pip 安装了 OpenAI 的包,这里面自带了命令行工具,方便我们把对应的 CSV 格式的数据转换成微调模型所需要的 JSONL 格式的文件。

df = pd.read_csv("data/ultraman_stories.csv")
df['sub_prompt'] = df['dynasty'] + "," + df['super_power'] + "," + df['story_type']
prepared_data = df.loc[:,['sub_prompt','story']]
prepared_data.rename(columns={'sub_prompt':'prompt', 'story':'completion'}, inplace=True)
prepared_data.to_csv('data/prepared_data.csv',index=False)

import subprocess

subprocess.run('openai tools fine_tunes.prepare_data --file data/prepared_data.csv --quiet'.split())

输出结果:

……
Wrote modified file to `data/prepared_data_prepared.jsonl`
Feel free to take a look!
Now use that file when fine-tuning:
> openai api fine_tunes.create -t "data/prepared_data_prepared.jsonl"
After you’ve fine-tuned a model, remember that your prompt has to end with the indicator string ` ->` for the model to start generating completions, rather than continuing with the prompt. Make sure to include `stop=["."]` so that the generated texts ends at the expected place.
Once your model starts training, it'll approximately take 8.82 minutes to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.

CompletedProcess(args=['openai', 'tools', 'fine_tunes.prepare_data', '--file', 'data/prepared_data.csv', '--quiet'], returncode=0)

上面的代码主要做了两个动作。首先,是对数据做了一些处理,来准备微调。对于微调,我们使用的 Prompt 不再是一个完整的句子,而是只用了“朝代”+“超能力”+“故事类型”拼接在一起的字符串,中间用逗号隔开。然后把这个字符串和生成的故事,用 Prompt 和 Completion 作为列名存储成了一个 CSV。

其次,我们通过 subprocess 调用了命令行里的 OpenAI 工具,把上面的 CSV 文件,转化成了一个 JSONL 格式的文件。从输出的日志里面可以看到,这个文件叫做 data/prepared_data_prepared.jsonl。

如果我们打开这个 JSONL 文件看一眼,是下面这样的。

{"prompt":"唐,隐形,轻松 ->","completion":" \n\n一位叫做李明的英雄人物,出生在唐朝时期。他是一个勇敢的将军,在他的一生中,他打败了许多敌人,为自己的国家和人民做出了许多贡献。\n\n一天,李明意外被一道神秘的光束穿越到了现代。他发现自己拥有了超能力,可以隐形,可以飞行,可以发射光束,可以放出火焰,可以穿墙而过等等。\n\n李明发现,在现代,怪兽们正在摧毁地球,他决定和奥特曼一起,用自己的超能力,帮助奥特曼消灭怪兽们。\n\n于是,李明和奥特曼开始了一场艰苦的战斗,他们用自己的超能力,一招一式,击退了怪兽们,最终成功地拯救了地球。\n\n在这场战斗之后,地球上的人们都对李明充满了敬佩,他从此成为了一位英雄,他的事迹也被传颂了很久。李明也因此被永远铭记在人们心中,成为了一位不朽的英雄。."}
{"prompt":"唐,隐形,轻松 ->","completion":" \n\n这是一个关于英雄的故事,发生在唐朝时期的中国。一个叫李自成的勇士,他拥有过人的勇气,英勇的行为让他成为当时最受尊敬的人物。\n\n一天,李自成被一道神秘的光芒笼罩住,他突然发现自己似乎穿越时空,来到了现代。他惊讶地发现,自己竟然拥有了一种超能力,可以让自己隐形。\n\n李自成接受了这种超能力,他发现这种能力可以让自己变得更加强大,他决定利用这种能力来帮助人们。\n\n一次,李自成发现有一群怪兽正在破坏城市,他决定和奥特曼一起出动,利用自己的超能力,把怪兽一个个击败,最终成功拯救了城市,令众人欢呼雀跃。\n\n自此,李自成受到了众人的尊敬,他成为了这个城市的英雄,他也把自己的超能力用在了正义的事业上,为人们做出了许多贡献,他也成为了一个英雄。."}

可以看到,转换后的数据文件,在 Prompt 的最后,多了一个“->”符号。而在 Completion 的开头,多了两个“\n\n”的换行,结尾则是多了一个“.”。这是为了方便我们后续在使用这个模型生成数据的时候,控制生成结果。未来在使用模型的时候,Prompt 需要以“->\n”这个提示符结束,并且将 stop 设置成“.”。这样,模型就会自然套用我们微调里的模式来生成文本。

有了准备好的数据,我们只要再通过 subprocess 调用 OpenAI 的命令行工具,来提交微调的指令就可以了。

subprocess.run('openai api fine_tunes.create --training_file data/prepared_data_prepared.jsonl --model curie --suffix "ultraman"'.split())

输出结果:

Upload progress: 100%|██████████| 446k/446k [00:00<00:00, 201Mit/s]
Uploaded file from data/prepared_data_prepared.jsonl: file-yn0BfnPmgvf7n0sfQzQRbbeE
Created fine-tune: ft-3oxkr1zBVB4fJWogJDDjQbr0
Streaming events until fine-tuning is complete...
(Ctrl-C will interrupt the stream, but not cancel the fine-tune)
[2023-04-04 10:51:51] Created fine-tune: ft-3oxkr1zBVB4fJWogJDDjQbr0

CompletedProcess(args=['openai', 'api', 'fine_tunes.create', '--training_file', 'data/prepared_data_prepared.jsonl', '--model', 'curie', '--suffix', '"ultraman"'], returncode=0)

在这个微调的指令里面,我们指定了三个参数,分别是用来训练的数据文件、一个基础模型,以及生成模型的后缀。这里,我们选用了 Curie 作为基础模型,因为是讲奥特曼的故事,所以模型后缀我给它取了一个 ultraman 的名字。

我们的数据量不大,所以微调很快,几分钟就能完成。那接下来我们就可以使用这个模型了。我们可以通过下面的 fine_tunes.list 指令,找出所有我们微调的模型。

subprocess.run('openai api fine_tunes.list'.split())

输出结果:

{
  "data": [
    {
      "created_at": 1680576711,
      "fine_tuned_model": "curie:ft-bothub-ai:ultraman-2023-04-04-03-03-26",
      "hyperparams": {
        "batch_size": 1,
        "learning_rate_multiplier": 0.2,
        "n_epochs": 4,
        "prompt_loss_weight": 0.01
      },
      "id": "ft-3oxkr1zBVB4fJWogJDDjQbr0",
      "model": "curie",
      "object": "fine-tune",
      "organization_id": "YOUR_ORGANIZATION_ID",
      "result_files": [
        {
          "bytes": 107785,
          "created_at": 1680577408,
          "filename": "compiled_results.csv",
          "id": "RESULT_FILE_ID",
          "object": "file",
          "purpose": "fine-tune-results",
          "status": "processed",
          "status_details": null
...
    }
  ],
  "object": "list"
}
CompletedProcess(args=['openai', 'api', 'fine_tunes.list'], returncode=0)

在输出的 JSON 里面,你可以看到我们有一个 fine_tuned_model 字段,里面的值叫做“curie:ft-bothub-ai:ultraman-2023-04-04-03-03-26”,这个就是刚刚让 OpenAI 给我们微调完的模型。

这个模型的使用方法,和我们使用 text-davinci-003 之类的模型是一样的,只要在 API 里面把对应的 model 字段换掉就好了,对应的代码也放在了下面。

import os
import openai

openai.api_key = os.getenv("OPENAI_API_KEY")

def write_a_story(prompt):
    response = openai.Completion.create(
        model="curie:ft-bothub-ai:ultraman-2023-04-04-03-03-26",
        prompt=prompt,
        temperature=0.7,
        max_tokens=2000,
        top_p=1,
        stop=["."])
    return response["choices"][0]["text"]

story = write_a_story("宋,发射激光,艰难 ->\n")
print(story)

输出结果:

宋朝时期,有一位叫林先生的英雄人物,他勇敢而又坚韧,曾经拯救过无数的人民,他的英勇表现让他赢得了众多的尊敬。
一天,林先生突然发现自己穿越到了现代,他发现自己拥有了一种神奇的超能力,可以发射激光,他开始研究自己的能力,发现自己可以用激光来攻击敌人。
林先生决定把自己的能力用来拯救人类,于是他和奥特曼一起出发,开始与怪兽作战。他们一路走来,林先生用他的激光来打击怪兽,奥特曼则用他的武器来打击怪兽。
在一场艰苦的战斗中,林先生和奥特曼终于击败了怪兽,拯救了人类。林先生也因此获得了无数的赞誉,他也成为了一位传奇英雄。
林先生的故事被传唱了几百年,他的英勇事迹也成为了一个永恒的传奇,让人们永远不忘。

对应在调用模型的时候,我们使用的提示语就是“朝代”+“超能力”+“故事类型”,并且跟着“->\n”,而 stop 则是设置成了“.”。

因为这是一个微调的模型,它不仅拥有我们训练数据提供的知识,也包括基础模型里的各种信息。所以我们使用的朝代、超能力和故事类型也可以是在之前微调数据里面没有出现过的。比如,上面的例子里,我们使用的超能力叫做“发射激光”,并不是我们拿来微调的数据里面有的一种超能力。你可以试试看,使用别的朝代、故事的类型,效果会是怎么样的。

story = write_a_story("秦,龙卷风,辛苦 ->\n")
print(story)

输出结果:


曾经有一位叫苏轼的英雄人物,他曾经英勇地抵抗过许多强大的敌人,拯救了许多被危险封印的百姓。他曾经在一次战争中发挥过自己的作用,赢得了许多胜利,被尊为英雄。
然而,苏轼却在一次激烈的战斗中牺牲了,他的灵魂被封印在一个古老的石头里,隔着一层玻璃,一直沉睡了几百年。
苏轼的灵魂在穿越时空,来到了现代,他发现自己拥有了一种超能力,这就是龙卷风,他可以使自己的身体具有超强的力量,甚至可以抵抗恶魔的攻击。
苏轼在现代的世界里,发现了一种可怕的怪兽,它们正在摧毁着人类的家园,苏轼决定要拯救这个世界,于是他和奥特曼一起出发,开始了一场史诗般的战斗。
在苏轼和奥特曼的帮助下,苏轼利用自己的超能力,一次次击退怪兽的攻击,最终他们成功地打败了怪兽,拯救了人类。
苏轼的事迹在这里传唱了很久,他成为了一位永恒的英雄,他的故事也被传唱了下来,让人们永远不会忘记他的英勇事迹。

模型微调的成本考量

细心的人可能注意到了,我们这里选用的基础模型是 Curie,而不是效果最好的 Davinci。之所以做出这样的选择,是出于成本的考虑。

注:数据来源于 https://openai.com/pricing#language-models

使用微调模型的成本要远远高于使用 OpenAI 内置的模型。以 Davinci 为基础微调的模型,使用的时候,每 1000 个 Token 的成本是 0.12 美元,是使用内置的 text-davinci-003 的 6 倍,是我们最常用的 gpt-3.5-turbo 的 60 倍。所以,如果只是一般的讲故事的应用,这个成本实在是太高了。就算是我们选择基于 Curie 微调,1000 个 Token 的使用成本也在 0.012 美元,虽然比 text-davinci-003 要便宜,但也是 gpt-3.5-turbo 的 6 倍。

对于模型微调的效果,我们也可以通过一个 OpenAI 提供的命令 fine_tunes.results 来看。对应的,我们需要提供给它一个微调任务的 id。这个 id,可以在 fine_tunes.list 列出的 fine_tunes 模型的 id 参数里找到。 

subprocess.run('openai api fine_tunes.results -i ft-3oxkr1zBVB4fJWogJDDjQbr0'.split())

输出结果:

step,elapsed_tokens,elapsed_examples,training_loss,training_sequence_accuracy,training_token_accuracy
1,625,1,0.8805545861742778,0.0,0.75
2,1258,2,0.8059815050491868,0.0,0.7766830870279147
3,1859,3,0.7964038042175758,0.0,0.7862068965517242
4,2548,4,0.805052303553852,0.0,0.7774436090225564
5,3197,5,0.7503930440556053,0.0,0.7808
6,3846,6,0.7992317049403261,0.0,0.7770700636942676
7,4775,7,0.6649006477473822,0.0,0.7927232635060639
8,5432,8,0.6493354803676822,0.0,0.8049921996879875
9,6265,9,0.6568901059838095,0.0,0.802937576499388
10,7122,10,0.6578856167468091,0.0,0.8100358422939068
11,7827,11,0.5687322367928961,0.0,0.8279411764705882
12,8404,12,0.6334827334911788,0.0,0.8172043010752689
13,9061,13,0.5771709139683721,0.0,0.825
14,9822,14,0.6079089517825593,0.0,0.8100407055630936
15,10399,15,0.6481047367374327,0.0,0.8154121863799283
16,11208,16,0.5528688982071029,0.0,0.8352490421455939
17,11913,17,0.6525803676480848,0.0,0.8093841642228738
18,12546,18,0.5230526420679229,0.0,0.8363047001620746
19,13163,19,0.6065665546680247,0.0,0.8236272878535774
20,13796,20,0.5983224045073889,0.0,0.8199672667757774
21,14549,21,0.6440337136896056,0.0,0.8267394270122783
22,15190,22,0.6029605409912032,0.0,0.8110749185667753
23,15759,23,0.5089513997451476,0.0,0.838475499092559
24,16440,24,0.557213810807506,0.0,0.8265460030165912
...
1855,1228711,1855,0.2610049068084409,0.0,0.9219765929778934
1856,1229312,1856,0.21196416716076574,0.0,0.9312714776632303
1857,1229945,1857,0.14050147435694596,0.0,0.9556650246305419

在这个命令的输出结果里,你可以在第二列 elapsed_tokens 看到训练消耗的 Token 数量。而最后一列 training_token_accuracy,则代表微调后的模型,成功预测微调的数据里下一个 Token 的准确率。在我们使用的这个例子里面,可以看到一开始准确率只有 75%,但是随着训练数据迭代轮数的增加,准确率越来越高,达到了 95% 以上。

增量训练,不断优化模型

微调模型比较高昂的价格,限制了它的使用。不过,微调模型还有一个能力,就是我们可以在已经微调了的模型上根据新数据做进一步地微调。这个在很多垂直领域是非常有用,比如在医学、金融这样的领域,我们就可以不断收集新的数据,不断在前一个微调模型的基础之上继续微调我们的模型,让模型的效果越来越好。而这些领域往往也能承受更高一些的成本。

进一步地微调其实操作起来并不复杂,就是再准备一些数据,以之前已经微调好的模型为基础模型来操作就好了。

生成一些额外的数据:

dynasties= ['秦', '五代', '隋']
super_powers = ['龙卷风', '冰冻大海', '流星火雨']
story_types = ['轻松', '努力', '艰难', '勇敢', '辛苦']

new_stories = "data/ultraman_stories_more.csv"
prepare_stories(dynasties, super_powers, story_types, repeat=3, output_file=new_stories)

转换数据:

df = pd.read_csv(new_stories)
df['sub_prompt'] = df['dynasty'] + "," + df['super_power'] + "," + df['story_type']
prepared_data = df.loc[:,['sub_prompt','story']]
prepared_data.rename(columns={'sub_prompt':'prompt', 'story':'completion'}, inplace=True)
new_stories_prepared = 'data/prepared_data_more.csv'
prepared_data.to_csv(new_stories_prepared, index=False)

subprocess.run('openai tools fine_tunes.prepare_data --file data/prepared_data_more.csv --quiet'.split())

继续微调:

subprocess.run('openai api fine_tunes.create --training_file data/prepared_data_more_prepared.jsonl --model curie:ft-bothub-ai:ultraman-2023-04-04-03-03-26 --suffix "ultraman" --learning_rate_multiplier 0.2'.split())

在原有的模型上微调的时候,我们要修改两个参数。

1. 第一个是 model 参数,我们把 Curie 换成了我们刚才微调之后的模型 curie:ft-bothub-ai:ultraman-2023-04-04-03-03-26

2. 第二个是 learning_rate_multiplier,这个参数的默认值是根据你的样本数量在 0.05 到 0.2 不等。如果你继续微调的样本数要比之前微调的数据量小很多,你就可以调得大一点。

微调更新之后,模型的名称没有变,老的模型就被更新成了微调后的新模型,我们再来试一下这个新模型。

fine_tuned = write_a_story("五代,流星火雨,艰难 ->\n")
print(fine_tuned)

输出结果:

这是一个发生在一个古老的世界,一个叫做“六代”的世界。这个世界有着一种叫做“超能力”的特性,可以让人穿越时空,穿越到现代。
一位叫做“英雄”的人物,他来自于六代,但他拥有了一种叫做“流星火雨”的超能力,他可以把自己的身体变成一个火焰,然后穿越时空,来到现代。
他来到现代,发现这个世界变得越来越危险,有一种叫做“怪兽”的存在,他们想要毁灭这个世界。英雄决定帮助奥特曼一起打败怪兽,于是他们开始了一场激烈的战斗。
英雄凭借着自己的超能力,以及奥特曼的力量,战胜了怪兽,拯救了这个世界。最后,英雄又一次穿越回六代,这次他拥有了一种叫做“流星火雨”的超能力,他可以把自己的身体变成一个火焰,然后穿越时空,拯救又一次六代。

流式生成

通过模型微调,我们拥有了一个可以讲故事的 AI 模型。不过,故事生成的体验稍微有点差。它不像是我们在 ChatGPT 的 Web 界面里那样一个词一个词地蹦出来,就像一个真人在给你讲故事那样。不过要做到这一点也并不难,因为 OpenAI 的 Completion 接口是提供了这样返回结果的模式的,只需要把代码小小地修改一下就好了。

def write_a_story_by_stream(prompt):
    response = openai.Completion.create(
        model="curie:ft-bothub-ai:ultraman-2023-04-04-03-03-26",
        prompt=prompt,
        temperature=0.7,
        max_tokens=2000,
        stream=True,
        top_p=1,
        stop=["."])
    return response

response = write_a_story_by_stream("汉,冰冻大海,艰难 ->\n")

for event in response:
    event_text = event['choices'][0]['text']
    print(event_text, end = '')

输出结果:

一位叫李英的汉朝时期的英雄人物,穿越到了现代,拥有了一种超能力,可以把自己的身体冰冻到极限,他发现自己可以拥有超越情感的力量,可以把任何人都冻僵,他也发现自己可以控制全局,可以控制时间,可以控制物质,可以控制情景,他发现自己可以控制一切,他变得更加强大。
李英发现,地球正面临着一个叫做怪兽的强大敌人的威胁,他决定去帮助奥特曼一起打败怪兽。于是,他和奥特曼一起开始了一系列的战斗,他们一起抵抗着怪兽的攻击,最终,他们成功地消灭了怪兽,拯救了地球。
李英受到了所有人的赞赏,他也成为了一个英雄,他的事迹被传颂了几百年,他的故事也被记录在历史书中,他也成为了一个永恒的传奇。

我们在调用 Completion 接口的时候,启用了 stream=True 这个参数。然后对于返回结果,我们不再是直接拿到整个 response 然后打印出来。而是拿到一个可以通过迭代器访问的一系列 events,每一个 event 都包含了一部分新生成的文本。试着运行一下这段代码,就能体验到 AI 把一个个词吐给你,好像真的在实时讲故事一样的感觉了。

小结

这一讲里,我们一起学习了 OpenAI 大语言模型里的最后两个功能。

第一个是模型微调,模型微调给我们提供了一个非常实用的能力,我们可以利用自己的数据,在 OpenAI 的基础模型上,调整模型参数生成一个新模型。这样我们就能够根据自己专有的垂直领域的数据,来生产一个专属于我们自己的模型。而且,我们可以根据新收集到的数据,不断在这个模型上继续微调迭代。不过,微调后的模型使用成本比较高,你需要自己核算一下,究竟是微调模型 ROI 比较高,还是使用前面的外部知识库的方式更划算一些。

在模型微调之外,我们还了解了 OpenAI 接口上的一个小功能,也就是流式地数据生成。通过开启流式地文本生成,我们可以交付给用户更好的交互体验。特别是在使用比较慢的模型,比如 GPT-4,或者生成的文本很长的时候,效果特别明显。用户不需要等上几十秒才能看到结果。

那到这里,整个课程的大语言模型部分我们也就介绍完了。从最基本的两个 API,Completion 和 Embedding 开始,介绍了各种各样的应用场景和使用方法。可以看到,现在的大语言模型几乎是“万能”的。下可以拿来做机器学习的输入数据,上可以直接让它自己决定调用什么 API,怎么解决用户的问题。相信看到这里的你,已经掌握如何使用大语言模型了,接下来就要多想想在你的实际工作里如何把它用起来了。

思考题

1. 在这一讲生成数据的时候,我们一条条去生成故事特别慢,而且每个组合的故事都要生成三条,特别消耗 Token。你想想这部分的代码,如何根据之前学到的内容优化一下呢?

2. 你能不能尝试通过流式处理,做一个讲故事的小应用?并且在界面上,用户能够看到故事真的是一个词儿一个词儿地蹦出来的。

3. OpenAI 的模型微调,其实还有很多更丰富的用法,比如可以拿来做分类,或者命名实体的提取。可以去官网的 Specific Guidelines 部分看一看,来试着微调一个模型,根据电商商品页的属性信息来写商品的详情描述。

推荐阅读

OpenAI 在自己的官方文档里,推荐了通过 Weight & Bias 这个公司的产品,来追踪微调后的模型的实验、模型与数据集。Weight & Bias 也在自己的文档里,提供了一个对 WIT 数据集进行模型微调的 Notebook,有兴趣的话也可以去看一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值