使用ChatGPT开发大规模数据集应用的实际考虑事项

ChatGPT中文站
Image generated with BlueWillow.

此帖子是前一次有关语言对ChatGPT文本分类任务性能影响的讨论的续篇,旨在提供关于开发使用大型语言模型(LLMs)处理大量数据的应用程序的实用见解。

在The Hotels Network,我们为全球超过19,000家酒店提供动态内容定制平台,以提高用户体验和网站性能。了解我们客户的网页和用户行为至关重要,而大型语言模型(LLMs)的多语言能力是关键。

管理海量网页内容和每日用户访问,需要专业的LLM策略。在这篇文章中,我们旨在探讨一些这些策略,概述应用程序开发和部署的关键方面。我们还讨论了三种处理大量数据的方法:训练机器学习模型以在嵌入中重现ChatGPT完成、使用嵌入的信息检索来形成提示上下文,并使用ChatGPT生成的代码回答关于表格数据的自然语言查询。

功能性考虑

在本部分,我们深入探讨了设计使用ChatGPT API应用时认为相关的几个方面。这些因素涵盖了技术和非技术考虑因素,严重影响不同方法解决数据密集型问题的可行性和可行性。

1. - 费用:ChatGPT API调用的费用是根据输入问题和生成的响应中的令牌(单词或单词片段)数量确定的。截至2023年6月16日,这些费用差异很大(如下图所示),特别是在文本完成请求和嵌入生成请求之间。

ChatGPT中文站
Cost per 1k prompt tokens for different ChatGPT completion and embedding models
ChatGPT中文站
Cost per 1k generated tokens for different OpenAI completion models

文本完成模型的成本可能使它们不适用于某些大规模应用,特别是当与特定嵌入式处理方法的成本效益相比较时。由于这些模型对生成的标记数量有重要依赖性,因此需要提示促使输出减少不必要的字符、符号和无关注释,从而优化自动处理效率。

2.- 延迟:API响应时间会显着影响用户体验和应用程序性能。ChatGPT API文本完成的响应时间与响应中生成的标记数量成正比。以下图表说明了这种关系,图表说明了各种文本完成实验的测量延迟。该图表展示了根据生成的单词数量生成响应所需的时间,考虑不同的ChatGPT模型。

ChatGPT中文站
Latency for different API requests depending on model and the number of words generated in the response

根据下图和表格所呈现的实证结果,与其他备选方案相比,GPT3.5 显示出稍微更快的性能。

ChatGPT中文站
Mean latency per word generated for ChatGPT models.

因此,在设计文本完成系统的提示时,建议明确要求它避免添加不必要的注释或文本。同样,在指定所需的输出格式以适应自动处理时,我们应避免不必要的符号和标点符号,这些符号最终将成为标记,并降低我们的系统的响应时间。

3. 请求限制:处理大量数据时,另一个重要考虑因素是在被OpenAI服务器阻止之前我们可以进行API请求的速率。根据OpenAI提供的值总结,OpenAI API最近放宽了并发请求的限制,如下表所示。

ChatGPT中文站
Limitations on the number of requests and tokens per minute when utilizing OpenAI completion models as of June 2023.

在延迟和请求速率方面的限制常常使得使用GPT进行大规模文本完成变得不切实际,特别是在需要快速响应时间或广泛数据处理的情况下。

4. - 数据隐私:数据隐私是一个需要仔细考虑的因素。我们必须问自己是否愿意或能够将我们的数据或客户的数据发送给第三方服务。基于嵌入的解决方案可以缓解这些隐私问题,只要我们能够在本地生成它们,就可以消除将数据发送到其他公司的需要。这种方法既确保了效率,也确保了数据安全。

5.-提示策略:撰写有效的提示非常重要。良好构建的提示旨在产生与我们目标一致的结果。关于提示生成策略,网上有大量信息可用。提示创建时要考虑的关键方面包括:

  • 明确定义上下文和目标。
  • 明确表明遵守所提供的背景,以防偏离该背景中报告的事实。
  • 为确保两个主要结果,将所需响应格式概述如下:第一,响应可直接被自动化方法使用,或需要最少的额外处理。第二,响应仅包含我们所需要的信息,在最短可能的长度内或使用最少数量的生成标记。这很重要,因为已经表明,不必要的标记将增加成本和延迟。

无论如何,评估不同的提示选项的结果都是一个明智的想法。结果可能存在相当大的差异,最优的提示技术可能取决于使用的模型。

6. 模型的一致性:

机器学习算法的发展往往面临一个窘境,即该算法的准确性提高还是维持模型输出的一致性。尽管优化响应可能很有益,但输出一致性对于确保用户信任模型输出和防止基于这些输出进行的过程改变至关重要。这个问题在OpenAI中也很明显,因为模型中的“改进”可能会改变它们的响应,迫使用户调整他们的交互。对于简单的任务,使用开放模型的嵌入作为输入,以及由GPT获得的输出作为所需输出,可以隔离来自OpenAI模型的调整。

