分享嘉宾:徐文浩, bothub 创始人

开篇词 & 导读

关于为什么我们要迎接AI爆发的时代

课程模块:
1、通过提示语(Prompt)和嵌入式表示(Embedding)这两个核心功能,看看大模型能帮我们解决哪些常见的任务。通过这一部分,熟悉 OpenAI 的 API,以及常见的分类、聚类、文本摘要、聊天机器人等功能,能够怎么实现。
2、进入真实的应用场景,把自己系统里面的信息,和 AI 系统结合到一起去,以解决和优化实际的业务问题
3、多模态,体验语音识别、语音合成,以及唇形能够配合语音内容的数字人,Stable Diffusion模型等


分享几个 AI 工具:

get ready with me

  1. 注册并获取你的 openAI 的API账号
    https://openai.com/api/

  2. 下载并安装 python 环境
    我使用的是 macos 系统,已经具备 python 环境

  3. 下载并安装 python 的包管理和环境管理工具
    接下来准备一个 python 的包管理和环境管理工具,比如miniconda

mkdir -p ~/miniconda3
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh -o ~/miniconda3/miniconda.sh
bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3
rm -rf ~/miniconda3/miniconda.sh

安装后,初始化

~/miniconda3/bin/conda init bash
~/miniconda3/bin/conda init zsh
  1. 创建一个独立的python 3.10环境,并命名为 py310
conda create --name py310 python=3.10
conda activate py310
  1. 在这个独立环境中安装 jupterlabopenAI等后面要用的一系列开发包
pip install jupyterlab==4.0.9
pip install ipywidgets==8.1.1
pip install openai==1.6.1
  1. 在安装后配置 API key 安装完 JupyterLab 之后,你只需要把刚才我们获取到的 API Key 设置到环境变量里,然后启动 JupyterLab。
export OPENAI_API_KEY=在这里写你获取到的ApiKey
  1. 打开 JupyterLab,并在浏览器里调试代码 通过 Jupyter Notebook 交互式地运行这个课程后面的代码,体验 OpenAI 的大语言模型神奇的效果。
jupyter-lab .
  1. 使用 Python 3 的 Notebook

有两种方法:
a) 本地搭建一个开发环境
b) 使用 Google 提供的叫做 Colab 的线上 Python Notebook 环境 https://colab.research.google.com/

Colab 可以让你免费使用一些 GPU 的资源,在你需要使用 GPU 尝试训练一些深度学习模型,而又没有一张比较好的显卡的时候,就可以直接使用它。另一方面,Colab 便于你在网络上把自己撰写的 Python 代码分享给其他人。
Colab 已经是一个 Python Notebook 的环境了。所以我们不需要再去安装 Python 和 JupyterLab 了。不过我们还是需要安装 OpenAI 的库,以及设定我们的 API Key。你只需要在 Notebook 开始的时候,执行下面这样一小段代码就可以做到这一点。

!pip install openai
%env OPENAI_API_KEY=在这里写你获取到的ApiKey

不过需要注意,如果你需要将 Notebook 分享出去,记得把其中 OpenAI 的 API key 删除掉,免得别人的调用,花费都算在了你头上。

  1. 编写代码并运行 可以参考 OpenAI 提供的示例,在上面的开发环境里运行体验一下: https://platform.openai.com/examples
from openai import OpenAI
client = OpenAI()

response = client.chat.completions.create(
  model="gpt-3.5-turbo",
  messages=[
    {
      "role": "system",
      "content": "You will be provided with a description of a mood, and your task is to generate the CSS code for a color that matches it. Write your output in json with a single key called \"css_code\"."
    },
    {
      "role": "user",
      "content": "Blue sky at dusk."
    }
  ],
  temperature=0.7,
  max_tokens=64,
  top_p=1
)

大语言模型工作原理分析

大型语言模型的接口其实非常简单。像 OpenAI 就只提供了 CompletionEmbedding 两个接口。
Completion 可以让模型根据你的输入进行自动续写;
Embedding 可以将你输入的文本转化成向量。

Completion 的参数

OpenAI 的接口文档: https://platform.openai.com/docs/api-reference/completions/create

/** parameter engine: 模型名称,required */
/** parameter prompt: 提示语,required */
/** parameter max_tokens: 调用生成的内容允许的最大token数目 */
/** parameter n: 备选答案 */
/** parameter stop: 停止符号 */
/** parameter temperature: 温度采样 */
/** parameter top_p: 原子核采样,和temperature类似 */
/** parameter suffix: 后缀 */
...
  • 参数 max_tokens,你可以简单地把 token 理解成一个单词。实际上,token 是分词之后的一个字符序列里的一个单元。有时候,一个单词会被分解成两个 token。比如,icecream 是一个单词,但是实际在大语言模型里,会被拆分成 ice 和 cream 两个 token。这样分解可以帮助模型更好地捕捉到单词的含义和语法结构。一般来说,750 个英语单词就需要 1000 个 token。
    注意:max_tokens不能超过模型的上下文长度。且这个数量既包括你输入的提示语,也包括 AI 产出的回答。
    我们这里用的 text-davinci-003 模型,允许最多有 4096 个 token。提示语和回答的长度加起来不能超过 4096 个 token。比如,你的输入有 1000 个 token,那么你这里设置的 max_tokens 就不能超过 3096。不然调用就会报错。

  • 参数 n,代表你希望 AI 给你生成几条内容供你选择。在这样自动生成客服内容的场景里,我们当然设置成 1。但是如果在一些辅助写作的场景里,你可以设置成 3 或者更多,供用户在多个结果里面自己选择自己想要的。

  • 参数stop,代表你希望模型输出的内容在遇到什么内容的时候就停下来,常常会选用 “\n\n"这样的连续换行。

  • 聊天AI 每次回复的内容不一样,则归功于我们使用的一个参数 temperature。这个参数的输入范围是 0-2 之间的浮点数,代表输出结果的随机性或者说多样性。top_p 也是随机性参数,两者使用其一即可。

