
相信我们在接触大模型已经从很多地方收集各类零零散散的信息,数据的高价值已是行业共识,但并非只有海量数据才有价值,对于类似我们这样的中小企业、个人开发者或垂直场景,如客服机器人、行业知识库、本地化模型微调等,小型的小语料库反而更易落地、成本更低。
但未经治理的小语料库往往充斥着重复文本、语义噪声、格式混乱、低质量内容等问题:比如电商客服语料中的重复问答、家居文本中的广告冗余、数码产品说明中的 OCR 错误。直接用这类脏数据训练模型,只会让模型学错知识、生成混乱内容;而经过专业治理的小语料库,能让本地化模型的效果提升 50% 以上。
今天我们将从基础概念到实践落地,完整讲解如何基于text2vec-base-chinese(语义分析)和bert-base-chinese(质量评分)实现小语料库治理,所有模型均通过本地加载方式部署,无需依赖云端服务,兼顾实用性和安全性。

语料库治理(Corpus Governance)是指对原始文本数据进行采集、清洗、去重、质量评估、存储管理的全流程,核心目标是:
对于小语料库,治理的核心原则是精而不是多,哪怕只有 1G 高质量语料,也远胜于 10G 脏数据。
问题体现:同一段文本像复读机一样反复出现。想象一下,你在电商评论中看到10条完全一样的“亲,啥时候发货?”,或者在新闻数据中连续出现多条一字不差的报道。
深层影响:
治理要点:建立去重流水线,不仅要去除完全相同的文本,还要设定合理的重复阈值,保留必要的重复(如常用问候语),同时剔除大量无意义重复。
问题体现:文本表面不同,但核心意思完全一样。比如“发货时间?”和“啥时候发货?”、“这件衣服有货吗?”和“这款服装还有库存吗?”。
深层影响:
治理要点:采用语义相似度算法识别近重复文本,同时保留必要的表达变体,以帮助模型学习语言的丰富性。
问题体现:
深层影响:
治理要点:建立多级质量过滤系统,结合规则过滤、模型识别和人工审核,确保语料的语言质量和内容安全。
问题体现:
深层影响:
治理要点:实施标准化的预处理流程,统一文本编码、标点格式,清除非文本标记,确保输入数据的整洁性。
问题体现:特定领域语料中混杂大量无关内容。例如,在医疗专业语料中出现大量电商广告,或者在法律文书中掺杂娱乐新闻。
深层影响:
治理要点:建立精细化的领域分类系统,为不同应用场景构建领域纯净或领域平衡的语料集。
语料库治理并非简单的“剔除所有问题”,而是一门融合的艺术。完全消除重复可能损失必要的语言模式;过度过滤可能使语料失去多样性;严格的专业领域限制可能削弱模型的泛化能力。
最佳实践总结:
1.1 基础定义
text2vec 是面向中文场景的句子/文本向量化模型,基于 BERT 架构优化,核心功能是将任意长度的中文文本转换为固定维度的向量(通常 768 维),向量的余弦相似度可直接反映文本的语义相似度。与通用模型相比,它在语义相似度计算上做到了“专而精”。
1.2 核心原理
1.3 关键参数与特性
1.4 适配场景:近重复文本识别
近重复文本识别是语料治理中的高频需求。我们需要判断“发货时间?”和“啥时候发货?”这类语义相同但表达不同的文本,而 text2vec-base-chinese 恰好为此而生。
技术优势体现:
1.5 模型下载
使用ModelScope 提供的模型下载工具,通过modelscope.hub.snapshot_download引用方式将模型文件下载到本地指定目录,后续直接加载本地文件,无需每次联网,下载方式参考:
from modelscope.hub.snapshot_download import snapshot_download
# 配置本地缓存目录(建议选择空间充足的磁盘)
cache_dir = "D:\\modelscope\\hub"
# 模型名称(ModelScope上的官方名称)
model_name = "Jerry0/text2vec-base-chinese"
# 下载模型到本地
local_text2vec_path = snapshot_download(
model_name,
cache_dir=cache_dir,
revision="master" # 下载主分支版本
)
print(f"text2vec模型本地路径:{local_text2vec_path}")下载成功后输出:
Downloading Model from modelscope to directory: D:\modelscope\hub\Jerry0\text2vec-base-chinese 2025-12-31 19:26:40,544 - modelscope - WARNING - Using branch: master as version is unstable, use with caution text2vec模型本地路径:D:\modelscope\hub\Jerry0\text2vec-base-chinese
本地模型概览:

1.6 验证 text2vec 加载
from transformers import AutoTokenizer, AutoModel
# 加载本地text2vec模型
tokenizer = AutoTokenizer.from_pretrained("D:\\modelscope\\hub\\Jerry0\\text2vec-base-chinese")
model = AutoModel.from_pretrained("D:\\modelscope\\hub\\Jerry0\\text2vec-base-chinese")
# 测试文本向量化
text = "亲,这个商品啥时候发货?"
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
# 输出向量维度(验证加载成功)
print(f"文本向量维度:{outputs.last_hidden_state.shape}")输出结果:
BERT隐藏层维度:torch.Size([1, 16, 768])
2.1 基础定义
BERT(Bidirectional Encoder Representations from Transformers)是谷歌发布的预训练语言模型,bert-base-chinese是专为中文优化的基础版本,核心能力是双向语义理解,可适配文本分类、情感分析、质量评分等下游任务。
如果说 text2vec 是“专科医生”,那么 bert-base-chinese 就是“全科医生”。它在文本理解、分类、质量评估等多维度任务上展现出了均衡而强大的能力。
2.2 核心原理
2.3 关键参数与特性
2.4 适配场景:多维度质量筛查
低质量文本筛选需要从多个角度进行判断,语法正确性、内容相关性、信息完整性等,而这正是 BERT 架构的强项。
技术优势体现:
2.5 模型下载
使用ModelScope 提供的模型下载工具,通过modelscope.hub.snapshot_download引用方式将模型文件下载到本地指定目录,后续直接加载本地文件,无需每次联网,下载方式参考:
from modelscope.hub.snapshot_download import snapshot_download
cache_dir = "D:\\modelscope\\hub"
model_name = "google-bert/bert-base-chinese"
# 下载模型到本地
local_bert_path = snapshot_download(
model_name,
cache_dir=cache_dir,
revision="master"
)
print(f"BERT模型本地路径:{local_bert_path}")本地模型概览:

下载成功后输出:
Downloading Model from modelscope to directory: D:\modelscope\hub\google-bert\bert-base-chinese 2025-12-31 19:31:54,036 - modelscope - WARNING - Using branch: master as version is unstable, use with caution BERT模型本地路径:D:\modelscope\hub\google-bert\bert-base-chinese
2.6 验证 BERT 加载
from transformers import BertTokenizer, BertModel
# 加载本地BERT模型
tokenizer = BertTokenizer.from_pretrained("D:\\modelscope\\hub\\google-bert\\bert-base-chinese")
model = BertModel.from_pretrained("D:\\modelscope\\hub\\google-bert\\bert-base-chinese")
# 测试文本编码
text = "苹果手机的电池容量为3000毫安时"
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
# 输出隐藏层维度(验证加载成功)
print(f"BERT隐藏层维度:{outputs.last_hidden_state.shape}")输出结果:
文本向量维度:torch.Size([1, 14, 768])
这两个模型的选择基于它们之间的互补性和协同效应。在实际的语料治理流水线中,这两个模型可以形成高效的分工协作:
这种“轻重结合”的架构设计,既保证了处理效率,又确保了质量评估的深度。
从计算资源角度看,这个组合实现了“该省省,该花花”的智慧:

目标:快速去除最明显的噪声和格式问题
核心任务:
目标:识别并处理语义相似的文本
核心任务:
目标:从多个维度评估文本质量
核心任务:
目标:应用业务特定规则进行精细筛选
核心任务:
目标:验证治理效果,确保最终语料质量达标
核心任务:
目标:将治理后的语料进行结构化存储和管理
核心任务:

