Python 正则表达式:LLM 噪声语料的精准清洗

AgenticCoding·十二月创作之星挑战赛 10w+人浏览 535人参与

(专栏:Python 从真零基础到纯文本 LLM 全栈实战・第 8 篇 | 字数:10000 字 | 零基础友好 | LLM 场景深度绑定 | 代码可运行)


开篇:LLM 的 “垃圾数据” 痛点

你有没有过这样的经历?

  • 爬了 1000 条电商评论作为 LLM 训练语料,结果里面全是 “★★★★★”“【好评】”“\n\t” 等无关内容
  • 调用 LLM 生成的内容,包含大量 “---”“###” 等格式符号
  • 处理用户输入的问题,里面有 “@#¥%” 等特殊字符

这些无关内容、格式符号、特殊字符就是 LLM 语料中的 “噪声”,它们会严重影响 LLM 的训练效果和推理质量。而Python 正则表达式(Regex)就是清洗 LLM 噪声语料的 “手术刀”—— 它能帮你:

  1. 精准匹配:快速定位语料中的噪声
  2. 高效替换:批量删除或修改噪声内容
  3. 灵活提取:从噪声语料中提取有效信息
  4. 统一格式:将语料格式标准化

本文将从LLM 噪声语料的真实场景出发,系统讲解 Python 正则表达式的核心技术,并结合电商评论清洗、用户输入预处理、LLM 生成结果过滤等实战需求给出代码示例。


一、核心概念:正则表达式的基础认知

1.1 什么是正则表达式?

正则表达式是一种用于匹配字符串的模式,它由普通字符(如字母、数字)和特殊字符(如*.[]等)组成,用于从字符串中查找、替换或提取符合模式的内容。

1.2 正则表达式的基本字符

字符含义示例
.匹配任意单个字符(除换行符外)a.b匹配aaba1ba@b
^匹配字符串的开头^abc匹配abcdef,不匹配xabc
$匹配字符串的结尾abc$匹配xyzabc,不匹配abcd
*匹配前面的字符 0 次或多次a*b匹配babaabaaab
+匹配前面的字符 1 次或多次a+b匹配abaabaaab等,不匹配b
?匹配前面的字符 0 次或 1 次a?b匹配bab,不匹配aab
[]匹配字符集中的任意一个字符[a-z]匹配任意小写字母
[^]匹配不在字符集中的任意一个字符[^0-9]匹配任意非数字字符
()分组,提取匹配的内容(a+b)匹配aab并将其分组
``或,匹配左边或右边的模式`ab匹配ab`

1.3 Python 正则表达式模块:re

Python 通过re模块支持正则表达式,以下是常用的方法:

  • re.match():从字符串开头匹配
  • re.search():在整个字符串中匹配第一个符合模式的内容
  • re.findall():在整个字符串中匹配所有符合模式的内容,返回列表
  • re.sub():替换字符串中符合模式的内容
  • re.split():根据模式分割字符串

二、核心操作:正则表达式的基本使用

2.1 re.match():从开头匹配

import re

# 匹配以"Hello"开头的字符串
pattern = r"^Hello"
result = re.match(pattern, "Hello LLM!")
print(result)  # 输出:<re.Match object; span=(0, 5), match='Hello'>
print(result.group())  # 输出:Hello

result2 = re.match(pattern, "Hi LLM!")
print(result2)  # 输出:None

2.2 re.search():匹配整个字符串

import re

# 匹配字符串中的"LLM"
pattern = r"LLM"
result = re.search(pattern, "Hello LLM! Welcome to LLM world!")
print(result)  # 输出:<re.Match object; span=(6, 9), match='LLM'>
print(result.group())  # 输出:LLM

2.3 re.findall():匹配所有符合模式的内容

import re

# 匹配字符串中的所有数字
pattern = r"\d+"  # \d匹配数字,+匹配1次或多次
result = re.findall(pattern, "苹果15手机壳29.9元,华为Mate60手机壳39.9元")
print(result)  # 输出:['15', '29', '9', '60', '39', '9']

2.4 re.sub():替换符合模式的内容

import re

# 替换字符串中的"LLM"为"大语言模型"
pattern = r"LLM"
result = re.sub(pattern, "大语言模型", "Hello LLM! Welcome to LLM world!")
print(result)  # 输出:Hello 大语言模型! Welcome to 大语言模型 world!

