推荐序
可略过不看。
初识GPT-4和ChatGPT
LLM概述
NLP的目标是让计算机能够处理自然语言文本,涉及诸多任务:
- 文本分类:将输入文本归为预定义的类别。
- 自动翻译:将文本从一种语言自动翻译成另一种语言,包括程序语言。
- 问题回答:根据给定的文本回答问题。
- 文本生成:根据给定的输入文本(提示词)生成连贯且相关的输出文本。
n-gram模型:通过使用词频来根据前面的词预测句子中的下一个词,其预测结果是在训练文本中紧随前面的词出现的频率最高的词。有时会生成不连贯的文本,在理解上下文和语法方面仍需改进。
改进n-gram模型的算法:循环神经网络(recurrent neural network,RNN)和长短期记忆(long short-term memory,LSTM)网络,依然存在问题:很难处理长文本序列并记住其上下文。
GPT模型简史:从GPT-1到GPT-4
监督学习:使用大量的手动标记数据。
GPT-1:引入无监督的预训练步骤,使用BookCorpus数据集。
GPT-2:GPT-1的扩展版本,其参数量和训练数据集的规模大约是GPT-1的10倍。公开可用,可从Hugging Face或GitHub下载。
GPT-3:主要区别在于模型的大小和用于训练的数据量,取消微调步骤,
InstructGPT:GPT-3的新版本,通过强化学习和人类反馈进行优化。训练过程主要有两个阶段:监督微调(supervised fine-tuning,SFT)和通过人类反馈进行强化学习(reinforcement learning from human feedback,RLHF)。
Codex:
GPT-4:
LLM用例和产品
如Be My Eyes、可汗学院、Yabble、Waymark、Inworld AI。
AI幻觉
AI会给出错误的,过时的,虚构的信息。
插件和微调
优化GPT模型,提高其能力的两种方式:插件和微调。
GPT模型的局限:没有直接访问互联网的权限,无法获取新信息,其知识仅限于训练数据。
深入了解GPT-4和ChatGPT的API
基本概念
OpenAI API提供的可用模型
OpenAI Playground
OpenAI Playground是一个Web站点,可用来测试OpenAI提供的语言模型,
使用OpenAI Python库
在Windows 11中永久添加或更改环境变量,Windows+R打开运行窗口,键入sysdm.cpl
打开系统属性
面板,在高级——环境变量里,可使用OpenAI密钥添加一个新的环境变量。
提供命令行实用程序:
openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world"
使用GPT-4和ChatGPT
借助于tiktoken库(参考GitHub),开发人员能够计算文本字符串中的标记数,进而估算使用成本。
OpenAI提供的可选参数
字段名称 | 类型 | 描述 |
---|---|---|
temperature | 数值,默认值为1,可接受介于0和2之间的值 | 温度为0意味着对于给定的输入,对模型的调用很可能会返回相同的结果,尽管响应结果会高度一致,但OpenAI不保证确定性输出,温度越高,结果的随机性就越强,LLM通过预测一系列标记来生成回答,根据输入上下文,LLM为每个潜在的标记分配概率,当温度被设置为0时,LLM将始终选择概率最高的标记,较高的温度可产生更多样化、更具创造性的输出 |
n | 整型,默认值为1 | 通过设置这个参数,可为给定的输入消息生成多个回答。如果将温度设为0,虽然可得到多个回答,但它们将完全相同或非常相似 |
stream | 布尔型,默认值为false | 允许回答以流的格式呈现,即并非一次性发送整条消息。当回答的内容较长时,可提供更好的用户体验 |
max_tokens | 整型 | 指定在聊天中生成的最大标记数。强烈建议将其设置为合适的值以控制成本。如果该参数设置得过大,会被OpenAI忽略:输入和生成的标记总数不能超过模型的上限 |
tools | ||
response_format | ||
seed |
输出参数
字段名称 | 类型 | 描述 |
---|---|---|
choices | 对象数组 | 包含模型实际响应的教组。默认情况下,该数组只有一个元素,可通过参数n (上表的输入参数)进行更改,该元素包含以下内容。finish_reason:字符串,回答结束的原因; index:整型,从choices数组中选择对象的索引; message:对象,包会一个role和一个content,role始终是assistant,content包括模型生成的文本。通常希望获得这样的字符串:response[‘choices’][0][‘message’][‘content’] |
created | 时间戳 | 生成时的时间戳 |
id | 字符串 | OpenAl内部使用的技术标识符 |
model | 字符串 | 所用的模型。这与作为输入设置的模型相同 |
object | 字符串 | 对于GPT-4和GPT-3.5模型,这始终应为chat.completion,使用的是ChatCompletion端点 |
usage | 字符串 | 提供有关在此查询中使用的标记数的信息,从而提供费用信息。prompt_tokens表示输入中的标记数,completion_tokens:表示输出中的标记数。total_tokens=prompt_tokens+completion_tokens |
其他文本补全模型
文本补全和聊天补全。
文本补全使用Completion端点,
字段名称 | 类型 | 描述 |
---|---|---|
model | 字符串,必埴 | 所用模型ID,与ChatCompletion相同 |
prompt | 字符串或数组,默认值是<|endoftext|> | 生成补全内容的提示词。体现Completion端点与 ChatCompletion端点的主要区别。Completion.create 应编码为字符串、字符串数组、标记数组或标记数组的数组。如果没有提供该参数,模型将从新文档的开头生成文本 |
max_tokens | 整型 | 最大标记数,默认为16,对于某些用例可能太小,应根据需求进行调整 |
suffix | 字符串,默认值是null | 补全之后的文本。该参数允许添加后缀文本、插入操作 |
考虑因素
使用API需考虑两个因素:成本和数据隐私。
OpenAI声称不会将作为模型输入的数据用于重新训练;但是,用户的输入将被保留30天,用于监控和使用合规检查目的。即,OpenAI员工和第三方承包商可能会访问你的数据。
个人信息和输入的数据可能会传输到OpenAI在美国的服务器上。
其他
使用GPT-4和ChatGPT构建应用程序
概述
用户提供API密钥时的管理原则:
- 对于Web应用程序,将API密钥保存在用户设备的内存中,而不要用浏览器存储;
- 如果选择后端存储API密钥,则需强制采取高安全性的措施,并允许用户自己控制API密钥,包括删除API密钥;
- 在传输期间和静态存储期间加密API密钥。
使用自己的API密钥,应遵循的最佳实践:
- 永远不要直接将API密钥写入代码中;
- 不要将API密钥存储在应用程序的源代码文件中;
- 不要在用户的浏览器中或个人设备上使用你的API密钥;
- 设置使用限制,以确保预算可控。
设计原则
没啥好说的,解耦合等架构设计原则。
漏洞
将用户输入作为提示词发送给LLM的任何面向用户的应用程序都容易受到提示词注入攻击。
建议结合两种方法:
- 添加分析层来过滤用户输入和模型输出;
- 意识到提示词注入不可避免,并采取一定的预防措施。
降低受提示词注入攻击的风险的方法:
- 使用特定规则控制用户输入:具体业务情况设置具体规则;
- 控制输入长度:控制成本。还能降低风险:输入越短,攻击者找到有效恶意提示词的可能性就越小;
- 控制输出:验证输出以检测异常情况;
- 监控和审计:监控应用程序的输入和输出,以便能够在事后检测到攻击。还可对用户进行身份验证,以便检测和阻止恶意账户;
- 意图分析:分析用户输入以检测提示词注入。
这个问题无法避免,因此需要考虑后果:
- 指令可能被泄露:确保你的指令不包含任何对攻击者有用的个人数据或信息;
- 攻击者可能尝试从你的应用程序中提取数据:如果你的应用程序需要操作外部数据源,请确保在设计上不存在任何可能导致提示词注入从而引发数据泄露的方式。
示例项目
获取YouTube字幕:使用第三方库,如youtube-transcript-api,又或者使用Captions Grabber等Web实用工具。
映射—归约:当输入文本超过模型的上限(某个模型不一致,如4096个标记),则需要多次执行请求。
三个组件:
- 意图服务:当用户向应用程序提问时,检测用户的意图。应检测出需要使用的数据源,用户所提的问题是否遵守OpenAI的使用规则,是否包含敏感信息。
- 信息检索服务:将获取意图服务的输出并检索正确的信息。这意味着你已经准备好数据,并且数据在该服务中可用。将比较自己的数据和用户查询之间的嵌入。嵌入将使用OpenAI API生成并存储在向量存储系统中。
- 响应服务:将使用信息检索服务的输出,并从中生成用户所提问题的答案。
OpenAI提供的Whisper库(参考GitHub)实现从语音到文本的转换功能。
Gradio:构建用户界面的工具,可将ML模型快速转换为可访问的Web界面。
GPT-4和ChatGPT高级技巧
提示工程
一门新兴学科,专注于以最佳实践构建LLM输入,从而尽可能以程序化方式生成目标输出。旨在为生成式AI模型设计和优化提示词,以获得更高质量的模型响应。
在提示词中定义三大要素:角色、上下文和任务。
高级技巧:零样本学习、少样本学习和微调。
TL;DR
是too long; didn't read
的缩写,意为太长,没读
。据发现,只需在文本末尾添加tl;dr
,即可请求模型对文本进行总结。
GPT-4不擅长计算。
思维链,CoT,指使用提示词鼓励模型逐步模仿推理的技术。
零样本思维链策略:zero-shot-CoT strategy。具体地,在提示词的末尾添加让我们逐步思考(Let's think step by step)
这样的话,已被证明可使模型解决更复杂的推理问题。
少样本学习:few-shot learning,仅通过提示词中的几个示例就能进行概括并给出有价值的结果。
单样本学习:one-shot learning,只提供一个示例来帮助模型执行任务。优点:更简单、生成速度更快、计算成本(API使用成本)更低。
提示工程中的其他技术:
- 自洽性(self-consistency)
- 思维树(tree of thoughts)
- 推理与行动(ReAct)
改善提示效果的技巧:
- 指示模型提出更多问题:在提示词的末尾,询问模型是否理解问题并指示模型提出更多问题。
- 格式化输出:JSON
- 重复指示:经验表明,重复指示会取得良好的效果,尤其是当提示词很长时。基本思路是,在提示词中多次添加相同的指令,但每次采用不同的表述方式。也可通过负面提示来实现。
- 使用负面提示:通过指定不希望在输出中看到的内容来引导模型,用于滤除某些类型的回答。
- 添加长度限制:比如限定200字以内,当然给出的答复可能会稍微超过200字。
微调
使用LangChain和插件增强LLM功能
LangChain
LangChain:专用于开发LLM驱动型应用程序的框架,安装:pip install langchain
。
关键模块:
- Models:模型,该模块是由LangChain提供的标准接口,可通过它与各种LLM进行交互。LangChain支持集成OpenAI、Hugging Face、Cohere、GPT4All等提供商提供的不同类型的模型。
- Prompts:提示词,已成为LLM编程的新标准。该模块包含许多用于管理提示词的工具。
- Retrieval:该模块让你能够将LLM与你的数据结合使用,之前的叫法是Indexes(索引);
- Chains:链,通过该模块,LangChain提供了Chain接口。你可以使用该接口创建一个调用序列,将多个模型或提示词组合在一起。
- Agents:智能体,该模块引入Agent接口。所谓智能体,就是一个可以处理用户输入、做出决策并选择适当工具来完成任务的组件。它以迭代方式工作,采取一系列行动,直到解决问题。
- Memory(记忆)该模块让你能够在链调用或智能体调用之间维持状态。默认情况下,链和智能体是无状态的。这意味着它们独立地处理每个传入的请求,就像LLM一样。
动态提示词
示例程序:
from langchain.chat_models import ChatOpenAI
from langchain import PromptTemplate, LLMChain
template = """Question: {question}
Let's think step by step.
Answer: """
prompt = PromptTemplate(template=template,
input_variables=["question"])
llm = ChatOpenAI(model_name="gpt-4")
llm_chain = LLMChain(prompt=prompt, llm=llm)
question = """ What is the population of the capital of the country
where the Olympic Games were held in 2016? """
llm_chain.run(question)
智能体及工具
LangChain中有许多预定义的工具,包括谷歌搜索、维基百科搜索、Python REPL、计算器、世界天气预报API等。
智能体安排的步骤如下所述。01.智能体收到来自用户的输入。02.智能体决定要使用的工具(如果有的话)和要输入的文本。03.使用该输入文本调用相应的工具,并从工具中接收输出文本。04.将输出文本输入到智能体的上下文中。05.重复执行步骤2~步骤4,直到智能体决定不再需要使用工具。此时,它将直接回应用户。
from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools, initialize_agent, AgentType
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# load_tools函数加载工具
tools = load_tools(["wikipedia", "llm-math"], llm=llm)
# initialize_agent函数创建智能体
agent = initialize_agent(
tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True # 设置为True,可查看智能体的推理过程,并理解它如何做出最终决策
)
question = """What is the square root of the population of the capital
of the Country where the Olympic Games were held in 2016?"""
agent.run(question)
在使用维基百科搜索工具前,需安装相应的Python包:pip install wikipedia
。
记忆
使用LangChain,可轻松地为链和智能体添加状态以管理记忆。
from langchain import OpenAI, ConversationChain
chatbot_llm = OpenAI(model_name='text-ada-001')
chatbot = ConversationChain(llm=chatbot_llm , verbose=True)
chatbot.predict(input='Hello')
嵌入
通过document_loaders模块,可快速地将文本数据从不同的来源加载到应用程序中。
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("ExplorersGuide.pdf")
pages = loader.load_and_split()
pip install pypdf
安装pypdf包。
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
使用OpenAIEmbeddings前,需安装tiktoken包。
Faiss:向量数据库,主要由Facebook AI团队开发的相似性搜索库。
GPT-4插件
OpenAI的目标是创建一个生态系统,插件可以帮助塑造AI与人类互动的未来。
GPT-4中的插件更专注于第三方服务,而不是LangChain工具。
在开发插件前,必须创建一个API并将其与两个描述性文件关联起来:
ai-plugin.json
:插件清单文件openapi.yaml
:OpenAPI规范
当你开始与GPT-4进行交互时,OpenAI会向GPT-4发送一条隐藏消息,以检查你的插件是否已安装。这条消息会简要介绍你的插件,包括其描述信息、端点和示例。
使用插件的快速入门,参考GitHub,main.py
如下:
import json
import quart
import quart_cors
from quart import request
# 创建一个Quart App,并配置它允许来自https://chat.openai.com的CORS(cross-origin resource sharing,跨源资源共享)请求
app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")
# 跟踪待办事项。如果Python会话重新启动,则不会持久保存
_TODOS = {}
@app.post("/todos/<string:username>")
async def add_todo(username):
request = await quart.request.get_json(force=True)
if username not in _TODOS:
_TODOS[username] = []
_TODOS[username].append(request["todo"])
return quart.Response(response='OK', status=200)
@app.get("/todos/<string:username>")
async def get_todos(username):
return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)
@app.delete("/todos/<string:username>")
async def delete_todo(username):
request = await quart.request.get_json(force=True)
todo_idx = request["todo_idx"]
# fail silently, it's a simple plugin
if 0 <= todo_idx < len(_TODOS[username]):
_TODOS[username].pop(todo_idx)
return quart.Response(response='OK', status=200)
@app.get("/logo.png")
async def plugin_logo():
filename = 'logo.png'
return await quart.send_file(filename, mimetype='image/png')
@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
host = request.headers['Host']
with open("./.well-known/ai-plugin.json") as f:
text = f.read()
return quart.Response(text, mimetype="text/json")
@app.get("/openapi.yaml")
async def openapi_spec():
host = request.headers['Host']
with open("openapi.yaml") as f:
text = f.read()
return quart.Response(text, mimetype="text/yaml")
def main():
app.run(debug=True, host="0.0.0.0", port=5003)
if __name__ == "__main__":
main()
解读:
- Quart是一个Python Web微框架
- Quart-CORS是一个扩展,可控制CORS
每个插件都需要在API域上有一个ai-plugin.json
文件:
{
"schema_version": "v1",
"name_for_human": "TODO List (no auth)",
"name_for_model": "todo",
"description_for_human": "Manage your TODO list. You can add, remove and view your TODOs.",
"description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "http://localhost:5003/openapi.yaml"
},
"logo_url": "http://localhost:5003/logo.png",
"contact_email": "legal@example.com",
"legal_info_url": "http://example.com/legal"
}
openapi.yaml
示例:
openapi: 3.0.1
info:
title: TODO Plugin
description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
version: 'v1'
servers:
- url: http://localhost:5003
paths:
/todos/{username}:
get:
operationId: getTodos
summary: Get the list of todos
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/getTodosResponse'
post:
operationId: addTodo
summary: Add a todo to the list
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/addTodoRequest'
responses:
"200":
description: OK
delete:
operationId: deleteTodo
summary: Delete a todo from the list
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/deleteTodoRequest'
responses:
"200":
description: OK
components:
schemas:
getTodosResponse:
type: object
properties:
todos:
type: array
items:
type: string
description: The list of todos.
addTodoRequest:
type: object
required:
- todo
properties:
todo:
type: string
description: The todo to add to the list.
required: true
deleteTodoRequest:
type: object
required:
- todo_idx
properties:
todo_idx:
type: integer
description: The index of the todo to delete.
required: true
https://github.com/openai/plugins-quickstart/blob/main/openapi.yaml
在撰写描述时,必须遵循以下最佳实践:
- 不要试图影响GPT的
情绪
、个性或确切回应; - 避免指示GPT使用特定的插件,除非用户明确要求使用该类别的服务;
- 不要为GPT指定特定的触发器来使用插件,因为它旨在自主确定何时使用插件。
https://github.com/openai/whisper