ARTICLE DETAIL

资讯详情

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

动手学深度学习(d2l-zh)文本预处理实战:从原始文本到词元索引的完整流水线

动手学深度学习(d2l-zh)文本预处理实战:从原始文本到词元索引的完整流水线 人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载文本是序列数据中最常见、也最具代表性的形式一篇文章既可以看作一串单词序列也可以看作一串字符序列。本章节以《动手学深度学习》中 H. G. Wells 的经典小说The Time Machine为语料系统讲解文本预处理的标准四步流水线加载文本、词元化tokenization、构建词表vocabulary、把文本转换为数字索引序列。读完本文你将完整掌握d2l工具库中read_time_machine、tokenize、Vocab、count_corpus与load_corpus_time_machine五个核心 API 的用法与底层实现并能为后续的语言模型训练如 语言模型与数据集 一节中的 RNN 训练准备好可直接喂给模型的数据。为什么文本需要预处理四个标准步骤在进入具体代码之前先建立整体认知。模型只能接收数值输入而原始文本是字符串二者之间存在天然的鸿沟。因此无论语料规模多大文本预处理通常都遵循以下四个步骤加载文本把原始文本作为字符串读入内存词元化把字符串拆分为词元token词元是文本的基本单位可以是单词也可以是字符构建词表建立一个词表vocabulary把拆分出的字符串词元映射到从 0 开始的数字索引转换为索引序列把整篇文本转换为数字索引序列方便模型直接操作。这四个步骤环环相扣构成了后续所有自然语言处理实验的数据基础。原文以The Time Machine为例——这是一个只有 3 万多个单词的小型语料库用来演示完整的预处理流程刚刚好而现实中的文档集合动辄包含数十亿个单词更需要这套流水线来保证数据可被高效、一致地处理。读取数据集read_time_machine与数据下载机制数据从哪里来DATA_HUB注册表在动手前需要先理解数据是如何获取的。d2l工具库维护了一个全局数据注册表DATA_HUB每个数据集以名称 - (URL, SHA-1 哈希)的形式登记。时光机器数据集的定义如下以 d2l/torch.py 为例四个后端实现完全一致d2l.DATA_HUB[time_machine] (d2l.DATA_URL timemachine.txt, 090b5e7e70c295757f55df93cb0a180b9691891a)其中d2l.DATA_URL指向数据集存储地址见 d2l/torch.py第二个参数是该文件的 SHA-1 校验值。d2l.download函数d2l/torch.py实现了完整的下载与缓存逻辑从DATA_HUB中取出 URL 与 SHA-1 哈希在本地../data目录cache_dir下创建文件若本地文件已存在则逐块计算其 SHA-1 值并与注册表比对校验一致则直接命中缓存、不重复下载校验失败或文件不存在时才发起下载。这套注册表 哈希校验的机制保证了数据下载一次后即可复用且能防止文件损坏带来的静默错误。读取与清洗read_time_machineread_time_machine函数d2l/torch.py负责把下载好的文本文件读入内存并做初步清洗def read_time_machine(): 将时间机器数据集加载到文本行的列表中 with open(d2l.download(time_machine), r) as f: lines f.readlines() return [re.sub([^A-Za-z], , line).strip().lower() for line in lines]这个函数做了三件事按行读取f.readlines()把全文拆成文本行列表每一行是一个字符串正则清洗re.sub([^A-Za-z], , line)把除英文字母大小写之外的所有字符数字、标点、空白等统一替换为空格从而忽略标点符号统一小写.strip().lower()去掉行首行尾空白并把字母转为小写从而忽略字母大小写。读取后即可验证效果原文档中lines[0]与lines[10]的输出展示了清洗后的干净文本行。re模块是 Python 标准库的正则表达式工具[^A-Za-z]是匹配任意非英文字母的连续字符的字符类取反写法——这是本函数清洗逻辑的核心。词元化tokenize把文本拆成基本单位词元化是文本预处理中承上启下的一步。tokenize函数d2l/torch.py接收文本行列表把每个文本序列拆分成词元列表最终返回词元列表的列表def tokenize(lines, tokenword): 将文本行拆分为单词或字符词元 if token word: return [line.split() for line in lines] elif token char: return [list(line) for line in lines] else: print(错误未知词元类型 token)函数通过token参数支持两种粒度的词元化参数值拆分方式说明word默认line.split()按空白把每行拆成单词列表词元是单词charlist(line)把每行拆成单个字符列表词元是字符word模式下由于read_time_machine已把标点替换为空格split()可以干净地切分出纯单词序列char模式则直接利用 Python 字符串的可迭代性逐字符切分。若传入未知的词元类型函数会打印错误提示——从源码实现看该分支没有显式return会返回None因此调用时应始终使用受支持的两种取值。原文档在词元化后打印了前 11 行文本的单词词元结果可以直观看到清洗后的文本被切分成了整齐的单词序列。构建词表Vocab与count_corpus词表的作用词元本质是字符串而模型需要数字输入因此必须建立字符串词元到数字索引的映射。Vocab类d2l/torch.py就是这样一个双向字典既支持词元 → 索引token_to_idx也支持索引 → 词元idx_to_token。词表的构建遵循两条重要约定按频率分配索引对语料中所有唯一词元做频率统计词频越高的词元索引越小稀有词元移除很少出现的词元通常被删除以降低复杂度语料中不存在或已被删除的词元统一映射到索引为 0 的特殊未知词元unk。此外Vocab还支持通过reserved_tokens参数预留一组特殊词元例如pad填充词元用于对齐变长序列bos序列开始词元beginning of sequenceeos序列结束词元end of sequence。构造器与频率排序Vocab.__init__的完整签名是Vocab(tokensNone, min_freq0, reserved_tokensNone)三个参数的含义参数默认值作用tokensNone语料词元列表用于统计频率可为 1D 列表或 2D 列表词元列表的列表min_freq0词频下限低于该值的词元将被排除在词表之外reserved_tokensNone需要预留的特殊词元列表如pad、bos、eos其索引排在unk之后构造逻辑对照 d2l/torch.py可以拆解为调用count_corpus(tokens)统计词频得到collections.Counter按词频降序排序keylambda x: x[1], reverseTrue词频相同再按词元本身排序保证确定性初始化idx_to_token [unk] reserved_tokens即unk的索引固定为 0预留词元紧随其后依频率从高到低遍历把词频不低于min_freq且未重复的词元追加进词表同时反向填充token_to_idx映射。这里有一个值得注意的实现细节仓库四个后端版本的Vocab在遍历频率表时都采用了遇到词频低于min_freq的词元立即break的策略见 d2l/torch.py。由于频率表已按降序排列一旦遇到不达标的词元其后的词频必然更低无需继续扫描——这是一个细微但有效的剪枝优化。双向映射与查询接口Vocab提供了四个核心接口__len__返回词表大小即len(self.idx_to_token)__getitem__词元 → 索引。传入单个词元时返回其索引若词元不在词表中则返回self.unk即 0传入列表或元组时递归地对每个词元映射返回索引列表。这正是vocab[tokens[i]]能把整行词元批量转成索引的底层原理to_tokens(indices)索引 → 词元是__getitem__的逆操作同样支持单个索引与索引列表两种输入unk属性在 torch 等后端的实现中以property形式固定返回 0见 d2l/torch.py保证未知词元索引恒为 0。原文档用时光机器语料构建词表后打印了token_to_idx的前 10 项展示出unk与最高频词元如the、i、and等的索引分配情况随后演示了vocab[tokens[i]]把第 0 行与第 10 行的单词词元批量转换为索引序列。频率统计count_corpuscount_corpusd2l/torch.py是词表构建的统计基础它要处理两种输入形态def count_corpus(tokens): 统计词元的频率 # 这里的tokens是1D列表或2D列表 if len(tokens) 0 or isinstance(tokens[0], list): # 将词元列表展平成一个列表 tokens [token for line in tokens for token in line] return collections.Counter(tokens)若输入是词元列表的列表2D即每行一个词元列表先用列表推导式展平成一维词元列表若输入是一维词元列表则直接使用最后交给collections.Counter统计每个词元的出现次数。这个设计让Vocab既能直接接收tokenize产出的 2D 结果也能接收预先展平的一维列表增强了复用性。整合所有功能load_corpus_time_machine前三个步骤的函数已经齐备最后用load_corpus_time_machined2l/torch.py把它们打包成一条开箱即用的流水线def load_corpus_time_machine(max_tokens-1): 返回时光机器数据集的词元索引列表和词表 lines read_time_machine() tokens tokenize(lines, char) vocab Vocab(tokens) # 因为时光机器数据集中的每个文本行不一定是一个句子或一个段落 # 所以将所有文本行展平到一个列表中 corpus [vocab[token] for line in tokens for token in line] if max_tokens 0: corpus corpus[:max_tokens] return corpus, vocab该函数返回两个对象corpus词元索引列表即整篇文本转换成的数字序列vocab时光机器语料库对应的词表对象。与原文档中基于单词的演示相比这里做了两处关键改动改用字符级词元化tokenize(lines, char)。原文明确说明这是为了简化后续章节中的训练——字符级词表规模远小于单词级词表训练成本更低这也是 RNN 从零开始 等后续章节采用字符级建模的原因之一返回展平的单列表时光机器数据集中每个文本行不一定是完整的句子或段落可能是断行、单词甚至空行因此用列表推导式[vocab[token] for line in tokens for token in line]把所有行的索引展平成一个连续的索引序列而不是词元列表的列表。这样既保留了行间的上下文连续性也便于后续按固定步长切分子序列。max_tokens参数提供序列长度上限默认-1表示不截断传入正整数时只保留前max_tokens个索引用于控制训练数据规模。调用load_corpus_time_machine()后原文档验证了len(corpus)整个语料的索引总数与len(vocab)词表大小字符级词表通常只有几十个字符索引加unk。在仓库中的落地四后端统一实现与下游消费同一套 API四个深度学习框架后端值得注意的是这套文本预处理工具并非只存在于某个单框架版本中。在仓库的 d2l 包中read_time_machine、tokenize、Vocab、count_corpus、load_corpus_time_machine五个 API 被同时实现于四个后端d2l/mxnet.py对应from d2l import mxnet as d2ld2l/torch.py对应from d2l import torch as d2ld2l/tensorflow.py对应from d2l import tensorflow as d2ld2l/paddle.py对应from d2l import paddle as d2l从源码结构看四个后端的预处理逻辑正则清洗、词元化分支、频率排序与break剪枝、展平处理保持一致仅顶层import collections/import re与数据下载的d2l.DATA_HUB注册方式相同。这意味着文本预处理环节与具体深度学习框架解耦无论读者选用 PyTorch、MXNet、TensorFlow 还是 PaddlePaddle都能以完全相同的调用方式获得相同的结果差异只体现在后续模型构建部分。这一设计正是能运行、可讨论的 d2l-zh 项目风格的体现。下游如何消费max_tokens10000的默认截断这套预处理流水线的价值在下一个章节得到直接验证。语言模型与数据集 一节中SeqDataLoader在初始化时直接调用d2l.load_corpus_time_machine(max_tokens)获取索引序列与词表见 language-models-and-dataset.md随后基于corpus按随机抽样或顺序分区生成小批量子序列用于 RNN 语言模型训练而公开的load_data_time_machine(batch_size, num_steps, use_random_iterFalse, max_tokens10000)接口language-models-and-dataset.md把max_tokens的默认值设为10000——即默认只用时光机器语料的前 1 万个字符索引来训练语言模型在保证示例可复现的同时显著缩短训练时间。这正体现了本文四个预处理步骤的最终目的把原始文本转换成模型可以直接切分、批量读取的数字序列。小结文本是序列数据最常见的形式之一预处理的目标是把字符串形态的文本转换为数值形态、便于模型操作的索引序列标准预处理流水线包含四步以字符串形式加载文本 → 拆分为词元单词或字符→ 构建词表将词元映射为从 0 开始的数字索引 → 将文本转换为词元索引序列词表构建以词频为核心unk固定占索引 0稀有词元通过min_freq剔除pad、bos、eos等特殊词元可经reserved_tokens预留d2l工具库在 MXNet、PyTorch、TensorFlow、PaddlePaddle 四个后端中提供了完全一致的预处理 API可直接被后续语言模型章节复用。延伸思考原文档练习词元化是关键的预处理步骤且因语言而异英文可基于空格与标点切分而中文没有天然的词边界。可以尝试调研基于子词如 BPE、WordPiece、基于形态学规则、以及基于统计的分词如 jieba 等中文分词工具背后的原理等不同思路理解它们各自适用的语言与场景。用tokenize(lines)单词级构建词表并改变Vocab的min_freq参数增大min_freq会剔除更多低频词词表大小len(vocab)将随之减小被剔除的词元在查询时会全部落入unk索引 0。不妨分别取min_freq0、1、2、5观察词表规模的变化曲线体会频率阈值这一超参数对词表规模与未知词占比的权衡。赞分享人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载相关推荐动手学深度学习d2l-zh实战softmax回归从零开始实现动手学深度学习d2l zh实战softmax回归从零开始实现 本文基于《动手学深度学习》中文版开源仓库d2l zh的 softmax 回归从零实现章节人工智能深度学习机器学习教程动手学深度学习d2l-zh多层感知机从零开始实现——从参数初始化到完整训练实战动手学深度学习d2l zh多层感知机从零开始实现——从参数初始化到完整训练实战 导读 本文以《动手学深度学习》d2l zh仓库中的 mlp scrat人工智能深度学习机器学习教程动手学深度学习d2l-zh转置卷积完全指南原理、矩阵本质与上采样实战动手学深度学习d2l zh转置卷积完全指南原理、矩阵本质与上采样实战 转置卷积transposed convolution是深度学习框架中最常被误解的人工智能深度学习机器学习教程上一篇PostHog elements_chain 格式完全解析读懂自动采集事件的 DOM 链、CSS 选择器与正则匹配下一篇Quiet-STaR版本控制transformers兼容性维护策略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表