AI大模型使用(七)-模型微调与流式生成

今天我们可以用自己的数据训练出一个模型,这样就可以回答相关内容的问题,我们可以用OpenAI提供的模型微调功能。

为什么要微调呢,就是在某些领域里一些数据比较丰富,会比基础模型的某些数据更丰富,所以就可以利用这些数据来“微调”一个特别擅长这个垂直领域的模型,这个模型微调完,我们就可以直接提问了。

OpenAI提供模型微调还是很简单的,只要遵守它的规则就可以,按照它的格式,每一行里都有prompt属性和completion内容,就像如下的格式:

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

模型微调的过程,就是根据输入的内容,在原来的基础模型上训练。所以我们需要提供一个基础模型,在它的基础上进行微调,可以是Ada、Babbage、Curie 和 Davinci等等等。。

因为你提供数据的不同,所以微调出来擅长的领域也不同,如你提供的是笑话类的数据,那么它就是笑话类的模型,那么我们接下来就微调一个擅长写“历史英雄人物和奥特曼一起打怪兽”的AI。对应的故事数据,也利用ChatGPT的模型来帮助我们生成,代码如下:

以下代码也是很简单,定义的gpt35()函数中,我们把prompt和其他的基础配置传入进去,让OpenAI给我们生成对应的故事就可以了。

在prepare_stories()函数里,我们则多层嵌套循环将对应列表的数据动态传入到prompt来告诉AI要生成什么样子的数据。

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)

这里大家可以不用执行了,浪费token,可以直接用我给你的ultraman_stories.csv文件使用即可。(文件可以进入我的主页点击资源里可以看到)

拿到了原始数据,我们为了微调,需要先把内容转换为OpenAI需要的数据格式,代码如下:

# 读取数据
df=pd.read_csv("data/ultraman_stories.csv")
# 定义了prompt用dynasty+super_power+story_type内容形式
df["sub_prompt"]=df['dynasty']+","+df['super_power']+","+df['story_type']
print(df.head())
# 选取所有行中的sub_prompt列和story列
prepared_data=df.loc[:,['sub_prompt',"story"]]
print("prepared_data:",prepared_data)
# 为了能够让OpenAI进行微调需要将列数据处理为prompt和completion
prepared_data.rename(columns={'sub_prompt':'prompt','story':'completion'},inplace=True)
# 将更改完后的数据存储成csv
prepared_data.to_csv('data/prepared_data.csv',index=False)

我们把新生成的prepared_data.csv, 格式数据转换成微调模型所需要的 JSONL 格式的文件,这里需要执行OpenAI的命令,(注:在在线Colab中可直接生成)

!openai tools fine_tunes.prepare_data --file data/prepared_data.csv

也可以这样用如下的方式:

import subprocess
# 通过 subprocess 调用了命令行里的 OpenAI 工具,把上面的 CSV 文件,转化成了一个 JSONL 格式的文件,
subprocess.run('openai tools fine_tunes.prepare_data --file data/prepared_data.csv --quiet'.split())

执行完后此时就会在对应的目录下创建一个prepared_data_prepared.jsonl文件。

输出结果:

Unnamed: 0 dynasty super_power story_type  \
0           0       唐          隐形         轻松   
1           1       唐          隐形         轻松   
2           2       唐          隐形         轻松   
3           3       唐          隐形         努力   
4           4       唐          隐形         努力   

                                               story sub_prompt  
0  \n\n一位叫做李明的英雄人物,出生在唐朝时期。他是一个勇敢的将军,在他的一生中,他打败了许...    唐,隐形,轻松  
1  \n\n这是一个关于英雄的故事,发生在唐朝时期的中国。一个叫李自成的勇士,他拥有过人的勇气,...    唐,隐形,轻松  
2  \n\n某一天,一位叫做萧炎的唐朝时期英雄人物突然被一道神秘的光芒所笼罩,他被带到了现代。他...    唐,隐形,轻松  
3  \n\n这是一个关于英雄的故事。\n\n一位叫李明的唐朝时期的英雄人物,在一次偶然的机会中,...    唐,隐形,努力  
4  \n\n一位叫李世民的唐朝英雄,在一次意外中获得了隐形的超能力,他发现自己可以隐藏自己的身体...    唐,隐形,努力  
prepared_data:       sub_prompt                                              story
0        唐,隐形,轻松  \n\n一位叫做李明的英雄人物,出生在唐朝时期。他是一个勇敢的将军,在他的一生中,他打败了许...
1        唐,隐形,轻松  \n\n这是一个关于英雄的故事,发生在唐朝时期的中国。一个叫李自成的勇士,他拥有过人的勇气,...
2        唐,隐形,轻松  \n\n某一天,一位叫做萧炎的唐朝时期英雄人物突然被一道神秘的光芒所笼罩,他被带到了现代。他...
3        唐,隐形,努力  \n\n这是一个关于英雄的故事。\n\n一位叫李明的唐朝时期的英雄人物,在一次偶然的机会中,...
4        唐,隐形,努力  \n\n一位叫李世民的唐朝英雄,在一次意外中获得了隐形的超能力,他发现自己可以隐藏自己的身体...
..           ...                                                ...
459  南北朝,瞬间移动,轻松  \n\n这个故事发生在南北朝朝时期,一位叫做“英雄”的英雄人物,他有着不凡的武功,曾经参与过...
460  南北朝,瞬间移动,轻松  \n\n故事发生在南北朝朝时期,一位英雄人物叫做李志,他拥有一股超乎常人的勇气和胆量,他曾经...
461  南北朝,瞬间移动,轻松  \n\n一位叫苏轼的南北朝朝时期的英雄人物,突然获得了一种叫做“瞬间移动”的超能力,可以在瞬...
462  南北朝,瞬间移动,努力  \n\n苏秦,一位来自南北朝时期的英雄人物,他拥有一股坚强的信念,他深知自己的使命,就是要拯...
463  南北朝,瞬间移动,努力  \n\n一位叫做张翼德的南北朝朝时期的英雄人物,一次偶然的机会,穿越到了现代,拥有了瞬间移动...

