- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊 | 接辅导、项目定制
目录
- 总结:
- one-hot编码和TF-IDF的区别与联系
- 区别
- 联系
- 具体示例
- 1. Embedding详解
- 2. EmbeddingBag详解
- 3. 任务描述
- 4. 任务代码详细解释
- a. 数据预处理
- b. 构建词汇表和索引序列
- c. 自定义 Dataset 和 DataLoader
- d. 定义词嵌入模型
- e. 测试模型
- f. 常用模块介绍
- 1. 自定义 Dataset
- 2. DataLoader
- g. 总结
总结:
之前有学习过文本预处理的环节,对文本处理的主要方式有以下三种:
1:词袋模型(one-hot编码)
2:TF-IDF
3:Word2Vec(词向量)
详细介绍及中英文分词详见pytorch文本分类(一):文本预处理
上期主要介绍one-hot编码示例流程(词汇表 -> 文本序列 -> One-hot编码)手撕模式 + 直接调用现成的词袋模型(CountVectorizer)
本期将简单介绍一下TF-IDF 与 one-hot编码的区别与联系,并主要介绍词嵌入(Word Embedding)
one-hot编码和TF-IDF的区别与联系
词袋模型(Bag of Words)中的one-hot编码和TF-IDF(Term Frequency-Inverse Document Frequency)都是用于文本数据表示的方法,但它们在具体实现和用途上有所不同。以下是两者的区别与联系:
区别
-
定义和计算方式:
-
One-hot编码:
- 定义:对每个词汇构建一个向量,每个向量的长度等于词汇表的大小,在词汇表中该词汇对应的位置标记为1,其余位置标记为0。
- 计算:如果词汇表有n个词,某个词w在词汇表中的位置是i,则w的one-hot编码为一个长度为n的向量,第i位为1,其余为0。
-
TF-IDF:
- 定义:一种加权方式,通过考虑词频(Term Frequency, TF)和逆文档频率(Inverse Document Frequency, IDF)来衡量词的重要性。
- 计算:
- TF:某词在文档中的出现次数除以该文档中的总词数。
- IDF:某词在所有文档中出现的文档数的对数值的倒数,通常表示为 (\text{IDF}(t) = \log(\frac{N}{1 + n_t})),其中N是文档总数,(n_t)是包含词t的文档数。
- TF-IDF:( \text{TF-IDF}(t, d) = \text{TF}(t, d) \times \text{IDF}(t) )。
-
-
结果表示:
- One-hot编码:结果是一个高维且稀疏的向量,向量中只有一个位置为1,其余全为0。
- TF-IDF:结果是一个权重向量,向量中包含实数值,每个值表示该词在特定文档中的重要性。
-
应用场景:
- One-hot编码:常用于简单的词汇表示,适合用于需要简单且直接表示词汇的场景,如初步的数据探索和简单模型训练。
- TF-IDF:更适合用于需要衡量词汇在文档中重要性的场景,如信息检索和文本分类,尤其是当文档集较大时。
联系
-
词袋模型基础:
- 两者都是基于词袋模型的文本表示方法,忽略了词汇顺序,只关注词汇的出现与频率。
-
向量化文本:
- 两者都将文本转换为向量形式,以便于后续的机器学习和数据处理。
-
高维稀疏性:
- 两者生成的向量通常都是高维且稀疏的,尤其是在词汇表较大时。
具体示例
假设有两个文档:
- 文档1: “the cat sat on the mat”
- 文档2: “the dog sat on the log”
One-hot编码:
- 词汇表:[“the”, “cat”, “sat”, “on”, “mat”, “dog”, “log”]
- 文档1的表示:[1, 1, 1, 1, 1, 0, 0]
- 文档2的表示:[1, 0, 1, 1, 0, 1, 1]
TF-IDF(假设进行了简单的TF-IDF计算):
- 文档1的表示:[0.3, 0.3, 0.3, 0.3, 0.3, 0, 0]
- 文档2的表示:[0.3, 0, 0.3, 0.3, 0, 0.3, 0.3]
这种表示方式更能体现词汇在不同文档中的重要性。
综上所述,one-hot编码和TF-IDF虽然都是词袋模型中的表示方法,但各自有不同的适用场景和计算方式。根据具体需求选择合适的方法可以更好地处理文本数据。
词嵌入是一种用于自然语言处理 (NLP) 的技术,用于将单词表示为数字,以便计算机可以处理它们。通俗的讲就是,一种把文本转为数值输入到计算机中的方法。
上周提到的将文本转换为字典序列、one-hot编码就是最早期的词嵌入方法。
Embedding和EmbeddingBag则是PyTorch中的用来处理文本数据中词嵌入(word embedding)的工具,它们将离散的词汇映射到低维的连续向量空间中,使得词汇之间的语义关系能够在向量空间中得到体现。
1. Embedding详解
Embedding是PyTorch中最基本的词嵌入操作,TensorFlow中也有相同的函数,功能是一样。它将每个离散的词汇映射到一个低维的连续向量空间中,并且保持了词汇之间的语义关系。在PyTorch中,Embedding的输入是一个整数张量,每个整数都代表着一个词汇的索引,输出是一个浮点型的张量,每个浮点数都代表着对应词汇的词嵌入向量。
嵌入层使用随机权重初始化,并将学习数据集中所有词的嵌入。它是一个灵活的层,可以以各种方式使用,如:
●
它可以用作深度学习模型的一部分,其中嵌入与模型本身一起被学习。
●
它可以用于加载训练好的词嵌入模型。
嵌入层被定义为网络的第一个隐藏层。
下面是一个简单的例子,用Embedding将两个句子转换为词嵌入向量:
import torch
import torch.nn as nnvocab_size = 12 # 词汇表大小
embedding_dim = 4 # 嵌入向量的维度embedding = nn.Embedding(vocab_size,embedding_dim)# 假设我们有一个包含两个单词索引的输入序列
input_sequence1 = torch.tensor([1,5,8],dtype = torch.long)
input_sequence2 = torch.tensor([2,4],dtype = torch.long)# 使用Embedding层将输入序列转换为词嵌入
embedded_sequence1 = embedding(input_sequence1)
embedded_sequence2 = embedding(input_sequence2)print(embedded_sequence1)
print(embedded_sequence2)
C:\Users\chengyuanting\.conda\envs\pytorch_cpu\lib\site-packages\tqdm\auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.htmlfrom .autonotebook import tqdm as notebook_tqdmtensor([[ 0.0696, 1.7589, -0.5145, -0.7778],[-1.4662, 1.7000, 0.1705, 0.2257],[ 1.4426, -0.6042, -0.7372, 0.2595]], grad_fn=<EmbeddingBackward>)
tensor([[ 1.4138, -0.3079, 0.4438, 1.4365],[ 1.1972, 0.7278, -0.2967, 0.1199]], grad_fn=<EmbeddingBackward>)
在这个例子中,我们定义了一个简单的词嵌入模型,它将大小为12的词汇表中的每个词汇映射到一个4维的向量空间中。然后我们输入了两个句子,分别是[1, 5, 8]和[2, 4],每个数字代表着词汇表中的一个词汇的索引。然后我们将这两个句子通过Embedding转换为词嵌入向量,并输出结果。可以看到,每个句子中的每个词汇都被映射成了一个4维的向量。
2. EmbeddingBag详解
EmbeddingBag是在Embedding基础上进一步优化的工具,它可以直接处理不定长的句子,并且可以计算句子中所有词汇的词嵌入向量的均值或总和。在PyTorch中,EmbeddingBag的输入是一个整数张量和一个偏移量张量,每个整数都代表着一个词汇的索引,偏移量则表示句子中每个词汇的位置,输出是一个浮点型的张量,每个浮点数都代表着对应句子的词嵌入向量的均值或总和。
下面是一个简单的例子,用EmbeddingBag将两个句子转换为词嵌入向量并计算它们的均值:
import torch
import torch.nn as nnvocab_size = 12 # 词汇表大小
embedding_dim = 4 # 嵌入向量的维度# 创建一个EmbeddingBag层
embedding_bag = nn.EmbeddingBag(vocab_size,embedding_dim,mode = 'mean')# 假设我们有两个不同长度的输入序列
input_sequence1 = torch.tensor([1,5,8],dtype=torch.long)
input_sequence2 = torch.tensor([2,4],dtype = torch.long)# 将两个输入序列拼接在一起,并创建一个偏移张量
input_sequences = torch.cat([input_sequence1,input_sequence2])offsets = torch.tensor([0,len(input_sequence1)],dtype = torch.long)# 使用EmbeddingBag层计算序列汇总(这里我们使用平均值)
embedded_bag = embedding_bag(input_sequences,offsets)print(embedded_bag)
tensor([[ 0.3596, 0.3660, 1.2236, 0.1613],[-0.6774, -0.1342, -0.1528, 0.3918]], grad_fn=<EmbeddingBagBackward>)
在这个例子中,我们先定义了一个大小为12的词汇表,并将每个词汇映射到一个4维的向量空间中。然后我们输入了两个句子,分别是[1, 5, 8]和[2, 4],每个数字代表着词汇表中的一个词汇的索引。接着,我们通过EmbeddingBag将每个句子中的每个词汇转换为词嵌入向量,并计算它们的均值。可以看到,每个句子的词嵌入向量的均值都是一个4维的向量。
EmbeddingBag 层中的 mode 参数用于指定如何对每个序列中的嵌入向量进行汇总,常用的模式有三种:‘sum’、‘mean’ 和 ‘max’。
●
‘sum’ 模式:将每个序列中的嵌入向量相加。例如,假设有一个序列 [2, 3, 1],每个数字表示一个离散特征的索引,对应的嵌入向量分别为 [0.1, 0.2, 0.3]、[0.2, 0.3, 0.4] 和 [0.3, 0.4, 0.5],则使用 ‘sum’ 模式汇总后的嵌入向量为 [0.6, 0.9, 1.2]。
●
‘mean’ 模式:将每个序列中的嵌入向量求平均值。例如,使用上述序列和嵌入向量,使用 ‘mean’ 模式汇总后的嵌入向量为 [0.2, 0.3, 0.4]。
●
‘max’ 模式:将每个序列中的嵌入向量取最大值。例如,使用上述序列和嵌入向量,使用 ‘max’ 模式汇总后的嵌入向量为 [0.3, 0.4, 0.5]。
这些模式的选择通常取决于具体的任务和数据集。例如,在文本分类任务中,通常使用 ‘mean’ 模式,因为它可以捕捉到每个序列的平均嵌入,反映出序列的整体含义;而在序列标注任务中,通常使用 ‘sum’ 模式,因为它可以捕捉到每个序列的所有信息,不会丢失任何关键信息。在实际应用中,可以根据具体情况灵活选择汇总模式,以获得最佳效果。
3. 任务描述
●
任务要求:加载下面的.txt文件,并使用Embeddingbag与Embedding完成词嵌入
为了完成这个任务,我们需要先对文本文件中的数据进行预处理,然后使用 Embedding
和 EmbeddingBag
完成词嵌入。以下是一个使用 PyTorch 完成这项任务的示例代码。
# 下面是示例代码:import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import re# 读取文件
with open('./data/任务文件.txt', 'r', encoding='utf-8') as file:data = file.readlines()# 数据预处理
def preprocess_text(text):# 去除标点和特殊字符,并转换为小写text = re.sub(r'[^\w\s]', '', text).lower()return text.split()corpus = [preprocess_text(line) for line in data]# 构建词汇表
vocab = set(word for sentence in corpus for word in sentence)
vocab_to_idx = {word: idx for idx, word in enumerate(vocab)}
idx_to_vocab = {idx: word for word, idx in vocab_to_idx.items()}# 将文本转换为索引序列
corpus_idx = [[vocab_to_idx[word] for word in sentence] for sentence in corpus]# 创建自定义Dataset
class TextDataset(Dataset):def __init__(self, corpus):self.corpus = corpusdef __len__(self):return len(self.corpus)def __getitem__(self, idx):return torch.tensor(self.corpus[idx], dtype=torch.long)dataset = TextDataset(corpus_idx)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)# 定义词嵌入模型
embedding_dim = 50 # 词嵌入维度class EmbeddingModel(nn.Module):def __init__(self, vocab_size, embedding_dim):super(EmbeddingModel, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim)self.embedding_bag = nn.EmbeddingBag(vocab_size, embedding_dim, mode='mean')def forward(self, x):embedded = self.embedding(x)embedded_bag = self.embedding_bag(x)return embedded, embedded_bagvocab_size = len(vocab)
model = EmbeddingModel(vocab_size, embedding_dim)# 测试模型
for batch in dataloader:embedded, embedded_bag = model(batch)print("Embedding Output:", embedded)print("EmbeddingBag Output:", embedded_bag)break
Embedding Output: tensor([[[-0.8719, -1.1706, 0.9556, 0.3113, 1.9427, -0.5971, -1.9869,0.6254, 0.0976, 1.0761, -0.5040, 0.1792, -1.7742, -1.4635,-0.6877, -0.3488, 0.5625, 0.1056, -0.2243, 0.2734, -0.7584,0.0856, 0.0091, -2.0143, -0.3909, -0.6707, 2.0094, 0.4527,-2.0077, 2.2954, -0.6976, 0.2475, 0.0983, 0.2450, -0.8813,-2.0154, 0.8784, -0.4759, -1.4560, 0.4556, -1.5770, 0.9049,-0.6961, -0.5243, 0.0360, 1.5304, 1.5517, 0.5879, -0.1599,-0.1657]],[[ 1.6609, -0.2311, 0.2427, 0.5641, -0.5610, -1.3006, -1.2281,0.8859, 1.0426, 1.1971, -0.3660, 0.8680, -0.8722, 2.1673,1.1645, -1.6982, -0.1001, -0.2370, 0.5118, -0.2705, -0.3031,0.6254, -0.4016, 1.2885, 0.9147, -0.0513, -0.5542, -0.7027,0.5360, -1.5996, 0.0173, -0.6962, -1.2412, -0.1224, -0.4773,-0.5792, 1.0192, 1.0937, -0.8528, -0.7854, -2.7524, -1.8929,-0.6022, -0.0322, -0.2165, 0.1942, 0.3575, -0.6974, -0.9809,-0.2842]]], grad_fn=<EmbeddingBackward>)
EmbeddingBag Output: tensor([[-1.0140e+00, 3.1918e-01, -1.6790e+00, -1.8847e+00, -7.6922e-01,3.7105e-01, 7.0660e-01, 9.1940e-01, 1.5190e+00, -1.1904e+00,7.9046e-01, -7.4542e-01, -4.6989e-01, -7.0428e-01, 7.0087e-01,1.9927e+00, 1.2606e+00, 2.0035e-01, -8.2797e-01, 1.6214e+00,-3.9816e-01, 1.2716e+00, -3.4531e-01, 2.1621e+00, 1.4054e+00,3.6051e-02, -1.3158e+00, 9.5829e-01, -7.9911e-01, -1.2633e+00,1.4537e+00, 1.9252e-01, -1.5727e+00, 9.6389e-01, 1.0623e+00,4.4513e-01, 4.9620e-01, -3.4999e-01, -1.8690e-01, -1.5243e+00,2.0293e-02, 7.2899e-01, 1.1042e+00, -5.1307e-01, -7.5490e-01,-1.2425e+00, 7.0598e-01, 1.6890e-01, -1.0748e+00, 2.9246e-01],[-2.2700e-01, -6.8644e-01, 1.5194e-01, 1.5241e+00, -9.3919e-01,-3.5573e-02, -6.0625e-01, -3.6545e-01, 5.6463e-01, 7.1813e-01,-1.3043e-01, 2.4254e-01, -1.2602e+00, -9.5747e-02, 2.2584e-01,-2.3986e-01, -1.4764e+00, 1.1248e+00, -1.5460e+00, 3.9039e-02,3.0375e-01, -1.0407e+00, 8.5656e-01, 7.8559e-01, -5.9085e-01,-1.0516e+00, 4.4840e-01, 8.6134e-01, 1.0366e+00, 5.8634e-01,5.5733e-01, 1.9635e-01, 2.5560e+00, 8.8802e-01, -2.3028e+00,-2.8261e-03, -1.4686e+00, -6.9830e-01, 6.6886e-01, 1.5034e-01,1.5365e-01, -1.7383e-01, 1.0430e-01, -1.0667e+00, 5.2534e-02,-1.4452e+00, 1.7330e-01, -2.0879e+00, -3.1347e+00, 3.9310e-01]],grad_fn=<EmbeddingBagBackward>)
解释:
-
数据预处理:
- 读取文件并清洗文本(去除标点、特殊字符,并转换为小写)。
- 将文本分词并构建词汇表。
- 将文本转换为索引序列以便输入模型。
-
自定义 Dataset 和 DataLoader:
- 创建一个自定义
Dataset
类来加载数据。 - 使用
DataLoader
创建一个批次数据加载器。
- 创建一个自定义
-
定义词嵌入模型:
- 使用
Embedding
和EmbeddingBag
两种方法来生成词嵌入。 EmbeddingBag
的模式设置为 ‘mean’ 来求词向量的均值。
- 使用
-
测试模型:
- 从数据加载器中获取一个批次数据,并通过模型计算词嵌入。
请根据实际需要调整 embedding_dim
和其他参数。这样你就可以完成文件中的文本数据的词嵌入工作。
4. 任务代码详细解释
a. 数据预处理
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import re# 读取文件
with open('/mnt/data/任务文件.txt', 'r', encoding='utf-8') as file:data = file.readlines()# 数据预处理
def preprocess_text(text):# 去除标点和特殊字符,并转换为小写text = re.sub(r'[^\w\s]', '', text).lower()return text.split()corpus = [preprocess_text(line) for line in data]
- 读取文件:通过
with open(...)
语句读取文件中的所有行。 - 数据预处理函数:
preprocess_text
函数去除了文本中的标点符号和特殊字符,并将文本转换为小写,然后将文本按空格分词。 - 处理整个语料库:对文件中的每一行应用预处理函数,生成一个嵌套的列表,其中每个子列表是一个分词后的句子。
b. 构建词汇表和索引序列
# 构建词汇表
vocab = set(word for sentence in corpus for word in sentence)
vocab_to_idx = {word: idx for idx, word in enumerate(vocab)}
idx_to_vocab = {idx: word for word, idx in vocab_to_idx.items()}# 将文本转换为索引序列
corpus_idx = [[vocab_to_idx[word] for word in sentence] for sentence in corpus]
- 构建词汇表:通过集合
set
和嵌套循环获取语料库中的所有唯一词。 - 词汇表映射:使用
enumerate
构建从词到索引和从索引到词的映射字典。 - 索引序列:将每个句子中的词转换为对应的索引,生成一个嵌套的索引序列列表。
c. 自定义 Dataset 和 DataLoader
# 创建自定义Dataset
class TextDataset(Dataset):def __init__(self, corpus):self.corpus = corpusdef __len__(self):return len(self.corpus)def __getitem__(self, idx):return torch.tensor(self.corpus[idx], dtype=torch.long)dataset = TextDataset(corpus_idx)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
- 自定义 Dataset:继承自
torch.utils.data.Dataset
类,实现__len__
和__getitem__
方法。__len__
返回数据集的大小。__getitem__
返回指定索引的样本。
- DataLoader:使用
DataLoader
将自定义数据集封装起来,支持批量处理和随机打乱数据。batch_size=2
指定每个批次包含 2 个样本。
d. 定义词嵌入模型
# 定义词嵌入模型
embedding_dim = 50 # 词嵌入维度class EmbeddingModel(nn.Module):def __init__(self, vocab_size, embedding_dim):super(EmbeddingModel, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim)self.embedding_bag = nn.EmbeddingBag(vocab_size, embedding_dim, mode='mean')def forward(self, x):embedded = self.embedding(x)embedded_bag = self.embedding_bag(x)return embedded, embedded_bagvocab_size = len(vocab)
model = EmbeddingModel(vocab_size, embedding_dim)
- Embedding 和 EmbeddingBag:
nn.Embedding
是一个简单的查找表,从索引到嵌入向量。nn.EmbeddingBag
是一个带有加权求和或均值操作的嵌入层。这里使用mean
模式。
- 定义模型:创建一个包含两个嵌入层的模型,分别是
embedding
和embedding_bag
。 - 前向传播:定义前向传播函数
forward
,返回两个嵌入结果。
e. 测试模型
# 测试模型
for batch in dataloader:embedded, embedded_bag = model(batch)print("Embedding Output:", embedded)print("EmbeddingBag Output:", embedded_bag)break
- 批量处理:从数据加载器中获取一个批次数据。
- 模型计算:将批次数据传入模型,获取嵌入结果并打印输出。
f. 常用模块介绍
1. 自定义 Dataset
Dataset
是一个抽象类,用户可以通过继承该类并实现 __len__
和 __getitem__
方法来自定义数据集。
from torch.utils.data import Datasetclass CustomDataset(Dataset):def __init__(self, data):self.data = datadef __len__(self):return len(self.data)def __getitem__(self, idx):return self.data[idx]
__init__
:初始化数据集。__len__
:返回数据集大小。__getitem__
:根据索引返回数据集中的一个样本。
2. DataLoader
DataLoader
用于将 Dataset
封装,提供批量处理、打乱和多线程加载数据的功能。
from torch.utils.data import DataLoaderdata = [i for i in range(100)]
dataset = CustomDataset(data)
dataloader = DataLoader(dataset, batch_size=10, shuffle=True)for batch in dataloader:print(batch)
batch_size
:每个批次的样本数。shuffle
:是否在每个 epoch 开始时打乱数据。num_workers
:用于数据加载的子进程数量(默认为0,即在主进程中加载数据)。
g. 总结
通过以上代码和解释,你可以了解如何从文本文件中读取数据,进行预处理,构建词汇表和索引序列,自定义数据集,并使用 PyTorch 的 Embedding
和 EmbeddingBag
层进行词嵌入。希望这对你有帮助!如果有任何疑问或需要进一步的帮助,请随时告诉我。