2.5 re.split():分割字符串

import re

# 根据数字分割字符串
pattern = r"\d+"
result = re.split(pattern, "苹果15手机壳29.9元")
print(result)  # 输出:['苹果', '手机壳', '.', '元']

三、进阶技巧:正则表达式的高级应用

3.1 特殊字符与转义

如果要匹配特殊字符本身,需要使用\转义。

import re

# 匹配字符串中的"."
pattern = r"\."  # 转义"."
result = re.findall(pattern, "29.9元")
print(result)  # 输出:['.']

# 匹配字符串中的"^"
pattern = r"\^"
result = re.findall(pattern, "Hello ^LLM!")
print(result)  # 输出:['^']

3.2 预定义字符类

Python 正则表达式提供了预定义字符类,用于匹配常见的字符类型:

字符类含义
\d匹配任意数字(0-9)
\D匹配任意非数字
\w匹配任意字母、数字或下划线
\W匹配任意非字母、数字或下划线
\s匹配任意空白字符(空格、制表符、换行符等)
\S匹配任意非空白字符
import re

# 匹配字符串中的所有空白字符
pattern = r"\s+"
result = re.sub(pattern, " ", "Hello \n\t LLM!")
print(result)  # 输出:Hello  LLM!

# 匹配字符串中的所有字母和数字
pattern = r"\w+"
result = re.findall(pattern, "Hello @#$ LLM! 2024")
print(result)  # 输出:['Hello', 'LLM', '2024']

3.3 重复次数

可以使用 **{m,n}** 指定匹配的重复次数:

  • {m}:匹配 m 次
  • {m,}:匹配至少 m 次
  • {,n}:匹配最多 n 次
  • {m,n}:匹配 m 到 n 次
import re

# 匹配3位数字
pattern = r"\d{3}"
result = re.findall(pattern, "123 4567 89")
print(result)  # 输出:['123', '456']

# 匹配2到4位数字
pattern = r"\d{2,4}"
result = re.findall(pattern, "12 345 6789 0")
print(result)  # 输出:['12', '345', '6789']

3.4 分组与提取

使用 **()** 分组,提取匹配的内容。

import re

# 提取邮箱地址的用户名和域名
pattern = r"(\w+)@(\w+\.\w+)"
result = re.match(pattern, "test@example.com")
print(result.group())  # 输出:test@example.com
print(result.group(1))  # 输出:test
print(result.group(2))  # 输出:example.com

# 提取所有邮箱的用户名
pattern = r"(\w+)@\w+\.\w+"
result = re.findall(pattern, "test1@example.com test2@gmail.com")
print(result)  # 输出:['test1', 'test2']

四、LLM 噪声语料清洗实战

在 LLM 开发中,噪声语料主要包括以下几类

  1. 格式噪声:如★、【】、---、### 等
  2. 空白噪声:如空格、制表符、换行符等
  3. 特殊字符噪声:如 @#¥%& 等
  4. 冗余内容噪声:如 “此用户没有填写评价内容” 等

4.1 实战 1:电商评论语料清洗

4.1.1 原始语料
★★★★★ 苹果15手机壳质量很好!\n
【中评】物流有点慢\n
★★★ 材质一般\n
【好评】支持无线充电\n
此用户没有填写评价内容\n
4.1.2 清洗目标
  • 去除星级评分(★★★★★)
  • 去除评论标签(【好评】、【中评】)
  • 去除空白字符(\n、\t 等)
  • 去除冗余评价内容
4.1.3 清洗代码
import re

# 原始语料
raw_corpus = [
    "★★★★★ 苹果15手机壳质量很好!\n",
    "【中评】物流有点慢\n",
    "★★★ 材质一般\n",
    "【好评】支持无线充电\n",
    "此用户没有填写评价内容\n"
]

# 清洗函数
def clean_comment(comment):
    # 去除星级评分
    comment = re.sub(r"★+", "", comment)
    # 去除评论标签
    comment = re.sub(r"【.*?】", "", comment)
    # 去除空白字符
    comment = re.sub(r"\s+", "", comment)
    # 去除冗余评价内容
    if comment == "此用户没有填写评价内容":
        return None
    return comment

