基于语义的NLP任务去重:大语言模型应用与实践

news2024/12/22 0:32:05

引言

在自然语言处理(NLP)任务中,数据质量是模型性能的关键因素之一。重复或冗余的数据会导致模型过度拟合或浪费计算资源,特别是在大语言模型(如 BERT、GPT 系列等)训练和推理阶段。传统的基于字符匹配的去重方法(如字符串哈希或编辑距离)在面对语义相似的文本时表现有限,而语义相似度算法则能更好地捕获文本之间的深层语义关系。

本文将介绍一种基于语义表示的去重方法,通过大语言模型生成的嵌入向量结合高效的相似度计算工具(如 FAISS),对大规模文本数据进行去重。此方法不仅适用于数据清洗,还可以应用在搜索引擎、推荐系统等需要衡量语义相似度的场景。

原理与方法

1. 传统去重方法的局限性

在 NLP 任务中,传统的去重方法包括:

  • 字符串哈希:
    基于文本的哈希值进行判重,适合完全重复的文本,但无法处理语义相似但表达不同的情况,例如:
    • 文本 A:我喜欢吃苹果。
    • 文本 B:苹果是我最喜欢的水果。

虽然两者语义相近,但哈希值完全不同。

  • 编辑距离(Levenshtein Distance):
    衡量两个字符串的编辑代价,适合处理少量字符差异的文本,但无法捕捉深层语义关系。

上述方法对文本的语义相似性缺乏鲁棒性,特别是在短文本或同义表达常见的场景下。例如,问答生成、文档去重、语料清洗等任务中,语义相似的重复数据可能会严重影响模型性能。

2. 基于语义嵌入的去重

语义嵌入(Semantic Embedding)是一种将文本映射到高维向量空间的技术,向量的物理距离或角度可以反映文本语义的相似程度。常见的嵌入生成模型包括:

  • BERT、RoBERTa、GPT 等大语言模型:能够生成上下文相关的语义表示。
  • Sentence-BERT(SBERT):专为语义相似度任务设计,提升了嵌入的语义表达能力。

基本流程:

    1. 文本嵌入生成:
      使用大语言模型将文本转化为固定维度的向量表示(如 768 维)。
    1. 相似度计算:
      通过数学距离(如余弦相似度或内积)衡量文本向量之间的相似性。
    1. 去重判断:
      基于相似度阈值判断文本是否为重复内容。

3. 相似度计算方法对比

在语义嵌入的基础上,常用的相似度计算方法包括:

3.1. 余弦相似度(Cosine Similarity)

余弦相似度衡量两个向量的夹角余弦值,范围为 [ − 1 , 1 ] [-1, 1] [1,1],归一化后范围为 [ 0 , 1 ] [0, 1] [0,1]。公式如下:
Cosine Similarity ( A , B ) = A ⋅ B ∥ A ∥ ∥ B ∥ \text{Cosine Similarity}(A, B) = \frac{A \cdot B}{\|A\| \|B\|} Cosine Similarity(A,B)=A∥∥BAB

  • 优点:消除向量模长的影响,只关注向量方向。
  • 缺点:计算开销稍高。
3.2. 内积相似度(Inner Product Similarity)

内积相似度直接计算两向量的点积值:
Inner Product ( A , B ) = A ⋅ B \text{Inner Product}(A, B) = A \cdot B Inner Product(A,B)=AB

  • 优点:计算简单,速度快。
  • 缺点:受向量模长影响,需要确保输入向量已归一化(模长为 1),否则结果不等价于余弦相似度。
欧几里得距离(Euclidean Distance)

衡量两个向量在高维空间中的直线距离:
Euclidean Distance ( A , B ) = ∑ i = 1 n ( A i − B i ) 2 \text{Euclidean Distance}(A, B) = \sqrt{\sum_{i=1}^n (A_i - B_i)^2} Euclidean Distance(A,B)=i=1n(AiBi)2

  • 优点:适合绝对位置相关的任务。
  • 缺点:不适合捕获方向性的语义相似度。

4. 高效的大规模相似度计算

直接比较所有嵌入向量的相似度在大规模数据中效率低下(复杂度为 O ( n 2 ) O(n^2) O(n2))。为此,我们借助 FAISS(Facebook AI Similarity Search)工具,能够在百万级甚至亿级数据中高效实现近似最近邻搜索。

4.1. FAISS 简介

FAISS 是一个高效的相似度搜索库,专为高维向量的最近邻搜索设计,支持以下特性:

  • 多种索引结构:
    • Flat:暴力搜索,适合中小规模数据。
    • IVF(倒排文件索引):适合大规模数据。
    • PQ(分组量化):进一步压缩内存占用。
  • GPU 加速:支持 GPU 版本,在大规模数据上极大提升搜索速度。
  • 灵活的距离度量:支持内积、余弦、欧几里得距离等。