配置 temperature 使回答不呆板(AI 客服)

例如:

from openai import OpenAI
import os

client = OpenAI(api_key = os.environ.get("OPENAI_API_KEY"))

COMPLETION_MODEL = "text-davinci-003"

prompt = '请你用朋友的语气回复给到客户,并称他为“亲”,他的订单已经发货在路上了,预计在3天之内会送达,订单号2021AEDG,我们很抱歉因为天气的原因物流时间比原来长,感谢他选购我们的商品。'

def get_response(prompt, temperature = 1.0, stop=None):
    completions = client.completions.create (
        model=COMPLETION_MODEL,
        prompt=prompt,
        max_tokens=1024,
        n=1,
        stop=stop,
        temperature=temperature,        
    )
    message = completions.choices[0].text
    return message

相同的提示语,连续调用两次后,给到含义相同、遣词造句不同的结果。

print(get_response(prompt))

亲,您的订单已经顺利发货啦!订单号是 2021AEDG,预计在 3 天之内会寄到您指定的地址。不好意思,给您带来了不便,原计划到货时间受天气原因影响而有所延迟。期待您收到衣服后给我们反馈意见哦!谢谢你选购我们的商品!

亲,您的订单 2021AEDG 刚刚已经发出,预计 3 天之内就会送达您的手中。抱歉由于天气的原因造成了物流延迟,但我们会尽快将订单发到您的手中。感谢您对我们的支持!

若配置 temperature = 0,则每次调用的回答都将得到一致的结果。

print(get_response(prompt, 0.0))

亲,您的订单 2021AEDG 已经发货,预计在 3 天之内会送达。很抱歉因为天气的原因物流时间比原来长,感谢您选购我们的商品,祝您购物愉快!
这样,我们就可以根据具体使用的场景调整 temperature 的值了。

如何实现联系上下文( AI 聊天机器人)

想要实现问答,我们只需要在提示语里,在问题之前加上 “Q :” 表示这是一个问题,然后另起一行,加上 “A :” 表示我想要一个回答,那么 Completion 的接口就会回答你在 “Q : ” 里面跟的问题。

对于聊天机器人来说,只理解当前用户的句子是不够的,能够理解整个上下文是必不可少的。而 GPT 的模型,要完成支持多轮的问答也并不复杂。我们只需要在提示语里增加一些小小的工作就好了———我们把整个对话过程都发送给 AI 来回答(相当于做递归/循环)。

代码可能写成这样:

from openai import OpenAI
import os

client = OpenAI(api_key = os.environ["OPENAI_API_KEY"])

def ask_gpt3(prompt):
    response = client.completions.create(
        model="text-davinci-003",
        prompt=prompt,
        max_tokens=512,
        n=1,
        stop=None,
        temperature=0.5,
    )

    message = response.choices[0].text.strip()
    return message

print("你好,我是一个聊天机器人,请你提出你的问题吧?")

questions = []
answers = []

def generate_prompt(prompt, questions, answers):
    num = len(answers)
    for i in range(num):
        prompt += "\n Q : " + questions[i]
        prompt += "\n A : " + answers[i]
    prompt += "\n Q : " + questions[num] + "\n A : "        
    return prompt

while True:
    user_input = input("> ")
    questions.append(user_input)
    if user_input.lower() in ["bye", "goodbye", "exit"]:
        print("Goodbye!")
        break
    
    prompt = generate_prompt("", questions, answers)

    answer = ask_gpt3(prompt)
    print(answer)
    answers.append(answer)

这段代码的意思是,AI 会从命令行读入你的问题,然后给出回答。你可以继续提问,然后我们把整个对话过程都发送给 AI 来回答。你可以尝试着体验一下,AI 是不是能够理解整个对话过程的上下文。你想要退出的时候,就在需要提问的时候,输入 “bye” 就好了。


使用更方便的模型 (gpt-3.5-turbo )实现聊天

在 2023年 3 月 2 日,因为 ChatGPT 的火热,OpenAI 放出了一个直接可以进行对话聊天的接口。这个接口叫做 ChatCompletion,对应的模型叫做 gpt-3.5-turbo,不但用起来更容易了,速度还快,而且价格也是我们之前使用的 text-davinci-003 的十分之一,可谓是物美价廉了。

这里查看 Chat Completions API 官方文档: https://platform.openai.com/docs/guides/text-generation/chat-completions-api

import openai
openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
    ]
)

在 OpenAI 的官方文档里,可以看到这个接口也非常简单。你需要传入的参数,从一段 Prompt 变成了一个数组,数组的每个元素都有 role 和 content 两个字段。

role 这个字段一共有三个角色可以选择,其中 system 代表系统,user 代表用户,而 assistant 则代表 AI 的回答。

当 role 是 system 的时候,content 里面的内容代表我们给 AI 的一个指令,也就是告诉 AI 应该怎么回答用户的问题。比如我们希望 AI 都通过中文回答,我们就可以在 content 里面写“你是一个只会用中文回答问题的助理”,这样即使用户问的问题都是英文的,AI 的回复也都会是中文的。

而当 role 是 user 或者 assistant 的时候,content 里面的内容就代表用户和 AI 对话的内容。和我们在第 03 讲里做的聊天机器人一样,你需要把历史上的对话一起发送给 OpenAI 的接口,它才能有理解整个对话的上下文的能力。

我们在下一篇详细了解如何制作一个聊天机器人。

logic


论文精读

如果你想知道 GPT 系列大模型到底是怎么回事儿,可以看一下李沐老师讲解 GPT 系列论文的视频