# 批量清洗
clean_corpus = [clean_comment(comment) for comment in raw_corpus if clean_comment(comment)]
print("清洗前语料:")
for comment in raw_corpus:
    print(comment, end="")

print("\n清洗后语料:")
for comment in clean_corpus:
    print(comment)
4.1.4 清洗结果
清洗前语料:
★★★★★ 苹果15手机壳质量很好!

【中评】物流有点慢

★★★ 材质一般

【好评】支持无线充电

此用户没有填写评价内容


清洗后语料:
苹果15手机壳质量很好!
物流有点慢
材质一般
支持无线充电

4.2 实战 2:用户输入预处理

4.2.1 原始用户输入
"@AI助手 你好!我想知道<<苹果15手机壳>>的发货时间???\t\n
4.2.2 预处理目标
  • 去除 @提到的用户
  • 去除 <<>> 等格式符号
  • 去除多余的标点符号
  • 去除空白字符
4.2.3 预处理代码
import re

# 原始用户输入
raw_input = "@AI助手 你好!我想知道<<苹果15手机壳>>的发货时间???\t\n"

# 预处理函数
def preprocess_user_input(input_text):
    # 去除@提到的用户
    input_text = re.sub(r"@.*?\s", "", input_text)
    # 去除<<>>等格式符号
    input_text = re.sub(r"<<|>>", "", input_text)
    # 去除多余的标点符号
    input_text = re.sub(r"[?!]{2,}", "?", input_text)
    # 去除空白字符
    input_text = re.sub(r"\s+", " ", input_text).strip()
    return input_text

# 预处理结果
processed_input = preprocess_user_input(raw_input)
print(f"原始输入:{repr(raw_input)}")
print(f"预处理后:{processed_input}")
4.2.4 预处理结果
原始输入:'@AI助手 你好!我想知道<<苹果15手机壳>>的发货时间???\t\n'
预处理后:你好!我想知道苹果15手机壳的发货时间?

4.3 实战 3:LLM 生成结果过滤

4.3.1 原始生成结果
"---LLM生成结果---\n
苹果15手机壳的发货时间是24小时内。\n
### 更多信息:\n
1. 支持顺丰快递\n
2. 偏远地区3-5天到达\n
---"
4.3.2 过滤目标
  • 去除 ---、### 等格式符号
  • 去除多余的换行符
  • 提取核心内容
4.3.3 过滤代码
import re

# 原始生成结果
raw_output = "---LLM生成结果---\n苹果15手机壳的发货时间是24小时内。\n### 更多信息:\n1. 支持顺丰快递\n2. 偏远地区3-5天到达\n---"

# 过滤函数
def filter_llm_output(output):
    # 去除---分隔符
    output = re.sub(r"---.*?---", "", output, flags=re.DOTALL)  # re.DOTALL匹配换行符
    # 去除###标题
    output = re.sub(r"###.*?\n", "", output)
    # 去除多余的换行符
    output = re.sub(r"\n+", "\n", output).strip()
    return output

# 过滤结果
filtered_output = filter_llm_output(raw_output)
print(f"原始输出:{repr(raw_output)}")
print(f"过滤后:{filtered_output}")
4.3.4 过滤结果
原始输出:'---LLM生成结果---\n苹果15手机壳的发货时间是24小时内。\n### 更多信息:\n1. 支持顺丰快递\n2. 偏远地区3-5天到达\n---'
过滤后:苹果15手机壳的发货时间是24小时内。
1. 支持顺丰快递
2. 偏远地区3-5天到达

4.4 实战 4:语料格式统一

4.4.1 原始语料
苹果15手机壳(防摔)
华为Mate60手机壳_磨砂
小米14手机壳-轻薄
4.4.2 统一目标
  • 将所有分隔符(( )、_、-)统一为空格
4.4.3 统一代码
import re

# 原始语料
raw_corpus = [
    "苹果15手机壳(防摔)",
    "华为Mate60手机壳_磨砂",
    "小米14手机壳-轻薄"
]

# 统一函数
def unify_format(corpus):
    return [re.sub(r"[()_-]", " ", text) for text in corpus]

