6-大语言模型—预训练:数据处理
目录
1、质量过滤(Quality Filtering)
1.1、语言过滤(Language Filtering)
1.2、 指标过滤(Metric Filtering)
1.3、 统计特征过滤(Statistical Feature Filtering)
1.4、 关键词过滤(Keyword Filtering)
2、冗余去除(Redundancy Removal)
2.1、句子级别(Sentence-Level Redundancy)
2.2、文档级别(Document-Level Redundancy)
2.3、数据集级别(Dataset-Level Redundancy)
3、隐私消除(Privacy Removal)
3.1、 隐私数据发现(Private Data Discovery)
3.2、隐私数据消除(Private Data Removal)
4、词元切分(Tokenization)
4.1、 子词词元化(Subword Tokenization)
4.2、字节对编码(Byte Pair Encoding, BPE)
4.3、 WordPierce
5、总结
6、实验代码
7、训练结果
数据质量对模型的影响非常大,因此,在收集了各种类型的数据之后,需要对数据进行处理,去除低质量数据、重复数据、有害信息、个人隐私等内容。典型的数据处理主要包括质量过滤、冗余去除、隐私消除、词元切分。
1、质量过滤(Quality Filtering)
目的:筛选出高质量、符合任务需求的数据,剔除噪声(如乱码、无意义文本、低相关性内容),确保数据 “有用且可靠”。
质量过滤:挑出 “正经数据”
就像买菜时挑新鲜的,去掉烂叶子。
- 语言过滤:只留目标语言(比如做中文任务,就把夹杂英文、乱码的去掉)。
- 指标过滤:去掉太短 / 太长、全是符号的 “垃圾”(比如 “嗯”“!?!?” 这种没意义的)。
- 统计过滤:用数据规律筛异常(比如反复说 “广告” 的刷屏文本,正常人不会这么说话)。
- 关键词过滤:按主题删无关内容(比如做美食推荐,就去掉含 “暴力”“政治” 的文本)。
1.1、语言过滤(Language Filtering)
- 定义:确保数据符合目标语言(如仅保留中文数据,剔除混杂的外文内容),避免跨语言噪声干扰。
- 方法:
- 使用语言检测工具(如
langdetect
、cld3
)识别文本语言; - 设定语言置信度阈值(如仅保留置信度>0.9 的中文文本)。
- 使用语言检测工具(如
- 实例:在中文情感分析任务中,过滤掉包含大量英文、乱码的文本(如 “abc 你好 @#¥”)。
1.2、 指标过滤(Metric Filtering)
- 定义:通过预设指标(如长度、清晰度)筛选文本,剔除 “无效格式” 数据。
- 核心指标:
- 文本长度:过滤过短(如<5 个字符)或过长(如>10000 字)的文本(短文本可能无意义,长文本可能包含冗余);
- 清晰度:过滤含过多特殊符号(如 “***&&&&”)、乱码(如 “��”)或重复字符(如 “啊啊啊啊啊”)的文本;
- 标点比例:剔除标点符号占比过高的文本(如 “!?!?……”)。
- 实例:在对话生成任务中,过滤掉长度<10 字的 “无效回复”(如 “嗯”“不知道”)。
1.3、 统计特征过滤(Statistical Feature Filtering)
- 定义:通过文本的统计特征(如词频、熵值)识别异常数据,剔除 “不符合语言规律” 的文本。
- 常用统计特征:
- 词频分布:过滤高频词占比过高的文本(如反复重复 “广告” 的垃圾文本);
- 文本熵:熵值过低的文本(如 “1234567890”)通常是无意义序列,熵值反映文本的随机性和信息量;
- 停用词比例:停用词(如 “的、是、在”)占比过高的文本可能信息量低(如 “的的的是是是在在在”)。
- 实例:在新闻分类任务中,过滤熵值<2 的 “乱码文本”(正常中文文本熵值通常>3)。
1.4、 关键词过滤(Keyword Filtering)
- 定义:通过预设关键词(敏感词、无关词)筛选文本,剔除 “主题不符” 或 “违规” 内容。
- 方法:
- 构建关键词列表(如任务为 “医疗问答” 时,剔除含 “暴力、政治” 等无关关键词的文本);
- 使用正则表达式或字符串匹配(如
fnmatch
、re
库)快速检测关键词。
- 实例:在儿童教育数据集构建中,过滤含 “色情、脏话” 等敏感关键词的文本。
2、冗余去除(Redundancy Removal)
目的:删除重复或高度相似的内容,减少数据冗余(冗余会增加计算成本,且可能导致模型过拟合)。
冗余去除:删掉 “重复内容”
就像整理衣柜,扔掉一模一样的衣服。
- 句子级别:同一段里反复出现的句子(比如 “这个好!这个好!这个好!” 只留一句)。
- 文档级别:完全一样的文章(比如同一篇新闻被多个网站转载,只留一份)。
- 数据集级别:训练集和测试集不能有重复(不然模型作弊,成绩不准)。
2.1、句子级别(Sentence-Level Redundancy)
- 定义:剔除文本内部的重复句子(如段落中反复出现的同一句子)。
- 方法:
- 计算句子间相似度(如用余弦相似度、编辑距离),阈值(如>0.9)以上视为重复;
- 对长文本按句子分割后去重(如用
nltk.sent_tokenize
分割句子)。
- 实例:某产品评论中重复出现 “这个产品很好用” 5 次,仅保留 1 次。
2.2、文档级别(Document-Level Redundancy)
- 定义:剔除整个文档的重复(如完全相同的两篇文章)或高度相似文档(如仅少数词语不同的 “洗稿” 文本)。
- 方法:
- 哈希去重:对文档计算哈希值(如 MD5、SHA256),删除哈希值相同的文档;
- 相似度去重:用 MinHash、SimHash 等算法计算文档相似度,剔除相似度>0.8 的文档。
- 实例:爬取新闻时,同一篇报道被多个网站转载,仅保留 1 份。
2.3、数据集级别(Dataset-Level Redundancy)
- 定义:处理跨子集的冗余(如训练集与测试集存在重复数据,导致模型评估失真)。
- 方法:
- 跨集去重:计算不同子集(如 train/test/valid)间的文档相似度,确保测试集数据不在训练集中出现;
- 分布对齐:避免某一类别的数据在子集中过度重复(如训练集中 “体育” 类占比 90%,需平衡分布)。
- 实例:在情感分析数据集划分时,确保测试集中的句子未在训练集中出现过。
3、隐私消除(Privacy Removal)
目的:识别并移除数据中的隐私信息(如个人身份、敏感属性),符合隐私保护法规(如 GDPR、中国《个人信息保护法》)。
隐私消除:擦掉 “敏感信息”
就像发朋友圈打码,保护隐私。
- 找隐私:先找出身份证号、手机号、姓名这些敏感信息(比如 “我叫张三,电话 13800000000”)。
- 消隐私:把敏感信息换成代号(比如 “[姓名]”“[手机号]”),或者直接删掉。
3.1、 隐私数据发现(Private Data Discovery)
- 定义:定位数据中包含的隐私信息,常见类型包括:
- 个人标识:姓名、身份证号、手机号、邮箱、地址;
- 敏感属性:银行卡号、病历、宗教信仰、政治倾向;
- 间接隐私:通过组合可推断身份的信息(如 “2000 年出生 + 某小学 + 班长” 可能定位到具体个人)。
- 方法:
- 规则匹配:用正则表达式识别格式固定的隐私(如手机号:
r'1[3-9]\d{9}'
,身份证号:r'\d{17}[\dXx]'
); - 模型识别:用命名实体识别(NER)模型(如 BERT-NER)检测非固定格式隐私(如姓名、地址)。
- 规则匹配:用正则表达式识别格式固定的隐私(如手机号:
3.2、隐私数据消除(Private Data Removal)
- 定义:对发现的隐私信息进行处理,使其无法关联到具体个人。
- 常用方法:
- 替换 / 掩码:用占位符替换隐私信息(如 “张三”→“[姓名]”,“13800138000”→“[手机号]”);
- 泛化:降低精度以模糊隐私(如 “北京市海淀区 XX 街道”→“北京市”);
- 删除:直接剔除包含隐私的文本(如无法脱敏的病历数据);
- 加密:用哈希函数(如 SHA256)对隐私信息加密(不可逆,仅用于验证)。
- 实例:用户评论中 “我叫李明,电话 13800138000”→处理为 “我叫 [姓名],电话 [手机号]”。
4、词元切分(Tokenization)
目的:将文本分割为模型可理解的基本单位(词元,Token),是 NLP 模型输入的前提(如 Transformer、LSTM 均需输入 Token 序列)。
词元切分:把句子 “拆成小块”
就像把一篇文章拆成一个个词,方便模型 “认字”。
- 子词切分:长词拆成短的(比如 “人工智能” 拆成 “人工 + 智能”,模型更容易学)。
- 字节对编码(BPE):智能合并高频字符(比如 “low”“lowest” 会拆成 “low+est”,兼顾效率和理解)。
- WordPierce:专门处理中文、日文这种没空格的语言(比如 “我爱中国” 拆成 “我 + 爱 + 中国”)。
4.1、 子词词元化(Subword Tokenization)
- 定义:将单词拆分为更小的 “子词单元”(如 “unhappiness”→“un+happiness” 或 “un+happy+ness”),平衡 “词汇表大小” 和 “未登录词(OOV)” 问题。
- 优势:
- 解决生僻词 / 复合词问题(如中文 “人工智能”→“人工 + 智能”,英文 “chatbot”→“chat+bot”);
- 减少词汇表大小(无需为每个新词新增 Token)。
- 常用工具:Hugging Face 的
Tokenizer
(支持多种子词策略)。
4.2、字节对编码(Byte Pair Encoding, BPE)
- 定义:一种主流的子词切分算法,通过 “合并高频字符对” 逐步构建子词表,是 GPT、BERT、LLaMA 等模型的核心切分方法。
- 原理:
- 初始化:将文本拆分为最小单位(如字符,“low”→“l+o+w”);
- 统计:计算所有相邻字符对的出现频率(如 “low” 中 “l+o” 出现 1 次,“o+w” 出现 1 次);
- 合并:合并频率最高的字符对(如 “o+w”→“ow”,“low”→“l+ow”);
- 重复:迭代合并,直到达到预设词汇表大小(如 30000 个子词)。
- 实例:“low+lowest”→经 BPE 合并后可能切分为 “low+est”(“low” 和 “est” 为高频子词)。
4.3、 WordPierce
- 定义:一种针对多语言(尤其是中文、日文等无空格语言)的词元切分工具,结合了规则和统计方法,擅长处理歧义切分(如中文 “学生会” 可切分为 “学生 + 会” 或 “学生会”)。
- 特点:
- 支持跨语言切分,无需依赖空格;
- 结合词典(如《现代汉语词典》)和语言模型(如 n-gram 统计)解决歧义;
- 常用于中文 NLP 任务(如分词、机器翻译)。
5、总结
数据处理的四大步骤环环相扣:
- 质量过滤确保数据 “有用”,
- 冗余去除确保数据 “精简”,
- 隐私消除确保数据 “合规”,
- 词元切分确保数据 “可输入模型”。
数据处理就是:先挑好的,再去重,擦隐私,最后拆成小块
6、实验代码
import re
from typing import List
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import TfidfVectorizer
import pkuseg # 导入 pkusegclass DataProcessor:def __init__(self):self.keywords_to_filter = ["暴力", "色情", "政治"]self.privacy_patterns = {"phone": r"1[3-9]\d{9}","id_card": r"\d{17}[\dXx]","name": r"[张王李赵刘陈杨黄赵吴周][\u4e00-\u9fa5]{1,2}"}self.min_text_length = 10self.max_text_length = 500self.similarity_threshold = 0.8self.chinese_char_threshold = 0.5# 1. 质量过滤(不变)def quality_filter(self, texts: List[str]) -> List[str]:filtered = []for text in texts:if not text.strip():continue# 中文检测chinese_chars = re.findall(r'[\u4e00-\u9fff]', text)chinese_ratio = len(chinese_chars) / len(text) if text else 0if chinese_ratio < self.chinese_char_threshold:continue# 长度过滤if not (self.min_text_length <= len(text) <= self.max_text_length):continue# 关键词过滤if any(keyword in text for keyword in self.keywords_to_filter):continuefiltered.append(text)return filtered# 2. 冗余去除(不变)def remove_redundancy(self, texts: List[str]) -> List[str]:if not texts:return []# 完全重复去重unique_texts = list(dict.fromkeys(texts))# 高相似去重vectorizer = TfidfVectorizer(ngram_range=(1, 2))tfidf_matrix = vectorizer.fit_transform(unique_texts)similarities = cosine_similarity(tfidf_matrix)keep = [True] * len(unique_texts)for i in range(len(unique_texts)):if not keep[i]:continuefor j in range(i + 1, len(unique_texts)):if similarities[i][j] > self.similarity_threshold:keep[j] = Falsereturn [unique_texts[i] for i in range(len(unique_texts)) if keep[i]]# 3. 隐私消除(不变)def privacy_removal(self, texts: List[str]) -> List[str]:processed = []for text in texts:masked = re.sub(self.privacy_patterns["phone"], "[手机号]", text)masked = re.sub(self.privacy_patterns["id_card"], "[身份证]", masked)masked = re.sub(self.privacy_patterns["name"], "[姓名]", masked)processed.append(masked)return processed# 4. 词元切分(使用 pkuseg 分词)def tokenize(self, texts: List[str]) -> List[List[str]]:# 初始化 pkuseg 分词器seg = pkuseg.pkuseg()tokenized = []for text in texts:# 清洗文本(保留中文和常用标点)clean_text = re.sub(r'[^\u4e00-\u9fff,。!?、;:,.!?;:\s]', '', text)# 使用 pkuseg 进行分词tokens = seg.cut(clean_text)tokenized.append(tokens)return tokenized# 测试代码
if __name__ == "__main__":test_data = ["这是一段正常的中文文本,长度合适。","这是一段正常的中文文本,长度合适。", # 完全重复"这是一段正常的中文文本,长度刚好合适。", # 高相似"太短", # 长度不足"This is English text (需要过滤)", # 非中文"包含政治敏感词的文本", # 敏感关键词"我叫张三,手机号13800138000,身份证110101199001011234", # 含隐私"这段文本长度非常长," * 50, # 超长文本"另一段正常的中文内容,适合保留。"]processor = DataProcessor()print("===== 原始数据 =====")for i, text in enumerate(test_data):print(f"{i + 1}. {text[:50]}...")filtered = processor.quality_filter(test_data)print("\n===== 1. 质量过滤后 =====")for i, text in enumerate(filtered):print(f"{i + 1}. {text[:100]}...") # 超长文本截断显示non_redundant = processor.remove_redundancy(filtered)print("\n===== 2. 冗余去除后 =====")for i, text in enumerate(non_redundant):print(f"{i + 1}. {text[:100]}...")deidentified = processor.privacy_removal(non_redundant)print("\n===== 3. 隐私消除后 =====")for i, text in enumerate(deidentified):print(f"{i + 1}. {text[:100]}...")tokenized = processor.tokenize(deidentified)print("\n===== 4. 词元切分后 =====")for i, tokens in enumerate(tokenized):print(f"{i + 1}. {tokens[:10]}...") # 只显示前10个词元
7、训练结果
===== 原始数据 =====
1. 这是一段正常的中文文本,长度合适。...
2. 这是一段正常的中文文本,长度合适。...
3. 这是一段正常的中文文本,长度刚好合适。...
4. 太短...
5. This is English text (需要过滤)...
6. 包含政治敏感词的文本...
7. 我叫张三,手机号13800138000,身份证110101199001011234...
8. 这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,...
9. 另一段正常的中文内容,适合保留。...===== 1. 质量过滤后 =====
1. 这是一段正常的中文文本,长度合适。...
2. 这是一段正常的中文文本,长度合适。...
3. 这是一段正常的中文文本,长度刚好合适。...
4. 这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,...
5. 另一段正常的中文内容,适合保留。...===== 2. 冗余去除后 =====
1. 这是一段正常的中文文本,长度合适。...
2. 这是一段正常的中文文本,长度刚好合适。...
3. 这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,...
4. 另一段正常的中文内容,适合保留。...===== 3. 隐私消除后 =====
1. 这是一段正常的中文文本,长度合适。...
2. 这是一段正常的中文文本,长度刚好合适。...
3. 这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,这段文本长度非常长,...
4. 另一段正常的中文内容,适合保留。...
===== 4. 词元切分后 =====
1. ['这', '是', '一', '段', '正常', '的', '中文', '文本', ',', '长度']...
2. ['这', '是', '一', '段', '正常', '的', '中文', '文本', ',', '长度']...
3. ['这', '段', '文本', '长度', '非常', '长', ',', '这', '段', '文本']...
4. ['另', '一', '段', '正常', '的', '中文', '内容', ',', '适合', '保留']...