(专栏:Python 从真零基础到纯文本 LLM 全栈实战・第 8 篇 | 字数:10000 字 | 零基础友好 | LLM 场景深度绑定 | 代码可运行)
开篇:LLM 的 “垃圾数据” 痛点
你有没有过这样的经历?
- 爬了 1000 条电商评论作为 LLM 训练语料,结果里面全是 “★★★★★”“【好评】”“\n\t” 等无关内容
- 调用 LLM 生成的内容,包含大量 “---”“###” 等格式符号
- 处理用户输入的问题,里面有 “@#¥%” 等特殊字符
这些无关内容、格式符号、特殊字符就是 LLM 语料中的 “噪声”,它们会严重影响 LLM 的训练效果和推理质量。而Python 正则表达式(Regex)就是清洗 LLM 噪声语料的 “手术刀”—— 它能帮你:
- 精准匹配:快速定位语料中的噪声
- 高效替换:批量删除或修改噪声内容
- 灵活提取:从噪声语料中提取有效信息
- 统一格式:将语料格式标准化
本文将从LLM 噪声语料的真实场景出发,系统讲解 Python 正则表达式的核心技术,并结合电商评论清洗、用户输入预处理、LLM 生成结果过滤等实战需求给出代码示例。
一、核心概念:正则表达式的基础认知
1.1 什么是正则表达式?
正则表达式是一种用于匹配字符串的模式,它由普通字符(如字母、数字)和特殊字符(如*、.、[]等)组成,用于从字符串中查找、替换或提取符合模式的内容。
1.2 正则表达式的基本字符
| 字符 | 含义 | 示例 | ||
|---|---|---|---|---|
. | 匹配任意单个字符(除换行符外) | a.b匹配aab、a1b、a@b等 | ||
^ | 匹配字符串的开头 | ^abc匹配abcdef,不匹配xabc | ||
$ | 匹配字符串的结尾 | abc$匹配xyzabc,不匹配abcd | ||
* | 匹配前面的字符 0 次或多次 | a*b匹配b、ab、aab、aaab等 | ||
+ | 匹配前面的字符 1 次或多次 | a+b匹配ab、aab、aaab等,不匹配b | ||
? | 匹配前面的字符 0 次或 1 次 | a?b匹配b、ab,不匹配aab | ||
[] | 匹配字符集中的任意一个字符 | [a-z]匹配任意小写字母 | ||
[^] | 匹配不在字符集中的任意一个字符 | [^0-9]匹配任意非数字字符 | ||
() | 分组,提取匹配的内容 | (a+b)匹配aab并将其分组 | ||
| ` | ` | 或,匹配左边或右边的模式 | `a | b匹配a或b` |
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 开发中,噪声语料主要包括以下几类:
- 格式噪声:如★、【】、---、### 等
- 空白噪声:如空格、制表符、换行符等
- 特殊字符噪声:如 @#¥%& 等
- 冗余内容噪声:如 “此用户没有填写评价内容” 等
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 语料清洗系统,实现以下功能:
- 支持清洗电商评论、新闻、用户输入等多种类型的语料
- 支持自定义清洗规则
- 支持读取 TXT/JSON/CSV 格式的语料
- 支持将清洗后的语料保存到文件
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 匹配不到内容
问题:正则表达式没有匹配到预期的内容。解决:
- 检查正则表达式是否正确
- 使用
re.DEBUG参数查看匹配过程 - 确保使用了正确的字符转义
- 检查是否需要使用
re.DOTALL或re.IGNORECASE等标志
7.2 性能问题
问题:正则表达式匹配速度很慢。解决:
- 预编译正则表达式
- 避免贪婪匹配
- 简化正则表达式
- 限制匹配范围
7.3 转义字符错误
问题:使用了错误的转义字符。解决:
- 使用原始字符串定义正则表达式
- 确保所有特殊字符都被正确转义
7.4 贪婪匹配错误
问题:匹配到的内容比预期的长。解决:在重复次数后面加?,使用非贪婪匹配。
八、总结:正则表达式与 LLM 开发的「对应关系」
| 正则表达式功能 | LLM 开发场景 |
|---|---|
| 匹配格式噪声 | 去除星级评分、评论标签、格式符号等 |
| 匹配空白噪声 | 去除换行符、制表符、多余空格等 |
| 匹配特殊字符噪声 | 去除 @#¥%& 等特殊字符 |
| 匹配冗余内容噪声 | 去除 “此用户没有填写评价内容” 等冗余内容 |
| 替换内容 | 统一语料格式、替换分隔符等 |
| 提取内容 | 从噪声语料中提取有效信息 |
Python 正则表达式是LLM 语料清洗的核心技术,掌握它能帮你快速、精准地清洗语料,提高 LLM 的训练效果和推理质量。在实际开发中,要注意:
- 使用原始字符串定义正则表达式
- 预编译频繁使用的正则表达式
- 避免贪婪匹配
- 结合生成器处理大文件
- 多测试正则表达式的匹配效果
下一篇我们将学习《Python装饰器:LLM API的安全与可观测性增强》,讲解如何使用异步 IO 提高 LLM 批量推理的性能。
1385

被折叠的 条评论
为什么被折叠?