流程总结说明:
模拟包含噪声的原始语料(重复、短文本、格式混乱、错别字),覆盖 3 个核心领域。
import pandas as pd
import random
# ===================== 1. 模板定义(贴合真实语境) =====================
ecommerce_templates = [
"亲,{商品}啥时候发货?{语气} {问题}?",
"客服在吗?想咨询下{问题},{附加信息}?",
"咋地{商品}还不发货?是不是{原因}?",
"发货后能改{信息}吗?{补充说明}",
"{商品}的{属性}有问题,能{解决方案}吗?!"
]
home_templates = [
"【爆款】{家具}限时特价!{促销信息}",
"<p>{家具}用着{用户评价},{具体描述}</p>",
"{家具}也太好看了吧,{具体描述}!",
"这个{家具}质量{评价_key},{评价_value}",
"我吃的{食物}口感{口感},{推荐理由}!",
"{家具}的{材质}怎么样?用着{使用体验}吗?"
]
digital_templates = [
"{品牌}{数码产品}的{参数}是{数值},{效果}{附加功能}",
"请问{数码产品}的{参数}适合{使用场景}吗?",
"【秒杀】{数码产品}限时{折扣}!{促销时间}",
"{数码产品}的{参数}比上一代{对比信息},{效果}",
"{品牌}新款{数码产品}主打{亮点功能},价格{价格}"
]
# ===================== 2. 填充词表(全英文变量名,避免中文拼写错误) =====================
# 基础词表
products = ["手机", "衣服", "鞋子", "沙发", "床垫", "电脑", "耳机", "充电宝", "手表", "键盘"]
brands = ["苹果", "华为", "小米", "vivo", "OPPO", "三星", "荣耀"]
params = ["电池容量", "续航时间", "摄像头像素", "屏幕尺寸", "处理器型号", "内存大小"]
values = ["3000mAh", "4000mAh", "5000mAh", "20000mAh", "6.5英寸", "8GB", "12GB"]
effects = ["拍照超清晰", "能充3次手机", "用一整天不关机", "游戏流畅不卡顿", "音质很赞"]
# 电商场景词表
tones = ["急急急!", "麻烦快点~", "谢谢啦~", "求回复!", ""] # 语气
additional_infos = ["地址写错了想改", "订单号123456", "快递单号能发我吗", ""] # 附加信息
reasons = ["仓库没货", "物流延迟", "系统出问题了", "我地址填错了", ""] # 原因(修复原reason拼写错误)
supplementary_notes = ["地址是北京市朝阳区", "收货人是张三", "联系电话13800138000", ""] # 补充说明
attributes = ["颜色", "尺寸", "功能", "包装"] # 属性
solutions = ["换货", "退款", "补发", "维修"] # 解决方案
questions = ["发货时间", "改地址", "退款", "退货", "换货"] # 问题
infos = ["地址", "收货人", "联系方式", "订单号"] # 信息
# 家居场景词表
promotion_infos = ["买一送一!", "满1000减200", "前100名送赠品", ""] # 促销信息
user_evaluations = ["超舒服", "性价比超高", "颜值绝了", "一般般"] # 用户评价
detailed_descs = ["设计简约大气", "做工特别精细", "颜色很正", "尺寸很合适"] # 具体描述
evaluation_dict = { # 评价字典
"差": ["千万别买!", "建议换一家"],
"一般": ["凑合用", "可以入手试试"],
"好": ["特别推荐!", "必买!"],
"很好": ["强烈推荐!", "闭眼入!"]
}
tastes = ["非常好吃", "味道一般", "超级辣", "清淡可口"] # 口感
recommend_reasons = ["适合家庭聚餐", "孩子特别爱吃", "营养又健康"] # 推荐理由
materials = ["实木", "金属", "布艺", "皮质", "板材"] # 材质
use_experiences = ["很舒服", "有点硬", "容易清洁", "耐用"] # 使用体验
furnitures = ["沙发", "床垫", "桌子", "椅子", "衣柜", "书架"] # 家具
foods = ["饭", "面条", "火锅", "烤肉", "披萨", "寿司"] # 食物
# 数码场景词表
additional_funcs = ["支持快充", "防水防尘", "无线充电", ""] # 附加功能
use_scenes = ["出差用", "打游戏", "日常办公", ""] # 使用场景
discounts = ["5折", "7折", "8折", "9折"] # 折扣
promotion_times = ["今晚24点截止", "限时3天", "仅限今日"] # 促销时间
highlight_funcs = ["AI拍照", "超长续航", "折叠屏", "快充"] # 亮点功能
prices = ["2999元", "3999元", "性价比超高", "高端旗舰款"] # 价格
compare_infos = ["提升很多", "没区别", "略差一点", "", ""] # 对比信息
digital_products = ["手机", "电脑", "耳机", "充电宝", "平板", "智能手表"] # 数码产品
extended_params = params + ["重量", "厚度", "分辨率"] # 扩展参数
extended_values = values + ["1.5kg", "8mm", "4K"] # 扩展数值
extended_brands = brands + ["联想", "戴尔", "索尼"] # 扩展品牌
# ===================== 3. 生成语料 =====================
corpus = []
# 1. 电商客服:5000条
for _ in range(5000):
template = random.choice(ecommerce_templates)
text = template.format(
商品=random.choice(products),
问题=random.choice(questions),
信息=random.choice(infos),
语气=random.choice(tones),
附加信息=random.choice(additional_infos),
原因=random.choice(reasons), # 修复:使用正确的变量名reasons
补充说明=random.choice(supplementary_notes),
属性=random.choice(attributes),
解决方案=random.choice(solutions)
)
if len(text.strip()) >= 10: # 过滤短文本
corpus.append({"text": text, "domain": "电商客服"})
# 2. 家居生活:4000条
for _ in range(4000):
template = random.choice(home_templates)
eval_key = random.choice(list(evaluation_dict.keys()))
eval_value = random.choice(evaluation_dict[eval_key])
text = template.format(
家具=random.choice(furnitures),
食物=random.choice(foods),
促销信息=random.choice(promotion_infos),
用户评价=random.choice(user_evaluations),
具体描述=random.choice(detailed_descs),
评价_key=eval_key,
评价_value=eval_value,
口感=random.choice(tastes),
推荐理由=random.choice(recommend_reasons),
材质=random.choice(materials),
使用体验=random.choice(use_experiences)
)
if len(text.strip()) >= 10:
corpus.append({"text": text, "domain": "家居生活"})
# 3. 数码产品:4000条
for _ in range(4000):
template = random.choice(digital_templates)
text = template.format(
品牌=random.choice(extended_brands),
数码产品=random.choice(digital_products),
参数=random.choice(extended_params),
数值=random.choice(extended_values),
效果=random.choice(effects + ["", ""]),
附加功能=random.choice(additional_funcs),
使用场景=random.choice(use_scenes),
折扣=random.choice(discounts),
促销时间=random.choice(promotion_times),
亮点功能=random.choice(highlight_funcs),
价格=random.choice(prices),
对比信息=random.choice(compare_infos)
)
if len(text.strip()) >= 10:
corpus.append({"text": text, "domain": "数码产品"})
# ===================== 4. 处理重复+保存 =====================
# 转为DataFrame
df = pd.DataFrame(corpus)
# 随机添加短文本(长度小于10字符)
short_texts = [
{"text": "发货", "domain": "电商客服"},
{"text": "好", "domain": "家居生活"},
{"text": "快", "domain": "数码产品"},
{"text": "改", "domain": "电商客服"},
{"text": "买", "domain": "家居生活"},
{"text": "赞", "domain": "数码产品"}
]
df_short = pd.DataFrame(short_texts)
# 控制重复文本比例(总条数的5%左右)
duplicate_num = int(len(df) * 0.05)
df_duplicate = df.sample(n=duplicate_num, random_state=42)
df_final = pd.concat([df, df_duplicate, df_short], ignore_index=True)
# 打乱顺序
df_final = df_final.sample(frac=1, random_state=42).reset_index(drop=True)
# 保存CSV
df_final.to_csv("raw_corpus.csv", index=False, encoding="utf-8")
# 输出统计信息
total_size = df_final.memory_usage().sum() / 1024 / 1024
print(f"生成raw_corpus.csv完成!")
print(f"总语料条数:{len(df_final)}条")
print(f"文件大小:{total_size:.2f}MB")
print(f"重复文本占比:{duplicate_num / len(df_final) * 100:.2f}%")
# 输出示例语料
print("\n=== 示例语料(前5条)===")
for idx, row in df_final.head(5).iterrows():
print(f"领域:{row['domain']} | 文本:{row['text']}")生成结果:
生成raw_corpus.csv完成! 总语料条数:13599条 文件大小:0.21MB 重复文本占比:4.76% === 示例语料(前5条)=== 领域:电商客服 | 文本:客服在吗?想咨询下退货,? 领域:数码产品 | 文本:请问充电宝的屏幕尺寸适合出差用吗? 领域:电商客服 | 文本:咋地耳机还不发货?是不是系统出问题了? 领域:电商客服 | 文本:发货后能改订单号吗?地址是北京市朝阳区 领域:家居生活 | 文本:【爆款】沙发限时特价!满1000减200
短文本过滤:剔除 <10 字符的无意义文本(如 “啊啊啊沙发好看”); 完全去重:基于 MD5 哈希值识别一模一样的文本,避免冗余。
import pandas as pd
import hashlib
import re
# 1. 加载本地原始语料
df = pd.read_csv("raw_corpus.csv", encoding="utf-8")
print(f"初始语料条数:{len(df)},初始大小:{df.memory_usage().sum()/1024/1024:.2f}MB")
# 2. 完全去重(基于MD5哈希,剔除一模一样的文本)
def get_text_hash(text):
return hashlib.md5(str(text).encode("utf-8")).hexdigest()
df["text_hash"] = df["text"].apply(get_text_hash)
df = df.drop_duplicates(subset=["text_hash"])
print(f"完全去重后条数:{len(df)}")
# 3. 格式规整(去HTML标签、统一标点、去乱码)
def clean_format(text):
text = str(text).strip()
# 去HTML标签(比如<p>、<div>)
text = re.sub(r"<.*?>", "", text)
# 统一中英文标点(把.换成。,,换成,)
punc_map = {".": "。", ",": ",", "?": "?", "!": "!", ":": ":"}
for en_punc, cn_punc in punc_map.items():
text = text.replace(en_punc, cn_punc)
# 去乱码(只保留中文、数字、常用标点)
text = re.sub(r"[^\u4e00-\u9fa50-9,。?!:()、]", "", text)
return text
df["clean_text"] = df["text"].apply(clean_format)
# 4. 过滤短文本(至少10个字符,剔除“啊啊啊”这种垃圾)
df = df[df["clean_text"].apply(lambda x: len(x) >= 10)]
print(f"格式清洗+短文本过滤后条数:{len(df)}")
# 5. 保存初步清洗后的语料
df.to_csv("clean_corpus_v1.csv", index=False, encoding="utf-8")
print("初步清洗完成,保存为clean_corpus_v1.csv")输出结果:
初始语料条数:13599,初始大小:0.21MB 完全去重后条数:3012 格式清洗+短文本过滤后条数:2998 初步清洗完成,保存为clean_corpus_v1.csv

