所有文章 > API产品 > OpenAI API—文本分析的瑞士军刀

OpenAI API—文本分析的瑞士军刀

前言:文本分析的瑞士军刀

自2022年底以来,ChatGPT/OpenAI一直是我耳边最频繁出现的热词。在金融/会计领域的研究中,大型语言模型的出现已经深刻地影响了我们的研究范式。从2023年开始,出现了越来越多的工作论文开始应用OpenAI API进行文本分析。

本系列的标题是“文本分析的瑞士军刀”。瑞士军刀以其多功能性和精巧设计而闻名,虽然体积小巧,但功能却强大。这一性质恰如调用OpenAI API进行文本分析,它提供了多种文本分析工具,包括语言生成、嵌入、模型微调等,极大地简化了之前传统文本分析的过程,使得我们能够更加高效地处理各种文本分析任务。

本系列推文的目的在于学会如何简单高效地调用OpenAI API进行文本分析。我们将从API的基本使用入手,详解各类文本分析功能的调用方法和应用场景,介绍代码实例,并分享相关文献。

了解 OpenAI API

尽管在很多场景中,LLM、ChatGPT和OpenAI API等概念常常交叉使用,但它们实际上是有着明确分工的。

LLMs (Large Language Models)

  • LLMs(Large Language Models)是指那些具有数十亿至数百亿参数的深度学习模型,经过大规模的语言数据训练而得到。它们具备了强大的自然语言理解和生成能力,可应用于多种文本处理任务。

ChatGPT

  • ChatGPT 是 OpenAI 基于 GPT(Generative Pre-trained Transformer)架构开发的聊天机器人,是GPT技术在聊天对话领域的具体应用。

OpenAI API

  • OpenAI API指的是由OpenAI公司提供的一系列应用程序接口(API)。用户可以通过 API 调用来使用各类模型,而无需自行训练和部署复杂的模型,从而实现低成本、低门槛、高效地应用自然语言处理等技术。

总的来说,LLM是基础模型和技术,ChatGPT是针对对话任务的具体产品实现,而OpenAI API则是OpenAI将其人工智能能力开放获取的渠道和方式。

OpenAI API 模型

OpenAI 提供了一系列可选择的模型,其官方文档对这些模型进行了详细说明,并提供了示例代码,在此不做重复说明。

本系列我们主要关注以下两个模型:

  • GPT-3.5- Turbo:用于文本生成、对话系统和语言理解等任务。
  • text-embedding-ada-002:用于文本嵌入(Text Embedding)任务,将文本转换成高维向量表示。

调用OpenAI API

API Key

在开始使用 OpenAI API之前,首先需要前往 Azure 或 OpenAI 官网申请 API 密钥(不同的服务提供商申请流程略有不同)。但是由于&*%@的原因……,总之这个过程没有统一完备的解法和攻略,需要大家各显神通。

为了确保API密钥的安全性,我们首先应当学会将其储存在环境变量中,而不是直接写入源代码。将API密钥直接硬编码在代码中是非常不安全的做法,会增加密钥泄露的风险。

右键点击”此电脑”,选择”属性”。在弹出窗口中,点击”高级系统设置”。接着,在”系统属性”对话框中,依次点击”高级”→”环境变量”。在这里,我们可以为系统或当前用户新建”OPENAI_API_KEY”的环境变量,并将OpenAI API密钥字符串作为变量值进行存储。

安装 OpenAI Python 库

