在大模型训练的前期准备中,数据清洗是最基础也是最重要的环节之一。原始文本数据往往存在各种格式问题、冗余信息和噪声干扰,直接使用会影响模型训练效果。今天我就以实际项目经验,分享如何将原始TXT文本转换为结构化的JSONL格式,并进行基础清洗的全套方法。
JSONL(JSON Lines)是大模型训练中最常用的数据格式之一,相比普通TXT文件具有三大优势:
在实际项目中,我通常会先用JSONL作为中间格式,后续再根据具体训练框架要求转换为其他格式(如HuggingFace的Dataset格式)。
让我们看一个完整的Python实现示例。假设我们有一个名为"Qin.txt"的原始文本文件:
python复制import json
input_file = "Qin.txt"
output_file = "Qin_raw_test.jsonl"
# 1. 读取txt文件内容
with open(input_file, "r", encoding="utf-8") as f:
content = f.read()
# 2. 按空行切分段落
paragraphs = content.split("\n\n")
# 3. 清洗段落
clean_paragraphs = []
for p in paragraphs:
p = p.strip()
if p != "":
clean_paragraphs.append(p)
# 4. 写入jsonl文件
with open(output_file, "w", encoding="utf-8") as f:
for p in clean_paragraphs:
data = {
"text": p # 基础结构,后续可扩展其他字段
}
f.write(json.dumps(data, ensure_ascii=False) + "\n")
print(f"转换成功!共写入{len(clean_paragraphs)}条数据")
关键点说明:ensure_ascii=False参数确保中文字符正常保存,避免被转义为Unicode编码
这段代码完成了几个关键操作:
从网页或PDF复制的文本常带有各种噪声,例如:
以百度百科文本为例,直接复制可能会包含这样的内容:
code复制秦始皇[106](前259年—前210年)[45],嬴姓...
这些[106][45]等标记会影响模型学习,需要特别处理。
获得原始JSONL文件后,我们需要进行更细致的数据清洗。以下是完整的清洗流程和代码实现。
python复制import json
import re
input_file = "Qin_raw_test.jsonl"
output_file = "Qin_cleaned.jsonl"
seen = set() # 用于去重
keep_count = 0
skip_count = 0
def clean_text(text):
# 1. 去掉引用标记
text = re.sub(r"\[\d+\]", "", text)
# 2. 去掉首尾空白
text = text.strip()
# 3. 处理每行内容
lines = text.splitlines()
cleaned_lines = []
for line in lines:
line = line.strip()
if line != "":
cleaned_lines.append(line)
# 4. 重组段落结构
text = "\n".join(cleaned_lines)
return text
with open(output_file, "w", encoding="utf-8") as out_f:
with open(input_file, "r", encoding="utf-8") as in_f:
for line in in_f:
data = json.loads(line)
text = data["text"]
# 执行清洗
cleaned_text = clean_text(text)
# 去重判断
if cleaned_text not in seen:
seen.add(cleaned_text)
new_data = {"text": cleaned_text}
out_f.write(json.dumps(new_data, ensure_ascii=False) + "\n")
keep_count += 1
else:
skip_count += 1
print(f"清洗完成,保留{keep_count}条,跳过{skip_count}条重复数据")
引用标记去除:
r"\[\d+\]"匹配所有[数字]形式的标记空白字符处理:
strip()去除首尾空白数据去重:
实际项目中,我建议保留去重统计日志,这对后续分析数据质量很有帮助
在基础清洗之上,根据项目需求还可以添加:
长度过滤:
python复制if len(cleaned_text) < 20: # 过滤过短文本
continue
关键词过滤:
python复制blacklist = ["广告", "联系方式", "版权声明"]
if any(word in cleaned_text for word in blacklist):
continue
特殊符号处理:
python复制# 替换连续标点
text = re.sub(r"[。,;?!]{2,}", "。", text)
编码统一化:
python复制# 全角转半角
text = text.translate(str.maketrans(',。!?【】()%#@&', ',.!?[]()%#@&'))
在实际数据清洗过程中,会遇到各种预料之外的情况。以下是几个典型案例和解决方法。
中文文本常见编码问题表现:
解决方案:
python复制# 尝试不同编码读取
encodings = ['utf-8', 'gbk', 'gb18030', 'big5']
for enc in encodings:
try:
with open(file, 'r', encoding=enc) as f:
content = f.read()
break
except UnicodeDecodeError:
continue
经验分享:gb18030是最全面的中文编码,能处理绝大多数简体繁体中文文本
当处理从PDF或复杂网页复制的文本时,可能会遇到:
应对策略:
python复制text = re.sub(r"第\d+页\s+", "", text)
处理GB级别的大文本文件时,基础方法可能很慢。优化方法:
分批处理:
python复制batch_size = 10000
for i in range(0, len(lines), batch_size):
batch = lines[i:i+batch_size]
# 处理批次数据
多进程加速:
python复制from multiprocessing import Pool
def process_line(line):
# 单行处理逻辑
return cleaned_line
with Pool(processes=4) as pool:
results = pool.map(process_line, lines)
内存映射处理超大文件:
python复制import mmap
with open('huge_file.jsonl', 'r+') as f:
mm = mmap.mmap(f.fileno(), 0)
# 使用mm对象处理
数据清洗后必须进行质量检查,以下是几种有效的方法。
随机抽取100-200条清洗后的数据,人工检查:
python复制import random
def sample_check(file_path, n=100):
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
samples = random.sample(lines, min(n, len(lines)))
for i, line in enumerate(samples, 1):
print(f"=== 样本 {i} ===")
print(json.loads(line)['text'][:200]) # 打印前200字符
print("\n")
建立关键指标帮助评估:
python复制def calculate_metrics(file_path):
total_chars = 0
total_punct = 0
count = 0
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
text = json.loads(line)['text']
total_chars += len(text)
total_punct += len(re.findall(r'[,。、;:?!]', text))
count += 1
return {
'avg_length': total_chars / count,
'punct_ratio': total_punct / total_chars
}
为关键清洗逻辑编写测试用例:
python复制def test_clean_text():
test_cases = [
("测试[1]文本", "测试文本"),
(" 前后空格 ", "前后空格"),
("第一行\n\n第二行", "第一行\n第二行")
]
for input_text, expected in test_cases:
assert clean_text(input_text) == expected, f"Failed: {input_text}"
print("所有测试用例通过!")
在实际项目中,数据清洗需要系统化的工程实践。以下是我的经验总结。
推荐的标准目录布局:
code复制/data
/raw # 原始数据
/processed # 处理后的数据
/scripts # 清洗脚本
/logs # 运行日志
/tests # 测试用例
完善的日志帮助追踪问题:
python复制import logging
logging.basicConfig(
filename='logs/cleaning.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
try:
# 清洗代码
except Exception as e:
logging.error(f"处理文件{file_path}出错: {str(e)}")
将参数抽离为配置文件(如config.yaml):
yaml复制cleaning:
min_length: 20
blacklist: ["广告", "版权"]
patterns_to_remove:
- "\[\d+\]" # 引用标记
- "\s+" # 多余空格
数据版本管理建议:
清洗脚本应该记录数据版本信息:
python复制{
"text": "清洗后的内容",
"metadata": {
"version": "1.0.0",
"source": "raw/Qin.txt",
"cleaned_at": "2023-08-20"
}
}
在大模型项目中,数据质量直接决定模型效果上限。良好的数据清洗流程不仅能提升模型性能,还能减少训练过程中的各种问题。本文介绍的方法虽然基础,但构成了数据处理的核心框架,后续的复杂清洗都可以在此基础上扩展实现。