总结:

之前有学习过文本预处理的环节,对文本处理的主要方式有以下三种:

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)都是用于文本数据表示的方法,但它们在具体实现和用途上有所不同。以下是两者的区别与联系:

区别

  1. 定义和计算方式

    • 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) )。
  2. 结果表示

    • One-hot编码:结果是一个高维且稀疏的向量,向量中只有一个位置为1,其余全为0。
    • TF-IDF:结果是一个权重向量,向量中包含实数值,每个值表示该词在特定文档中的重要性。
  3. 应用场景

    • One-hot编码:常用于简单的词汇表示,适合用于需要简单且直接表示词汇的场景,如初步的数据探索和简单模型训练。
    • TF-IDF:更适合用于需要衡量词汇在文档中重要性的场景,如信息检索和文本分类,尤其是当文档集较大时。

联系

  1. 词袋模型基础

    • 两者都是基于词袋模型的文本表示方法,忽略了词汇顺序,只关注词汇的出现与频率。
  2. 向量化文本

    • 两者都将文本转换为向量形式,以便于后续的机器学习和数据处理。
  3. 高维稀疏性

    • 两者生成的向量通常都是高维且稀疏的,尤其是在词汇表较大时。

具体示例

假设有两个文档:

  • 文档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 nn

vocab_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.html
  from .autonotebook import tqdm as notebook_tqdm


tensor([[ 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 nn

vocab_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完成词嵌入

为了完成这个任务,我们需要先对文本文件中的数据进行预处理,然后使用 EmbeddingEmbeddingBag 完成词嵌入。以下是一个使用 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 = corpus
    
    def __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_bag

vocab_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>)

解释:

  1. 数据预处理

    • 读取文件并清洗文本(去除标点、特殊字符,并转换为小写)。
    • 将文本分词并构建词汇表。
    • 将文本转换为索引序列以便输入模型。
  2. 自定义 Dataset 和 DataLoader

    • 创建一个自定义 Dataset 类来加载数据。
    • 使用 DataLoader 创建一个批次数据加载器。
  3. 定义词嵌入模型

    • 使用 EmbeddingEmbeddingBag 两种方法来生成词嵌入。
    • EmbeddingBag 的模式设置为 ‘mean’ 来求词向量的均值。
  4. 测试模型

    • 从数据加载器中获取一个批次数据,并通过模型计算词嵌入。

请根据实际需要调整 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 = corpus

    def __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_bag

vocab_size = len(vocab)
model = EmbeddingModel(vocab_size, embedding_dim)
  • Embedding 和 EmbeddingBag
    • nn.Embedding 是一个简单的查找表,从索引到嵌入向量。
    • nn.EmbeddingBag 是一个带有加权求和或均值操作的嵌入层。这里使用 mean 模式。
  • 定义模型:创建一个包含两个嵌入层的模型,分别是 embeddingembedding_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 Dataset

class CustomDataset(Dataset):
    def __init__(self, data):
        self.data = data

    def __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 DataLoader

data = [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 的 EmbeddingEmbeddingBag 层进行词嵌入。希望这对你有帮助!如果有任何疑问或需要进一步的帮助,请随时告诉我。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