资讯详情

资讯详情

AI-For-Beginners 课程详解:如何将文本表示为张量 —— 词袋、N-gram 与 TF/IDF 实战指南

教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载本文是 AI-For-Beginners 课程「自然语言处理NLP」模块 第 13 课 的技术详解。课程以文本分类为核心任务以 AG News 新闻数据集为例将新闻归入 World / Sports / Business / Sci-Tech 四个类别完整讲解从「文本 → 字符/词 → 数字 → 张量」的表示链路覆盖字符级与词级表示、N-gram、Bag-of-WordsBoW与 TF-IDF四种经典方法并给出 PyTorch 与 TensorFlow 两套可运行代码。读完本文你将掌握如何把任意 NLP 任务中的原始文本转换为神经网络可消费的张量输入并能独立构建一个 BoW/TF-IDF 文本分类器。1 背景为什么 NLP 任务需要「文本表示」计算机无法直接理解自然语言。虽然计算机内部以 ASCII / UTF-8 等编码把字符映射为数字这些数字对应屏幕上的字形但正如课程 README 指出的人类知道每个字母代表什么、字符如何组成词语而计算机自身不具备这种理解神经网络的意义完全要在训练过程中学习。因此我们需要的不是字形编号而是一种语义友好的张量表示。以字符编码为例H对应 ASCII 码 100、二进制 01100100但这种表示对神经网络毫无语义信息图片来源课程 13-TextRep/README.md与图像、表格数据不同文本的显著特征是变长序列。本节 NLP 模块的 5-NLP/README.md 专门指出图像输入尺寸预先已知而文本长度不固定同时文本中的模式远比图像复杂——例如否定词与被否定对象之间可以隔着任意多的词I do not like oranges与I do not like those big colorful tasty oranges语义相同但词距不同这促使后续课程引入 RNN、Transformer 等新架构。而在那之前第一步永远是把文本变成张量。课程聚焦的起点任务是文本分类给定一段新闻文本标题 正文判断它属于哪个类别。本节使用 AG News 数据集典型样本如下类别Sci/Tech标题Ky. Company Wins Grant to Study Peptides (AP)正文AP - A company founded by a chemistry researcher at the University of Louisville won a grant to develop...2 两种基础表示字符级与词级无论采用何种表示流程都是先将文本切分为token序列一个 token 可以是字符、词、甚至是词的一部分再把 token 映射为数字通常借助词汇表 vocabulary最后以 one-hot 编码送入神经网络。课程给出了两种基础粒度2.1 字符级表示Character-level把每个字符当作一个数字处理。假设语料中共有C个不同字符那么单词Hello会被表示为5×C 的张量每个字母对应 one-hot 编码下张量的一列该字符位置为 1其余为 0。2.2 词级表示Word-level对全部文本构建一个词汇表vocabulary然后用 one-hot 编码表示每个词。课程认为这种方案某种程度上更好单个字母本身几乎没有含义而采用更高层的语义概念——词——能为神经网络简化任务。代价是字典规模大需要处理高维稀疏张量。这两种表示正是后续 BoW / TF-IDF / 嵌入方法的基础one-hot 是一次一词的表示而 Bag-of-Words 就是对 one-hot 向量做聚合。3 N-gram让表示捕获局部上下文自然语言中词的精确含义只能在上下文中确定。课程给出的经典例子neural network神经网络与fishing network渔网含义完全不同。一种把上下文纳入表示的方法是基于词对构建模型——把相邻词对当作独立的词汇表 token。句子I like to go fishing将表示为 token 序列I like, like to, to go, go fishing这种方法的明显缺陷有两个字典规模急剧膨胀若扩展为三元组tri-gram乃至 n 元组即所谓n-gram规模增长更快语义隔离go fishing与go shopping虽然共享同一个动词go却被编码为完全不同的 token二者没有任何语义相似性。此外课程指出n-gram 也适用于字符级表示——此时 n-gram 大致对应不同的音节组合syllabi这对形态丰富的语言如芬兰语、土耳其语尤其有意义。4 Bag-of-Words词袋固定长度向量表示做文本分类时我们最终需要一个固定长度的向量作为稠密分类器的输入。最简单的方法是把文本中所有词的表示聚合起来——例如直接相加。将每个词的 one-hot 向量相加就得到频率向量每一维记录对应词在文本中出现的次数。这就是Bag-of-WordsBoW。图注BoW 本质上是文本中所有词 one-hot 向量的累加。图片来自课程 13-TextRep/README.md。BoW 的价值在于哪些词出现、出现多少次本身就是内容的强指示器。政治新闻很可能包含president、country科学论文则会出现collider、discovered等词——因此词频在多数场景下是内容的好信号。但 BoW 有一个致命问题and、is这类高频停用词几乎出现在所有文本中拥有最高的频率把真正重要的词掩盖了。解决思路是考虑该词在整个文档集合中的出现频率来降低其权重——这正是下一节 TF-IDF 的核心思想。下图展示了词袋向量在内存中的实际形态词汇表中每个词对应唯一位置向量元素为该词在文档中的出现次数4.1 用 sklearn 快速构造 BoW两个配套 notebookTextRepresentationPyTorch.ipynb 与 TextRepresentationTF.ipynb都演示了用 Scikit-Learn 构造 BoWfrom sklearn.feature_extraction.text import CountVectorizer vectorizer CountVectorizer() corpus [ I like hot dogs., The dog ran fast., Its hot outside., ] vectorizer.fit_transform(corpus) vectorizer.transform([My dog likes hot dogs on a hot day.]).toarray() # 输出形如array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtypeint64)5 TF-IDF用逆文档频率给词重新加权TF-IDFterm frequency–inverse document frequency词频–逆文档频率是 BoW 的变体不再用 0/1 或原始计数而是用与语料中词出现频率相关的浮点权重。形式上词i在文档j中的权重定义为$$ w_{ij} tf_{ij}\times\log({N\over df_i}) $$其中$tf_{ij}$词i在文档j中的出现次数即前文 BoW 的值$N$文档集合中的文档总数$df_i$整个集合中包含词i的文档数。直观理解$w_{ij}$ 随词在单篇文档中出现次数增加而增大同时被包含该词的文档数抵消——如果一个词出现在每一篇文档中$df_iN$则 $w_{ij}0$该词被完全忽略。这样a、in这类无处不在的词权重趋近于零而专业术语获得高权重。sklearn 实现同样一行即可from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer(ngram_range(1, 2)) vectorizer.fit_transform(corpus) vectorizer.transform([My dog likes hot dogs on a hot day.]).toarray()课程 notebook 特别指出TF-IDF 虽为不同词提供了频率权重但仍无法表示词的语义与顺序。真正捕获语义要等后续课程引入词嵌入embeddings与语言模型。6 实战一用 PyTorch 从零构建 BoW 文本分类器TextRepresentationPyTorch.ipynb 给出了完整的端到端流程。任务设定基于AG_NEWS数据集把新闻标题分类到 4 个类别之一。6.1 加载数据集AG_NEWS 内置于torchtext模块可直接加载import torch import torchtext import os import collections os.makedirs(./data, exist_okTrue) train_dataset, test_dataset torchtext.datasets.AG_NEWS(root./data) classes [World, Sports, Business, Sci/Tech]数据集是迭代器返回(类别编号, 文本)二元组若需多次使用应先转为 listtrain_dataset, test_dataset torchtext.datasets.AG_NEWS(root./data) train_dataset list(train_dataset) test_dataset list(test_dataset)6.2 Tokenization 与词汇表构建词级表示需要两步分词tokenizer构建词汇表vocabulary。使用 torchtext 内置的basic_english分词器与Counter统计词频tokenizer torchtext.data.utils.get_tokenizer(basic_english) tokenizer(He said: hello) # [he, said, hello] counter collections.Counter() for (label, line) in train_dataset: counter.update(tokenizer(line)) vocab torchtext.vocab.vocab(counter, min_freq1)利用词汇表把 token 编码为数字。课程代码中 AG News 全量词汇表约95810词vocab_size len(vocab) print(fVocab size if {vocab_size}) stoi vocab.get_stoi() # dict: token - index def encode(x): return [stoi[s] for s in tokenizer(x)] encode(I love to play with my words) # 例如 [599, 3279, 97, 1220, 329, 225, 7368]6.3 实现 BoW 向量在编码基础上累加计数即可def to_bow(text, bow_vocab_sizevocab_size): res torch.zeros(bow_vocab_size, dtypetorch.float32) for i in encode(text): if i bow_vocab_size: res[i] 1 return res调参提示notebook 原文强调vocab_size默认取全量词汇表但可以人为调小以只保留最高频词。你会发现精度略有下降但并不剧烈换来的是更高性能——这是控制高维稀疏表示规模的第一种手段。6.4 训练单层 BoW 分类器通过collate_fn参数把原始样本批处理为 BoW 张量再定义一个单线性层 LogSoftmax 的分类网络from torch.utils.data import DataLoader import numpy as np def bowify(b): return ( torch.LongTensor([t[0]-1 for t in b]), # 标签AG News 编号从 1 开始减 1 对齐到 0..3 torch.stack([to_bow(t[1]) for t in b]), # BoW 特征 ) train_loader DataLoader(train_dataset, batch_size16, collate_fnbowify, shuffleTrue) test_loader DataLoader(test_dataset, batch_size16, collate_fnbowify, shuffleTrue) net torch.nn.Sequential(torch.nn.Linear(vocab_size, 4), torch.nn.LogSoftmax(dim1))课程提供了标准训练循环NLLLoss Adam可通过epoch_size限制训练量、report_freq控制进度打印。在 notebook 中仅训练约 15000 个样本后训练准确率已快速逼近86%输出示例12800: acc0.85765625。对 4 分类任务这已是不错的基线。7 实战二用 TensorFlow/Keras 实现 BoW、自动计数与 TF-IDFTextRepresentationTF.ipynb 以 TensorFlow Datasets 的ag_news_subset为数据源训练集 120000 条、测试集 7600 条全程围绕 KerasTextVectorization层展开。7.1 环境准备与数据加载import tensorflow as tf from tensorflow import keras import tensorflow_datasets as tfds # 按需增长 GPU 显存多模型训练时避免显存溢出 physical_devices tf.config.list_physical_devices(GPU) if len(physical_devices) 0: tf.config.experimental.set_memory_growth(physical_devices[0], True) dataset tfds.load(ag_news_subset) ds_train dataset[train] ds_test dataset[test]关于 GPU 显存的说明来自本节 5-NLP/README.md训练大模型时若遇到显存不足优先缩小 minibatch 大小旧版 TensorFlow 在同一内核中连续训练多个模型可能不释放显存上述set_memory_growth配置可缓解。7.2 用 TextVectorization 分词并限定词汇表课程强调AG News 全量词汇超过10 万而稀有的词模型几乎学不到东西因此限制词汇表规模很有必要。TextVectorization通过max_tokens参数一步完成分词 建表vocab_size 50000 vectorizer keras.layers.experimental.preprocessing.TextVectorization(max_tokensvocab_size) vectorizer.adapt(ds_train.take(500).map(lambda x: x[title] x[description]))注意notebook 原文这里只用 500 条子集建表以加快执行代价是有部分语料词未进词汇表、训练时被忽略。用全量数据adapt可小幅提升最终精度。查看词汇表与编码结果vocab vectorizer.get_vocabulary() print(vocab[:10]) # [, [UNK], the, to, a, in, of, and, on, for] print(fLength of vocabulary: {len(vocab)}) vectorizer(I love to play with my words) # tf.Tensor shape(7,), dtypeint64, ...注意前两个位置是保留位padding/空位与[UNK]未知词——这是 Keras 向量化层与 torchtext 词汇表的一个实现差异。7.3 两种 BoW 构造方式方式一手动累加 one-hot。把每个词编号转成 one-hot 再沿序列维求和def to_bow(text): return tf.reduce_sum(tf.one_hot(vectorizer(text), vocab_size), axis0) to_bow(My dog likes hot dogs on a hot day.).numpy()理解差异notebook 原文提示这里得到的向量长度等于基于整个 AG News 数据集的词汇表规模而前面 sklearn 示例的词汇表是当场从样例文本构建的所以两者向量维度与结果不同。方式二output_modecount自动计数。最新版 TensorFlow 允许向量化层直接输出计数向量模型定义与训练显著简化model keras.models.Sequential([ keras.layers.experimental.preprocessing.TextVectorization(max_tokensvocab_size, output_modecount), keras.layers.Dense(4, input_shape(vocab_size,), activationsoftmax) ]) print(Training vectorizer) model.layers[0].adapt(ds_train.take(500).map(extract_text)) model.compile(losssparse_categorical_crossentropy, optimizeradam, metrics[acc]) model.fit(ds_train.map(tupelize).batch(batch_size), validation_datads_test.map(tupelize).batch(batch_size))7.4 把向量化层并入网络端到端训练因为TextVectorization本身也是 Keras 层可以把它放进网络内部让原始字符串直接进网络def extract_text(x): return x[title] x[description] def tupelize(x): return (extract_text(x), x[label]) inp keras.Input(shape(1,), dtypetf.string) x vectorizer(inp) x tf.reduce_sum(tf.one_hot(x, vocab_size), axis1) out keras.layers.Dense(4, activationsoftmax)(x) model keras.models.Model(inp, out) model.compile(losssparse_categorical_crossentropy, optimizeradam, metrics[acc]) model.fit(ds_train.map(tupelize).batch(batch_size), validation_datads_test.map(tupelize).batch(batch_size))模型摘要显示各层参数量text_vectorization0 参数、tf.one_hot0 参数、tf.math.reduce_sum0 参数唯一可训练的是输出层dense_221344 参数 5335 × 4 4。端到端训练后验证准确率约87.4%与手动map向量化的版本约 87.0%相当但管线更简洁。7.5 Keras 下自动计算 TF-IDF把output_mode换成tf-idf即可让向量化层自动完成 TF-IDF 加权model keras.models.Sequential([ keras.layers.experimental.preprocessing.TextVectorization(max_tokensvocab_size, output_modetf-idf), keras.layers.Dense(4, input_shape(vocab_size,), activationsoftmax) ]) print(Training vectorizer) model.layers[0].adapt(ds_train.take(500).map(extract_text)) model.compile(losssparse_categorical_crossentropy, optimizeradam, metrics[acc]) model.fit(ds_train.map(tupelize).batch(batch_size), validation_datads_test.map(tupelize).batch(batch_size))课程 notebook 的运行结果显示手动 BoW 验证准确率约 86.97%output_modecount约 87.72%而TF-IDF 达到约 88.49%——可见去掉停用词干扰后分类效果进一步改善。8 Bigram 与 N-gram 实践为何需要嵌入降维前文提到hot dog的含义与单独的hot、dog完全不同若二者始终用同一向量会误导模型。解决方案是在词汇表中加入词对bigram。sklearn 中一行切换bigram_vectorizer CountVectorizer(ngram_range(1, 2), token_patternr\b\w\b, min_df1) bigram_vectorizer.fit_transform(corpus) print(Vocabulary:\n, bigram_vectorizer.vocabulary_) bigram_vectorizer.transform([My dog likes hot dogs on a hot day.]).toarray()输出词汇表会同时包含单词hot、dog与词对hot dogs、dog ran等向量的某些维度来自词对计数。但代价极其可观课程在 AG News 上用torchtext.data.utils.ngrams_iterator构建 bigram 词汇表时长度高达1,308,842超过 130 万TF 版本同样指出 bigram 词汇超过 130 万 token。如此高维的稀疏向量直接训练分类器是内存低效的。两个 notebook 都给出两条出路调高min_freq只保留出现足够多次的 n-gram显著降维。PyTorch 版本注释提示把min_freq设为更高值观察词汇表长度变化TF 版本则建议给 bigram 也设定合理的max_tokens上限。引入嵌入embeddings这是下一课14-Embeddings的主题——用低维稠密向量代替高维稀疏 one-hot从根本上解决维度爆炸。9 课后实践用自己的数据集重跑 notebook课程为 13-TextRep 配套了独立 assignment.md使用本节两个 notebookPyTorch 或 TensorFlow 任一版本换成你自己的数据集重新运行。建议数据集可从 Kaggle 获取并注明出处attribution尝试有创意的数据集notebook 原文举例了 NUFORC 的UFO 目击报告数据集——这类非常规语料往往能带来意想不到的发现改写 notebook突出你自己的发现例如词频分布、类别不平衡、n-gram 阈值对精度的敏感度。依赖安装参考本节 5-NLP/README.mdPyTorch 路线执行pip install -r requirements-pytorch.txtTensorFlow 路线执行pip install -r requirements-tf.txt对应文件位于 5-NLP/requirements-pytorch.txt 与 5-NLP/requirements-tf.txt。10 小结与下一步表示方法维度是否考虑上下文主要缺点一句话适用场景字符级 one-hot字符数 × 序列长否无语义、维度高需处理拼写/形态变体的底层任务词级 one-hot词汇表大小否高维稀疏理论铺垫实际很少直接用N-gram词汇表含组合部分局部维度爆炸、词对语义隔离配合降维或少量局部上下文BoW词汇表大小否停用词掩盖关键词简单文本分类基线TF-IDF词汇表大小否仍无语义与顺序文本检索、分类的经典特征正如课程结尾引用的语言学家 J. R. Firth1935所言一个词的完整意义总是由语境决定的脱离语境研究意义是不可取的。BoW、N-gram 与 TF-IDF 都只能为词附加频率权重却无法表达含义与顺序。因此下一步的学习路径是清晰的14-Embeddings用稠密向量表示词义解决稀疏高维问题15-LanguageModeling 与 16-RNN从文本中捕获上下文与顺序信息直至 18-Transformers 等现代架构——它们正是语境即意义这一思想在工程上的完整落地。本节全部资源课程讲义 13-TextRep/README.md、PyTorch notebook TextRepresentationPyTorch.ipynb、TensorFlow notebook TextRepresentationTF.ipynb以及配套作业 assignment.md。赞分享教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载相关推荐AI-For-Beginners NLP 入门如何将文本表示为张量——字符级/词级表示、N-Gram、词袋与 TF/IDF 实战指南AI For Beginners NLP 入门如何将文本表示为张量——字符级/词级表示、N Gram、词袋与 TF/IDF 实战指南 本指南对应 AI For教程人工智能机器学习深度学习AI-For-Beginners 第 13 课如何将文本表示为张量——字符级、词级、N-Gram 与 BoW/TF-IDF 实战指南AI For Beginners 第 13 课如何将文本表示为张量——字符级、词级、N Gram 与 BoW/TF IDF 实战指南 本文基于 AI For教程人工智能机器学习深度学习AI-For-Beginners 课程详解将文本表示为张量——从字符编码、N-Gram 到 BoW 与 TF-IDF 的完整实战AI For Beginners 课程详解将文本表示为张量——从字符编码、N Gram 到 BoW 与 TF IDF 的完整实战 本文是微软 AI For B教程人工智能机器学习深度学习上一篇如何在Windows上实现高效屏幕标注ppInk免费开源标注工具终极指南下一篇HMCL启动器5分钟掌握Minecraft跨平台游戏管理终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →