这个笔记本演示了一种定制OpenAI嵌入到特定任务的方法。
输入是训练数据,格式为[text_1, text_2, label],其中label为+1表示这些对是相似的,-1表示这些对是不相似的。
输出是一个矩阵,您可以用它来乘以您的嵌入。这个乘法的结果是一个“定制嵌入”,它将更好地强调与您的用例相关的文本方面。在二元分类用例中,我们看到错误率下降了多达50%。
在下面的示例中,我使用了从SNLI语料库中挑选的1,000个句子对。每对句子在逻辑上是蕴涵的(即,一个句子暗示另一个句子)。这些对是我们的正例(label
1)。我们通过组合不同对的句子来生成合成的负例,这些句子被认为不是逻辑上蕴涵的(label = -1)。
对于聚类用例,您可以通过创建来自相同簇中的文本对来生成正例,并通过创建来自不同簇中的句子对来生成负例。
对于其他数据集,我们已经看到只需大约100个训练示例就可以看到相当不错的改进。当然,使用更多示例性能会更好。
0. 导入模块
# 导入
from typing import List, Tuple # 对于类型提示
import numpy as np # 用于操作数组
import pandas as pd # 用于在数据框中操作数据
import pickle # 用于保存嵌入缓存
import plotly.express as px # 对于情节
import random # 用于生成运行ID
from sklearn.model_selection import train_test_split # 用于划分训练与测试数据
import torch # 对于矩阵优化
from utils.embeddings_utils import get_embedding, cosine_similarity # 对于嵌入式系统
1. 输入
大多数输入都在这里。需要更改的关键内容包括从哪里加载数据集,将嵌入的缓存保存到哪里,以及要使用哪种嵌入引擎。
根据数据 的格式,您可能需要重写process_input_data函数。
# 输入参数
embedding_cache_path = "data/snli_embedding_cache.pkl" # 嵌入数据将在此处保存/加载。
default_embedding_engine = "text-embedding-3-small"
num_pairs_to_embed = 1000 # 1000这个数字是随意选定的。
local_dataset_path = "data/snli_1.0_train_2k.csv" # 下载链接:https://nlp.stanford.edu/projects/snli/
def process_input_data(df: pd.DataFrame) -> pd.DataFrame:
# 您可以自定义此项以预处理您自己的数据集。
# 输出应为一个包含3列的数据框:text_1、text_2、label(1表示相似,-1表示不相似)
df["label"] = df["gold_label"]
df = df[df["label"].isin(["entailment"])]
df["label"] = df["label"].apply(lambda x: {"entailment": 1, "contradiction": -1}[x])
df = df.rename(columns={"sentence1": "text_1", "sentence2": "text_2"})
df = df[["text_1", "text_2", "label"]]
df = df.head(num_pairs_to_embed)
return df
2. 加载和处理输入数据
# 加载数据
df = pd.read_csv(local_dataset_path)
# 处理输入数据
df = process_input_data(df) # 这表明训练数据仅包含正样本。
# 查看数据
df.head()
/var/folders/r4/x3kdvs816995fnnph2gdpwp40000gn/T/ipykernel_17509/1977422881.py:13: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df["label"] = df["label"].apply(lambda x: {"entailment": 1, "contradiction": -1}[x])
text_1 | text_2 | label | |
---|---|---|---|
2 | A person on a horse jumps over a broken down a... | A person is outdoors, on a horse. | 1 |
4 | Children smiling and waving at camera | There are children present | 1 |
7 | A boy is jumping on skateboard in the middle o... | The boy does a skateboarding trick. | 1 |
14 | Two blond women are hugging one another. | There are women showing affection. | 1 |
17 | A few people in a restaurant setting, one of t... | The diners are at a restaurant. | 1 |
3. 将数据分割为训练集和测试集
请注意,在生成合成的负例或正例之前,将数据分割为训练集和测试集是很重要的。您不希望训练数据中的任何文本字符串出现在测试数据中。如果有污染,测试指标看起来会比实际在生产环境中的情况要好。
# 将数据分割为训练集和测试集
test_fraction = 0.5 # 0.5这个数值相当随意。
random_seed = 123 # 随机种 子是任意的,但在确保可重复性方面非常有用。
train_df, test_df = train_test_split(
df, test_size=test_fraction, stratify=df["label"], random_state=random_seed
)
train_df.loc[:, "dataset"] = "train"
test_df.loc[:, "dataset"] = "test"