ChatGPT中文站
Comparison of GPT-3.5-Turbo before and after June 13 on multi language text content classification.

以下是2023年6月13日之前和之后,gpt3.5-Turbo模型在性能上的表现差异。该图表展示了与文本内容分类实验相关的系统准确度显著降低,类似于先前文章中所进行的实验格式。此文章测量了基于文本分类的模型在不同语言中的表现。虽然该实验不是为了比较模型而是为了比较提示和语言对同一模型的影响,但结果清晰地表明,当要分类的文本不是英语时,即使使用完全相同的提示和模型跨越两个不同的时间段,准确性也会下降。

使用ChatGPT的可扩展性策略

之前的章节已经强调了大规模使用完成型GPT模型存在的挑战,特别是在成本、延迟和请求限制方面。本节介绍了三种不同的策略来解决在处理大型数据集时遇到的这些问题。我们将通过Python实现的实际示例展示每种策略。

1.- 模型训练以重现从嵌入中生成的ChatGPT完成:ChatGPT可以用来解决复杂任务,为特定的文本样本子集生成响应,从而产生一个由ChatGPT请求和回复组成的训练数据集。

利用这个数据集,可以训练模型将 ChatGPT 请求文本的嵌入映射到由 ChatGPT 生成的相应的期望响应。当要完成的响应可以用分类、数值或二进制术语表达时,这种策略特别有效。

这种方法有助于用对应的嵌入代替原始文本。这些嵌入可以使用OpenAI模型计算,也可以使用其他免费商业使用的模型,提供一种经济高效的替代传统文本补全方法。

2.- 上下文提示创建:此策略利用嵌入来识别和索引与需要回答的查询相似或相关的内容。通过利用Langchain、向量数据库(如Pinecone和Nuclia)和模型嵌入,它从大量文本数据中生成上下文。该上下文由类似或相关的文本组成,随后附加到原始查询中。因此,富含相关上下文的查询被转发给ChatGPT进行处理。

3. 使用 ChatGPT 生成数据查询代码:这种策略允许通过在 Pandas 数据框架中使用自动生成的数据查询函数来访问大量的表格数据,而 PandasAI 就是这种策略的一种实现。

我们将会献此文余篇讨论第一策略,并打算在随后的帖子中讨论其余策略。

使用嵌入式来训练分类器模仿GPT聊天行为

为了说明第一个策略,让我们考虑这个问题:我们的目标是根据宾馆展示的积极性对其进行排名。我们将请求ChatGPT比较两条评论,并指出哪一条呈现了更积极的宾馆形象。一旦我们获得了这些个体比较,我们就可以建立一个积极评级。该评级可以从酒店被认为更积极的次数中得出,也可以通过更复杂的指标,例如Elo评级进行推导。

然而,由于系统延迟和成本的问题,使用 ChatGPT 进行此过程并不可行。相反,我们可以使用嵌入和分类器训练来复制 GPT 的行为。首先,我们将选择一组建议,形成成对,然后询问 ChatGPT,哪个成对投射了更积极的内涵。根据这些响应,我们将使用文本对的嵌入作为输入来训练分类器,以模仿 ChatGPT 的行为。

用于确定一对评论中哪一个更积极的提示可能如下所示:

import os
import openai

openai.organization = "my_organization"
openai.api_key = os.getenv('OPENAI_KEY')


def compare_review_sentiments(message1: str, message2: str, model: str = "gpt-3.5-turbo") -> str:
prompt = f'Based on the following two hotel reviews:\n\n1: "{message1}"\n2: "{message2}"' \
'\n\nPlease determine which review portrays a more positive impression to potential customers.' \
' Choose between "1", "2", or "TIE" if both reviews seem equally positive or if it is '\
'unclear which is superior. Your response should only include "1", "2", or "TIE", '\
'without any additional comments. The most positive review is:'

# Create a chat completion request with GPT-4
result = openai.ChatCompletion.create(
model=model,
messages=[
{"role": "system", "content": "You are an expert in marketing with specialized experience in hotels."},
{"role": "user", "content": prompt}
],
temperature=0,
max_tokens=3000
)

# Extract and return the response
return result['choices'][0]['message']['content'].strip()

然后,给定一组评论,我们可以生成一个包含随机对评论的数据集:

import numpy as np

reviews = ['review 1', 'review 2'....'reciew n']
n_reviews = len(reviews)
r1 = list(range(n_reviews))
r2 = [np.random.choice([y for y in list(range(n_reviews)) if y != x]) for x in r1]

然后我们使用ChatGPT来确定每个成对评论中哪一个更为正面,以构建顺序。

import time

n_reviews = len(reviews)

forward = []
backward = []
i = 0
while (i < n_reviews):
try:
# Compare sentiments from review 1 to review 2, and vice versa
f = compare_review_sentiments(reviews[r1[i]], reviews[r2[i]])
b = compare_review_sentiments(reviews[r2[i]], reviews[r1[i]])
forward.append(f)
backward.append(b)
i += 1
except Exception as e:
# sleep when OpenAI API raises error due to request per minute limit
print(f'Processing pair {i} resulted in an error: {e}. Entering sleep mode...')
time.sleep(20)

