Bootstrap

深度学习Day-37:NLP中的文本嵌入

  🍨 本文为:[🔗365天深度学习训练营] 中的学习记录博客
 🍖 原作者:[K同学啊 | 接辅导、项目定制]

任务:加载第N1周的.txt文件,使用Embeddingbag与Embedding完成词嵌入 

        词嵌入是一种用于自然语言处理 (NLP) 的技术,用于将单词表示为数字,以便计算机可以处理它们。通俗的讲就是,一种把文本转为数值输入到计算机中的方法。

        在《深度学习Day-35》中提到的将文本转换为字典序列、one-hot编码就是最早期的词嵌入方法。
        Embedding和EmbeddingBag则是PyTorch中的用来处理文本数据中词嵌入(word embedding)的工具,它们将离散的词汇映射到低维的连续向量空间中,使得词汇之间的语义关系能够在向量空间中得到体现。

1.Embedding嵌入

        Embedding是PyTorch中最基本的词嵌入操作,TensorFlow中也有相同的函数,功能是一样。它将每个离散的词汇映射到一个低维的连续向量空间中,并且保持了词汇之间的语义关系。在PyTorch中,Embedding的输入是一个整数张量,每个整数都代表着一个词汇的索引,输出是一个浮点型的张量,每个浮点数都代表着对应词汇的词嵌入向量。

  • 输入shape:[batch, seqSize](seqSize为单个文本长度)
  • 输出shape:[batch, seqSize, embed_dim](embed_dim嵌入维度)

        嵌入层使用随机权重初始化,并将学习数据集中所有词的嵌入。它是一个灵活的层,可以以各种方式使用,如:

  1. 它可以用作深度学习模型的一部分,其中嵌入与模型本身一起被学习
  2. 它可以用于加载训练好的词嵌入模型

        嵌入层被定义为网络的第一个隐藏层
函数原型:

torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, 
                   max_norm=None,norm_type=2.0,scale_grad_by_freq=False, 
                   sparse=False,_weight=None,_freeze=False, device=None, 
                   dtype=None)

常见参数:

  1. num_embeddings:词汇表大小,即最大整数index+1
  2. embedding_dim:词向量的维度

下面是一个简单的例子,用Embedding将两个句子转换为词嵌入向量:

1.1 自定义数据集类

import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset


class MyDataset(Dataset):
    def __init__(self, texts, labels):
        self.texts = texts
        self.labels = labels

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        texts = self.texts[idx]
        labels = self.labels[idx]

        return texts, labels

1.2 定义填充函数

def collate_batch(batch):
    texts, labels = zip(*batch)
    max_len = max(len(text) for text in texts)
    padded_texts = [F.pad(text, (0, max_len - len(text)), value=0) for text in texts]
    padded_texts = torch.stack(padded_texts)
    labels = torch.tensor(labels, dtype=torch.float).unsqueeze(1)
    return padded_texts, labels

1.3 准备数据和数据加载器

# 假设我们有以下三个样本,分别由不同数量的单词索引组成
text_data = [
    torch.tensor([1, 1, 1, 1], dtype=torch.long),  # 样本1
    torch.tensor([2, 2, 2], dtype=torch.long),     # 样本2
    torch.tensor([3, 3], dtype=torch.long)         # 样本3
]

# 对应的标签
labels = torch.tensor([4, 5, 6], dtype=torch.float)

# 创建数据集和数据加载器
my_dataset  = MyDataset(text_data, labels)
data_loader = DataLoader(my_dataset, batch_size=2, shuffle=True, collate_fn=collate_batch)

for batch in data_loader:
    print(batch)

代码输出: 

(tensor([[2, 2, 2, 0],
        [1, 1, 1, 1]]), tensor([[5.],
        [4.]]))
(tensor([[3, 3]]), tensor([[6.]]))

1.4 定义模型

