链载Ai

标题: Embedding模型微调:基于已有数据快速构建训练与评估数据集 [打印本页]

作者: 链载Ai    时间: 前天 21:00
标题: Embedding模型微调:基于已有数据快速构建训练与评估数据集

Embedding模型微调:基于已有数据快速构建训练与评估数据集

本文目标

本文主要面向希望在特定领域或任务中提升Embedding模型表现的初学者。希望读完之后,能帮助大家:


? 目录


? 前言

Embedding模型通过将文本映射为向量,在语义理解相关的NLP任务中扮演着基础性角色。然而,通用的预训练模型在特定领域往往因缺乏专业知识而表现不佳。

为了解决这一问题,模型微调(Fine-tuning),它通过在领域数据上进一步训练,使模型适应特定语义特性。高质量的数据集是微调成功的核心。本文旨在探讨如何基于现有数据,高效构建用于Embedding模型微调的训练与评估数据集,以提升模型在特定场景下的表现。



核心概念解析:Embedding微调与数据要素

在咱们具体聊怎么弄数据集之前,有几个核心的概念得先弄明白。

Embedding模型微调释义

原理阐述:Embedding模型微调是指在一个已经经过大规模通用语料预训练的Embedding模型基础上,利用特定任务或特定领域的数据集进行进一步的训练,从而调整模型参数,使其能更好地捕捉和表达目标数据中的语义信息。

核心目标:

微调的核心目标可以从度量学习 (Metric Learning)的视角来理解。它旨在优化文本在向量空间中的表示,使得在语义上相似或相关的文本对(例如,一个问题和它的高质量答案)在向量空间中的距离更近,而语义上不相似或不相关的文本对在向量空间中的距离更远。这可以通过一个损失函数来形式化,例如三元组损失 (Triplet Loss):

其中是锚点(如查询)的向量,是正例的向量,是负例的向量,是向量和之间的距离度量(如欧氏距离或余弦距离的相反数),是一个超参数,用于确保正例对和负例对之间的间隔。目标是最小化这个损失。

这种优化使得模型在执行如相似度计算、信息检索等任务时更为精准。Embedding 微调: 三元组损失 (Triplet Loss)

构建专用数据集的必要性

专门为微调构建数据集主要基于以下两点考虑:

第一,弥合领域差异。通用预训练模型学习到的是广泛的语言知识,而特定应用场景往往有其独特的语言模式、术语体系和知识结构。例如,金融领域的文本与日常对话的语言风格和核心词汇差异巨大。微调数据集承载了这种特定领域的信息,帮助模型弥合通用知识与特定需求之间的差距。

第二,数据驱动的学习范式。模型的学习过程是数据驱动的。通过向模型展示精心构造的、能够反映目标任务需求的样本(例如,哪些文本应被视为相关,哪些不相关),模型能够逐步学习到在该特定场景下区分文本语义的有效模式。

微调数据集的关键组成部分

一个典型的用于对比学习或度量学习的Embedding模型微调数据集,核心通常包含以下几种要素:

查询(Query):

这是信息需求的表示,可以是一个用户提出的问题、一个检索关键词,或任何需要模型为其寻找相关信息的文本。

正例 (Positive Sample,pos):

这是与给定查询(Query)高度相关或语义一致的文本。在训练过程中,模型会学习拉近查询向量与正例向量之间的距离。

负例 (Negative Sample,neg):

这是与给定查询(Query)不相关、相关性低或语义不一致的文本。在训练过程中,模型会学习推远查询向量与负例向量之间的距离。负样本的质量和选择策略对模型学习区分细微语义差异至关重要。理解这些基本构成及其在模型训练中的作用,是后续高效构建数据集的基础。

Embedding 模型微调数据集组成

? 实践指南:构建微调训练数据集

接下来,我们将结合一个具体的案例场景(financial-qa-10K数据集处理示例),分步骤阐述如何构建高质量的微调训练数据集。

数据源评估

微调的首要前提是拥有数据。需要评估并选择已有的、能够反映目标应用场景的数据资源。这可能包括用户行为日志、已有的问答对、文档库、知识库内容等。以金融问答为例,financial-qa-10K这样的数据集包含了金融领域的问题、对应的答案以及答案所在的上下文,是非常适合用于微调的数据源。

