目录
前言
🍨 本文为[🔗365天深度学习训练营]中的学习记录博客
🍖 原作者:[K同学啊]
说在前面
本周任务:了解Embedding和Embeddingbag,并加载.txt文件完成词嵌入
我的环境:Python3.8、Pycharm2020、torch1.12.1+cu113
数据来源:[K同学啊]
一、基本概念
1.1 什么是词嵌入?
词嵌入是一种用于自然语言处理(NLP)的技术,用于将单词表示为数字,以便计算机可以处理它们,即一种把文本转化为数值输入到计算机中的方法。
将文本转换为字典序列、one-hot编码就是最早起的词嵌入方法,Embedding和EmbeddingBag是PyTorch中用来处理文本数据中词嵌入(word embedding)的工具,它们将离散的词汇映射到低维的连续向量空间中,使得词汇之间的语义关系能够在向量空间中得到体现。
1.2 Embedding详解
Embedding是PyTorch中最基本的词嵌入操作,TensorFlow中也有相同的函数,功能是一样的,它将每个离散的词汇映射到一个低维的连续向量空间中,并且保持了词汇之间的语义关系,在PyTorch中,Embedding的输入是一个整数张量,每个整数都代表着一个词汇的索引,输出是一个浮点型的张量,每个浮点数都代表着对应词汇的词嵌入向量。
嵌入层使用随机权重初始化,并将学习数据集中所有词的嵌入,它是一个灵活的层,可以以各种方式使用,例如
- 它可以用作深度学习模型的一部分,其中嵌入与模型本身一起被学习
- 它可以用于加载训练好的词嵌入模型
嵌入层被定义为网络的第一个隐藏层
函数原型: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)
官方API:Embedding — PyTorch 2.4 documentation
常见参数:
num_embeddings:词汇表大小,即最大整数index+1
embedding_dim:词向量的维度
示例(用Embedding将两个句子转换为词嵌入向量)
本例中,定义了一个简单的词嵌入模型,它将大小为12的词汇表中的每个词汇映射到一个4维的向量空间中,然后输入两个句子,分别是[1,5,8]和[2,4],每个数字代表着词汇表中的一个词汇的索引,随后将两个句子通过Embedding转换为词嵌入向量,并输出结果,可以看到,每个句子中的每个词汇都被映射成一个4维的向量。
import torch
import torch.nn as nn
vocab_size = 12 #词汇表大小
embedding_dim = 4 #嵌入向量的维度
#创建一个Embedding层
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)
输出结果:
tensor([[-7.2832e-01, 1.0047e+00, -8.5490e-02, 1.9378e+00],
[ 5.0610e-01, -1.8192e-01, -2.0392e+00, 6.3155e-01],
[ 1.9518e+00, 4.9482e-01, -1.0382e+00, -1.1013e-03]],
grad_fn=<EmbeddingBackward0>)
tensor([[-0.3932, -0.2167, -0.0483, -0.6653],
[ 0.2050, -0.4289, 0.7200, 0.1471]], grad_fn=<EmbeddingBackward0>)进程已结束,退出代码 0
1.3 EmbeddingBag详解
EmbeddingBag是在Embedding基础上进一步优化的工具,它可以直接处理不定长的句子,并且可以计算句子中所有词汇的词嵌入向量的均值或总和,在Pytorch中,EmbeddingBag的输入是一个整数张量和一个偏移量张量,每个整数都代表着一个词汇的索引,偏移量则表示句子中每个词汇的位置,输出是一个浮点型的张量,每个浮点数都代表着对应句子的词嵌入向量的均值或总和。
示例(在1.2示例的基础上将两个句子转换为词嵌入向量并计算它们的均值)
import torch
import torch.nn as nn
vocab_size = 12 #词汇表大小
embedding_dim = 4 #嵌入向量的维度
#创建一个Embedding层
embedding_bag = nn.EmbeddingBag(vocab_size, embedding_dim)
#假设有一个包含两个单词索引的输入序列
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)
#使用Embedding层将输入序列转换为词嵌入
embedded_bag = embedding_bag(input_sequences,offsets)
print(embedded_bag)
输出结果:
tensor([[ 0.6257, -1.3751, -0.7692, -0.7584],
[ 0.5431, -0.4553, -0.1833, 0.6991]], grad_fn=<EmbeddingBagBackward0>)进程已结束,退出代码 0
PS: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模式,因为它可以捕捉到每个序列的所有信息,不会丢失任何关键信息,在实际应用中,可以根据具体情况灵活选择汇总模式,以获得最佳效果。
二、练习任务
2.1任务要求
.txt文件内容如下,加载.txt文件,并使用Embedding和Embeddingbag完成词嵌入
2.2 实现代码
import torch
import jieba
# 确认打开的文本文件路径和文件名
file_name = "任务文件.txt"
# 从文件中读取文本行,并替代预定义的Sentences
with open(file_name, "r", encoding="utf-8") as file:
context = file.read()
sentences = context.split()
print("文本分句: \n", sentences) # 打印核对结果
# 使用jieba.cut()函数逐句进行分词,结果输出为一个列表
tokenized_texts = [list(jieba.lcut(sentence)) for sentence in sentences]
print("分词结果:\n", tokenized_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
print("词汇表: \n", word_index) # 打印核对结果
# 将文本转化为整数序列
sequences = [[word_index[word] for word in text] for text in tokenized_texts]
print("文本序列: \n",sequences) # 打印核对结果
# 获取词汇表大小, 并+1
vocab_size = len(word_index) + 1
# 创建一个EmbeddingBag层
embedding_dim = 100 # 定义嵌入向量的维度
embedding_bag = torch.nn.EmbeddingBag(vocab_size, embedding_dim, mode="mean")
# 将多个输入序列拼接在一起,并创建一个偏移量张量
# 首先需要创建空张量和空列表
input = torch.tensor([], dtype=torch.long)
offset = []
# 逐句处理,进行张量拼接、向列表中添加偏移量数值
for sequence in sequences:
offset.append(len(input))
input = torch.cat([input, torch.tensor(sequence, dtype=torch.long)])
# 将列表形式的偏移量转换为张量形式,用于embedding_bag()函数的输入
offset = torch.tensor(offset, dtype=torch.long)
# 检查序列张量拼接和索引生成结果
print("-"*80)
print(offset)
print(input)
print("-"*80)
# 使用Embedding层将输入序列转换为词嵌入
embedded_bag = embedding_bag(input, offset)
# 打印输出结果
print("词嵌入结果: \n", embedded_bag)
输出结果:
文本分句:
['比较直观的编码方式是采用上面提到的字典序列。例如,对于一个有三个类别的问题,可以用1、2和3分别表示这三个类别。但是,这种编码方式存在一个问题,就是模型可能会错误地认为不同类别之间存在一些顺序或距离关系,而实际上这些关系可能是不存在的或者不具有实际意义的。', '为了避免这种问题,引入了one-hot编码(也称独热编码)。one-hot编码的基本思想是将每个类别映射到一个向量,其中只有一个元素的值为1,其余元素的值为0。这样,每个类别之间就是相互独立的,不存在顺序或距离关系。例如,对于三个类别的情况,可以使用如下的one-hot编码:']
Loading model cost 0.342 seconds.
Prefix dict has been built successfully.
分词结果:
[['比较', '直观', '的', '编码方式', '是', '采用', '上面', '提到', '的', '字典', '序列', '。', '例如', ',', '对于', '一个', '有', '三个', '类别', '的', '问题', ',', '可以', '用', '1', '、', '2', '和', '3', '分别', '表示', '这', '三个', '类别', '。', '但是', ',', '这种', '编码方式', '存在', '一个', '问题', ',', '就是', '模型', '可能', '会', '错误', '地', '认为', '不同', '类别', '之间', '存在', '一些', '顺序', '或', '距离', '关系', ',', '而', '实际上', '这些', '关系', '可能', '是', '不', '存在', '的', '或者', '不', '具有', '实际意义', '的', '。'], ['为了', '避免', '这种', '问题', ',', '引入', '了', 'one', '-', 'hot', '编码', '(', '也', '称', '独热', '编码', ')', '。', 'one', '-', 'hot', '编码', '的', '基本', '思想', '是', '将', '每个', '类别', '映射', '到', '一个', '向量', ',', '其中', '只有', '一个', '元素', '的', '值', '为', '1', ',', '其余', '元素', '的', '值', '为', '0', '。', '这样', ',', '每个', '类别', '之间', '就是', '相互', '独立', '的', ',', '不', '存在', '顺序', '或', '距离', '关系', '。', '例如', ',', '对于', '三个', '类别', '的', '情况', ',', '可以', '使用', '如下', '的', 'one', '-', 'hot', '编码', ':']]
词汇表:
{'有': 0, '。': 1, '编码方式': 2, '直观': 3, '顺序': 4, '模型': 5, ',': 6, '的': 7, '称': 8, '关系': 9, '分别': 10, '问题': 11, '或': 12, '或者': 13, '不': 14, '其中': 15, '序列': 16, '相互': 17, '其余': 18, '独立': 19, '三个': 20, '地': 21, '-': 22, '使用': 23, '具有': 24, '存在': 25, '为了': 26, '向量': 27, '情况': 28, '独热': 29, '避免': 30, '可以': 31, '一个': 32, '值': 33, '为': 34, '2': 35, '一些': 36, '、': 37, '编码': 38, '字典': 39, '思想': 40, '错误': 41, '用': 42, '到': 43, '类别': 44, '比较': 45, '这种': 46, '不同': 47, '0': 48, '3': 49, '元素': 50, '例如': 51, '将': 52, '这些': 53, '和': 54, '就是': 55, '距离': 56, '映射': 57, '可能': 58, '表示': 59, '基本': 60, '实际意义': 61, '会': 62, '提到': 63, ')': 64, '(': 65, '实际上': 66, '之间': 67, '采用': 68, '引入': 69, '但是': 70, 'hot': 71, '上面': 72, '认为': 73, '每个': 74, '这样': 75, '只有': 76, '1': 77, 'one': 78, '而': 79, ':': 80, '如下': 81, '对于': 82, '这': 83, '是': 84, '了': 85, '也': 86}
文本序列:
[[45, 3, 7, 2, 84, 68, 72, 63, 7, 39, 16, 1, 51, 6, 82, 32, 0, 20, 44, 7, 11, 6, 31, 42, 77, 37, 35, 54, 49, 10, 59, 83, 20, 44, 1, 70, 6, 46, 2, 25, 32, 11, 6, 55, 5, 58, 62, 41, 21, 73, 47, 44, 67, 25, 36, 4, 12, 56, 9, 6, 79, 66, 53, 9, 58, 84, 14, 25, 7, 13, 14, 24, 61, 7, 1], [26, 30, 46, 11, 6, 69, 85, 78, 22, 71, 38, 65, 86, 8, 29, 38, 64, 1, 78, 22, 71, 38, 7, 60, 40, 84, 52, 74, 44, 57, 43, 32, 27, 6, 15, 76, 32, 50, 7, 33, 34, 77, 6, 18, 50, 7, 33, 34, 48, 1, 75, 6, 74, 44, 67, 55, 17, 19, 7, 6, 14, 25, 4, 12, 56, 9, 1, 51, 6, 82, 20, 44, 7, 28, 6, 31, 23, 81, 7, 78, 22, 71, 38, 80]]
--------------------------------------------------------------------------------
tensor([ 0, 75])
tensor([45, 3, 7, 2, 84, 68, 72, 63, 7, 39, 16, 1, 51, 6, 82, 32, 0, 20,
44, 7, 11, 6, 31, 42, 77, 37, 35, 54, 49, 10, 59, 83, 20, 44, 1, 70,
6, 46, 2, 25, 32, 11, 6, 55, 5, 58, 62, 41, 21, 73, 47, 44, 67, 25,
36, 4, 12, 56, 9, 6, 79, 66, 53, 9, 58, 84, 14, 25, 7, 13, 14, 24,
61, 7, 1, 26, 30, 46, 11, 6, 69, 85, 78, 22, 71, 38, 65, 86, 8, 29,
38, 64, 1, 78, 22, 71, 38, 7, 60, 40, 84, 52, 74, 44, 57, 43, 32, 27,
6, 15, 76, 32, 50, 7, 33, 34, 77, 6, 18, 50, 7, 33, 34, 48, 1, 75,
6, 74, 44, 67, 55, 17, 19, 7, 6, 14, 25, 4, 12, 56, 9, 1, 51, 6,
82, 20, 44, 7, 28, 6, 31, 23, 81, 7, 78, 22, 71, 38, 80])
--------------------------------------------------------------------------------
词嵌入结果:
tensor([[-0.0028, 0.0883, -0.1803, -0.0017, -0.0561, -0.0496, 0.0610, 0.0799,
0.0215, -0.1439, -0.3511, -0.0244, -0.1269, 0.0621, -0.1917, -0.0330,
-0.0108, -0.0143, 0.1122, 0.0086, -0.1345, -0.1837, 0.1484, 0.0031,
-0.0627, 0.0307, 0.1063, 0.1553, 0.3025, 0.2026, -0.2784, 0.0885,
0.0741, 0.1068, 0.0560, -0.1928, -0.1547, 0.2161, 0.2422, 0.2020,
0.1286, -0.0543, 0.3036, 0.0410, 0.0630, 0.2551, 0.1188, -0.0403,
-0.0871, 0.1336, 0.0515, -0.0873, -0.1294, -0.2602, 0.2367, -0.2241,
0.1964, -0.1551, -0.3029, 0.1840, 0.0770, -0.0770, -0.0226, -0.1519,
0.3536, 0.1987, -0.0469, -0.3632, 0.0950, -0.0976, 0.0756, -0.1388,
-0.0878, -0.0055, 0.0851, 0.0243, 0.0860, 0.0094, 0.0794, 0.0887,
0.0170, 0.0032, 0.0632, -0.0476, 0.0946, -0.1442, 0.1408, -0.0956,
-0.0719, -0.0244, 0.1289, 0.2144, 0.0929, 0.2216, 0.0381, 0.0060,
-0.0706, 0.0095, 0.0120, 0.0249],
[ 0.1284, 0.3078, -0.1073, -0.0893, -0.0442, 0.0480, 0.2314, 0.3570,
0.1892, -0.0687, -0.3248, 0.1977, -0.0051, 0.0833, 0.0205, -0.0424,
0.1120, 0.0081, -0.0332, 0.0675, -0.1181, -0.4904, -0.2325, 0.3382,
0.2005, -0.0265, -0.0373, 0.0288, 0.1449, 0.3063, -0.3906, 0.1438,
0.0045, 0.0732, -0.1817, -0.0577, -0.0849, 0.0446, 0.0474, 0.2678,
-0.1891, -0.0364, 0.2461, 0.1305, 0.2117, 0.1139, 0.3798, 0.1735,
0.0087, 0.1314, 0.0134, -0.0435, -0.1014, -0.1785, 0.2012, -0.0962,
0.1756, -0.0697, -0.0561, 0.3970, 0.0581, -0.0891, 0.1152, -0.0571,
0.2871, 0.1138, -0.1827, -0.2865, 0.0237, -0.0835, 0.2664, -0.1628,
-0.0767, 0.1087, -0.0632, 0.0504, -0.1810, 0.1344, 0.0688, -0.0080,
0.0405, 0.1930, 0.2549, 0.0585, 0.1745, -0.3068, 0.2549, -0.3024,
-0.0840, 0.0148, 0.1119, 0.0125, -0.2456, -0.0398, 0.1784, 0.0605,
0.1001, -0.0087, 0.0550, 0.1379]], grad_fn=<EmbeddingBagBackward0>)
总结
学习了Embedding和EmbeddingBag的使用