import pandas as pd
import numpy as np
from transformers import AutoTokenizer, AutoModel
import torch
from sklearn.metrics.pairwise import cosine_similarity
from tqdm import tqdm
import matplotlib.pyplot as plt
import os
# ===================== 基础配置 =====================
# 设置中文字体(避免可视化乱码)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 本地模型路径(ModelScope下载的text2vec路径)
LOCAL_MODEL_PATH = "D:\\modelscope\\hub\\Jerry0\\text2vec-base-chinese"
# 输入/输出文件路径
INPUT_CORPUS_PATH = "clean_corpus_v1.csv"
OUTPUT_CORPUS_PATH = "clean_corpus_v2.csv"
VISUALIZATION_PATH = "语义相似度分布.png"
# ===================== 核心修复:加载text2vec模型(HuggingFace原生接口) =====================
def load_text2vec_model(model_path, device="cpu"):
"""
加载本地text2vec模型(兼容ModelScope下载的文件结构)
:param model_path: 本地模型路径
:param device: 运行设备(cpu/cuda)
:return: tokenizer, model
"""
print(f"===== 加载本地text2vec模型 =====")
print(f"模型路径:{model_path}")
print(f"运行设备:{device}")
# 加载tokenizer和model(HuggingFace原生接口,避免ModelScope registry报错)
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModel.from_pretrained(model_path).to(device)
model.eval() # 预测模式
print("模型加载成功!")
return tokenizer, model
def get_sentence_embedding(texts, tokenizer, model, device="cpu", batch_size=100):
"""
批量计算文本语义向量(替代ModelScope pipeline)
:param texts: 文本列表
:param tokenizer: 分词器
:param model: text2vec模型
:param device: 运行设备
:param batch_size: 批次大小
:return: 语义向量列表
"""
embeddings = []
# 分批处理
for i in tqdm(range(0, len(texts), batch_size), desc="计算语义向量"):
batch_texts = texts[i:i+batch_size]
# 分词
inputs = tokenizer(
batch_texts,
padding=True,
truncation=True,
max_length=128,
return_tensors="pt"
).to(device)
# 计算向量(text2vec采用[CLS] token的均值作为句子向量)
with torch.no_grad():
outputs = model(**inputs)
# 取最后一层隐藏层的均值作为句子向量
last_hidden = outputs.last_hidden_state
mask = inputs['attention_mask'].unsqueeze(-1).expand(last_hidden.size())
sum_embeddings = torch.sum(last_hidden * mask, dim=1)
sum_mask = torch.clamp(mask.sum(1), min=1e-9)
batch_emb = (sum_embeddings / sum_mask).cpu().numpy()
embeddings.extend(batch_emb)
return embeddings
# ===================== 主流程:语义去重 =====================
if __name__ == "__main__":
# 1. 加载初步清洗后的语料
print("===== 加载语料 =====")
try:
df = pd.read_csv(INPUT_CORPUS_PATH, encoding="utf-8")
# 检查列名(兼容不同清洗版本的列名)
text_col = "clean_text" if "clean_text" in df.columns else "text"
texts = df[text_col].tolist()
# 过滤空值
texts = [str(t).strip() for t in texts if pd.notna(t) and len(str(t).strip()) > 0]
df = df[df[text_col].apply(lambda x: pd.notna(x) and len(str(x).strip()) > 0)].reset_index(drop=True)
print(f"待语义去重的文本条数:{len(texts)}")
except FileNotFoundError:
print(f"错误:未找到文件 {INPUT_CORPUS_PATH}")
exit()
except Exception as e:
print(f"加载语料失败:{e}")
exit()
# 2. 加载本地text2vec模型(核心修复:改用HuggingFace接口)
device = "cuda" if torch.cuda.is_available() else "cpu"
tokenizer, model = load_text2vec_model(LOCAL_MODEL_PATH, device=device)
# 3. 批量计算语义向量
embeddings = get_sentence_embedding(texts, tokenizer, model, device=device, batch_size=100)
emb_array = np.array(embeddings)
print(f"语义向量计算完成!向量维度:{emb_array.shape}")
# 4. 语义去重(余弦相似度≥0.95判定为近重复)
print("\n===== 开始语义去重 =====")
keep_indices = []
for i in tqdm(range(len(emb_array)), desc="语义去重"):
# 第一条直接保留
if i == 0:
keep_indices.append(i)
continue
# 计算当前文本与已保留文本的相似度
sim_scores = cosine_similarity([emb_array[i]], emb_array[keep_indices])[0]
# 相似度<0.95才保留(避免近重复)
if np.max(sim_scores) < 0.95:
keep_indices.append(i)
# 5. 筛选去重后的语料
df_dedup = df.iloc[keep_indices].reset_index(drop=True)
print(f"原始文本条数:{len(df)}")
print(f"语义去重后条数:{len(df_dedup)}")
print(f"过滤掉的近重复文本条数:{len(df) - len(df_dedup)}")
print(f"语义去重保留率:{len(df_dedup)/len(df)*100:.2f}%")
# 6. 保存语义去重后的语料
df_dedup.to_csv(OUTPUT_CORPUS_PATH, index=False, encoding="utf-8")
print(f"\n语义去重后语料已保存:{OUTPUT_CORPUS_PATH}")
# 7. 可视化:语义相似度分布(直观看重复程度)
print("\n===== 生成可视化图表 =====")
# 随机选1000条计算相似度(避免计算量过大)
sample_size = min(1000, len(emb_array))
sample_idx = np.random.choice(len(emb_array), sample_size, replace=False)
sample_emb = emb_array[sample_idx]
# 计算相似度矩阵
sim_matrix = cosine_similarity(sample_emb)
# 取上三角矩阵(排除自身相似度)
sim_vals = sim_matrix[np.triu_indices_from(sim_matrix, k=1)]
# 绘制直方图
plt.figure(figsize=(10, 5))
plt.hist(sim_vals, bins=50, color="#1f77b4", alpha=0.7)
plt.axvline(x=0.95, color="red", linestyle="--", linewidth=2, label="相似度阈值0.95")
plt.title("文本语义相似度分布", fontsize=14)
plt.xlabel("余弦相似度", fontsize=12)
plt.ylabel("文本对数量", fontsize=12)
plt.legend(fontsize=10)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(VISUALIZATION_PATH, dpi=300)
print(f"可视化图表已保存:{VISUALIZATION_PATH}")
# plt.show() # 如需弹窗显示,取消注释
print("\n===== 语义去重流程完成!=====")输出结果:
===== 加载语料 ===== 待语义去重的文本条数:3063 ===== 加载本地text2vec模型 ===== 模型路径:D:\modelscope\hub\Jerry0\text2vec-base-chinese 运行设备:cpu 模型加载成功! 计算语义向量: 100%|████████████████████████| 31/31 [01:36<00:00, 3.11s/it] 语义向量计算完成!向量维度:(3063, 768) ===== 开始语义去重 ===== 语义去重: 100%|███████████████████████| 3063/3063 [00:15<00:00, 197.39it/s] 原始文本条数:3063 语义去重后条数:2592 过滤掉的近重复文本条数:471 语义去重保留率:84.62% 语义去重后语料已保存:clean_corpus_v2.csv ===== 生成可视化图表 ===== 可视化图表已保存:语义相似度分布.png ===== 语义去重流程完成!=====
结果图示:

import pandas as pd
import torch
from transformers import BertTokenizer, BertForSequenceClassification
from tqdm import tqdm
import matplotlib.pyplot as plt
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 加载语义去重后的语料
df = pd.read_csv("clean_corpus_v2.csv", encoding="utf-8")
texts = df["text"].tolist()
texts = [str(t).strip() for t in texts if pd.notna(t)]
print(f"待质量评分的文本条数:{len(texts)}")
# 2. 加载本地BERT模型(适配质量评分任务)
local_bert_path = "D:\\modelscope\\hub\\google-bert\\bert-base-chinese"
tokenizer = BertTokenizer.from_pretrained(local_bert_path)
# 构建质量评分模型(回归任务,输出0~1的分数)
model = BertForSequenceClassification.from_pretrained(
local_bert_path,
num_labels=1 # 回归任务,单标签
).to("cpu")
model.eval()
# 3. 批量预测质量分
quality_scores = []
batch_size = 32
for i in tqdm(range(0, len(texts), batch_size), desc="计算质量分"):
batch_texts = texts[i:i+batch_size]
inputs = tokenizer(
batch_texts,
truncation=True,
padding="max_length",
max_length=128,
return_tensors="pt"
)
with torch.no_grad():
outputs = model(**inputs)
# 转换为0~1的分数
scores = torch.sigmoid(outputs.logits).cpu().numpy().flatten()
quality_scores.extend(scores)
# 4. 筛选高质量文本(≥0.8分)
df["quality_score"] = quality_scores
df_high_quality = df[df["quality_score"] >= 0.5].reset_index(drop=True)
print(f"质量分≥0.5的文本条数:{len(df_high_quality)}")
print(f"过滤低质量文本条数:{len(df) - len(df_high_quality)}")
# 5. 保存高质量语料
df_high_quality.to_csv("clean_corpus_v3.csv", index=False, encoding="utf-8")
# 6. 可视化质量分分布
plt.figure(figsize=(10, 5))
plt.hist(quality_scores, bins=50, color="#ff7f0e", alpha=0.7)
plt.axvline(x=0.8, color="red", linestyle="--", label="质量分阈值0.8")
plt.title("文本质量分分布", fontsize=14)
plt.xlabel("质量分(0-1)", fontsize=12)
plt.ylabel("文本数量", fontsize=12)
plt.legend()
plt.grid(alpha=0.3)
plt.savefig("文本质量分分布.png", dpi=300)
plt.show()
print(f"质量评分完成!高质量语料保存为clean_corpus_v3.csv")输出结果:
待质量评分的文本条数:2592 Some weights of BertForSequenceClassification were not initialized from the model checkpoint at D:\modelscope\hub\google-bert\bert-base-chinese and are newly initialized: ['classifier.bias', 'classifier.weight'] You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference. 计算质量分: 100%|██████████████████████████| 81/81 [04:41<00:00, 3.47s/it] 质量分≥0.5的文本条数:83 过滤低质量文本条数:2509 质量评分完成!高质量语料保存为clean_corpus_v3.csv
结果图示:

import pandas as pd
import re
# 1. 加载高质量语料
df = pd.read_csv("clean_corpus_v3.csv", encoding="utf-8")
# 2. 规则细筛
def clean_text(text):
# 修正错别字
typo_map = {"mah": "mAh", "啥时候": "什么时候", "咋地": "怎么样"}
for typo, correct in typo_map.items():
text = text.replace(typo, correct)
# 修正病句(如“我吃了饭非常好吃”→“我吃的饭非常好吃”)
text = re.sub(r"吃了(.*?)非常好吃", r"吃的\1非常好吃", text)
# 去除HTML标签
text = re.sub(r"<.*?>", "", text)
# 统一标点(英文→中文)
punc_map = {".": "。", ",": ",", "?": "?", "!": "!"}
for en_punc, cn_punc in punc_map.items():
text = text.replace(en_punc, cn_punc)
# 去除偏见词汇
bias_map = {"垃圾": "劣质", "没用": "效果不佳", "脑残": "不合适"}
for bias, neutral in bias_map.items():
text = text.replace(bias, neutral)
return text.strip()
df["final_text"] = df["text"].apply(clean_text)
# 3. 质量评估
# 3.1 重复率(完全+近重复)
total = len(df)
duplicate_rate = (1 - len(df.drop_duplicates(subset=["final_text"]))/total) * 100
# 3.2 语法正确率(根据数据量动态抽样)
sample_size = min(100, total) # 如果数据不足100条,则取全部数据
sample_texts = df["final_text"].sample(sample_size, random_state=42).tolist()
# 模拟人工审核(实际需人工标注)
correct_grammar = int(sample_size * 0.96) # 假设96%语法正确
grammar_correct_rate = (correct_grammar/sample_size) * 100
# 3.3 噪声率(1 - 最终语料/原始语料)
raw_total = len(pd.read_csv("raw_corpus.csv"))
noise_rate = (1 - len(df)/raw_total) * 100
# 4. 保存最终语料
df_final = df[["final_text", "domain", "quality_score"]]
df_final.to_csv("final_high_quality_corpus.csv", index=False, encoding="utf-8")
# 5. 输出评估结果
print("===== 质量评估结果 =====")
print(f"重复率:{duplicate_rate:.2f}%")
print(f"语法正确率:{grammar_correct_rate:.2f}%")
print(f"噪声率:{noise_rate:.2f}%")
print(f"\n最终语料保存为:final_high_quality_corpus.csv")
print(f"最终语料条数:{len(df_final)}条")输出结果:
===== 质量评估结果 ===== 重复率:0.00% 语法正确率:95.18% 噪声率:99.39% 最终语料保存为:final_high_quality_corpus.csv 最终语料条数:83条
小语料库治理的核心在于以质取胜,相较于海量但混杂的脏数据,1GB精炼的高质量语料往往能带来更优异的模型训练效果,因为它让模型专注于学习正确、纯净的语义信息,而非噪声。
技术选型需精准适配任务特性。在治理流程中,text2vec凭借其高效的语义向量化能力,擅长深度识别并去重语义相似的文本;而BERT基于其强大的语义理解能力,则更适用于对文本的语法、逻辑和内容质量进行精准评分与筛选。两者结合,构建了从识别重复到评估质量的完整技术闭环。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。