选定数据源后,需要对数据进行初步理解。

这包括分析其原始数据结构,例如包含哪些字段,每个字段的含义是什么。

同时,进行必要的初步数据清洗,如去除无效字符、处理缺失值、统一文本编码等,确保数据质量。

financial-qa-10K的例子中,原始数据包含'question', 'answer', 'context', 'ticker', 'filing'等列,我们需要理解这些列如何服务于我们的微调目标。

其中的数据数据格式说明:

原始数据格式中各字段含义如下:

标准化训练样本结构定义

目标格式的确定

训练样本结构Json Lines

不同的微调框架或模型可能对输入数据格式有特定要求。一个常见且有效的结构是JSON Lines格式,其中每一行是一个JSON对象,代表一个训练样本。该对象通常包含查询、正例、负例等字段,例如:

{
"query": str, // 查询文本
"pos": List[str], // 正样本列表
"neg": List[str], // 负样本列表
"pos_scores": List[int],// 正样本得分列表
"neg_scores": List[int],// 负样本得分列表
"prompt": str, // 提示信息
"type": str // 数据类型
}

这里面,query就是查询的句子,pos是一个或者好几个正向的文本列表,neg呢,也是一个或者好几个负向的文本列表。

要是用知识蒸馏的话,pos_scoresneg_scores就可能用得上,它们代表了对应样本的得分。如果不用知识蒸馏的话,就不需要用上这里的两个参数。

prompt就是给模型的一个提示,告诉它怎么处理这个查询。那个type字段。

下面是一个具体的单条训练数据样本示例:

{
"query":"什么是市盈率它如何帮助投资者评估股票价值",
"pos": [
"市盈率(Price-to-Earnings Ratio, P/E Ratio)是衡量股票价格相对于每股收益的指标。计算公式为:市盈率 = 当前股价 / 每股收益(EPS)。它反映了投资者愿意为每一元盈利支付多少价格。",
"投资者通常使用市盈率来判断股票估值是否合理。较低的市盈率可能意味着股票被低估,而较高的市盈率则可能表示股票被高估或市场预期其未来盈利高速增长。然而,比较市盈率时应考虑行业特性和公司成长阶段。"
],
"neg": [
"市净率(Price-to-Book Ratio, P/B Ratio)是股价与每股净资产的比率,常用于评估银行、保险等资产密集型公司的价值。",
"股息收益率(Dividend Yield Ratio)是指公司年度总派息额与当前市价的比率,是衡量股票投资回报的指标之一。",
"技术分析主要关注股票价格和交易量的历史数据,通过图表模式预测未来价格走势,与基本面分析方法不同。"
],
"prompt":"Represent this sentence for searching relevant documents: ",
"type":"normal"
}

在这个例子中:

字段映射与类型转换

根据定义好的目标格式,需要从原始数据中选取核心信息,并将其转换为目标结构中的对应字段。

financial-qa-10K的示例中,我们将原始的'question'列选作query,将'context'(或'answer',取决于具体任务目标)选作pos。这一步通常伴随着字段的重命名和数据类型的转换。

正样本 (pos) 的构建与优化

所谓正样本(Positive Sample,pos),指的是与我们关心的查询(Query)在语义上高度相关或匹配的文本。这些样本旨在教会模型理解“什么是相似的”或“哪些内容是针对查询的正确答案/相关文档”。 正样本的质量直接影响模型对“相关性”的理解。

构建正样本的核心原则是确保与对应的query之间具有强相关性或语义一致性。这意味着需要从原始数据中准确地抽取或匹配那些真正能够回答查询、或与查询语义高度一致的内容作为正样本。

正样本的文本粒度(是选择一个完整的句子、一个段落还是整个文档)需要根据具体的应用场景和模型能力来确定。如果上下文对理解至关重要,那么选择包含更完整上下文的段落可能比单个句子更优。

在抽取正样本时,应尽量减少其中包含的无关信息或噪声。一个“干净”的正样本能让模型更高效地学习到核心的语义关联,避免受到无关文本片段的干扰。

通常将正样本pos处理成列表形式(List[str])。这样做一方面可以支持一个查询对应多个高质量正例的场景,另一方面也使得数据处理流程更为统一,即便当前只有一个正例,也用列表包装。

