在过滤掉原始文本中明显的质量问题后,为大型语言模型准备数据的下一步通常是文本标准化。标准化的目的是通过减少那些不会大幅改变意义,但可能导致数据稀疏或模型不一致的变体来规范文本。可以将其看作是消除表面差异,以更统一地呈现基础内容。这个过程通常在分词之前进行,因为标准化选择直接影响分词器如何拆分文本并构建其词汇表。应用一致的标准化对于降低模型需要处理的复杂性很重要。没有标准化,像“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 规范化从各种来源抓取的文本数据通常包含以多种方式在 Unicode 中表示的字符。例如,像“é”这样的带重音字符可以表示为单个预组字符(U+00E9),也可以表示为基础字符“e”后跟一个组合尖音符(U+0065 U+0301)。尽管视觉上相同,但如果未经标准化,这些不同的字节序列可能会被后续处理视为不同实体。Python 中的 unicodedata 模块提供了标准化的形式:NFC (规范化形式组合): 尽可能组合字符。'e' + '´' 变为 'é'。这通常是网页内容的默认选择。NFD (规范化形式分解): 将字符分解为基础字符和组合标记。'é' 变为 'e' + '´'。NFKC (规范化形式兼容性组合): 进行兼容性分解,将兼容性字符替换为其规范等效项,然后进行规范组合 (NFC)。这种方式更激进,可能改变视觉外观或含义(例如,连字如 'fi' 变为 'f' + 'i',分数 '½' 变为 '1/2')。NFKD (规范化形式兼容性分解): 进行兼容性分解,然后进行规范分解 (NFD)。对于 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.'这一步确保间距一致,有助于分词,并避免模型基于任意空白符变体学习到虚假模式。在LLM环境中的考虑事项对分词的影响: 标准化直接影响分词器的输入。小写化减少所需词汇量。Unicode 规范化避免视觉上相同的字符获得不同的词元。过度激进的标准化(例如删除所有标点符号或重音)可能使子词分词器更难找到有意义的单元,或者错误地合并单词。信息丢失: 总要权衡标准化的好处与有用信息潜在的丢失。大小写、标点符号和变音符号可以承载语义权重。对于大型通用模型,通常最好执行最小限度的标准化(例如 NFC、基本空白符清理、可能小写化),并让模型通过接触大量数据和语境来学习处理变体。语言依赖性: 标准化规则可能因语言而异。例如,大小写或重音的重要性在不同语言中差异很大。多语言数据集需要仔细考量,可能需要对每种语言应用不同的规则,或选择最小化、与语言无关的标准化。流程集成: 这些标准化步骤通常在初始清理(如HTML标签移除)和质量过滤之后,但在分词之前执行。它们通常作为大型数据处理流程的一部分来实现,处理数TB文本时可能会使用分布式计算框架来实现可扩展性。深思熟虑地应用这些文本标准化方法有助于创建更干净、更一致的数据集,这反过来有助于更稳定的训练和可能更好的大型语言模型性能。核心在于一致性以及对所涉权衡做出明智选择。