4.2. 使用 FAISS 的语义去重流程
  1. 初始化 FAISS 索引:选择适合任务的数据结构(如 IndexFlatIP)。
  2. 添加向量:将嵌入向量添加到索引。
  3. 查询相似度:对每个新向量,查找与索引中最近的向量,判断是否重复。

代码实现

import json
from transformers import BertTokenizer, BertModel
import torch
from tqdm import tqdm
import faiss
from typing import List, Dict, Union


class TextDeduplicatorWithFAISS:
    """
    使用 FAISS 索引实现的文本去重类(基于余弦相似度)。
    """

    def __init__(self, model_name: str = 'bert-base-chinese', device: str = None) -> None:
        """
        初始化文本去重类。

        参数:
        - model_name: 使用的预训练模型名称,默认为 'bert-base-chinese'。
        - device: 指定运行设备('cpu' 或 'cuda'),默认为自动检测。
        """
        self.tokenizer = BertTokenizer.from_pretrained(model_name)
        self.model = BertModel.from_pretrained(model_name)
        self.device = device if device else ('cuda' if torch.cuda.is_available() else 'cpu')
        self.model = self.model.to(self.device)

        # 初始化 FAISS 索引
        self.embedding_dim = 768  # BERT 输出嵌入维度
        self.index = faiss.IndexFlatIP(self.embedding_dim)  # 使用内积(IP)作为相似度度量
        self.index_ids = []  # 存储对应嵌入的 ID,方便后续处理

    def get_embeddings(self, texts: List[str]) -> torch.Tensor:
        """
        计算文本的嵌入表示,并进行归一化。

        参数:
        - texts: 要计算嵌入的一组文本列表。

        返回:
        - 归一化后的文本嵌入张量,形状为 (batch_size, hidden_size)。
        """
        inputs = self.tokenizer(texts, return_tensors="pt", padding=True, truncation=True, max_length=512)
        inputs = inputs.to(self.device)  # 将输入张量移动到指定设备
        with torch.no_grad():  # 禁用梯度计算以节省内存
            outputs = self.model(**inputs)
        embeddings = outputs.last_hidden_state[:, 0, :].cpu()  # 获取 [CLS] 的嵌入并移动到 CPU
        # 对嵌入进行归一化处理(实现余弦相似度)
        embeddings = embeddings / torch.norm(embeddings, dim=1, keepdim=True)
        return embeddings

    def is_duplicate(self, embedding: torch.Tensor, threshold: float = 0.9) -> bool:
        """
        检查一个嵌入是否与 FAISS 索引中的嵌入重复。

        参数:
        - embedding: 待检查的嵌入向量,形状为 (1, hidden_size)。
        - threshold: 相似度的阈值,默认为 0.9。

        返回:
        - 是否为重复项(True / False)。
        """
        if self.index.ntotal == 0:  # 如果索引为空,肯定不是重复
            return False

        # 通过 FAISS 查找最近的向量及其相似度
        embedding_np = embedding.numpy()  # 转为 NumPy 格式
        distances, _ = self.index.search(embedding_np, k=1)  # 查找最近的 1 个向量

        # 检查最近向量的相似度是否高于阈值
        max_similarity = distances[0][0]  # FAISS 返回的是归一化向量的内积(等价于余弦相似度)
        return max_similarity >= threshold

    def add_to_index(self, embedding: torch.Tensor, doc_id: int) -> None:
        """
        将新的嵌入添加到 FAISS 索引中。

        参数:
        - embedding: 要添加的嵌入向量,形状为 (1, hidden_size)。
        - doc_id: 该嵌入对应的文档 ID。
        """
        embedding_np = embedding.numpy()  # 转为 NumPy 格式
        self.index.add(embedding_np)  # 添加到索引中
        self.index_ids.append(doc_id)  # 保存对应的文档 ID

    def process_and_save(self, input_path: str, output_path: str, threshold: float = 0.9) -> None:
        """
        处理输入文件,去除相似文本并保存到输出文件。

        参数:
        - input_path: 输入 JSONL 文件路径。
        - output_path: 输出 JSONL 文件路径。
        - threshold: 去重的相似度阈值,默认值为 0.9。
        """
        doc_id = 0  # 用于标记每条文档的唯一 ID

        with open(input_path, 'r', encoding='utf-8') as infile, open(output_path, 'w', encoding='utf-8') as outfile:
            for line in tqdm(infile, desc="Processing lines"):
                item: Dict[str, Union[str, int, float]] = json.loads(line)  # 从 JSONL 文件中读取一条数据
                output_text: str = item['output']  # 获取文本内容

                # 获取当前文本的嵌入
                current_embedding = self.get_embeddings([output_text])

                # 检查是否为重复
                if not self.is_duplicate(current_embedding, threshold):
                    # 如果不重复,保存文本,并将嵌入添加到索引
                    outfile.write(json.dumps(item, ensure_ascii=False) + '\n')
                    self.add_to_index(current_embedding, doc_id)
                    doc_id += 1