OpenAI 提供了一个Python库(https://github.com/openai/openai-python)。安装之前,请确保你的 Python 版本是 3.7.1 或更高版本。

pip install openai

调用OpenAI’s (Python) API

下面我将以 GPT-3.5-turbo 模型为例,展示调用API的代码。需要注意,不同的服务提供商(Azure和OpenAI)调用API的方式略有不同,下文会分别讲解。调用OpenAI API的计费取决于调用的模型类型以及消耗的Token数量,一般按照每百万或每千个Tokens进行结算。作为参考,根据OpenAI官方文档:对于英文文本,1 个Token大约为 4 个字符或 0.75 个单词

之前我们已经将API KEY储存在环境变量中,而dotenv是一个用于加载环境变量的Python库。利用下面的代码,我们从环境变量中获取OpenAI API密钥,并将其设置为全局变量,以便在后续的OpenAI API调用中安全使用。

import openai
import os

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

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

接下来,我们定义一个函数get_completion_openai

它有两个参数:prompt表示输入文本,model表示要使用的模型,默认为gpt-3.5-turbo

import os
import openai
import json

def get_completion_openai(prompt, model="gpt-3.5-turbo"):
openai.api_key = os.getenv('OPENAI_API_KEY')

messages = [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": prompt
}
]
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=0
)
return response.choices[0].message['content']

在这段代码中,messages 列表中的每个字典代表一个对话消息,其中包含两个键值对:

  • "role":在 OPENAI API 中,提供了三种角色,允许与 LLM 进行不同类型的交互。而"role"参数指定了消息的角色,可以是 “system” ,”user”或”assistant “。
  • "content":包含了消息的内容。在上面的代码中,第一个"content"是系统(system)的介绍性消息,第二个"content"是用户(user)输入的内容。

“system” 角色

  • 系统角色是一种隐藏的角色,通常用于提示助手如何与用户交互、提供一些系统生成的信息或指导。你可以想象它在助手的耳边低语,引导它的回应,但用户不会注意到系统消息。在我们平常和ChatGPT Web的交互中,其实系统角色也是存在的,只是我们在网页上看不见而已。

“user” 角色

  • 表示用户角色,即用户发出的消息或输入的内容,这个内容将被传递给 OpenAI 的文本生成模型,以进行后续的对话补全。

“assistant” 角色

  • 助手角色代表着对话系统中的 AI 助手,根据用户的输入来生成相应的回复。比如,在 ChatGPT 网页界面中,我们的消息就是用户消息,而 ChatGPT 的消息称为助手消息。

response = openai.ChatCompletion.create(……)这一行使用 OpenAI API向指定的 GPT 模型发送请求。

  • model 参数指定了要使用的模型名称
  • messages 参数传递了之前创建的 messages 列表
  • temperature=0 设置确保模型的输出是确定的。temperature接近 1 时,模型会产生更多出人意料的、新颖的内容(但我们做实证研究中的文本分析,为了确保结果是可复现的,一般这个参数都要设置为0)。

openai.ChatCompletion.create(……)上述示例代码中没有列出,但也同样常用的其他参数包括:

max_tokens

  • 指定模型生成响应的最大token数量
  • 默认为 inf,表示不设置上限
  • 可用于控制响应长度,防止模型输出过长

n

  • 指定要生成的响应数量
  • 默认为 1,即只生成一个响应
  • 设置 n > 1 可以得到多个可能的响应,用于比较和选择

调用Azure OpenAI API

Azure 与 OpenAI 的调用 API 的方式略有不同。我们首先需要创建一个 Azure 订阅(Subscription),以及相应的资源组(Resource Group)和部署(Deployments),三者缺一不可。在 Azure 门户中创建上述资源后,你将获得一个终结点 URL(Endpoint) 和一个访问密钥(API Key),这两者是调用API的必备信息。

注意:Deployments虽然可以自定义,但最好不要乱起名,用什么模型起什么名,比如用gpt-35-turbo,就取gpt-35-turbo的部署名。不然部署的模型多了容易乱成一锅粥。

在获取了Endpoint和API Key之后,我们用以下代码进行调用。

首先,让我们看一下Azure官方提供的使用Azure OpenAI API的示例代码。类似地,我们需要在环境变量中设置并获取”AZURE_OPENAI_API_KEY”和”AZURE_OPENAI_ENDPOINT”。

import os
import openai
openai.api_type = "azure"
openai.api_base = os.getenv("AZURE_OPENAI_ENDPOINT")
openai.api_key = os.getenv("AZURE_OPENAI_API_KEY")
openai.api_version = "2023-05-15"