负样本 (neg) 构建策略:提升模型辨识力

相应地,负样本(Negative Sample,neg)则是指与查询(Query)不相关、相关性低或语义不一致的文本。它们的作用是帮助模型学会区分“什么是不相似的”或“哪些内容不是查询想要的”。 负样本在Embedding模型微调中扮演着至关重要的角色,它们帮助模型学习区分看上去相似但实际不相关的文本,从而塑造有效的决策边界,防止模型将所有内容都视为相似(即模型坍塌)。所谓模型坍塌(Model Collapse),通常指的是在训练过程中,模型学习到的所有或大部分文本的向量表示都变得非常相似,失去了区分度,导致模型无法有效识别不同文本之间的语义差异。

高质量的负样本能够显著提升模型的细粒度语义辨识能力。它们迫使模型不仅仅是学习"什么是相关的",更要学习"什么是不相关的,以及为什么不相关"。

构建负样本的技术路径多种多样,每种方法各有特点和适用场景。以下我们将详细介绍几种常见且有效的负样本构建策略。

批内负样本 (In-batch Negatives)

原理: 在训练过程中,对于当前处理批次(batch)内的每一个查询:

批内负样本构建示例

让我们通过一个具体的例子来理解批内负样本的构建过程。假设我们有一个批次,包含4个训练样本:

batch = [
{
"query":"什么是市盈率",
"pos": ["市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除以每股收益。"]
},
{
"query":"比特币是如何工作的",
"pos": ["比特币是一种基于区块链技术的加密货币,通过挖矿的方式生成,交易被记录在公共分布式账本上。"]
},
{
"query":"如何计算复利",
"pos": ["复利计算公式为A=P(1+r)^n,其中A为最终金额,P为本金,r为利率,n为时间周期。"]
},
{
"query":"什么是通货膨胀",
"pos": ["通货膨胀是指一般物价水平持续上涨,导致货币购买力下降的经济现象。"]
}
]

非对称任务场景(如问答)下的批内负样本构建:

对于每个查询,我们将批次中其他样本的正例作为该查询的负例:

# 为第一个查询"什么是市盈率"构建批内负样本
query_1 = batch[0]["query"]
pos_1 = batch[0]["pos"][0]
neg_1 = [batch[1]["pos"][0], batch[2]["pos"][0], batch[3]["pos"][0]]

# 最终第一个查询的训练数据结构
sample_1 = {
"query":"什么是市盈率",
"pos": ["市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除以每股收益。"],
"neg": [
"比特币是一种基于区块链技术的加密货币,通过挖矿的方式生成,交易被记录在公共分布式账本上。",
"复利计算公式为A=P(1+r)^n,其中A为最终金额,P为本金,r为利率,n为时间周期。",
"通货膨胀是指一般物价水平持续上涨,导致货币购买力下降的经济现象。"
]
}

对称任务场景(如语义相似度匹配)下的批内负样本构建:

在对称任务中,不仅其他样本的正例可以作为负例,其他样本的查询本身也可以作为负例:

# 为第一个查询"什么是市盈率"构建批内负样本
query_1 = batch[0]["query"]
pos_1 = batch[0]["pos"][0]
neg_1 = [
# 其他样本的查询
batch[1]["query"],
batch[2]["query"],
batch[3]["query"],
# 其他样本的正例
batch[1]["pos"][0],
batch[2]["pos"][0],
batch[3]["pos"][0]
]

# 最终第一个查询的训练数据结构
sample_1 = {
"query":"什么是市盈率",
"pos": ["市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除以每股收益。"],
"neg": [
# 其他查询作为负例
"比特币是如何工作的",
"如何计算复利",
"什么是通货膨胀",
# 其他正例作为负例
"比特币是一种基于区块链技术的加密货币,通过挖矿的方式生成,交易被记录在公共分布式账本上。",
"复利计算公式为A=P(1+r)^n,其中A为最终金额,P为本金,r为利率,n为时间周期。",
"通货膨胀是指一般物价水平持续上涨,导致货币购买力下降的经济现象。"
]
}

批内负样本的实现流程:

在实际训练中,批内负样本通常在数据加载器或训练循环中动态构建,而不是预先准备好。下面是一个简化的实现流程:

defconstruct_in_batch_negatives(batch, symmetric=False):
"""
为批次中的每个样本构建批内负样本

Args:
batch: 包含多个训练样本的批次
symmetric: 是否为对称任务

Returns:
包含批内负样本的增强批次
"""
enhanced_batch = []

fori, sampleinenumerate(batch):
query = sample["query"]
pos = sample["pos"]
neg = []

# 从批次中其他样本收集负例
forj, other_sampleinenumerate(batch):
ifi != j: # 排除自身
# 对于对称任务,其他样本的查询也可作为负例
ifsymmetric:
neg.append(other_sample["query"])

# 其他样本的正例作为负例
neg.extend(other_sample["pos"])

# 构建增强样本
enhanced_sample = {
"query": query,
"pos": pos,
"neg": neg
}

enhanced_batch.append(enhanced_sample)

returnenhanced_batch

批内负样本的优势在于它们是顺手添加过去的,不需要额外的数据准备,同时能提供比随机负样本更有挑战性的反例,因为它们来自同一批次,具有一定的相关性。不过,显而易见的是它们的质量和难度可能不如专门设计的难负样本。

难负样本 (Hard Negatives)

难负样本对模型提出了更精细的辨识挑战,迫使模型学习更细微的语义差别,从而显著提升模型在真实应用场景中的准确性和鲁棒性。

定义: 指那些在语义上或文本表征上与查询具有较高相似度,容易被模型误判为正例,但实际上与查询不相关或相关性较低的样本。

挖掘技术

难负样本构建示例

让我们通过一个具体示例来说明如何使用BM25和预训练Embedding模型结合的方式挖掘难负样本:

fromrank_bm25importBM25Okapi
importnumpyasnp
fromsentence_transformersimportSentenceTransformer
importjieba # 添加jieba导入用于中文分词

# 1. 准备语料库
corpus = [
"市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除以每股收益。",
"市净率是股价与每股净资产的比率,常用于评估银行等资产密集型公司价值。",
"股息收益率是公司年度总派息额与股票现价之比,衡量投资回报的指标。",
"市销率是股票价格与每股销售收入的比值,适用于评估尚未盈利的成长型公司。",
"企业价值倍数是企业价值与EBITDA的比率,考虑了公司债务水平的估值指标。",
"现金流折现模型通过预测未来现金流并折现至今来评估公司内在价值。",
"技术分析主要关注股票价格和交易量的历史数据,预测未来趋势。",
"基本面分析关注公司财务状况、管理层质量和市场地位等因素。",
"投资组合理论主张通过资产多样化来分散风险,优化风险回报比。",
"被动投资策略通过购买指数基金或ETF来追踪特定市场指数表现。"
]

# 查询和已知的正例
query ="什么是市盈率如何使用它评估股票价值"
true_positive = corpus[0] # 第一条关于市盈率的文本是真正的正例

# 2. 使用jieba分词进行BM25检索(稀疏检索阶段)
tokenized_corpus = [list(jieba.cut(doc))fordocincorpus] # 对corpus进行分词
tokenized_query = list(jieba.cut(query)) # 对query进行分词

bm25 = BM25Okapi(tokenized_corpus)
bm25_scores = bm25.get_scores(tokenized_query)

# 获取BM25排序后的文档索引(按相关性从高到低排序)
sorted_indices = np.argsort(bm25_scores)[::-1] # 降序排序
print("BM25检索结果排序:")
foridxinsorted_indices[:5]: # 取前5名
print(f"文档{idx}(得分:{bm25_scores[idx]:.4f}):{corpus[idx][:50]}...")

# 3. 使用Embedding模型进行重排序(稠密检索阶段)
# 加载预训练的Embedding模型
model = SentenceTransformer(r'C:\Users\k\Desktop\BaiduSyncdisk\baidu_sync_documents\hf_models\bge-m3', trust_remote_code=True) # 示例模型

# 计算查询和所有文档的嵌入向量
query_embedding = model.encode([query])[0]
corpus_embeddings = model.encode(corpus)

# 计算余弦相似度
fromsklearn.metrics.pairwiseimportcosine_similarity
similarities = cosine_similarity([query_embedding], corpus_embeddings)[0]