[464 rows x 2 columns]

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

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

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

有了准备好的数据以后,我们来提交微调模型的指令:

# 提交微调指令
# 1.第一个参数为用来训练的数据文件--training_file
# 2.第二个参数是基础模型--model,第三个参数是生成模型的后缀--suffix
subprocess.run('openai api fine_tunes.create -t data/prepared_data_prepared.jsonl -m curie --suffix "ultraman"'.split())
# 或者用这个指令,用上面指令不展示任何信息
!openai api fine_tunes.create -t "data/prepared_data_prepared.jsonl" -m curie --suffix "ultraman"

执行这个指令可能会有些慢,可以稍等下,然后执行完成以后我们可以看下自己微调的列表是否有这个模型,用fine_tunes.list指令,找出所有我们微调的模型,

!openai api fine_tunes.list

结果:给出部分,我可能执行了很多次,第一次就成功了,后面就一直重复执行就导致都是空的,所以我这个结果就给出对的数据给大家参考下:

 {
      "object": "fine-tune",
      "id": "ft-YhqevPsG5vqQYsOBsT2DNzug",
      "hyperparams": {
        "n_epochs": 4,
        "batch_size": 1,
        "prompt_loss_weight": 0.01,
        "learning_rate_multiplier": 0.2
      },
      "organization_id": "org-WdebHKFlmMZipWcgP2HMj6CQ",
      "model": "curie",
      "training_files": [
        {
          "object": "file",
          "id": "file-1wlslxaby6NtF3ete7gRfWcM",
          "purpose": "fine-tune",
          "filename": "data/prepared_data_prepared.jsonl",
          "bytes": 446199,
          "created_at": 1696212949,
          "status": "processed",
          "status_details": null
        }
      ],
      "validation_files": [],
      "result_files": [
        {
          "object": "file",
          "id": "file-rpDyjhpSQvbeVgvDoaetLD8o",
          "purpose": "fine-tune-results",
          "filename": "compiled_results.csv",
          "bytes": 107765,
          "created_at": 1696257798,
          "status": "processed",
          "status_details": null
        }
      ],
      "created_at": 1696256754,
      "updated_at": 1696257798,
      "status": "succeeded",
      "fine_tuned_model": "curie:ft-personal:ultraman-2023-10-02-14-43-17"
    }
  ],
  "next_starting_after": null
}

fine_tuned_model:这个属性就是微调完的模型,也是属于我自己的模型,这个模型就擅长讲奥特曼的故事。这个模型怎么使用呢?

使用是很简单的,跟平常我们用OpenAI的内置模型一样使用,在使用model时用我们自己的模型就可以啦,prompt则需要我们定义时的那般,对应在调用模型的时候,我们使用的提示语就是“朝代”+“超能力”+“故事类型”,并且跟着“->\n”,而 stop 则是设置成了“.”。

from openai.api_resources import model
import os
import openai

# 可以使用自己微调的模型了
def write_a_story(prompt):
  response=openai.Completion.create(
      model="curie:ft-personal:ultraman-2023-10-02-14-43-17",
      prompt=prompt,
      temperature=0.7, 
      max_tokens=2000, 
      top_p=1, 
      stop=["."]
    )
  return response["choices"][0]["text"]

# prompt就是朝代+超能力+故事类型
story=write_a_story("宋,发射激光,艰难->\n")
print(story)

结果:

宋朝时期,有一位叫做李晓明的英雄人物,他拥有超凡的武功,曾经帮助宋朝抵御了许多强大的敌人,他也曾经受到宋朝皇帝的尊重和爱戴。
一天,李晓明突然被一道神秘的光束穿越到了现代,他发现自己拥有了发射激光的超能力,他可以用这种能力来抵抗外来敌人的侵略。
他发现,现代的世界也遭受着怪兽的侵略,他决定帮助奥特曼一起抵抗怪兽的侵略。于是,他和奥特曼一起出发了,他们经历了许多艰难的战斗,最终成功地抵抗了怪兽的侵略。
最后,李晓明帮助奥特曼把怪兽赶走了,他们终于安然无恙,李晓明也回到了宋朝,他从此成为了宋朝时期最传奇的英雄人物。