# 统一结果
unified_corpus = unify_format(raw_corpus)
print(f"原始语料:{raw_corpus}")
print(f"统一后:{unified_corpus}")
4.4.4 统一结果
原始语料:['苹果15手机壳(防摔)', '华为Mate60手机壳_磨砂', '小米14手机壳-轻薄']
统一后:['苹果15手机壳 防摔 ', '华为Mate60手机壳 磨砂', '小米14手机壳 轻薄']

五、高级实战:批量 LLM 语料清洗系统

5.1 系统需求

开发一个批量 LLM 语料清洗系统,实现以下功能:

  1. 支持清洗电商评论、新闻、用户输入等多种类型的语料
  2. 支持自定义清洗规则
  3. 支持读取 TXT/JSON/CSV 格式的语料
  4. 支持将清洗后的语料保存到文件

5.2 系统结构

llm_corpus_cleaner/
├── cleaner.py  # 清洗核心模块
├── main.py  # 主程序

5.3 核心代码实现

5.3.1 cleaner.py
import re
import json
import csv

class LLMCorpusCleaner:
    def __init__(self, rules=None):
        # 默认清洗规则
        self.default_rules = [
            # 去除星级评分
            (r"★+", ""),
            # 去除评论标签
            (r"【.*?】", ""),
            # 去除@提到的用户
            (r"@.*?\s", ""),
            # 去除格式符号
            (r"---+|###+|<<|>>", ""),
            # 去除多余的标点符号
            (r"[?!]{2,}", "?"),
            (r"[!!]{2,}", "!"),
            # 去除空白字符
            (r"\s+", " ")
        ]
        
        # 自定义清洗规则
        self.custom_rules = rules if rules else []
        
        # 合并所有规则
        self.all_rules = self.default_rules + self.custom_rules
    
    def clean_text(self, text):
        """清洗单条文本"""
        if not isinstance(text, str):
            return None
        
        cleaned = text
        for pattern, replacement in self.all_rules:
            cleaned = re.sub(pattern, replacement, cleaned, flags=re.IGNORECASE)
        
        # 去除首尾空格
        cleaned = cleaned.strip()
        
        # 去除空字符串
        return cleaned if cleaned else None
    
    def clean_corpus(self, corpus):
        """清洗批量语料"""
        return [self.clean_text(text) for text in corpus if self.clean_text(text)]
    
    def read_corpus(self, file_path):
        """读取不同格式的语料文件"""
        if file_path.endswith(".txt"):
            with open(file_path, "r", encoding="utf-8") as file:
                return file.readlines()
        elif file_path.endswith(".json"):
            with open(file_path, "r", encoding="utf-8") as file:
                data = json.load(file)
                return [item["content"] for item in data]
        elif file_path.endswith(".csv"):
            with open(file_path, "r", encoding="utf-8") as file:
                reader = csv.DictReader(file)
                return [row["content"] for row in reader]
        else:
            raise ValueError("不支持的文件格式")
    
    def save_corpus(self, corpus, file_path):
        """保存清洗后的语料到文件"""
        if file_path.endswith(".txt"):
            with open(file_path, "w", encoding="utf-8") as file:
                for text in corpus:
                    file.write(text + "\n")
        elif file_path.endswith(".json"):
            data = [{"content": text} for text in corpus]
            with open(file_path, "w", encoding="utf-8") as file:
                json.dump(data, file, ensure_ascii=False, indent=2)
        elif file_path.endswith(".csv"):
            with open(file_path, "w", encoding="utf-8", newline="") as file:
                fieldnames = ["content"]
                writer = csv.DictWriter(file, fieldnames=fieldnames)
                writer.writeheader()
                for text in corpus:
                    writer.writerow({"content": text})
        else:
            raise ValueError("不支持的文件格式")
5.3.2 main.py
from cleaner import LLMCorpusCleaner

# 初始化清洗器
cleaner = LLMCorpusCleaner()

# 读取原始语料
raw_corpus = cleaner.read_corpus("raw_corpus.txt")
print(f"原始语料数量:{len(raw_corpus)}")

# 清洗语料
clean_corpus = cleaner.clean_corpus(raw_corpus)
print(f"清洗后语料数量:{len(clean_corpus)}")

# 保存清洗后的语料
cleaner.save_corpus(clean_corpus, "clean_corpus.json")
print("清洗后的语料已保存到clean_corpus.json")