response = openai.ChatCompletion.create(
engine="gpt-35-turbo", # engine = "deployment_name".
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Does Azure OpenAI support customer managed keys?"},
{"role": "assistant", "content": "Yes, customer managed keys are supported by Azure OpenAI."},
{"role": "user", "content": "Do other Azure AI services support this too?"}
]
)

print(response)
print(response['choices'][0]['message']['content'])

我们在Azure官方提供的代码的基础上,进行修改,封装成一个函数:

import os
import openai

def get_completion_azure(prompt, model="gpt-35-turbo"):
openai.api_key = os.getenv("AZURE_OPENAI_API_KEY")
openai.api_base = os.getenv("AZURE_OPENAI_ENDPOINT")
openai.api_type = "azure"
openai.api_version = "2023-05-15"

response = openai.ChatCompletion.create(
engine = model,
temperature = 0,
messages = [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": prompt
}
]
)
return response['choices'][0]['message']['content']

注意,OpenAI对于gpt-3.5-turbo的引用是gpt-3.5-turbo,而Azure是gpt-35-turbo,这个地方比较细节,容易出错……

在设置好上面的get_completion_openaiget_completion_azure函数后,我们就可以在各种场景下灵活应用OpenAI API 。

以下是一个基础的API调用示例:

get_completion_openai("TextMatters是一个专注于基于文本分析的金融会计领域实证研究的微信公众号,请你为它撰写一个30字以内的简短介绍。")

以上API调用代码会返回以下结果:

'TextMatters是专注于金融会计领域实证研究的微信公众号,致力于通过文本分析揭示财务报告中的信息和趋势,为投资者和研究者提供有益的见解和分析。关注TextMatters,了解最新的研究成果和行业动态。'

Prompt Engineering

Prompt最初是为下游任务而设计的一种任务专属的输入形式或模板,在ChatGPT引发大语言模型新时代后,Prompt成为与大模型交互输入的代名词。因此,我们通常将提供给大模型的输入称为Prompt。

Prompt Engineering是指设计和调整Prompt的过程。正如吴恩达本人所强调,通过正确地使用和优化提示词,我们才能够充分发挥LLM的潜力,指导它按照我们的意图生成所需输出。

Prompt 实践指南

上一章讲到的调用API的函数,我们需要在本章进行复用。因此我们在Python中运行以下代码,定义函数get_completion

def get_completion(prompt, model="gpt-3.5-turbo"):
openai.api_key = os.getenv('OPENAI_API_KEY')

messages = [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": prompt
}
]
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=0
)
return response.choices[0].message['content']

吴恩达和 Iza Fulford给出了设定Prompt的两条基础原则。它们分别是:

原则1:Prompt需要清晰具体

在撰写指令时,明确并具体的Prompt是十分重要的。在具体的应用过程中,原则1可以体现为以下几种Prompt实践。

(1)使用分隔符清楚地表示输入的不同部分