因为这是一个微调的模型,它不仅拥有我们训练数据提供的知识,也包括基础模型里的各种信息。所以我们使用的朝代、超能力和故事类型也可以是在之前微调数据里面没有出现过的。

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

仍然给了关于prompt需要的相关的故事。 

一位叫陈家豪的秦朝英雄人物,一次偶然的机会,被一位神秘的老人带到了现代。老人告诉陈家豪,
他可以在现代拥有超能力,可以控制风,可以控制雷电,可以控制火焰,可以控制雪地,
可以控制时空,可以控制时空,可以控制时空,可以控制时空,可以控制时空,

1.微调模型的成本

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

!openai api fine_tunes.results -i ft-YhqevPsG5vqQYsOBsT2DNzug

结果:(注:列出部分数据),

step,elapsed_tokens,elapsed_examples,training_loss,training_sequence_accuracy,training_token_accuracy
1845,1221861,1845,0.22137291308707682,0.0,0.9357664233576642
1846,1222614,1846,0.20198583161737482,0.0,0.9428571428571428
1847,1223247,1847,0.17845800522111036,0.0,0.9510603588907015
1848,1223840,1848,0.17119306431039957,0.0,0.9491228070175438
1849,1224585,1849,0.20582847552223815,0.0,0.9349030470914127
1850,1225306,1850,0.21110617109638313,0.0,0.9457142857142857
1851,1225859,1851,0.16637289547212905,0.0,0.9472693032015066
1852,1226396,1852,0.13065658116122625,0.0,0.9636015325670498
1853,1227149,1853,0.23351608868746224,0.0,0.9290586630286494
1854,1227750,1854,0.18731578068527877,0.0,0.9499136442141624
1855,1228711,1855,0.2482453721564184,0.0,0.9262820512820513
1856,1229312,1856,0.14391189835758778,0.0,0.9517241379310345
1857,1230153,1857,0.17097125453625922,0.0,0.9434194341943419

elapsed_tokens:消耗的token

training_token_accuracy:训练的准确性,可以发现准确性越来越高

2 增量模型

随着数据越来越多,模型也需要升级,需要将新增的数据添加到以前的模型中,这样我们怎么操作呢?

我们假如又有了额外的数据,我们在生成一些如下的数据,这个你不用生成,直接拿我给你的文件ultraman_stories_more.csv

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)

! openai tools fine_tunes.prepare_data --file data/prepared_data_more.csv --quiet

微调数据提交,--model模型改为我们自己微调的数据,--learning_rate_multiplier如果微调的数据量很小,则可以调的大一些,0.05 到 0.2

! openai api fine_tunes.create --training_file data/prepared_data_more_prepared.jsonl --model curie:ft-personal:ultraman-2023-10-02-14-43-17 --suffix "ultraman" --learning_rate_multiplier 0.2

微调更新之后,模型的名称没有变,老的模型就被更新成了微调后的新模型,继续使用

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

结果:

这是一个关于英雄的传奇故事,讲述的是一个公元前五代的人物,他叫马英九。他拥有超凡的勇气和智慧,帮助他的国家战胜了强大的敌人,赢得了国家的尊重和荣耀。
然而,在一次重大的战斗中,马英九被一颗神秘的宇宙石击中,穿越到了现代。他发现自己拥有了一种神奇的能力,可以使用流星火雨这样的超能力,来帮助他的国家抵御敌人的侵略。
他发现,现代世界也有怪兽,他们正在破坏着这个世界,马英九决定要帮助奥特曼一起打败他们。他凭借着自己的勇气和智慧,和奥特曼一起,打败了怪兽,拯救了这个世界。
最终,马英九又回到了自己的时代,他的传奇故事也传遍了整个国家,他成为了一个永恒的英雄,永远被人们铭记。

3 流式生成

如果你想要增加趣味,可以在web界面上看到数据一个一个打出来,可以通过添加属性stream=True,这样就一个字一个字蹦出来,得到的结果用循环取出数据。

def write_a_story_by_stream(prompt):
    response = openai.Completion.create(
        model="curie:ft-personal:ultraman-2023-10-02-14-43-17",
        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='')

结果:

汉朝时期,一位叫陈洋的英雄人物,他拥有超凡的武功,曾经帮助汉朝统治者抵抗外族的侵略,为国家作出了巨大的贡献。
然而,最终,陈洋被汉朝的政治体制所困扰,他被抛弃在一个封闭的房间里,被无情地抛弃,被抛弃了整整一世纪。
直到有一天,陈洋突然被神秘的力量抽离了汉朝的时代,穿越到了现代,他发现自己拥有了一种不可思议的超能力——冰冻大海。
他发现,现代的世界也面临着一个可怕的威胁——怪兽的入侵,他被迫决定拯救世界,与奥特曼一起,陈洋利用自己的超能力,帮助奥特曼打败了怪兽,拯救了世界。
最终,陈洋成功地将怪兽击退,世界得以安宁,他也成为了现代的英雄,他的英勇事迹也被永远铭记在人们的心中。

相关的视频内容:

模型微调与流式生成_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值