# 使用示例
if __name__ == "__main__":
    # 初始化去重器
    deduplicator = TextDeduplicatorWithFAISS(model_name='bert-base-chinese')

    # 去重并保存结果
    deduplicator.process_and_save(
        input_path='=./processed_unique_data-5.jsonl',
        output_path='=./processed_unique_data-6.jsonl',
        threshold=0.95
    )

数据示例:

{"id": 1, "output": "什么是人工智能?人工智能是指让机器具备人类智能的技术。"}
{"id": 2, "output": "人工智能的定义是什么?人工智能是赋予机器类似人类智能的能力。"}

总结

本文介绍了一种基于语义嵌入的大规模文本去重方法,通过结合大语言模型(如 BERT)和高效相似度搜索工具(FAISS),实现了对语料库的语义级去重。该方法具有以下优点:

  • 高精度:捕捉语义相似性,避免遗漏同义表达的重复数据。
  • 高扩展性:支持大规模数据处理,适用于百万级文本的去重任务。
  • 通用性强:不仅适用于去重,还可扩展至相似文本检索、推荐系统等任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2263500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp自定义树型结构数据弹窗,给默认选中的节点,禁用所有子节点

兼容H5、安卓App、微信小程序 实现逻辑&#xff1a;给默认选中节点的所有子节点添加一个disabled属性&#xff0c;以此禁用子节点。 /components/sonTreeNode/sonTreeNode.vue 封装成组件 <template><view><view :class"[item,item.is_level1?pL1:item…

水仙花数(流程图,NS流程图)

题目&#xff1a;打印出所有的100-999之间的"水仙花数"&#xff0c;并画出流程图和NS流程图。所谓"水仙花数"是指一个三位数&#xff0c;其各位数字立方和等于该数本身。例如&#xff1a;153是一个"水仙花数"&#xff0c;因为1531的三次方&#…

【C++读写.xlsx文件】OpenXLSX开源库在 Ubuntu 18.04 的编译、交叉编译与使用教程

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a; 2024-12-17 …

Kioptix Level 2靶场练习保姆级---春不晚

1.将靶机导入至vm中 首先将靶机的网络设置为nat模式&#xff0c;然后在kali中使用arp-scan命令查找靶机ip 靶机ip为&#xff1a;61.139.2.130 arp-scan -l 2.使用nmap扫描目标ip的端口 nmap -p- 61.139.2.130 3.对存在端口进行服务版本和、系统版本、默认脚本检测 nmap -p…

电子元器件与电路之-MOS管的介绍和作用

一、基本概念 MOS 管&#xff0c;或MOSFET&#xff0c;全称是Metal-Oxide-Semiconductor Field-Effect Transistor&#xff08;金属 - 氧化物 - 半导体场效应晶体管&#xff09;。和三极管利用电流控制电流不同&#xff0c;它是一种利用电场效应来控制电流的半导体器件。和三级…

异地组网最简单的方法

01、使用硬件路由器的VPN功能 这是一种相对简单且常用的异地组网方法。你需要有支持VPN功能的路由器&#xff0c;如华硕、中兴等品牌。在主站点的路由器上配置VPN服务器&#xff0c;并在异地设备上通过操作系统自带的VPN连接功能添加一个VPN连接&#xff0c;输入主站点路由器的…

【GO环境安装】mac系统+GoLand使用

文章目录 下载安装包环境配置GoLandGo Modules 下载安装包 地址&#xff1a;GO下载地址 下载好后直接进行安装&#xff1a; 进入terminal&#xff0c;查看是否安装成功&#xff1a; 环境配置 在文稿下面创建工作目录&#xff1a; 在文稿下新建Go_Works文件夹&#xff0c;在…

点击数字层级从 admin.vue 跳转到 inviter-list.vue 组件

文章目录 1、admin.vue2、inviter-list.vue 1、admin.vue 好的&#xff0c;我们来分析一下代码中“层级”这一列的逻辑&#xff0c;并探讨它与后端的关联。 “层级” 列的逻辑 在您的代码中&#xff0c;“层级”列的渲染逻辑如下&#xff1a; <el-table-columnalign&quo…

LabVIEW实时信号采集与频谱分析

系统通过LabVIEW与PXIe硬件结合&#xff0c;实现高精度模拟信号的实时采集、频谱分析与可视化显示。核心功能包括采样率配置、快速傅里叶变换&#xff08;FFT&#xff09;、功率谱图生成及动态缩放调整&#xff0c;同时支持信号平均与噪声抑制。系统设计灵活&#xff0c;适用于…