我们可以使用分隔符清晰地划分输入的不同部分,这有助于模型准确理解输入的结构。分隔符可以是各种形式的,比如双引号""、三引号''',尖括号<>、标签<tag></tag>或者冒号:

示例代码:

text = f"""
您应该通过提供尽可能清晰和具体的指令来表达您希望模型执行的任务。这将引导模型朝着期望的输出方向发展,并减少接收到无关或不正确响应的几率。不要混淆写清晰提示与写简短提示。在许多情况下,更长的提示为模型提供了更多的清晰度和上下文,这可能导致更详细和相关的输出。
"""

prompt = f"""
把用三个反引号括起来的文本总结成一句话。
```{text}```
"""

# 调用函数
response = get_completion(prompt)
print(response)

以上代码的输出结果为:

提供清晰和具体的指令可以引导模型朝着期望的输出方向发展,减少接收到无关或不正确响应的几率。

(2)设定结构化的输出

当我们调用API与模型进行交互时,为了获得更有用的结果,有时候可以要求它提供结构化输出,例如JSON或HTML格式的数据。

示例代码:

prompt = f"""
生成三本虚构中文书籍标题,包括它们的作者和流派。
以JSON格式提供,包含以下键:
book_id,title,author,genre。
"""
# 调用函数
response = get_completion(prompt)
print(response)

以上代码的输出结果为:

```json
[
{
"book_id": 1,
"title": "梦境之城",
"author": "张小明",
"genre": "奇幻"
},
{
"book_id": 2,
"title": "时间之门",
"author": "王小红",
"genre": "科幻"
},
{
"book_id": 3,
"title": "幽灵之谜",
"author": "李大山",
"genre": "悬疑"
}
]
```

(3)要求模型检查是否满足条件

在处理任务时,我们经常会面对各种假设或条件,有些条件可能并非总是被满足。这种方法可以帮助我们在开始任务之前排除一些可能导致错误结果的情况。如果发现条件不满足,模型会及时指出并停止执行后续的完整流程。

示例代码:

text = f"""
今天阳光明媚,鸟儿在欢唱。今天是去公园散步的好日子。花儿盛开着,树木在微风中轻轻摇曳。人们出门在外,享受着美好的天气。
"""

prompt = f"""
您将获得由三个引号界定的文本。如果其中包含一系列指令,请按以下格式重新编写这些指令:

步骤1 - ...
步骤2 - …

步骤N - …

如果文本中不包含一系列指令,则简单写为“未提供步骤”。

'''{text}'''
"""
# 调用函数
response = get_completion(prompt)
print(response)

以上代码的输出结果为:

未提供步骤。

(4)Few-shot prompting

Few-shot prompting 的核心思想是提供一个简短的提示或示例,然后让模型根据这些提示进行推理和生成,指导它按照我们的意图生成所需输出。

示例代码:

text = f"""
龙腾虎跃闹春意
"""
prompt = f"""
您的任务是以对联的格式回答问题。
对联是中国传统文化中的一种形式,通常由两句相对、呼应或对仗的诗句构成。这两句诗句往往在意义、韵律或结构上相呼应,构成了一种文学艺术形式。
例如:
“春风吹绿柳,夏雨润花枝。”
下面三引号中的内容是你需要回答的问题:
'''{text}'''
"""
# 调用函数
response = get_completion(prompt)
print(response)

以上代码的输出结果为:

鸟语花香迎新春

原则2:给模型充分“思考”的时间

我们可以在Prompt中先要求语言模型自己尝试解决这个问题,思考出自己的解法,然后再与提供的解答进行对比,判断正确性。在具体的应用过程中,原则2可以体现为以下几种Prompt实践。

(1)指定完成任务所需的步骤

在Prompt中明确指定语言模型需要执行的操作步骤,以便引导模型有条不紊地进行推理和生成。

示例代码:

text = f"""
毕竟西湖六月中,风光不与四时同。\
接天莲叶无穷碧,映日荷花别样红。
"""
prompt = f"""
执行以下操作:
1-请描述下面用三个反引号括起来的古诗文本的意思。
2-找出其中的提到的颜色。
3-找出颜色对应的物体。
4-输出一个 JSON 对象,其中包含以下键:颜色,物体。
请用换行符分隔您的答案。
Text:
```{text}```
"""
# 调用函数
response = get_completion(prompt)
print(response)

输出结果为:

1- 这首古诗描述了西湖六月的景色,与其他季节不同。莲叶碧绿无穷,荷花在阳光下映出别样的红色。
2- 绿色、红色
3- 莲叶、荷花
4-
```json
{
"颜色": ["绿色", "红色"],
"物体": ["莲叶", "荷花"]
}
```

(2)在指导模型得出结论之前让它自行思考问题

当我们要求语言模型判断数学问题的解答是否正确,但仅仅提供问题和解答是不够的,过于匆忙可能导致错误的判断。我们应该在Prompt中鼓励模型首先尝试自己解决问题,思考出自己的解决方案。这种方式能够让模型有足够的时间去理解问题,从而提高解决问题的准确性。

迭代优化 Prompt

我们接下来展示一个初始Prompt被不断迭代优化的过程,包括:添加长度限制;提取信息并结构化输出。

text = f"""
Author: Chen Yi-Chun,Hung Mingyi,Wang Yongxiang \
Journal: Journal of Accounting and Economics \
Year: 2018 \
Abstract: We examine how mandatory disclosure of corporate social responsibility (CSR) \
impacts firm performance and social externalities. Our analysis exploits China’s 2008 mandate \
requiring firms to disclose CSR activities, using a difference-in-differences design. \
Although the mandate does not require firms to spend on CSR, we find that mandatory CSR \
reporting firms experience a decrease in profitability subsequent \
to the mandate. In addition, the cities most impacted by the disclosure mandate \
experience a decrease in their industrial wastewater and SO2 emission levels. \
These findings suggest that mandatory CSR disclosure alters firm behavior and \
generates positive externalities at the expense of shareholders.
"""

prompt = f"""
请你用中文概括下面论文摘要中的核心结论。
```{text}```
"""
response = get_completion(prompt)
print(response)

输出结果为:

这段论文摘要的核心结论是:强制性披露企业社会责任(CSR)对公司绩效和社会外部性产生影响。研究发现,虽然强制性CSR披露并不要求公司在CSR方面进行支出,但披露CSR活动的公司在法规实施后经历了盈利能力下降。此外,受披露要求影响最大的城市在工业废水和二氧化硫排放水平上也出现下降。这些发现表明,强制性CSR披露改变了公司行为,并在股东的代价下产生了积极的外部性影响。

可以看到,这个回复还是太长了,没有起到概括的作用。因此我们可以在Prompt中添加输出长度限制。

prompt = f"""
请你用中文概括下面论文摘要中的核心结论。长度限制到20字以内。
```{text}```
"""
response = get_completion(prompt)
print(response)

输出结果为:

强制CSR披露影响企业绩效和社会外部性,减少盈利,改善环境。

最后,我们进一步优化prompt,让它提取我们感兴趣的特定信息并生成结构化输出。

prompt = f"""
针对三个反引号括起来的文本,请你提取下列信息并组织成Json格式输出:
作者,期刊,年份,核心结论(20字以内)
```{text}```
"""
response = get_completion(prompt)
print(response)

输出结果为:

```json
{
"Author": "Chen Yi-Chun, Hung Mingyi, Wang Yongxiang",
"Journal": "Journal of Accounting and Economics",
"Year": "2018",
"Core Conclusion": "Mandatory CSR disclosure affects firm performance and social externalities."
}
```

模型局限性:幻觉 (Hallucination)

当我们让语言模型描述一个实际上不存在的产品或概念时,它可能会凭空捏造出貌似合理的细节,自行”补全”这个虚构的事物,一本正经地向我们提供这些编造的”知识”。这种现象被称为”幻觉”(Hallucination),是语言模型的一大局限性和缺陷。

虽然我们在前面的内容中学习了如何设定准确合适的Prompt来指导模型的输出方向,但我们仍然需要警惕幻觉现象可能带来的问题和风险。

text = f"""
用50字向我描述小米公司最新开发的Mate60手机的特点。
"""
prompt = f"""
请你用专业的语言回答下列三个反引号括起来的问题
Text:
```{text}```
"""
# 调用函数
response = get_completion(prompt)
print("prompt")
print(response)

输出结果为:

小米公司最新开发的Mate60手机具有高性能处理器、创新设计、多摄像头系统、大容量电池和快速充电技术等特点。

读取数据集

我们先读入数据集,再在DataFrame中选取名为'Text'这一列,即包含产品评论文本内容的那一列。

import pandas as pd

amazon = pd.read_excel('./amazon_review.xlsx')
amazon_review = amazon['Text'].tolist()
# 取前5条数据
amazon_review = amazon_review[:5]

定义OpenAI API函数

我们定义函数get_setiment,它接受两个参数:

  1. amazon_review是一个字符串,表示待分析的产品评论文本
  2. model是一个可选参数,默认值为"gpt-3.5-turbo"代表使用OpenAI的gpt-3.5-turbo模型

在函数内部,我们设计了一个prompt,明确指示模型对给定的产品评论进行情感分类,并仅输出”positive”或”negative”两个词汇作为结果,确保输出的准确性。

import openai
import os

# 设定情感分类相关的OpenAI API调用函数
def get_setiment(amazon_review, model="gpt-3.5-turbo"):
openai.api_key = os.getenv('OPENAI_API_KEY')
prompt = f"""
What is the sentiment of the following product review,
which is delimited with triple backticks
Give your answer as a single word, either "positive" or "negative".
Review text: ```{amazon_review}```
"""
messages = [
{
"role": "user",
"content": prompt
}
]
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=0
)
return response.choices[0].message['content']

#运行函数
setiment = {}
for text in amazon_review:
setiment[text] = get_setiment(text)
sentiment_df = pd.DataFrame(setiment.items(), columns=['Review', 'Sentiment'])

输出结果整理如下。从分类结果来看,大型语言模型展现出了非常优秀的性能,模型能够精准地区分出正面(positive)和负面(negative)两种情感。

识别情感类型

在这一小节,我们将尝试设定一个Prompt来分析评论本文的情感类型。我们期望模型可以识别出评论作者表达的情感,并将其整理成一个不超过五个项目的列表。

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

Review text: '''{amazon_review[0]}'''
"""
response = get_completion(prompt)
print(response)

输出结果如下:

satisfied, pleased, impressed, grateful, content

识别愤怒

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

Review text: '''{amazon_review[0]}'''"""
response = get_completion(prompt)
print(response)

输出结果如下:

No

文本嵌入(Text Embeddings)

文本嵌入(Text embeddings)是一种将文本转换为向量表示(Vector Representation)的技术(更加通俗地说,嵌入就是将人类可读的文字变成模型可读的数字)。其核心思想是捕捉单词或短语之间的语义,并将其表示为向量空间中的距离。

换句话说,语义上相似的单词在嵌入空间中会有更接近的向量表示,而语义上不相关的单词则会有较远的向量表示。

常见的嵌入模型包括Word2VecGloVeBERT等。

OpenAI 嵌入模型

*下文提到的内容具有一定时效性,建议定期跟踪OpenAI API官方文档,更新信息。

2024 年年初,OpenAI API发布了全新的V3嵌入模型 text-embedding-3-small 和 text-embedding-3-large

模型比较

截至目前(2024年3月),OpenAI 提供的嵌入模型主要包括:

text-embedding-ada-002

  • 上一代嵌入模型
  • 嵌入维度:1536维
  • 计价:$0.0001/ 1k tokens

text-embedding-3-small

  • 性能不如Large,但是与ada-002相比性能显著提升。
  • 嵌入维度:1536维
  • 计价:$0.00002/ 1k tokens

text-embedding-3-large

  • 目前性能最好的嵌入模型
  • 嵌入维度:3072维
  • 计价:$0.00013/ 1k tokens
  • 与 text-embedding-3-small 相比价格较高,此外较大的嵌入维度可能导致较高的计算和内存成本。

缩短嵌入维度

考虑到使用较大的嵌入会消耗更多的计算、内存和存储,OpenAI 现在允许用户通过API 传递维度参数来缩短嵌入维度,且不会使嵌入失去其代表概念的属性。根据下图官方测试中提供的信息,两个V3嵌入模型即使在缩短嵌入维度后的表现仍然高于ada-002。

因此综上,如果只是用于金融/会计学术领域的文本研究,我强烈建议无条件使用text-embedding-3-small模型。

如何调用API获得嵌入

下列代码中,我们定义了一个函数 get_embedding_v1(text_to_embed),该函数接受一个字符串参数 text_to_embed,该字符串是要嵌入的文本。函数内我们调用了 openai.Embedding.create() 方法,发起一个 API 请求。

import json
import openai
import os
import time

def get_embedding(text_to_embed):
openai.api_key = os.getenv('OPENAI_API_KEY')

response = openai.Embedding.create(
model= "text-embedding-3-small",
input=[text_to_embed]
)

try:
embedding = response["data"][0]["embedding"]
except:
embedding = None
time.sleep(2)
print("error")
pass

if embedding:
with open("./mydata_embedding.json", "a+") as f:
json.dump({text_to_embed: embedding}, f)
f.write("\n")
return embedding

上述代码的响应是一个 JSON 对象,我们用embedding = response["data"][0]["embedding"]提取其中的嵌入,它是一个长度(维度)为 1536 维的向量。我们将文本及其对应的嵌入向量写入一个名为 mydata_embedding.json 的 JSON 文件中。

我们可以在一个简单文本上,运行上述函数:

get_embedding("Harry potter, the boy who lived.")

得到嵌入返回的向量:

[-0.029458941891789436,
0.04615234211087227,
-0.0445975624024868,
0.033100392669439316,
0.015087478794157505,
-0.061004556715488434,
-0.005027454812079668,
...
-0.01764467917382717,
-0.02884521335363388,
-0.021930545568466187,
-0.011804034002125263,
...]

我们看一下这个向量的维度:

print(len(get_embedding("Harry potter, the boy who lived.")))

会得到嵌入维度:

1536

通常,使用更大的嵌入向量会消耗更多的计算、内存和存储资源。上文提到,OpenAI 现在允许在两个 V3 嵌入模型上通过 API 传递维度参数来缩短嵌入维度(ada-002不支持维度调整)。

具体实现代码如下,我们只需在 response 中添加对维度参数的要求:

response = openai.Embedding.create(
model= "text-embedding-3-small",
input= [text_to_embed],
dimensions = 512
)

此时维度从1536维下降至512维:

print(len(get_embedding("Harry potter, the boy who lived.")))

返回值:

512

文本相似度

文本嵌入为机器学习模型和其他算法执行各种下游任务(如聚类或检索)提供了基础。包括但不限于:

  • 文本分类,文本嵌入有助于创建用于情感分析或主题识别任务的准确模型。
  • 信息检索,它们可以用于检索与特定查询相关的信息,类似于搜索引擎中的功能。
  • 文本相似性,嵌入可以识别和量化文本片段之间的语义相似性。
  • 文本生成,嵌入用于生成更连贯和在语境上相关的文本。

本节进一步展示一个简化后的下游任务:计算文本相似度。通过将文本转换成嵌入向量,我们可以利用向量空间中的距离或相似度度量来比较它们之间的相似性。

作为一个示例,我同时获取了哈利波特和指环王主角们的姓名文本嵌入。通过比较这些嵌入向量之间的距离或相似度,我们可以探索这些主角之间在文本语境中的相似性或相关性。

# 哈利波特人物
harry_potter = get_embedding("Harry potter")
ron_weasley = get_embedding("Ron Weasley")
hermione_granger = get_embedding("Hermione Granger")

# 指环王人物
gandalf = get_embedding("Gandalf the Grey")
frodo = get_embedding("Frodo Baggins")
legolas = get_embedding("Legolas")

我们使用scipy库中的distance.cosine函数来计算哈利波特与其他人物之间的余弦距离。

# 计算哈利到各个人的距离

from scipy.spatial import distance

print(distance.cosine(harry_potter, ron_weasley))
print(distance.cosine(harry_potter, hermione_granger))
print(distance.cosine(harry_potter, gandalf))
print(distance.cosine(harry_potter, frodo))
print(distance.cosine(harry_potter, legolas))

上述代码输出结果如下。我们可以看出,同一故事的角色之间的余弦距离(= 1-余弦相似度)更小,这表明这些角色在语义上更为相似或者相关。

0.4510517158795876
0.46182439707308476
0.613488967442906
0.6424822454338903
0.6501364169064245

为了更清晰地展示嵌入的空间距离,我们利用主成分分析将文本的高维向量表示降维至二维空间,并将其呈现在二维图表中。结果显示,同一系列或同一故事中的角色之间的文本嵌入更相似,在图表中呈现为更接近的空间距离。

本文章转载微信公众号@TextMatters

#你可能也喜欢这些API文章!