class EmbeddingModel(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super(EmbeddingModel, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.fc = nn.Linear(embed_dim, 1)  # 假设我们做一个二分类任务

    def forward(self, text):        
        print("embedding输入文本是:",text)
        print("embedding输入文本shape:",text.shape)
        embedding = self.embedding(text)
        embedding_mean = embedding.mean(dim=1)  # 对每个样本的嵌入向量进行平均
        print("embedding输出文本shape:",embedding_mean.shape)
        return self.fc(embedding_mean)

注意:如果使用embedding_mean = embedding.mean(dim=1)语句对每个样本的嵌入向量求平均,输出shape为[batch, embed_dim]。若注释掉该语句,输出shape则为[batch, seqSize, embed_dim] 

1.5 训练模型 

# 示例词典大小和嵌入维度
vocab_size = 10
embed_dim = 6

# 创建模型实例
model = EmbeddingModel(vocab_size, embed_dim)

# 定义一个简单的损失函数和优化器
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(1):  # 训练1个epoch
    for batch in data_loader:
        texts, labels = batch

        # 前向传播
        outputs = model(texts)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f'Epoch {epoch+1}, Loss: {loss.item()}')

代码输出: 

embedding输入文本是: tensor([[3, 3, 0],
        [2, 2, 2]])
embedding输入文本shape: torch.Size([2, 3])
embedding输出文本shape: torch.Size([2, 6])
embedding输入文本是: tensor([[1, 1, 1, 1]])
embedding输入文本shape: torch.Size([1, 4])
embedding输出文本shape: torch.Size([1, 6])
Epoch 1, Loss: 1.5545356273651123

2.EmbeddingBag嵌入

        EmbeddingBag是在Embedding基础上进一步优化的工具,其核心思想是将每个输入序列的嵌入向量进行合并,能够处理可变长度的输入序列,并且减少了计算和存储的开销,并且可以计算句子中所有词汇的词嵌入向量的均值或总和。
        在PyTorch中,EmbeddingBag的输入是一个整数张量和一个偏移量张量,每个整数都代表着一个词汇的索引,偏移量则表示句子中每个词汇的位置,输出是一个浮点型的张量,每个浮点数都代表着对应句子的词嵌入向量的均值或总和.

  • 输入shape:[seqsSize](seqsSize为单个batch文本总长度)

  • 输出shape:[batch, embed_dim](embed_dim嵌入维度)

        假定原始输入数据为:[[1, 1, 1, 1],[2, 2, 2],[3, 3]]
        输入:

  • 输入是一个展平的词汇索引张量(input),例如 [2, 2, 2, 1, 1, 1, 1]
  • 对应的偏移量(offsets),例如 [0, 3],表示每个样本在展平张量中的起始位置

        合并操作:

  • 根据偏移量,将嵌入向量进行合并操作
  • 合并操作可以是求和、平均或取最大值,默认是平均(mean)

函数原型:

torch.nn.EmbeddingBag(num_embeddings, embedding_dim, max_norm=None, 
                      norm_type=2.0, scale_grad_by_freq=False, 
                      mode='mean', sparse=False, _weight=None, 
                      include_last_offset=False, padding_idx=None, 
                      device=None, dtype=None)

常见参数:

  1. num_embeddings (int):词典的大小
  2. embedding_dim (int):每个词向量的维度,即嵌入向量的长度
  3. mode (str):指定嵌入向量的聚合方式。可选值为 ‘sum’、‘mean’ 和 ‘max’
  • (假设有一个序列 [2, 3, 1],每个数字表示一个离散特征的索引,对应的嵌入向量分别为 [[0.1, 0.2, 0.3],[0.2, 0.3, 0.4],[0.3, 0.4, 0.5]])
    sum:对所有的嵌入向量求和,则使用 ‘sum’ 模式汇总后的嵌入向量为 [0.6, 0.9,1.2]
    mean:对所有的嵌入向量求平均值,使用 ‘mean’ 模式汇总后的嵌入向量为 [0.2,0.3, 0.4]
    max:对所有的嵌入向量求最大值,使用 ‘max’ 模式汇总后的嵌入向量为 [0.3,0.4,0.5]

下面是一个简单的例子,用EmbeddingBag将两个句子转换为词嵌入向量并计算它们的均值:

2.1 自定义数据集类

import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset

class MyDataset(Dataset):
    def __init__(self, texts, labels):
        self.texts  = texts
        self.labels = labels

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        texts  = self.texts[idx]
        labels = self.labels[idx]
        
        return texts, labels

2.2 准备数据和数据加载器

# 假设我们有以下三个样本,分别由不同数量的单词索引组成
text_data = [
    torch.tensor([1, 1, 1, 1], dtype=torch.long),  # 样本1
    torch.tensor([2, 2, 2], dtype=torch.long),     # 样本2
    torch.tensor([3, 3], dtype=torch.long)         # 样本3
]

# 对应的标签
labels = torch.tensor([4, 5, 6], dtype=torch.float)

# 创建数据集和数据加载器
my_dataset     = MyDataset(text_data, labels)
data_loader = DataLoader(my_dataset, batch_size=2, shuffle=True, collate_fn=lambda x: x)

for batch in data_loader:
    print(batch)

代码输出: 

[(tensor([2, 2, 2]), tensor(5.)), (tensor([1, 1, 1, 1]), tensor(4.))]
[(tensor([3, 3]), tensor(6.))]

2.3 定义模型

class EmbeddingBagModel(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super(EmbeddingBagModel, self).__init__()
        self.embedding_bag = nn.EmbeddingBag(vocab_size, embed_dim, mode='mean')
        self.fc = nn.Linear(embed_dim, 1)  # 假设我们做一个二分类任务

    def forward(self, text, offsets):
        print("embedding_bag输入文本是:",text)
        print("embedding_bag输入文本shape:",text.shape)
        embedded = self.embedding_bag(text, offsets)
        print("embedding_bag输出文本shape:",embedded.shape)
        return self.fc(embedded)

2.4 训练模型 

# 示例词典大小和嵌入维度
vocab_size = 10
embed_dim  = 6

# 创建模型实例
model = EmbeddingBagModel(vocab_size, embed_dim)

# 定义一个简单的损失函数和优化器
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(1):  # 训练1个epoch
    for batch in data_loader:
        # 将批处理的数据展平并计算偏移量
        texts, labels = zip(*batch)

        offsets = [0] + [len(text) for text in texts[:-1]]
        offsets = torch.tensor(offsets).cumsum(dim=0)
        texts   = torch.cat(texts)
        labels  = torch.tensor(labels).unsqueeze(1)

        # 前向传播
        outputs = model(texts, offsets)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f'Epoch {epoch+1}, Loss: {loss.item()}')

代码输出: 

embedding_bag输入文本是: tensor([2, 2, 2, 3, 3])
embedding_bag输入文本shape: torch.Size([5])
embedding_bag输出文本shape: torch.Size([2, 6])
embedding_bag输入文本是: tensor([1, 1, 1, 1])
embedding_bag输入文本shape: torch.Size([4])
embedding_bag输出文本shape: torch.Size([1, 6])
Epoch 1, Loss: 0.2634810507297516

 3.任务1

3.1 自定义数据集类

import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset

class MyDataset(Dataset):
    def __init__(self, texts, labels):
        self.texts  = texts
        self.labels = labels

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        texts  = self.texts[idx]
        labels = self.labels[idx]
        
        return texts, labels

3.2 定义填充函数 

def collate_batch(batch):
    texts, labels = zip(*batch)
    max_len = max(len(text) for text in texts)
    padded_texts = [F.pad(text, (0, max_len - len(text)), value=0) for text in texts]
    padded_texts = torch.stack(padded_texts)
    labels = torch.tensor(labels, dtype=torch.float).unsqueeze(1)
    return padded_texts, labels

3.3 准备数据和数据加载器

import torch
import torch.nn.functional as F
import jieba


# 打开txt文件
file_name = "./N1/任务文件.txt"
with open(file_name,"r",encoding = "utf-8") as file:
    context = file.read()
    texts = context.split()
texts


# 使用结巴分词进行分词
tokenized_texts = [list(jieba.cut(text)) for text in texts]

# 构建词汇表
word_index = {}
index_word = {}
for i, word in enumerate(set([word for text in tokenized_texts for word in text])):
    word_index[word] = i
    index_word[i] = word

# 将文本转化为整数序列
sequences = [[word_index[word] for word in text] for text in tokenized_texts]

# 获取词汇表大小
vocab_size = len(word_index)

# 将整数序列转化为one-hot编码
one_hot_results = torch.zeros(len(texts), vocab_size)
for i, seq in enumerate(sequences):
    one_hot_results[i, seq] = 1

# 打印结果
print("词汇表:")
print(word_index)
print("\n文本:")
print(texts)
print("\n分词结果")
print(tokenized_texts)
print("\n文本序列:")
print(sequences)
print("\nOne-Hot编码:")
print(one_hot_results)

代码输出: 

词汇表:
{'“': 0, '一些': 1, '引入': 2, '是': 3, '就是': 4, '其余': 5, '地': 6, '。': 7, '将': 8, '直观': 9, '基本': 10, '实际上': 11, '编码': 12, '采用': 13, '编码方式': 14, '0': 15, '啊': 16, '认为': 17, '这种': 18, '向量': 19, '2': 20, '情况': 21, '了': 22, '”': 23, '值': 24, '对于': 25, '或': 26, '避免': 27, '1': 28, '天': 29, '序列': 30, '存在': 31, '这些': 32, '但是': 33, '分别': 34, '不': 35, '相互': 36, '3': 37, '会': 38, '或者': 39, '-': 40, '元素': 41, ')': 42, 'K': 43, '这样': 44, '距离': 45, ',': 46, '比较': 47, '问题': 48, '、': 49, '而': 50, '教案': 51, 'hot': 52, '其中': 53, '和': 54, '独热': 55, '每个': 56, '一个': 57, '的': 58, '映射': 59, '训练营': 60, '为': 61, '不同': 62, '模型': 63, '(': 64, '这是': 65, '表示': 66, '关系': 67, '提到': 68, '可以': 69, '思想': 70, '独立': 71, '内容': 72, '这': 73, 'one': 74, '字典': 75, '也': 76, '上面': 77, '用': 78, '实际意义': 79, '365': 80, '为了': 81, '同学': 82, '使用': 83, '例如': 84, ':': 85, '具有': 86, '类别': 87, '到': 88, '有': 89, '学习': 90, '如下': 91, '错误': 92, '之间': 93, '深度': 94, '三个': 95, '可能': 96, '只有': 97, '称': 98, '顺序': 99}

文本:
['比较直观的编码方式是采用上面提到的字典序列。', '例如,对于一个有三个类别的问题,可以用1、2和3分别表示这三个类别。', '但是,这种编码方式存在一个问题,就是模型可能会错误地认为不同类别之间存在一些顺序或距离关系', '而实际上这些关系可能是不存在的或者不具有实际意义的,为了避免这种问题,引入了one-hot编码(也称独热编码)。', 'one-hot编码的基本思想是将每个类别映射到一个向量,其中只有一个元素的值为1,其余元素的值为0。', '这样,每个类别之间就是相互独立的,不存在顺序或距离关系。', '例如,对于三个类别的情况,可以使用如下的one-hot编码:', '这是K同学啊的“365天深度学习训练营”教案内容']

分词结果
[['比较', '直观', '的', '编码方式', '是', '采用', '上面', '提到', '的', '字典', '序列', '。'], ['例如', ',', '对于', '一个', '有', '三个', '类别', '的', '问题', ',', '可以', '用', '1', '、', '2', '和', '3', '分别', '表示', '这', '三个', '类别', '。'], ['但是', ',', '这种', '编码方式', '存在', '一个', '问题', ',', '就是', '模型', '可能', '会', '错误', '地', '认为', '不同', '类别', '之间', '存在', '一些', '顺序', '或', '距离', '关系'], ['而', '实际上', '这些', '关系', '可能', '是', '不', '存在', '的', '或者', '不', '具有', '实际意义', '的', ',', '为了', '避免', '这种', '问题', ',', '引入', '了', 'one', '-', 'hot', '编码', '(', '也', '称', '独热', '编码', ')', '。'], ['one', '-', 'hot', '编码', '的', '基本', '思想', '是', '将', '每个', '类别', '映射', '到', '一个', '向量', ',', '其中', '只有', '一个', '元素', '的', '值', '为', '1', ',', '其余', '元素', '的', '值', '为', '0', '。'], ['这样', ',', '每个', '类别', '之间', '就是', '相互', '独立', '的', ',', '不', '存在', '顺序', '或', '距离', '关系', '。'], ['例如', ',', '对于', '三个', '类别', '的', '情况', ',', '可以', '使用', '如下', '的', 'one', '-', 'hot', '编码', ':'], ['这是', 'K', '同学', '啊', '的', '“', '365', '天', '深度', '学习', '训练营', '”', '教案', '内容']]

文本序列:
[[47, 9, 58, 14, 3, 13, 77, 68, 58, 75, 30, 7], [84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34, 66, 73, 95, 87, 7], [33, 46, 18, 14, 31, 57, 48, 46, 4, 63, 96, 38, 92, 6, 17, 62, 87, 93, 31, 1, 99, 26, 45, 67], [50, 11, 32, 67, 96, 3, 35, 31, 58, 39, 35, 86, 79, 58, 46, 81, 27, 18, 48, 46, 2, 22, 74, 40, 52, 12, 64, 76, 98, 55, 12, 42, 7], [74, 40, 52, 12, 58, 10, 70, 3, 8, 56, 87, 59, 88, 57, 19, 46, 53, 97, 57, 41, 58, 24, 61, 28, 46, 5, 41, 58, 24, 61, 15, 7], [44, 46, 56, 87, 93, 4, 36, 71, 58, 46, 35, 31, 99, 26, 45, 67, 7], [84, 46, 25, 95, 87, 58, 21, 46, 69, 83, 91, 58, 74, 40, 52, 12, 85], [65, 43, 82, 16, 58, 0, 80, 29, 94, 90, 60, 23, 51, 72]]

One-Hot编码:
tensor([[0., 0., 0., 1., 0., 0., 0., 1., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.,
         0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,
         0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 0., 0., 0.,
         1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0.,
         0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 1.,
         0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
        [0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1.,
         1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 1., 0., 0.,
         0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 0., 0.,
         0., 0., 0., 1., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 1., 1., 0., 0., 1., 0., 0., 1.],
        [0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0.,
         1., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 1.,
         0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 1., 0., 1., 0., 1., 0., 1., 0.,
         0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0.,
         0., 0., 1., 0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 1., 0., 1., 0.],
        [0., 0., 0., 1., 0., 1., 0., 1., 1., 0., 1., 0., 1., 0., 0., 1., 0., 0.,
         0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 1.,
         0., 0., 1., 1., 1., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
         0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0.,
         0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
        [0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 1.,
         1., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 1.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 0., 1., 0., 0., 0., 0., 0., 1.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
         0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,
         0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 0.,
         0., 1., 0., 0., 0., 1., 0., 0., 0., 0.],
        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
         0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
         1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0.,
         1., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])

转换为张量:

# 将文本序列转换为PyTorch张量
text_data = [torch.tensor(seq, dtype=torch.long) for seq in sequences]
# 假设标签是一些浮点数值(根据实际任务定义标签)
labels = torch.tensor([1.0, 2.0], dtype=torch.float)
# 输出结果
print("Text Data:", text_data)
print("Labels:", labels)

 代码输出:

Text Data: [tensor([47,  9, 58, 14,  3, 13, 77, 68, 58, 75, 30,  7]), tensor([84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34,
        66, 73, 95, 87,  7]), tensor([33, 46, 18, 14, 31, 57, 48, 46,  4, 63, 96, 38, 92,  6, 17, 62, 87, 93,
        31,  1, 99, 26, 45, 67]), tensor([50, 11, 32, 67, 96,  3, 35, 31, 58, 39, 35, 86, 79, 58, 46, 81, 27, 18,
        48, 46,  2, 22, 74, 40, 52, 12, 64, 76, 98, 55, 12, 42,  7]), tensor([74, 40, 52, 12, 58, 10, 70,  3,  8, 56, 87, 59, 88, 57, 19, 46, 53, 97,
        57, 41, 58, 24, 61, 28, 46,  5, 41, 58, 24, 61, 15,  7]), tensor([44, 46, 56, 87, 93,  4, 36, 71, 58, 46, 35, 31, 99, 26, 45, 67,  7]), tensor([84, 46, 25, 95, 87, 58, 21, 46, 69, 83, 91, 58, 74, 40, 52, 12, 85]), tensor([65, 43, 82, 16, 58,  0, 80, 29, 94, 90, 60, 23, 51, 72])]
Labels: tensor([1., 2.])

3.4创建数据集和加载器 

# 创建数据集和数据加载器
my_dataset  = MyDataset(text_data, labels)
data_loader = DataLoader(my_dataset, batch_size=2, shuffle=True, collate_fn=collate_batch)

for batch in data_loader:
    print(batch)

代码输出: 

(tensor([[84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34,
         66, 73, 95, 87,  7],
        [47,  9, 58, 14,  3, 13, 77, 68, 58, 75, 30,  7,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0]]), tensor([[2.],
        [1.]]))

3.5 定义模型

class EmbeddingModel(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super(EmbeddingModel, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.fc = nn.Linear(embed_dim, 1)  # 假设我们做一个二分类任务

    def forward(self, text):
        print("embedding输入文本是:",text)
        print("embedding输入文本shape:",text.shape)
        embedding = self.embedding(text)
        embedding_mean = embedding.mean(dim=1)  # 对每个样本的嵌入向量进行平均
        print("embedding输出文本shape:",embedding_mean.shape)
        return self.fc(embedding_mean)

3.6 训练模型 

# 示例词典大小和嵌入维度
vocab_size = vocab_size
embed_dim = 10

# 创建模型实例
model = EmbeddingModel(vocab_size, embed_dim)

# 定义一个简单的损失函数和优化器
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(1):  # 训练1个epoch
    for batch in data_loader:
        texts, labels = batch

        # 前向传播
        outputs = model(texts)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f'Epoch {epoch+1}, Loss: {loss.item()}')

代码输出: 

embedding输入文本是: tensor([[84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34,
         66, 73, 95, 87,  7],
        [47,  9, 58, 14,  3, 13, 77, 68, 58, 75, 30,  7,  0,  0,  0,  0,  0,  0,
          0,  0,  0,  0,  0]])
embedding输入文本shape: torch.Size([2, 23])
embedding输出文本shape: torch.Size([2, 10])
Epoch 1, Loss: 0.48922550678253174

4.任务2

4.1 自定义数据集类

import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset

class MyDataset(Dataset):
    def __init__(self, texts, labels):
        self.texts  = texts
        self.labels = labels

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        texts  = torch.tensor(self.texts[idx], dtype=torch.long)
        labels = torch.tensor(self.labels[idx], dtype=torch.float)
        
        return texts, labels

4.2 准备数据和数据加载器

import torch
import torch.nn.functional as F
import jieba


# 打开txt文件
file_name = "./N1/任务文件.txt"
with open(file_name,"r",encoding = "utf-8") as file:
    context = file.read()
    texts = context.split()
texts


# 使用结巴分词进行分词
tokenized_texts = [list(jieba.cut(text)) for text in texts]

# 构建词汇表
word_index = {}
index_word = {}
for i, word in enumerate(set([word for text in tokenized_texts for word in text])):
    word_index[word] = i
    index_word[i] = word

# 将文本转化为整数序列
sequences = [[word_index[word] for word in text] for text in tokenized_texts]

# 获取词汇表大小
vocab_size = len(word_index)

# 将整数序列转化为one-hot编码
one_hot_results = torch.zeros(len(texts), vocab_size)
for i, seq in enumerate(sequences):
    one_hot_results[i, seq] = 1

# 打印结果
print("词汇表:")
print(word_index)
print("\n文本:")
print(texts)
print("\n分词结果")
print(tokenized_texts)
print("\n文本序列:")
print(sequences)
print("\nOne-Hot编码:")
print(one_hot_results)

代码输出: 

词汇表:
{'“': 0, '一些': 1, '引入': 2, '是': 3, '就是': 4, '其余': 5, '地': 6, '。': 7, '将': 8, '直观': 9, '基本': 10, '实际上': 11, '编码': 12, '采用': 13, '编码方式': 14, '0': 15, '啊': 16, '认为': 17, '这种': 18, '向量': 19, '2': 20, '情况': 21, '了': 22, '”': 23, '值': 24, '对于': 25, '或': 26, '避免': 27, '1': 28, '天': 29, '序列': 30, '存在': 31, '这些': 32, '但是': 33, '分别': 34, '不': 35, '相互': 36, '3': 37, '会': 38, '或者': 39, '-': 40, '元素': 41, ')': 42, 'K': 43, '这样': 44, '距离': 45, ',': 46, '比较': 47, '问题': 48, '、': 49, '而': 50, '教案': 51, 'hot': 52, '其中': 53, '和': 54, '独热': 55, '每个': 56, '一个': 57, '的': 58, '映射': 59, '训练营': 60, '为': 61, '不同': 62, '模型': 63, '(': 64, '这是': 65, '表示': 66, '关系': 67, '提到': 68, '可以': 69, '思想': 70, '独立': 71, '内容': 72, '这': 73, 'one': 74, '字典': 75, '也': 76, '上面': 77, '用': 78, '实际意义': 79, '365': 80, '为了': 81, '同学': 82, '使用': 83, '例如': 84, ':': 85, '具有': 86, '类别': 87, '到': 88, '有': 89, '学习': 90, '如下': 91, '错误': 92, '之间': 93, '深度': 94, '三个': 95, '可能': 96, '只有': 97, '称': 98, '顺序': 99}

文本:
['比较直观的编码方式是采用上面提到的字典序列。', '例如,对于一个有三个类别的问题,可以用1、2和3分别表示这三个类别。', '但是,这种编码方式存在一个问题,就是模型可能会错误地认为不同类别之间存在一些顺序或距离关系', '而实际上这些关系可能是不存在的或者不具有实际意义的,为了避免这种问题,引入了one-hot编码(也称独热编码)。', 'one-hot编码的基本思想是将每个类别映射到一个向量,其中只有一个元素的值为1,其余元素的值为0。', '这样,每个类别之间就是相互独立的,不存在顺序或距离关系。', '例如,对于三个类别的情况,可以使用如下的one-hot编码:', '这是K同学啊的“365天深度学习训练营”教案内容']

分词结果
[['比较', '直观', '的', '编码方式', '是', '采用', '上面', '提到', '的', '字典', '序列', '。'], ['例如', ',', '对于', '一个', '有', '三个', '类别', '的', '问题', ',', '可以', '用', '1', '、', '2', '和', '3', '分别', '表示', '这', '三个', '类别', '。'], ['但是', ',', '这种', '编码方式', '存在', '一个', '问题', ',', '就是', '模型', '可能', '会', '错误', '地', '认为', '不同', '类别', '之间', '存在', '一些', '顺序', '或', '距离', '关系'], ['而', '实际上', '这些', '关系', '可能', '是', '不', '存在', '的', '或者', '不', '具有', '实际意义', '的', ',', '为了', '避免', '这种', '问题', ',', '引入', '了', 'one', '-', 'hot', '编码', '(', '也', '称', '独热', '编码', ')', '。'], ['one', '-', 'hot', '编码', '的', '基本', '思想', '是', '将', '每个', '类别', '映射', '到', '一个', '向量', ',', '其中', '只有', '一个', '元素', '的', '值', '为', '1', ',', '其余', '元素', '的', '值', '为', '0', '。'], ['这样', ',', '每个', '类别', '之间', '就是', '相互', '独立', '的', ',', '不', '存在', '顺序', '或', '距离', '关系', '。'], ['例如', ',', '对于', '三个', '类别', '的', '情况', ',', '可以', '使用', '如下', '的', 'one', '-', 'hot', '编码', ':'], ['这是', 'K', '同学', '啊', '的', '“', '365', '天', '深度', '学习', '训练营', '”', '教案', '内容']]

文本序列:
[[47, 9, 58, 14, 3, 13, 77, 68, 58, 75, 30, 7], [84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34, 66, 73, 95, 87, 7], [33, 46, 18, 14, 31, 57, 48, 46, 4, 63, 96, 38, 92, 6, 17, 62, 87, 93, 31, 1, 99, 26, 45, 67], [50, 11, 32, 67, 96, 3, 35, 31, 58, 39, 35, 86, 79, 58, 46, 81, 27, 18, 48, 46, 2, 22, 74, 40, 52, 12, 64, 76, 98, 55, 12, 42, 7], [74, 40, 52, 12, 58, 10, 70, 3, 8, 56, 87, 59, 88, 57, 19, 46, 53, 97, 57, 41, 58, 24, 61, 28, 46, 5, 41, 58, 24, 61, 15, 7], [44, 46, 56, 87, 93, 4, 36, 71, 58, 46, 35, 31, 99, 26, 45, 67, 7], [84, 46, 25, 95, 87, 58, 21, 46, 69, 83, 91, 58, 74, 40, 52, 12, 85], [65, 43, 82, 16, 58, 0, 80, 29, 94, 90, 60, 23, 51, 72]]

One-Hot编码:
tensor([[0., 0., 0., 1., 0., 0., 0., 1., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.,
         0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,
         0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 0., 0., 0.,
         1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0.,
         0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 1.,
         0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
        [0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1.,
         1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 1., 0., 0.,
         0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 0., 0.,
         0., 0., 0., 1., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 1., 1., 0., 0., 1., 0., 0., 1.],
        [0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0.,
         1., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 1.,
         0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 1., 0., 1., 0., 1., 0., 1., 0.,
         0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0.,
         0., 0., 1., 0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 1., 0., 1., 0.],
        [0., 0., 0., 1., 0., 1., 0., 1., 1., 0., 1., 0., 1., 0., 0., 1., 0., 0.,
         0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 1.,
         0., 0., 1., 1., 1., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
         0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0.,
         0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
        [0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 1.,
         1., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 1.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 0., 1., 0., 0., 0., 0., 0., 1.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
         0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,
         0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 0.,
         0., 1., 0., 0., 0., 1., 0., 0., 0., 0.],
        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
         0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
         0., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
         1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0.,
         1., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])

标签对应:

# 分词后的文本和对应的标签
text_data = sequences

# 对应的标签:假设有两个标签
labels = [0, 1]

# 创建数据集和数据加载器
my_dataset     = MyDataset(text_data, labels)
data_loader = DataLoader(my_dataset, batch_size=2, shuffle=True, collate_fn=lambda x: x)

for batch in data_loader:
    print(batch)

 代码输出:

[(tensor([47,  9, 58, 14,  3, 13, 77, 68, 58, 75, 30,  7]), tensor(0.)), (tensor([84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34,
        66, 73, 95, 87,  7]), tensor(1.))]

4.3 定义模型 

class EmbeddingBagModel(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super(EmbeddingBagModel, self).__init__()
        self.embedding_bag = nn.EmbeddingBag(vocab_size, embed_dim, mode='mean')
        self.fc = nn.Linear(embed_dim, 1)  # 假设我们做一个二分类任务

    def forward(self, text, offsets):
        print("embedding_bag输入文本是:",text)
        print("embedding_bag输入文本shape:",text.shape)
        embedded = self.embedding_bag(text, offsets)
        print("embedding_bag输出文本shape:",embedded.shape)
        return self.fc(embedded)

4.4 训练模型 

# 示例词典大小和嵌入维度
vocab_size = vocab_size
embed_dim  = 6

# 创建模型实例
model = EmbeddingBagModel(vocab_size, embed_dim)

# 定义一个简单的损失函数和优化器
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练模型
for epoch in range(1):  # 训练1个epoch
    for batch in data_loader:
        # 将批处理的数据展平并计算偏移量
        texts, labels = zip(*batch)

        offsets = [0] + [len(text) for text in texts[:-1]]
        offsets = torch.tensor(offsets).cumsum(dim=0)
        texts   = torch.cat(texts)
        labels  = torch.tensor(labels).unsqueeze(1)

        # 前向传播
        outputs = model(texts, offsets)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f'Epoch {epoch+1}, Loss: {loss.item()}')

代码输出: 

embedding_bag输入文本是: tensor([84, 46, 25, 57, 89, 95, 87, 58, 48, 46, 69, 78, 28, 49, 20, 54, 37, 34,
        66, 73, 95, 87,  7, 47,  9, 58, 14,  3, 13, 77, 68, 58, 75, 30,  7])
embedding_bag输入文本shape: torch.Size([35])
embedding_bag输出文本shape: torch.Size([2, 6])
Epoch 1, Loss: 0.7018395662307739
;