ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

多语言AI数据收集与清洗实战:从语料到模型的必修课

多语言AI数据收集与清洗实战:从语料到模型的必修课 做多语言AI应用最容易被低估的就是数据环节。我这两年陆续做完几个跨国业务的项目——跨境客服意图识别、多语种评论审核、出海产品的舆情监控每次排计划的时候模型训练只占一小块真正吃时间的是多语言数据的收集与清洗。甚至可以说谁先把多语言的脏数据收拾干净谁就赢在了起跑线上。这篇文章不聊大而全的架构也不堆术语就把我在实际项目里用过的数据收集思路、清洗脚本、踩过的坑一五一十拿出来聊聊。适合刚开始做多语言NLP项目、正在被乱码、混杂语言、重复文本折磨的工程师参考也适合准备入行AI数据方向的朋友当一份实操地图。全文以Python生态为主线数据集、代码、参数全部给到你可以直接拿去改。1. 先想清楚多语言AI应用到底需要什么样的数据很多人一上来就急着写爬虫、找数据集结果忙活一周数据堆了一大堆清洗的时候才发现语种比重完全失控、格式乱七八糟、还有一堆版权和隐私隐患。所以第一步不是动手是想清楚你的AI应用在什么场景下跑数据要求自然就出来了。1.1 从业务场景反推数据要求同样是“多语言AI应用”任务类型不同数据要求差得很远。文本分类/意图识别需要覆盖目标语种的标注数据每个意图类别在每种语言里都要有足够样本而且最好来自真实的业务场景。跨境客服项目里光“退款”这一个意图中文、英语、西班牙语、阿拉伯语的表达方式就完全不一样你不可能靠翻译中文样本凑数。机器翻译/多语种问答需要平行语料也就是同一句话的两种语言版本。这类数据清洗的难点在对齐质量句子级别对不对得上直接影响模型效果。情感分析/舆情监控需要带情感标签的多语种文本而且语体分布很杂有短评论、有长文章、有社交媒体碎片化表达。清洗时要小心处理网络用语、emoji、表情符号。检索/语义匹配需要大量单语语料就够了但领域覆盖度和语种均衡特别重要否则模型容易“偏科”。我的习惯是接到项目先写一张数据需求清单语种列表、任务类型、每条数据的来源渠道、预估数量级、标注要求、合规约束。这张清单花半小时后面能省好几天。1.2 语种覆盖、数据规模与质量三角多语言场景里存在一个铁三角语种覆盖、数据规模、数据质量三者很难同时拉满。低资源语言比如泰语、越南语、印地语、阿拉伯语本身就是数据洼地公开数据集少爬虫采集也不容易。你花同样的精力中文数据能攒100万条阿拉伯语可能连1万条都凑不齐。这时候就要衡量是把现有语种做深做透还是把语种铺开但每个都浅尝辄止我的建议是如果是做分类这种相对简单的任务优先保证每个语种至少5000条高质量数据如果是做生成式任务数据量要求会高一个量级那就得考虑合成数据、回译这些手段。质量方面也有一个常见误区以为清洗就是“去掉脏东西”其实清洗的最终目标是“让数据可被模型稳定地学会规律”。如果100万条数据里有50万条是网页模板生成的重复段模型学到的就是模板特征不是语言特征正确率再高也是假的。2. 数据收集实战语料从哪来怎么拿得又快又合规数据收集是体力活但体力活也要讲策略。选错数据源后面清洗阶段会成倍地还债。我个人把数据源分成四类每一类的特点非常鲜明。2.1 数据源选型公开数据集、爬虫、众包怎么权衡公开数据集、网页爬虫、众包标注、合成数据这是个四选一甚至四组合的题目。数据源类型典型例子语种覆盖质量成本主要风险公开数据集OPUS、WMT、Common Crawl多语子集中到高参差不齐低领域偏差、版权边界网页采集维基百科、多语种新闻站、电商评论高取决于站点较高但噪声多中反爬、robots、隐私众包标注人工翻译/标注平台按需定制高高成本、标注一致性合成数据AIGC生成文本、回译只要你愿意覆盖多少都行需要严格抽检中语义漂移、模式固化公开数据集我一般是“开局先拉一遍”用来建立基线。例如OPUS这个平行语料库里面有很多语对language pair的公开数据质量参差但作为冷启动完全够用。真正做业务模型的时候还是要靠爬虫采集目标站点的真实文本因为公开数据集的领域往往和你的业务不完全匹配。爬虫采集需要注意三件事第一严格遵守目标网站的robots.txt我是把合规看成不能踩的红线宁可少采一些也不碰这块第二采集频率要做限速别给对方服务器制造压力第三必须做去标识化用户评论、对话数据里经常带着邮箱、手机号、地址采集下来立刻处理不要等清洗阶段。2.2 采集环节就要做的三件事抽样探查、去标识化、原始留档这三个习惯帮我在多个项目里少踩了无数坑。抽样探查我每次搭建新采集任务会先限制只抓几百条样本人工扫一遍内容质量。有一次做泰语评论采集第一眼没细看等攒了20万条才发现大量内容是系统自动回复前后都一样等于20万条噪声。先抽样探查这种问题当天就能发现。去标识化并不是等到清洗阶段才做。采集入库那一刻就要做一次通行扫描把明显的手机号、邮箱、身份证号替换成占位符。我在一个电商评论项目里就吃过亏数据拿去做模型训练结果发现模型学会了对邮箱格式特别敏感根源就是没在早期处理PII。现在我的采集脚本里默认加一段正则匹配把常见PII先遮掉后面非但无损失反而让模型更关注文本语义。原始留档这个太重要了。清洗脚本写错了数据被覆盖了如果没有原始备份只能重新采集一遍那种痛苦谁经历谁知道。我的做法是采集下来的原始数据存一份只读存档洗好的数据另存一份两份分开管理绝不在原文件上原地修改。2.3 多语言数据扩充回译与合成数据的取与舍低资源语种扩充我试过最省事的方法还是回译back-translation。思路很简单拿一批高质量中文数据翻译成中文再翻译回中文但操作上有讲究。举个例子你有一批客服对话记录中文10000条泰语只有800条。你可以把中文的10000条翻译成泰语得到10000条泰语样本但这不是真正的泰语数据而是“翻译腔”数据。更好的做法是先翻译成泰语再翻译回中文保留下翻译回的那些中文变体作为增强数据用来修正模型的过拟合倾向。而泰语那10000条“伪数据”可以作为预训练阶段的辅助语料但正式评估还是要用真实采集的语料。回译最大的坑是语义漂移。我在一个情感分析项目里回译扩充数据后模型在测试集上分数看着正常但上线后对口语化表达的判断经常出错后来查下来回译文把一些情绪强度词改变了比如“非常生气”变成了“有点不高兴”。所以回译数据必须抽检我一般要求超过2%的样本人工审核确认语义方向没有被反转。合成数据也是同理能解决数量问题但不能解决真实分布问题。我把它定位成“安全气囊”在真实数据断层、模型实在学不动某个语种时才用永远不替代真数据。3. 数据清洗核心实操编码、去重、去噪三步走数据收集完就到了本文最核心的部分——清洗。清洗不是一条命令跑完就结束而是要分层次、分步骤地把数据从“原始状态”变成“可用状态”。我把它拆成编码统一、去噪过滤、去重三步每一步都有非常具体的坑。3.1 编码统一与Unicode规范化乱码问题的根源多语言数据最大的隐形杀手是编码。不同来源的数据可能是UTF-8、UTF-16、GB18030、Shift-JIS、Big5、ISO-8859-1如果处理不当在你屏幕上看到的会是““”这种鬼画符其实这不是数据坏了而是解码用错了编码表。我的做法是所有入库文本强制统一成UTF-8。采集到的是bytes就先检测编码Python里可以用 charset-normalizer 这个库比老牌的chardet更稳import charset_normalizer def detect_encoding(raw: bytes): result charset_normalizer.from_bytes(raw).best() return result.encoding if result and result.encoding else utf-8 # 使用时 raw open(file.bin, rb).read() enc detect_encoding(raw) text raw.decode(enc, errorsreplace) # 失败时用替换符不直接抛异常细节是 errors 参数一定要设成 replace 而不是默认的 strict否则一条坏字符就让整批任务挂掉。编码统一之后接着做Unicode规范化。这里要区分NFC、NFD、NFKC、NFKD四种形式。我直接在多语言数据集上默认用NFKC因为NFKC能处理兼容字符比如把全角字母转成半角、把连字fi拆成fi对中文、日文、韩文文本都很友好。import unicodedata def unify_unicode(text: str) - str: # 先清除不可见控制字符 invisible \u200b\u200c\u200d\u200e\u200f\u202a\u202b\u202c\u202d\u202e text .join(ch for ch in text if ch not in invisible) # 再做NFKC规范化 return unicodedata.normalize(NFKC, text)注意NFKC规范化之后某些字符的码点会变如果你有基于字符位置的标注比如NER的实体位置规范化会破坏对齐关系这时候要在规范化之前完成标注或者同步更新标注位置。这个细节我在做多语种实体识别时专门吃过亏位置偏移导致一批训练样本报废。3.2 语言识别过滤与噪声文本清除多语言数据里经常混着一些“伪多语言”噪声。举个例子表面上是英语新闻但正文里夹着大量中文链接、日文评论、阿拉伯语用户名这类数据不能直接进模型否则语种标签全乱套。处理方案是先做语言识别过滤。最常用的是fastText的lid.176.bin模型支持176种语言精度不错。我的用法如下import fasttext # 模型文件需要单独下载约900MB model fasttext.load_model(lid.176.bin) def detect_lang(text: str): label, score model.predict(text.replace(\n, ), k1) return label[0].replace(__label__, ), score[0]语言识别有一个绕不开的短板短文本准确率很低。“OK”“Hi”“谢谢”这种只有两三个词的文本模型经常误判西班牙语和葡萄牙语、瑞典语和丹麦语互相认错是家常便饭。我的经验是短文本不要依赖模型自己维护一个高频常见词表先按词表命中判断语种命中不了再用模型兜底。噪声文本清除也是同样的流程化处理。我总结了一个清理清单去掉HTML标签、markdown标记去掉URL、邮箱、电话号码、金额数字去掉多余空白、换行去掉连续重复字符比如“哈哈哈哈哈哈”超过一定长度就折叠去掉全是大写或全无标点的极端文本这类文本对大多数模型都不友好去掉过短文本和过长文本我一般卡在20到2000个字符之间这些规则听着简单执行顺序却有讲究先清格式再做规范化最后过滤语言。顺序反了比如先做了语言识别再清HTML就会被一堆标签干扰判断。3.3 精确去重与近似去重方案重复数据是训练集的隐形毒药。模型吃太多重复样本会严重过拟合到重复出现的模板上。去重分为精确去重和近似去重多语言场景要两层都做。精确去重用pandas就够import pandas as pd df pd.read_csv(multilingual_data.csv) df df.drop_duplicates(subset[text], keepfirst)但真正麻烦的是近似重复。同一个新闻事件会被多家媒体转载文字改几个词语义一样同一段产品说明在不同商品页里反复出现只改了商品名。这时候要用MinHash加LSH做近似去重。shingle大小要按语种调整中文我一般用2到3个字符英文用9到10个字符from datasketch import MinHash, MinHashLSH def build_minhash(text: str, ngram: int 5, num_perm: int 128): m MinHash(num_permnum_perm) text text.lower() shingles [text[i:ingram] for i in range(len(text) - ngram 1)] for shingle in shingles: m.update(shingle.encode(utf-8)) return m # 对所有文本分桶查重相似度阈值一般设置在0.85左右这里有个多语言场景特有的坑不要跨语言去重。中文“今天天气不错”和日文“今日は天気がいい”意思几乎一样但它们是两种语言如果你用统一的指纹去重会把不该删的平行语料删掉。我的做法是先按语言字段分组只在同语言内做近似去重跨语言的语义重复交给模型算法去学不是清洗阶段该管的事。4. 多语言清洗的特殊战场分词边界、脚本混写与文本格式如果说编码和去重是“普适性清洗”那分词和脚本混写就是多语言数据独有的“甄嬛传”。中文、日文、泰文、阿拉伯文、希伯来文各自的清洗姿势完全不一样想用一套规则通吃必然翻车。4.1 不同语种的分词策略怎么选分词这件事对英文是锦上添花对中文、日文、泰文却是生死攸关。因为英文单词天然有空格分隔而中文的“武汉市长江大桥”到底怎么断句机器和人都会犹豫。我在中文数据清洗里用jieba做粗切同时保留一个允许自定义词典的入口把业务术语加进去。比如做跨境电商“免运费”“仅退款”这类词必须切成一个整体否则后面做意图识别时特征会散掉。日文清洗则是另一套体系。日文没有空格而且混着平假名、片假名、汉字三种文字系统我一般用fugashi加MeCab引擎和UniDic词典from fugashi import Tagger tagger Tagger(-Owakati) result tagger.parse(自然言語処理の勉強をしています) # 自然 言語 処理 の 勉強 を し て い ます泰文更特殊它的单词之间没有空格甚至没有明确的词边界我一般用pythainlp的word_tokenizefrom pythainlp.tokenize import word_tokenize tokens word_tokenize(ฉันรักภาษาไทย, enginenewmm)一个容易被忽略的点是分词应该放在清洗流程的什么位置我的答案是放在编码和规范化之后但放在语言识别和相关性过滤之后。换句话说先用规则把明显的噪声清掉再分词避免分词器被HTML标签、乱码字符干扰。4.2 RTL方向、脚本混写和特殊控制字符阿拉伯语、希伯来语、波斯语是从右向左RTL书写的但数字和拉丁字母还是从左向右排列。这就造成了一个棘手问题文本在存储层、展示层、处理层的“视觉顺序”和“逻辑顺序”不一致。我在一个阿拉伯语舆情项目里遇到过清洗后跑一遍NER实体全乱套后来发现是阿拉伯语文本里混进了Unicode方向控制字符U200EU200FU202A到U202E这些字符在渲染时负责调整文字方向但模型拿去做特征时会当成噪声。我的处理方案是清洗时剥离所有方向控制字符让模型看到的是纯逻辑顺序文本展示层需要时再重新注入方向标记。这套处理逻辑对阿拉伯语、希伯来语都适用。脚本混写是多语言数据的另一个日常。一个印度尼西亚用户评论可能长这样“Harga nya sangat murah! 但是Kualitas kurang bagus”,印尼语里混着英文、中文让人崩溃。清洗时先按主语言跑识别如果主语言是印尼语就把混入的极短中文词组保留原样不要试图翻译因为翻译会改变语义但如果混入的是整段其他语言的长文本就要切出来单独处理或者丢弃。def filter_mixed_lang(text: str, primary_lang: str, model) - str: lang, score detect_lang(text, model) if lang primary_lang: return text # 如果整段被识别成其他语言丢弃否则保留 return if len(text) 50 else text4.3 繁体简体统一、全角半角归一与本地化格式繁体简体不统一是多语言中文数据的老大难。用户数据里“發財”和“发财”并存“裏面”和“里面”混用如果任务不需要区分繁体简体我建议统一转成简体。工具用OpenCC效果好词级别转换比逐字硬转准得多from opencc import OpenCC converter OpenCC(t2s) # 繁体转简体 converted converter.convert(這是一段繁體字文本)但是要注意如果业务本身面向港澳台地区繁体就是目标语言统一成简体反而是错。这个决策要在清洗之前就定死不要中途换。全角半角归一我之前在3.1提到NFKC已经覆盖了一部分但实践中最好单独处理一遍全角字母数字。日文数据里经常出现“”这个全角拉丁串转成半角之后才方便后续处理。处理全角半角可以复用前面那段NFC的代码也可以用这样一段定向转换def normalize_width(text: str) - str: result [] for ch in text: code ord(ch) if code 0x3000: # 全角空格 ch elif 0xFF01 code 0xFF5E: ch chr(code - 0xFEE0) result.append(ch) return .join(result)本地化格式这块很多做多语言的工程师会忘。日期格式、数字千分位、货币符号各语言写法极不统一。美国习惯“March 3, 2024”德国写“3. März 2024”日本写“2024年3月3日”。如果你清洗的目标是做序列标注或情感分析这些格式差异会导致特征稀疏。我的经验是非语义相关任务统一把日期转成ISO格式数字统一转成阿拉伯数字半角。但如果是做机器翻译或生成式任务数据就保留原始格式不需要归一。5. 清洗流水线的工程化工具选型、编排与质量监控很多人清洗数据用脚本一把梭跑完就完了。但多语言项目的数据会持续增长、持续更新清洗流程今天写完明天还要改所以必须做工程化设计。第五部分我聊聊工具链的选择、流水线的编排以及怎么避免清洗脚本和数据集变成一团乱麻。5.1 用Python搭建可复用的清洗骨架我的清洗管线核心就三件套pandas、re、unicodedata按需加fastText、datasketch、opencc和各语种分词库。有人会问为什么不用Spark我的回答是先分清数据量级。百万条级别的多语言文本单机Python脚本加多进程完全够跑而且调试方便如果到了千万级、亿级再考虑换Spark或者分布式框架也不迟搬之前先想清楚单机版能不能用。我一般把清洗流程封装成几个函数每一个都只做一件事最后用pipeline串起来def clean_multilingual_text(text: str, lang: str auto) - str: # 1. 宽度归一 text normalize_width(text) # 2. Unicode规范化 去控制字符 text unify_unicode(text) # 3. 去HTML标签、URL text re.sub(r[^], , text) text re.sub(rhttps?://\S|www\.\S, , text) # 4. 压缩连续重复字符 text re.sub(r(.)\1{3,}, r\1, text) # 5. 去多余空白 text .join(text.split()) return text.strip()每个函数都要能独立跑、独立测。这个习惯能救你命有一天全角转换出了问题你只需要单独跑这个函数调试而不用把整条流水线重跑一遍。5.2 幂等性与断点续跑清洗脚本必须是幂等的也就是“同一份数据跑一遍和跑两遍结果完全一样”。这个要求听着基础实际做到却需要刻意设计。最常见的不幂等操作是重复做正则替换、重复做NFKC规范化、重复去重。有些操作本身就是幂等的比如反复调用 unicodedata.normalize(NFKC, text) 第二次不会有变化但有些不是。比如你用“先清HTML再清空格”跑两次会有细微区别如果不清空重来数据就被二次加工了。我的方案是每条数据都带上原始文本的哈希值处理完再哈希一次两个哈希对比就能知道这条数据有没有被重复清洗发现重复处理就跳过。断点续跑是另一个工程关键。多语言数据量常在百万级别清洗时间以小时计中途断电、内存不足、写了一半崩溃都是常态。我用最朴素的方式解决每处理完一个批次写一个checkpoint文件记录当前进度重启脚本时读取checkpoint继续跑而不是从零开始。这个逻辑实现起来不到50行省下的时间却是以天计算的。5.3 质量监控与数据版本管理清洗完不等于万事大吉质量监控同样要做。我每次跑完流水线都会出一份数据报告清洗前多少行、清洗后多少行、每个语种保留多少条、平均长度多少、重复率降到多少。这份报告不需要复杂的BI工具pandas里用groupby加describe就能出来输出成一个CSV就够用。数据版本管理方面我给每批数据都加时间戳目录。比如raw_2024_06_01是原始数据clean_2024_06_01是清洗后数据中间还有sampled_2024_06_01用于抽检。不要用“final_final_v3”这种命名方式也绝不在原始数据上原地修改。万一清洗规则出了问题你可以随时回溯到上一个版本重新清洗。有时候清洗规则本身要调整比如你发现某个语种的过滤阈值设高了去掉太多有效样本那就需要重跑。这时候数据版本管理就显得尤其重要没有版本管理的项目改一次规则就心惊胆战一次。6. 常见问题与排查技巧实录这章我把这几年实操里踩过的坑集中写出来做了一张速查表和几个真实案例。你可以直接把这张表打印出来贴在工位上遇到问题先查一遍。6.1 高频问题速查表现象常见原因处理建议文本出现““”或“é”UTF-8被按Latin-1或Windows-1252解码用charset-normalizer重测编码按正确编码重新decode同一新闻有几十个近似重复版本网站模板、转载、聚合页检查页眉页脚对文档正文部分单独抽取再走MinHash去重短文本语言识别频繁出错“OK”“Hi”等词太短模型拿不到足够特征维护高频常见词表短文本先按词表判断模型做兜底日文/韩文在去重时被误删跨语言文本语义重复度高指纹却一样按语言分桶去重不跨语言做近似去重阿拉伯语文本在存储里顺序错乱RTL方向控制字符与LTR标点混杂清洗时剥离方向控制字符存储逻辑顺序文本中文数据清洗后长度变短很多模板噪声和标点符号被清掉或者NFKC合并了字符抽样查看清洗后的文本确认不是误删有效内容标点符号、数字格式不统一各语言使用不同本地化格式根据任务决定是否归一化别一刀切6.2 我在多个项目里踩过的坑第一个坑编码转换路径写反。之前做日文数据采集原始文件是Shift-JIS我用chardet检测出来然后写了个函数转成UTF-8。结果第二天发现数据全变成乱码一查原来是检测结果返回的编码名是“shift_jis”但我代码里写的是“SHIFT-JIS”Python识别不了回退到默认编码重新解码整个文本报废。这个问题的教训是编码名一定要用Python的codec标准名检测库返回什么就用什么不要自己脑补。第二个坑把“去重”做成了“删除平行语料”。项目需要中英平行语料我先用MinHash做了近似去重结果把一堆“中英文句子语义相同但在两个语言各有一条”的平行语料当成重复删了。后来加了语言字段分组再跑去重才保住这批数据。这个坑特别隐蔽因为单看语料内容确实很像但它们恰恰是平行语料的核心价值所在。第三个坑回译数据没有抽检直接进训练集。训练产出的模型在上线后对口语化文本的表现很差回查发现回译产生了大量语义反转比如“我不确定这个能不能用”被翻译成了“我不确定这个不能用”逻辑完全变了。后来我在回译流水线里强制加入一项人工抽检抽检率不低于2%并且标记出模型置信度低的样本优先让人工看。第四个坑模板噪声没有去干净。采集新闻网站时网页页脚那串联系方式和版权声明几乎每篇都有清洗脚本没处理被模型当成了固定特征。后来我在抽取阶段把正文和页脚分离单独保留正文区块噪声量直接降了一个数量级。这个教训就是清洗要从采集源头就开始做不要全指望后端脚本。第五个坑繁体简体统一导致任务目标被改变。有个台湾地区的项目老板说“都是中文”非要把繁体全转简体结果上线后用户反馈很多词看不懂尤其是“程式”“硬碟”这种台湾习惯用语转成简体后还要再映射回来反而多此一举。这件事之后我在做语言规范化之前都会先和业务确认一个原则目标语言变体是什么转换是否会影响业务理解不要为了“统一”而统一。文章结尾做了这么多个多语言AI项目之后我个人的体会是数据收集与清洗的工程量不会因为你换了更好的模型而减少它只会随着业务扩张越来越大。与其把它当成一个一次性的苦力活不如从第一天就当成一条需要持续维护的流水线来做。编码检测、语言识别、近似去重、格式规范化这些模块第一次搭好之后就是长期资产以后来一个新语种、接一个新项目改改配置就能复用。如果你正在做类似项目没有别的办法就是一步步来先想清楚需求再规划数据源然后写可复用的清洗管线最后加上质量监控和版本管理。把这些基础打牢模型训练反而是最轻松的那一环。
返回列表