Hexo

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

NLP——数据去重方法总结

注:本文包含 AI 辅助创作


整体总结

  • 在 NLP 数据去重中,常用的方法有 精确 Hash,N-gram,MinHash 和 SimHash 等,其中
    • 精确 Hash :完全重复检测
    • N-gram 、MinHash 和 SimHash 主要用于近似重复(Near-duplicate)检测
  • 各种去重方法的核心是,他们对“局部修改”的敏感度不同
  • 实战经验:
    • 极短文本或极少数据,可以直接使用 Query(或样本)Key,构造 dict 即可
    • 短文本 + 少数据量,建议使用 N-gram + 精确 Jaccard
    • 长文本建议使用 SimHash 方法
    • 其他情况建议使用类似 MinHash + LSH 或 Embedding + FAISS 方法

精确 Hash (Exact Hash)

  • 核心思想是使用加密级哈希算法(如 MD5、SHA-1)将文本映射为固定长度的字符串
    • 只要文本有一个字节不同,哈希值就完全不同
    • 部分处理中我们也会考虑将文本转换为小写并去除多余空格再进行比较
  • 适用场景于去除完全一致的日志、系统报错、重复爬取的完全相同网页
  • 速度极快、无碰撞(概率忽略不计)
  • 缺点是完全无法识别“差一个标点”或“差几个字”的近似文本
  • 代码示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import hashlib

    def exact_hash(text: str) -> str:
    return hashlib.md5(text.encode('utf-8')).hexdigest()

    # 测试
    t1 = "我爱自然语言处理"
    t2 = "我爱自然语言处理!" # 多了一个感叹号
    print(f"文本1 Hash: {exact_hash(t1)}")
    print(f"文本2 Hash: {exact_hash(t2)}")
    # 输出两个完全不同的字符串

N-gram (基于集合的相似度)

  • 核心思想是将文本切分成连续的 N 个字符(或词) 组成的滑动窗口集合
    • 通过计算两个集合的 Jaccard 相似度(交集大小 / 并集大小)来判断重复程度
  • 适用于短文本查重、数据清洗时的初步过滤
  • 优点是直观、可解释性强,能捕捉局部字词顺序
  • 缺点是计算开销大(需存储全量集合),N 太大漏匹配,N 太小噪音多
    • N-gram 的复杂度是很大的,在大规模数据集(百万级)上时几乎不可用,除非其中一个是比较小的数据集(其实也慢)
      • 长度为 L 的文本,提取 N-gram 为复杂度时 \(O(L)\)
    • 在使用 N-gram 前,也可以考虑使用一些其他方法先过滤一些,缩小量级
      • 比如对于绝对重复很多的场景,可以使用精确 Hash (MD5) 做第一轮极速过滤,剩下的再用 N-gram 精细看
  • 代码示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def get_ngrams(text: str, n: int = 3):
    """提取字符级 N-gram"""
    # 在文本首尾添加特殊边界符,防止短文本扰动
    padded = f"^{text}$"
    return {padded[i:i+n] for i in range(len(padded) - n + 1)}

    def jaccard_similarity(text1: str, text2: str, n: int = 3):
    set1 = get_ngrams(text1, n)
    set2 = get_ngrams(text2, n)
    intersection = len(set1 & set2)
    union = len(set1 | set2)
    return intersection / union if union != 0 else 0

    # 测试
    t1 = "我爱自然语言处理"
    t2 = "我爱自然语言处理技术" # 仅多了"技术"
    print(f"N-gram 相似度: {jaccard_similarity(t1, t2, n=2):.2f}")
    # 输出通常在 0.7~0.9 之间,说明很相似

MinHash

  • 核心思想是为了解决 N-gram 在大规模数据集(百万级)上两两比较的 \(O(N^2)\) 难题
    • 将高维的 N-gram 集合通过多个随机哈希函数映射为 固定长度的签名向量(指纹)
    • 通过签名向量来无偏估计 原始的 Jaccard 相似度
  • 一般配合 LSH (局部敏感哈希) 使用,将相似文档分到同一个桶中,避免全量比对
  • 优点是海量数据下计算极快,内存占用小
  • 缺点是实现复杂,结果是概率估计(非精确值)
  • 示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from datasketch import MinHash

    def create_minhash(text: str, num_perm: int = 128):
    """将文本转换为 MinHash 签名"""
    # 提取 N-gram (这里用字符级 3-gram)
    ngrams = get_ngrams(text, n=3)
    m = MinHash(num_perm=num_perm) # num_perm 越大,估计越准
    for gram in ngrams:
    m.update(gram.encode('utf-8'))
    return m

    # 测试
    t1 = "我爱自然语言处理"
    t2 = "我爱自然语言处理技术"
    m1 = create_minhash(t1)
    m2 = create_minhash(t2)

    # 计算 Jaccard 相似度的估计值
    estimated_sim = m1.jaccard(m2)
    print(f"MinHash 估计的 Jaccard 相似度: {estimated_sim:.2f}")
    # 该值非常接近上面 N-gram 直接计算的 Jaccard 结果