# 获取嵌入模型排序后的文档索引
sorted_indices_emb = np.argsort(similarities)[::-1] # 降序排序
print("\n嵌入模型重排序结果:")
foridxinsorted_indices_emb[:5]: # 取前5名
print(f"文档{idx}(相似度:{similarities[idx]:.4f}):{corpus[idx][:50]}...")

# 4. 识别难负样本(高相似度但实际不相关的文档)
# 去除真正的正例
hard_negatives_candidates = [idxforidxinsorted_indices_embifcorpus[idx] != true_positive]

# 从候选中选择前N个作为难负样本
hard_negatives = [corpus[idx]foridxinhard_negatives_candidates[:2]] # 取前2个作为难负样本

# 5. 最终的训练样本结构
training_sample = {
"query": query,
"pos": [true_positive],
"neg": hard_negatives
}

print("\n最终构建的包含难负样本的训练数据:")
print(f"查询:{training_sample['query']}")
print(f"正例:{training_sample['pos'][0]}")
print("难负样本:")
fori, neginenumerate(training_sample['neg']):
print(f" {i+1}.{neg}")

实际执行结果:

BM25检索结果排序:
文档0 (得分: 1.7982): 市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除...
文档5 (得分: 0.7829): 现金流折现模型通过预测未来现金流并折现至今来评估公司内在价值...
文档3 (得分: 0.7425): 市销率是股票价格与每股销售收入的比值,适用于评估尚未盈...
文档1 (得分: 0.7238): 市净率是股价与每股净资产的比率,常用于评估银行等资产...
文档9 (得分: 0.0000): 被动投资策略通过购买指数基金或ETF来追踪特定市场指数表现...

嵌入模型重排序结果:
文档0 (相似度: 0.8059): 市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除...
文档1 (相似度: 0.7493): 市净率是股价与每股净资产的比率,常用于评估银行等资产...
文档3 (相似度: 0.7044): 市销率是股票价格与每股销售收入的比值,适用于评估尚未盈...
文档2 (相似度: 0.5833): 股息收益率是公司年度总派息额与股票现价之比,衡量投资回报...
文档4 (相似度: 0.5568): 企业价值倍数是企业价值与EBITDA的比率,考虑了公司...

最终构建的包含难负样本的训练数据:
查询: 什么是市盈率如何使用它评估股票价值
正例: 市盈率是衡量股票价格相对于每股收益的指标,计算公式为股票价格除以每股收益。
难负样本:
1. 市净率是股价与每股净资产的比率,常用于评估银行等资产密集型公司价值。
2. 市销率是股票价格与每股销售收入的比值,适用于评估尚未盈利的成长型公司。

从这个实际运行结果中可以看出,难负样本往往是那些在字面上与查询有某种相似性(如都是讨论金融估值指标),甚至可能共享一些关键词("市X率"、"股票价格"),但实际上并不是查询真正想要的信息。典型的如"市净率"、"市销率"等指标,它们与"市盈率"形式类似,都是股票估值指标,但概念和使用场景不同。这种高相似度但实际不相关的文本正是最容易使模型产生混淆的地方,因此在训练中使用这类难负样本可以有效提升模型的细粒度语义区分能力。

负样本的数量通常会多于正样本(例如,每个正样本配备多个负样本)。同时,保证负样本的多样性也非常重要,即负样本应覆盖不同类型的不相关情况,而不仅仅是单一类型的易区分样本。需要在数量与多样性之间进行权衡,以达到最佳的训练效果和效率。

对比学习方法在Embedding微调中的应用

对比学习是一种表示学习方法,它通过构建正负样本对,让模型学习将语义相似的样本在表示空间中拉近,将不相似的样本推远。在Embedding微调中,这类方法特别有效,因为它们直接优化了向量空间中文本表示的分布,提高了语义相似性的准确性。

对比学习原理与实践

数学原理

对比学习的核心是InfoNCE损失函数,它通过最大化正样本对的相似度,同时最小化负样本对的相似度来优化模型:

其中是查询向量,是正样本向量,是负样本向量,是相似度函数,是温度参数。

关键参数说明:

优化目标

通过最小化InfoNCE损失,模型可以:

  1. 正样本优化:增大相关样本对的相似度(分子项)
  2. 负样本优化:减小不相关样本对的相似度(分母项)
  3. 分布调节:通过温度参数调整学习难度

主流技术实现

  1. SimCSE技术








欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5