【ComfyUI + 铅笔素描画风】艺术家DaTou发布了的彩色铅笔素描风格生成(真实感超强)

发布时间&#xff1a;2024年12月09日 项目主页&#xff1a;https://hf-mirror.com/Datou1111/shou_xin 基础模型&#xff1a;flux.1-dev comfyui工作流下载&#xff1a;https://pan.baidu.com/s/1FrLQ4o8ldckKwhIrN1Pv7g?pwd1220 自己测试 官方效果 生成猫猫 shou_xin, a m…

洛谷 B3644 【模板】拓扑排序 / 家谱树 C语言

题目&#xff1a; https://www.luogu.com.cn/problem/B3644 题目描述 有个人的家族很大&#xff0c;辈分关系很混乱&#xff0c;请你帮整理一下这种关系。给出每个人的后代的信息。输出一个序列&#xff0c;使得每个人的后辈都比那个人后列出。 输入格式 第 1 行一个整数 …

unity接入coze智能体

官网链接 coze智能体创建、设置 点击创建–选着智能体&#xff0c;随便起一个名字&#xff0c;就可以了 添加令牌 把随便起一个名字&#xff0c;设置时间&#xff0c;把所有选项都勾选上&#xff0c;一定要勾选所有团队空间&#xff0c;否则无法点击确定。 点击确定后&a…

EE308FZ_Sixth Assignment_Beta Sprint_Sprint Essay 3

Assignment 6Beta SprintCourseEE308FZ[A] — Software EngineeringClass Link2401_MU_SE_FZURequirementsTeamwork—Beta SprintTeam NameFZUGOObjectiveSprint Essay 3_Day5-Day6 (12.15-12.16)Other Reference1. WeChat Mini Program Design Guide 2. Javascript Style Guid…

国内主流的工程项目管理软件有哪些?

随着科技的发展&#xff0c;工程管理软件已经成为了工程管理的重要工具。在国内&#xff0c;有许多优秀的工程管理软件&#xff0c;它们可以帮助我们更好地管理工程项目。那么&#xff0c;你知道有哪些工程管理软件吗&#xff1f;下面就让我们一起来盘点一下。 1、广联达 广联…

网络变压器如何识别电路

1. 基本符号的理解 曲线&#xff1a;表示变压器的线圈&#xff08;windings&#xff09;&#xff0c;每个曲线代表一个独立的线圈。 直线&#xff1a;用于连接不同的元件或引脚&#xff0c;表明电流路径。 2. 关键标注解释 CT&#xff08;Center Tap&#xff09;&#xff1a;中…

【原生js案例】ajax的简易封装实现后端数据交互

ajax是前端与后端数据库进行交互的最基础的工具&#xff0c;第三方的工具库比如jquery,axios都有对ajax进行第二次的封装&#xff0c;fecth是浏览器原生自带的功能&#xff0c;但是它与ajax还是有区别的&#xff0c;总结如下&#xff1a; ajax与fetch对比 实现效果 代码实现 …

免费开源!推荐一款网页版数据库管理工具!

免费开源&#xff01;推荐一款网页版数据库管理工具&#xff01; DBGate 是一个开源的数据库管理工具&#xff0c;DBGate 的最大特点是可以 Web 访问&#xff01;&#xff0c;轻松实现一台机器部署&#xff0c;所有人使用&#xff01; 无论是 MySQL、PostgreSQL、SQLite 还是…

主要是使用#includenlohmannjson.hpp时显示找不到文件,但是我文件已正确导入visual studio配置,也保证文件正确存在

问题&#xff1a; 主要是在项目配置中包括了C/C配置中文件位置&#xff0c;但是没有把nlohmann上一级的目录包括进去&#xff0c;导致#include"nlohmann/json.hpp"找不到文件位置 解决&#xff1a; 加上上一级目录到附加包含目录 596513661)] 总结&#xff1a; 找不…

智慧公交指挥中枢,数据可视化 BI 驾驶舱

随着智慧城市的蓬勃发展&#xff0c;公共交通作为城市运营的核心枢纽&#xff0c;正朝着智能化和数据驱动的方向演进。通过整合 CAN 总线技术(Controller Area Network&#xff0c;控制器局域网总线)、车载智能终端、大数据分析及处理等尖端技术&#xff0c;构建的公交“大脑”…

[c++11(二)]Lambda表达式和Function包装器及bind函数

1.前言 Lambda表达式着重解决的是在某种场景下使用仿函数困难的问题&#xff0c;而function着重解决的是函数指针的问题&#xff0c;它能够将其简单化。 本章重点&#xff1a; 本章将着重讲解lambda表达式的规则和使用场景&#xff0c;以及function的使用场景及bind函数的相关使…