趋近智
在过滤掉原始文本中明显的质量问题后,为大型语言模型准备数据的下一步通常是文本标准化。标准化的目的是通过减少那些不会大幅改变意义,但可能导致数据稀疏或模型不一致的变体来规范文本。可以将其看作是消除表面差异,以更统一地呈现基础内容。这个过程通常在分词 (tokenization)之前进行,因为标准化选择直接影响分词器 (tokenizer)如何拆分文本并构建其词汇表 (vocabulary)。
应用一致的标准化对于降低模型需要处理的复杂性很重要。没有标准化,像“U.S.A.”、“USA”和“usa”这样的变体可能会被视为不同实体,从而分裂模型的理解。然而,标准化是一种平衡之举;过度激进的标准化会消除有意义的区别。
让我们看看LLM数据处理流程中经常使用的一些标准文本标准化方法。
将文本转换为单一大小写(通常是小写)是最简单和最常见的标准化步骤之一。它确保像“Apple”(公司名)和“apple”(水果)这样的词最初被同等对待,从而减少有效词汇量大小,并避免模型需要为句子开头或标题中使用的大写变体学习单独的表示。
import torch # 虽然此处未使用,但在机器学习情境中引入是标准做法
text = "The Quick Brown Fox Jumps Over The Lazy Dog."
lower_text = text.lower()
print(f"原始: {text}")
print(f"小写: {lower_text}")
# 输出:
# 原始: The Quick Brown Fox Jumps Over The Lazy Dog.
# 小写: the quick brown fox jumps over the lazy dog.
虽然大小写折叠通常有益,但它也有缺点。它会消除潜在有用的区别,例如区分公司名“Apple”和水果“apple”,或者区分缩写词“IT”(信息技术)和单词“it”。对于在海量、多样化数据集上训练的通用LLM,词汇量减少的好处通常超过了大小写信息丢失的坏处,因为模型无论如何都可能从语境中推断出这些信息。然而,对于特定用途或任务,保留大小写可能是必要的。
从各种来源抓取的文本数据通常包含以多种方式在 Unicode 中表示的字符。例如,像“é”这样的带重音字符可以表示为单个预组字符(U+00E9),也可以表示为基础字符“e”后跟一个组合尖音符(U+0065 U+0301)。尽管视觉上相同,但如果未经标准化,这些不同的字节序列可能会被后续处理视为不同实体。
Python 中的 unicodedata 模块提供了标准化的形式:
对于 LLM 训练,NFC 是一个安全基线,它确保了规范表示,而不会大幅改变内容。NFKC 有时用于更强的标准化,通过合并视觉变体可能进一步简化词汇,但需要谨慎,因为它在少数情况下会改变语义。
import unicodedata
# 示例: 预组字符与分解字符 'é'
char_nfc = 'é' # U+00E9
char_nfd = 'e\u0301' # U+0065 U+0301
print(f"NFC 字符串: '{char_nfc}', 长度: {len(char_nfc)}")
print(f"NFD 字符串: '{char_nfd}', 长度: {len(char_nfd)}")
print(f"它们是否相等? {char_nfc == char_nfd}") # False
# 将两者都规范化为 NFC
normalized_nfc_1 = unicodedata.normalize('NFC', char_nfc)
normalized_nfc_2 = unicodedata.normalize('NFC', char_nfd)
print(
f"规范化 NFC 1: '{normalized_nfc_1}', 长度: {len(normalized_nfc_1)}"
)
print(
f"规范化 NFC 2: '{normalized_nfc_2}', 长度: {len(normalized_nfc_2)}"
)
print(
f"规范化形式是否相等? {normalized_nfc_1 == normalized_nfc_2}" # True
)
# NFKC 合并连字的示例
ligature = 'fi' # U+FB01
normalized_nfkc = unicodedata.normalize('NFKC', ligature)
print(f"原始连字: '{ligature}'")
print(f"NFKC 规范化后: '{normalized_nfkc}'") # Output: 'fi'
选择正确的 Unicode 规范化形式取决于数据的特性和所需的标准化程度。NFC 通常建议作为起始点。
有时,会移除重音或其他变音符号,以进一步简化文本,将像 'é'、'ê'、'è' 这样的字符都映射到 'e'。这比大小写折叠或标准 Unicode 规范化更激进。
import unicodedata
def remove_accents(input_str):
# 规范化为 NFD (将字符分解为基础字符 + 组合标记)
nfkd_form = unicodedata.normalize('NFD', input_str)
# 过滤掉组合标记 (类别 'Mn')
return "".join([
c for c in nfkd_form
if not unicodedata.category(c) == 'Mn'
])
text_with_accents = "El niño juega al fútbol en el café."
text_without_accents = remove_accents(text_with_accents)
print(f"原始: {text_with_accents}")
print(f"重音符号已移除: {text_without_accents}")
# 输出:
# 原始: El niño juega al fútbol en el café.
# 重音符号已移除: El nino juega al futbol en el cafe.
尽管这简化了文本,但对于重音在语音学上有重要意义(区分含义)的语言,这可能会有问题,例如法语('pêche' - 桃子 vs. 'péché' - 罪)或西班牙语('año' - 年 vs. 'ano' - 肛门)。对于包含此类语言的多语言模型或数据集,通常不建议移除重音,因为它会丢弃重要的语言信息。它的使用仅在目标应用明确需要对重音不敏感的匹配,或者源数据在重音方面质量极差时才合理。
原始文本中常见不一致的空白符(多余的空格、制表符、换行符)。空白符标准化包括:
\r\n 或 \r 转换为 \n)。import re
text_with_bad_whitespace = " This text \t has extra \n\n whitespace. "
# 清除首尾空白符
stripped_text = text_with_bad_whitespace.strip()
# 将多个空白符替换为单个空格
normalized_whitespace_text = re.sub(r'\s+', ' ', stripped_text)
print(f"原始: '{text_with_bad_whitespace}'")
print(f"规范化后: '{normalized_whitespace_text}'")
# 输出:
# 原始: ' This text has extra
#
# whitespace. '
# 规范化后: 'This text has extra whitespace.'
这一步确保间距一致,有助于分词 (tokenization),并避免模型基于任意空白符变体学习到虚假模式。
深思熟虑地应用这些文本标准化方法有助于创建更干净、更一致的数据集,这反过来有助于更稳定的训练和可能更好的大型语言模型性能。核心在于一致性以及对所涉权衡做出明智选择。
这部分内容有帮助吗?
unicodedata 模块的官方文档,提供了访问Unicode字符数据库和文本规范化功能的接口。© 2026 ApX Machine LearningAI伦理与透明度•