# 打印部分清洗结果
print("\n清洗结果示例:")
for i in range(min(5, len(clean_corpus))):
    print(f"{i+1}. {clean_corpus[i]}")

5.4 运行结果

原始语料数量:1000
清洗后语料数量:850
清洗后的语料已保存到clean_corpus.json

清洗结果示例:
1. 苹果15手机壳质量很好!
2. 物流有点慢
3. 材质一般
4. 支持无线充电
5. 外观很好看

六、性能优化与最佳实践

6.1 预编译正则表达式

对于频繁使用的正则表达式,可以使用re.compile()预编译,提高匹配效率。

import re

# 预编译正则表达式
pattern = re.compile(r"\d+")

# 多次使用
result1 = pattern.findall("苹果15手机壳")
result2 = pattern.findall("华为Mate60手机壳")

6.2 避免贪婪匹配

默认情况下,正则表达式是贪婪匹配的,会尽可能匹配最长的字符串。可以使用?将其转换为非贪婪匹配

import re

# 贪婪匹配(默认)
text = "<div>Hello</div><div>LLM</div>"
pattern = r"<div>.*</div>"
result = re.findall(pattern, text)
print(result)  # 输出:['<div>Hello</div><div>LLM</div>']

# 非贪婪匹配(加?)
pattern = r"<div>.*?</div>"
result = re.findall(pattern, text)
print(result)  # 输出:['<div>Hello</div>', '<div>LLM</div>']

6.3 使用原始字符串

在 Python 中,使用原始字符串r"")定义正则表达式,可以避免转义字符的问题。

# 原始字符串(推荐)
pattern = r"\d+\.\d+"
# 非原始字符串(需要转义)
pattern = "\\d+\\.\\d+"

6.4 限制匹配范围

对于大文件处理,可以结合正则表达式和生成器,避免将整个文件加载到内存。

import re

# 预编译正则表达式
pattern = re.compile(r"[\w]+@[\w]+\.[\w]+")

# 使用生成器处理大文件
def extract_emails(file_path):
    with open(file_path, "r", encoding="utf-8") as file:
        for line in file:
            emails = pattern.findall(line)
            if emails:
                for email in emails:
                    yield email

# 使用示例
for email in extract_emails("large_corpus.txt"):
    print(email)

七、零基础避坑指南

7.1 匹配不到内容

问题:正则表达式没有匹配到预期的内容。解决

  1. 检查正则表达式是否正确
  2. 使用re.DEBUG参数查看匹配过程
  3. 确保使用了正确的字符转义
  4. 检查是否需要使用re.DOTALLre.IGNORECASE等标志

7.2 性能问题

问题:正则表达式匹配速度很慢。解决

  1. 预编译正则表达式
  2. 避免贪婪匹配
  3. 简化正则表达式
  4. 限制匹配范围

7.3 转义字符错误

问题:使用了错误的转义字符。解决

  1. 使用原始字符串定义正则表达式
  2. 确保所有特殊字符都被正确转义

7.4 贪婪匹配错误

问题:匹配到的内容比预期的长。解决:在重复次数后面加?,使用非贪婪匹配。


八、总结:正则表达式与 LLM 开发的「对应关系」

正则表达式功能LLM 开发场景
匹配格式噪声去除星级评分、评论标签、格式符号等
匹配空白噪声去除换行符、制表符、多余空格等
匹配特殊字符噪声去除 @#¥%& 等特殊字符
匹配冗余内容噪声去除 “此用户没有填写评价内容” 等冗余内容
替换内容统一语料格式、替换分隔符等
提取内容从噪声语料中提取有效信息

Python 正则表达式是LLM 语料清洗的核心技术,掌握它能帮你快速、精准地清洗语料,提高 LLM 的训练效果和推理质量。在实际开发中,要注意:

  1. 使用原始字符串定义正则表达式
  2. 预编译频繁使用的正则表达式
  3. 避免贪婪匹配
  4. 结合生成器处理大文件
  5. 多测试正则表达式的匹配效果

下一篇我们将学习《Python装饰器:LLM API的安全与可观测性增强》,讲解如何使用异步 IO 提高 LLM 批量推理的性能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值