传统上,任何自然语言处理文本分类项目都会从收集实例、定义各自的标签和训练分类模型(如逻辑回归模型)开始,对实例进行分类。目前,OpenAI 中提供的模型可直接用于分类任务,而这些任务通常需要收集大量标签数据来训练模型。这些预训练模型可用于多种文本处理任务,包括分类、摘要、拼写检查和关键词识别。
ChatGPT 为 OpenAI 实现的模型提供了图形界面。但是,如果我们想直接在 Python 中运行这些模型呢?我们可以使用 OpenAI API,它允许我们从编程环境中访问其模型。在本文中,我们将通过一个简单的示例来描述如何访问 API 以检测短信是否为垃圾短信。为此,我们将使用 Open AI 的一个模型,特别是 GPT-3.5-turbo 模型。
创建 Open AI 账户
访问 OpenAI API 的第一步是在 OpenAI 上创建一个账户,以获得访问模型所需的 API 密钥。创建账户后,我们将拥有 5 美元的信用额度,正如我们稍后将观察到的,这将允许我们进行大量测试。
在本例中,我们将使用 OpenAI 的免费版本,该版本对每分钟和每天的请求都有限制。遵守这些限制对于避免 "速率限制错误 "至关重要。这两个参数的值分别设置为每分钟 3 次请求和每天 200 次请求。虽然这自然会造成限制,尤其是对大型项目而言,但对于本文的示例而言,这已经足够了。
使用 Python 访问 OpenAI
创建账户后,我们就可以使用 OpenAI 库从 Python 访问免费版的 OpenAI 模型。首先,我们创建一个名为 chat_with_gpt 的函数来访问 GPT-3.5-turbo 模型。该函数的输入将是我们稍后设计的提示。
# Function to interact with ChatGPT API
def chat_with_gpt(prompt):
openai.api_key = 'XXXXXXXXX'
response = openai.chat.completions.with_raw_response.create(
model="gpt-3.5-turbo", # Specify the model
messages=[{"role": "system", "content": "You are a helpful SMS spam detector chatbot."},
{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=500,
)
return response
设计提示
下一步是创建我们将提供给 GPT-3.5-turbo模型的提示。在这种特殊情况下,我们对两件事感兴趣。首先是一个介于 0 和 1 之间的值,表示该短信是否为垃圾短信的概率;其次是模型对该决定背后原因的解释。此外,我们希望得到 JSON 格式的结果,以便日后将其转换为数据帧。以下是我们将用于这些预测的模板。
prompt_template = ('Please analyze the following SMS message and determine if it is spam'
'Additionally, please provide a value between 0 and 1 indicating the likelihood of it being spam.'
'Answer in the json format: {"spam": "probability", "reasoning": "detailed explanation why"}, '
'where "probability" is the probability of being a spam from 0 to 1. '
'Only output json. '
'Message: %s')
读取垃圾短信收集数据集
现在,我们需要短信来测试 OpenAI 模型预测短信是否为垃圾短信的能力。为此,我们将使用 UCI 机器学习资料库中的 SMS 垃圾邮件收集数据集。
我们读取数据并将其转换为 DataFrame。正如我们所观察到的,数据集由两列组成:一列包含信息,另一列包含相应的标签。 ham 表示信息不是垃圾邮件,而 spam 表示信息是垃圾邮件。
# Funtion to read the SMS Spam Collection Dataset
def read_sms_spam_dataset(file_object):
messages = []
labels = []
for line in file_object:
# Assuming each line in the file contains a message and a label separated by a delimiter
label, message = line.strip().split('\t') # '\t' is the delimiter
messages.append(message)
labels.append(label)
return messages, labels
# Path of the file
file_path = 'sms+spam+collection/SMSSpamCollection'
# Reading the file
with open(file_path, 'r', encoding='utf-8') as file:
messages, labels = read_sms_spam_dataset(file)
# Creating a DataFrame
data = {'Message': messages, 'Label': labels}
df = pd.DataFrame(data)
# Visualizing the first rows of the Dataframe
df.head()
利用 GPT-3.5-turbo 模型检测邮件中的垃圾邮件
现在,我们将使用该模型来检测邮件是否为垃圾邮件,并评估预训练的 OpenAI 模型对这一问题的预测能力。正如文章开头提到的,免费版本在每分钟和每天的请求数量上有很大的限制。数据集包含 5,574 个实例,但为了测试模型,我们将只使用前 50 个实例。如果你拥有 OpenAI 的付费版本,你可以增加测试的消息数量,因为时间限制要少得多。
# Selecting only the first 50 rows of the dataset
df_test = df.iloc[0:50]
# Checking the distribution of labels
df_test['Label'].value_counts()
在使用该模型进行预测之前,我们已经验证了由 50 个实例组成的数据集既包含垃圾邮件,也包含非垃圾邮件。总共有 40 条非垃圾邮件和 10 条垃圾邮件。
最后,我们开始进行预测。如下图所示,我们一直在跟踪可用的点数和令牌。此外,我们还在代码中执行了 60 秒的休眠功能,以确保遵守有关请求数量的限制。
模型提供的带有预测的消息已存储在名为 的变量中prediction。该消息是一个 JSON 文件,具有以下结构:{ “spam”: “0.1”, “reasoning”: “The message seems to be a typical promotional message about a buffet offer and does not contain any typical spam keywords or characteristics. The probability of this message being spam is low.” }.
# List to store the predictions
predictions_json = []
# Iterate through each message and classify it
for index, row in df_test.iterrows():
message = row['Message']
print(f"Message: {message}")
prompt = prompt_template % (message)
try:
# Make a request using the prompt
response = chat_with_gpt(prompt)
# Obtain the response with the prediction (JSON format)
prediction = response.parse().choices[0].message.content.strip()
print(f"Classification: {prediction}")
# Keep track of remaining credits and tokens
remaining_credits = response.headers.get('x-ratelimit-remaining-requests')
remaining_tokens = response.headers.get('x-ratelimit-remaining-tokens')
print(f"Remaining Credits: {remaining_credits}")
print(f"Remaining Tokens: {remaining_tokens}")
# Catch the RateLimitError in case it shows up
except openai.RateLimitError as e:
print("Rate Limit Error")
prediction = "Rate Limit Error"
# Append the prediction to a list
predictions_json.append(prediction)
# Pause the code to guarantee that the request limits are not surpassed
time.sleep(60)
预测值介于 0 和 1 之间,0 表示信息不是垃圾邮件,1 表示信息是垃圾邮件。在分配标签时,我们将使用 0.5 作为阈值,也就是说,如果模型的得分高于 0.5,就会将邮件归类为垃圾邮件。
# Adding predictions JSON data to the df_test dataframe
df_test['Predictions'] = predictions_json
# Converting predictions JSON data from string to dictionary
df_test['Predictions'] = df_test['Predictions'].apply(json.loads)
# Normalizing the predictions data and adding it to the dataframe
df_test = pd.concat([df_test.drop(['Predictions'], axis=1), pd.json_normalize(df_test['Predictions'])], axis=1)
# Renaming the columns of df_test dataframe
df_test.columns = ['Message', 'Label', 'Predicted Probability', 'Reasoning']
# Convert the column Predicted Probability in a float
df_test['Predicted Probability'] = df_test['Predicted Probability'].astype(float)
# Create a new column 'Predicted Label' based on the condition
df_test['Predicted Label'] = df_test['Predicted Probability'].apply(lambda x: 'ham' if x < 0.5 else 'spam')
# Displaying the first few rows of df_test dataframe
df_test.head()
现在,剩下的工作就是将实际标签与预测标签进行比较,并评估 GPT-3.5-turbo模型的预测效果如何。
# Create a sensitivity matrix (contingency table)
sensitivity_matrix = pd.crosstab(df_test['Label'], df_test['Predicted Label'])
# Create Plotly heatmap
fig = go.Figure(data=go.Heatmap(
z=sensitivity_matrix.values,
x=sensitivity_matrix.columns,
y=sensitivity_matrix.index,
colorscale='RdBu', # Change color scale
colorbar=dict(title='Count'), # Add colorbar title
showscale=True # Show color scale
))
# Add annotations
for i in range(len(sensitivity_matrix.index)):
for j in range(len(sensitivity_matrix.columns)):
fig.add_annotation(text=str(sensitivity_matrix.values[i][j]),
x=sensitivity_matrix.columns[j], y=sensitivity_matrix.index[i],
font=dict(color='white'), showarrow=False)
# Add labels and adjust plot size
fig.update_layout(
title='Sensitivity Matrix',
xaxis=dict(title='Predicted Label'),
yaxis=dict(title='Actual Label'),
width=600, # Adjust width
height=400, # Adjust height
autosize=False # Disable autosizing
)
# Show plot
fig.show()
以上是模型的混淆矩阵。
该模型在检测垃圾邮件方面表现出了良好的灵敏度,少数误报表明其精确度还有待提高。如图所示,该模型的准确率达到 94%,在 50 个实例中正确分类了 47 个。
由于我们要求该模型为我们提供有关其将邮件归类为垃圾邮件或非垃圾邮件的理由的信息,因此我们可以研究一下它为什么会错误地将 3 封不属于垃圾邮件类别的邮件归类为垃圾邮件。以下是模型提供的解释:
含有不恰当内容或大量拼写错误的信息往往会被归类为垃圾邮件。
总结
传统上,需要对文本进行分类的项目都是从标注数据库和需要训练的模型开始的。现在,预训练语言模型(LLM)的出现提供了对大量文本进行分类的可能性,而无需事先训练模型,因为这些模型已经针对许多用例进行过训练。在本文中,我们介绍了如何利用 OpenAI API 访问 GPT-3.5-turbo 模型,并确定一组信息是否为垃圾邮件。该模型能够对 94% 的实例进行正确分类,这表明其准确性很高。未来,我们有必要对其他 OpenAI 模型进行评估,并探索不同的提示,以获得更好的性能。