我们筛选出具有明显情感差异的评论对,特别是那些正反评估相反且均未被分类为平局的评论对,在我们的实验中有78%的评论对被明确分类。 我们将使用此数据集来训练和测试模型。

我们将评估利用OpenAI的API并使用“text-embedding-ada-002”模型计算嵌入向量的近似过程,以确定哪个评论更为正面,并将其与“all-mpnet-base-v2”、“stsb-xlm-r-multilingual”、“all-MiniLM-L6-v2”、“bert-base-multilingual-uncased-sentiment”模型生成的嵌入向量进行比较,所有这些模型都是Apache 2或MIT许可分发的模型,我们可能已经在我们的设备上安装了它们。

使用text-embedding-ada-002 OpenAI生成嵌入代码将如下所示:


MODEL = "text-embedding-ada-002"
def generate_embeddings(inputs):
res = openai.Embedding.create(
input=inputs, engine=MODEL
)
return [x["embedding"] for x in res['data']]


ada_embeddings_result = []

# Process reviews in chunks of 100
for i in range(0, len(reviews), 100):
# Get next chunk of reviews
chunk = reviews[i:i + 100]

# Generate embeddings for chunk
embeddings = generate_embeddings(chunk)

# Append embeddings to result list
embeddings_result.extend(embeddings)

剩余模型的评论嵌入可以使用以下代码生成:

import torch
from transformers import AutoTokenizer, AutoModel
import torch.nn.functional as F

LOCAL_MODEL_CACHE_PATH = "/my_hugin_face_local_model_cache_folder"

def _mean_pooling(model_output, attention_mask):
token_embeddings = model_output[0]
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)


class TextModel():

def __init__(self, model_name, device='cuda:0'):
self.model_name = model_name
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name,
cache_dir=LOCAL_MODEL_CACHE_PATH)
self.model = AutoModel.from_pretrained(self.model_name,
cache_dir=LOCAL_MODEL_CACHE_PATH).to(device)
self.device = device

def encode_text(self, texts):
encoded_input = self.tokenizer(texts, padding=True, truncation=True, return_tensors='pt').to(self.device)
with torch.no_grad():
model_output = self.model(**encoded_input)

sentence_embeddings = _mean_pooling(model_output, encoded_input['attention_mask'])

sentence_embeddings = F.normalize(sentence_embeddings, p=2, dim=1)
sentence_embeddings = sentence_embeddings.to('cpu')

return sentence_embeddings.numpy()

embeddings_result=[]
for block in tqdm(range(1+len(dataset['reviews'])//25)):
all_encodings.append(embeddings_model.encode_text(dataset['reviews'][block*25:(block+1)*25]))

embedding_model=TextModel(model_name)

embeddings_result = []
# Process reviews in chunks of 25
for i in range(0, len(reviews), 25):
# Get next chunk of reviews
chunk = reviews[i:i + 25]

# Generate embeddings for chunk
embeddings = embedding_model.encode_text(chunk)

# Append embeddings to result list
embeddings_result.extend(embeddings)

模型第一次从huginface下载并存储在您的本地磁盘上的“/ my_hugin_face_local_model_cache_folder”中。必须向TextModel构造函数提供完整的模型名称,完整的模型名称包括:“sentence-transformers / all-mpnet-base-v2”, “sentence-transformers / stsb-xlm-r-multilingual”, “sentence-transformers / all-MiniLM-L6-v2” ,“nlptown / bert-base-multilingual-uncased-sentiment”。

一旦我们获得了嵌入,我们就可以开始训练和评估我们的模型。为了说明这个过程,我们使用了一个较小的1,000条评论的数据集进行了实验。我们在不同的拓扑结构上训练了神经网络,应用了主成分分析(PCA)来降低数据集的维度,利用了100个特征向量。我们使用10倍交叉验证来测量整个过程的准确性。每组嵌入的结果都呈现在随后的图表中。

ChatGPT中文站
Comparison results on ChatGPT-3.5-turbo completion approximation using the embeddings of different models

这个减少的数据集的结果表明,我们可以使用嵌入技术来近似ChatGPT完成任务的结果。此外,“bert-base-multilingual-uncased-sentiment”模型经过情感分类微调后的表现与OpenAI模型相当。 “bert-base-multilingual-uncased-sentiment”模型的一个主要优点是可以在本地运行,因此可以避免与OpenAI模型相关的数据隐私和请求限制问题。

结论

ChatGPT的多功能性使其能够自动执行各种复杂任务。然而,当我们在具有大量数据的生产系统中使用它时,我们会遇到与延迟、API请求限制、成本、模型更新的一致性以及隐私有关的问题。

这些问题可以通过使用ChatGPT来标记数据集来缓解甚至消除。然后可以使用这些标记的数据集来训练模型,使用嵌入来复制ChatGPT的行为。

2023-10-20 17:02:36 AI中文站翻译自原文