SimHash

  • 核心思想是专门针对长文本(如整篇文章、网页)设计的指纹算法
    • SimHash 将文本中的每个词(带权重,如 TF-IDF)映射到固定位数(通常是 64 位或 128 位)的二进制向量,通过累加降维得到最终的哈希指纹
    • 两个文本的相似度通过汉明距离(二进制中不同位的个数)来衡量
  • 适用于海量网页去重(Google 早期使用)、搜索引擎索引去重
  • 优点是指纹极短(64bit),存储方便,使用汉明距离比较速度极快
  • 缺点是对短文本效果很差(噪声占主导),实现需要加权
  • 示例笔记:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    import hashlib

    def simhash(text: str, hash_bits: int = 64):
    # 1. 简单分词(这里按字符切分,实际应用会用 jieba 结合 TF-IDF 权重)
    tokens = list(text)

    # 2. 初始化长度为 hash_bits 的向量
    v = [0] * hash_bits

    for token in tokens:
    # 计算 token 的哈希值(这里用 MD5 取前 hash_bits 位)
    h = int(hashlib.md5(token.encode('utf-8')).hexdigest(), 16)
    # 3. 加权累加(这里权重设为 1,实际应用按词频调大,SimHash 的核心优势在于加权(通常用 TF-IDF))
    for i in range(hash_bits):
    bit = (h >> i) & 1
    if bit:
    v[i] += 1 # 该位为1,加权重
    else:
    v[i] -= 1 # 该位为0,减权重

    # 4. 降维:大于0置1,小于等于0置0
    fingerprint = 0
    for i in range(hash_bits):
    if v[i] > 0:
    fingerprint |= (1 << i)
    return fingerprint

    def hamming_distance(fp1: int, fp2: int) -> int:
    return bin(fp1 ^ fp2).count('1')

    # 测试
    t1 = "自然语言处理是人工智能的重要分支"
    t2 = "自然语言处理是AI的重要分支" # 仅将"人工智能"替换为"AI"
    fp1 = simhash(t1, hash_bits=16) # 为了演示取16位
    fp2 = simhash(t2, hash_bits=16)

    print(f"指纹1 (二进制): {bin(fp1)}")
    print(f"指纹2 (二进制): {bin(fp2)}")
    print(f"汉明距离: {hamming_distance(fp1, fp2)}")
    # 汉明距离越小(通常 < 3~5),说明文本越近似重复

NLP——Agentic-AI相关技术

本文仅包含简单介绍,更详细的描述可以搜索本人其他博客

  • 参考链接:
    • 可参考:Generative to Agentic AI: Survey, Conceptualization, and Challenges

Agentic AI 相关技术整体介绍

  • 定义:LLM agentic 技术是指让 LLM 具备智能代理(Agent)能力的相关技术
  • 不专业的定义,常见的 LLM agentic 技术包括:记忆技术 ,工具使用技术 ,推理与计划技术 和 多智能体协作技术 等

记忆技术

  • 通常包括短期记忆和长期记忆
  • 短期记忆 :主要实现对当前会话历史的记忆,最直接的方法是使用模型的上下文窗口,将完整的对话历史作为输入提示的一部分
    • 对于上下文窗口较小的模型,或者当对话历史较大时,可以改用另一个 LLM 来总结到目前为止发生的对话
  • 长期记忆 :通常将所有之前的互动、行动和对话存储在一个外部向量数据库中,构建数据库之后,通过检索增强生成(RAG)方式检索相关信息

工具使用技术

  • 模型调用工具实现一些 LLM 无法实现的功能,相关协议和工具如下
  • Toolformer :是最早实现训练用于决定调用哪些 API 以及如何调用的模型,通过工具使用增强 LLM 的 能力并弥补其不足
  • 模型上下文协议(MCP) :为天气应用和 GitHub 等服务标准化了 API 访问,由以下三个组件组成:
    • MCP 主机(LLM 应用,管理连接)
    • MCP 客户端(与 MCP 服务器保持一对一连接)
    • MCP 服务器(为 LLM 提供上下文、工具和能力)

Reasoning 与 Planning 技术(核心技术)

  • 这个技术是最复杂的,相关论文和方法也最多,关键技术包括 ReAct、Self-Refine、Reflexion、Plan-and-Execute 和 Retroformer 等

