当我们使用 PyTorch 构建神经网络时,经常使用到一些内置的网络层。本篇文章主要介绍下列层的使用:
-
线性层(Linear)
-
词嵌入层(Embedding)
-
循环网络层(RNN、GRU、LSTM)
-
线性层 {#title-0} =================
torch.nn.Linear(in_features, out_features, bias=True, device=None)
- in_features 表示输入数据特征维度
- out_features 表示输出数据特征的维度
- bias 表示使用计算偏置
- device 表示指定计算设备
我们可以通过以下代码访问线性层的参数、偏置:
linear = nn.Linear(10, 20)
# 权重
print(linear.weight)
# 偏置
print(linear.bias)
PyTorch 中线性层的 weight 和 bias 使用范围**(-sqrt(1/in_features), sqrt(1/in_features)**)的均匀分布进行初始化。
线性层的计算公式如下:
import torch.nn as nn
import torch
def test():
# 输入数据
inputs = torch.randn(1, 10)
# 线性层计算输出
linear = nn.Linear(10, 20)
outputs = linear(inputs)
print(outputs)
# 手动计算输出
outputs = inputs @ linear.weight.data.T + linear.bias.data
print(outputs)
if __name__ == '__main__':
test()
程序输出结果:
tensor([[ 0.6065, 0.8099, -0.2903, 0.5085, 0.9268, -0.2801, -0.3360, 0.2642,
-0.6695, 0.1063, 0.8341, -0.3041, 0.0055, 0.9461, -0.0116, 0.0872,
0.3282, -0.7090, -0.9739, -0.0739]], grad_fn=<AddmmBackward>)
tensor([[ 0.6065, 0.8099, -0.2903, 0.5085, 0.9268, -0.2801, -0.3360, 0.2642,
-0.6695, 0.1063, 0.8341, -0.3041, 0.0055, 0.9461, -0.0116, 0.0872,
0.3282, -0.7090, -0.9739, -0.0739]])
我们发现输出结果是一样的。
- 词嵌入层 {#title-1} ==================
torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, device=None)
- num_embeddings 表示词表中词的数量
- embedding_dim 表示词嵌入的维度
- padding_idx 表示填充词在词表中的序号,默认 None 表示不进行填充,稍后我们了解下该参数的作用
nn.Embedding 层的初始权重默认使用从均值为 0、标准差为 1 的标准正态分布中取值初始化,其并不是固定不变的,而是随着正向计算,反向传播会进行更新。
import torch
import torch.nn as nn
def test():
# num_embeddings 表示词表共有 10 个单词
# embedding_dim 表示每个词有 3 维度
embedding = nn.Embedding(num_embeddings=10, embedding_dim=3, padding_idx=4)
# 词嵌入矩阵
print(embedding.weight.data)
print('-' * 50)
# 句子进行词嵌入
sentence = torch.tensor([[1, 2, 3], [6, 7, 8]])
sentence_embedding = embedding(sentence)
print(sentence_embedding.data)
print('-' * 50)
sentence = torch.tensor([[1, 1, 4, 4], [6, 7, 8, 9]])
sentence_embedding = embedding(sentence)
print(sentence_embedding.data)
if __name__ == '__main__':
test()
程序输出结果:
tensor([[ 2.7255, 1.1158, 0.9272],
[-0.3430, 1.3805, 0.8425],
[ 0.1564, -0.9834, -0.5991],
[-0.9682, 0.0776, -2.0535],
[ 0.0000, 0.0000, 0.0000],
[-0.5484, -0.4215, 0.1984],
[ 0.1445, -0.4071, 1.0063],
[ 0.8116, 0.6240, -0.1881],
[ 0.4918, -0.8921, -0.1006],
[-0.2029, 1.5831, 0.5203]])
--------------------------------------------------
tensor([[[-0.3430, 1.3805, 0.8425],
[ 0.1564, -0.9834, -0.5991],
[-0.9682, 0.0776, -2.0535]],
[[ 0.1445, -0.4071, 1.0063],
[ 0.8116, 0.6240, -0.1881],
[ 0.4918, -0.8921, -0.1006]]])
--------------------------------------------------
tensor([[[-0.3430, 1.3805, 0.8425],
[-0.3430, 1.3805, 0.8425],
[ 0.0000, 0.0000, 0.0000],
[ 0.0000, 0.0000, 0.0000]],
[[ 0.1445, -0.4071, 1.0063],
[ 0.8116, 0.6240, -0.1881],
[ 0.4918, -0.8921, -0.1006],
[-0.2029, 1.5831, 0.5203]]])
我们发现,当 padding_idx 设置为 4 时,embedding.weight.data 第 4 下标位置就被设置为 0,那么当我们的 sentence 中句子长度不一,需要对较短的句子进行 padding 时,就可以用 4 来代替了。同理的,默认 padding_idx 的值为 0,所以在不修改其默认值的情况下,可以使用 0 来 padding 较短的 sentence。