ReAct (Reasoning + Acting)

  • 论文参考:ReAct: Synergizing Reasoning and Acting in Language Models, Shunyu Yao, 2022 & ICLR 2023
    • 算是 Agent 领域开创性的工作
  • 基本思路:结合Reasoning和行动(Acting),通过动态生成推理步骤和交互动作(如调用工具、搜索)来完成任务
    • 强调在推理过程中与环境互动以获取额外信息
  • 一句话目标总结:通过动态推理与实时环境交互完成任务
  • 方法流程简述:推理 -> 行动 -> 观察 -> 循环
    • 接收任务(如“回答复杂问题”)
    • 生成推理步骤(如“需先查证XX数据”)
    • 执行动作(调用工具/搜索API)
    • 观察结果(获取工具返回信息)
    • 循环(结合新信息继续推理或终止)
    • 最终输出 :最终答案或解决方案

Self-Refine

  • 论文参考:Self-Refine: Iterative Refinement with Self-Feedback, NeurIPS 2023, NVIDIA & Google Deepmind
  • 基本思路:模型通过自我反馈迭代优化输出。首先生成初始结果,然后自我批评(Self-Critique)并修正错误,无需外部监督
  • 一句话目标总结:通过自我迭代优化单次输出质量
  • 方法流程简述:生成 -> 批评 -> 修正 -> 循环
    • 生成初始输出(如一段代码)
    • 自我批评(检查语法/逻辑错误)
    • 修正输出(基于批评重新生成)
    • 重复 直至满足条件(如无错误或达到最大迭代次数)
    • 最终输出 :优化后的文本/代码

Reflexion

  • 论文参考:Reflexion: Language Agents with Verbal Reinforcement Learning, NeurIPS 2023
  • 基本思路:赋予模型“记忆”能力,通过保存历史交互的反思(Reflection)来指导未来决策,避免重复错误,帮助代理从之前的失败中学习,包含了行动者、评估者和自我反思三个 LLM 角色
  • 一句话目标总结:通过记忆历史反思改进长期策略
  • 方法流程简述:行动 -> 反馈 -> 反思 -> 存储 -> 未来检索
    • 执行任务(如对话/游戏动作)
    • 接收反馈(用户评分/任务成败)
    • 生成反思(如“失败因未查询用户偏好”)
    • 存储反思至记忆库
    • 未来任务优先检索相关反思指导行动
    • 最终输出 :持续优化的长期表现

Plan-and-Execute

  • 代表方法 :Plan-and-Solve Prompting: Improving Zero-Shot Chain-of-Thought Reasoning by Large Language Models, 2023 和 HuggingGPT (利用LLM协调专家模型) 等
  • 基本思路:将任务分解为规划(Plan)和执行(Execute)两阶段:首先生成高层次计划,再逐步执行子任务
  • 一句话目标总结:通过分阶段规划与执行解决复杂任务
  • 方法流程简述:规划 -> 执行子任务 -> 整合
    • 任务分解 :生成高层次计划(如“写论文需:1.查资料 2.列大纲 3.写作”)
    • 执行子任务 :按顺序完成各步骤
    • 整合结果 :合并子任务输出
    • 最终输出 :结构化任务结果

Retroformer

  • 论文参考:Retroformer: Retrospective large language agents with policy gradient optimization, ICLR 2024, Salesforce AI Research
  • 基本思路:通过逆向推理(Retrospective Reasoning)生成假设并验证,结合前向和后向推理提升逻辑一致性
  • 一句话目标总结:通过逆向推理验证逻辑合理性
  • 方法流程简述:正向假设 -> 逆向验证 -> 修正 -> 输出
    • 生成假设(如数学证明的中间结论)
    • 逆向验证 :从目标反推假设是否成立
    • 修正假设 :若验证失败,调整推理路径
    • 输出最终结论
    • 最终输出 :逻辑严谨的结果

Reasoning 与 Planning 技术对比总结

方法 核心能力 交互性 适用场景 关键局限
ReAct 推理+环境交互 高 动态信息获取 依赖环境反馈
Self-Refine 自我迭代优化 无 生成任务优化 可能陷入错误循环
Reflexion 记忆与反思 中等 长期学习/对话 记忆管理复杂
Plan-and-Execute 分阶段任务分解 低 复杂任务规划 规划错误传导
Retroformer 双向推理验证 中等 逻辑严谨性要求高的任务 计算成本高

多智能体协作技术

  • 多智能体由专业化的 Agent 组成,每个 Agent 都配备了自己的一套工具,并由一个主管监督,主管管理 Agent 之间的通信,并为专业化的代理分配特定的任务,以解决单个 Agent 存在的工具选择复杂、上下文复杂和任务专业化等问题
1…979899…352
San Ye

San Ye

Stay Hungry. Stay Foolish.

704 posts
53 tags
© 2026 San Ye
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4