本以为 PyTorch 的文章要写两个月,结果发现 PyTorch 真的太轻了,写了不到一个月就写完了。本篇为完结篇,对一些零星的功能进行总结。
1. torch.nn.utils
里的一些功能
1.1 梯度剪枝
在 PyTorch 折桂 8:torch.nn.init 里提到过梯度爆炸的问题,当时我们的解决方法是对神经元权重的初始化进行控制,这里再介绍一个简单粗暴的方式:直接限制权重的上限。对应导数超过上限的权重,将其导数重置为上限值。
torch.nn.utils.clip_grad_value_(parameters, clip_value)
parameters
:需要修改的权重clip_value
:权重的上限1
2
3
4
5
6
7
810.), requires_grad=True) weight = torch.tensor((
# 对大于 0 的值,ReLU 的处理结果为 1. relu = nn.ReLU()
out = relu(weight)
# 反向传播 weight.backward()
0.5) # 梯度从 1.0 被限制为 0.5 nn.utils.clip_grad_value_(weight,
print(weight.grad)
tensor(0.5000)
可以看到,clip_grad_value_
为 inplace 操作,需要在 Tensor.backward
与 optimizer.step
之间使用。
除此以外,还有一个 torch.nn.utils.clip_grad_norm_(parameters, max_norm, norm_type=2)
函数,将若干个权重修改为服从正态分布的范围,这里不多赘述。
1.2 PyTorch 对可变长度序列的处理
在 NLP 任务中,我们经常要处理不定长度的序列。PyTorch 提供了将不定长度的序列进行打包的函数。
torch.nn.utils.rnn.pad\_sequence(sequences, batch\_first=False, padding\_value=0)
将不定长序列补全至最长序列的长度。接受三个参数:
sequences
:接受补全的序列;batch\_first
:批是否为第一个维度,默认为 False;padding\_value
:填充的值,默认为 0。1
2
3
4
525, 300) a = torch.ones(
22, 300) b = torch.ones(
15, 300) c = torch.ones(
torch.nn.utils.rnn.pad_sequence([a, b, c]).size()
torch.Size([25, 3, 300])torch.nn.utils.rnn.pack\_sequence(sequences, enforce\_sorted=True)
将序列直接打包成一个 PackedSequence
实例。有两个参数:
sequences
:要打包的序列;enforce\_sorted
:若为 True,则将序列以长度的降序进行排列,默认为 True。1
2
3
4
51,2,3]) a = torch.tensor([
4,5]) b = torch.tensor([
6]) c = torch.tensor([
False) torch.nn.utils.rnn.pack_sequence([a, b, c], enforce_sorted=
PackedSequence(data=tensor([1, 4, 6, 2, 5, 3]), batch_sizes=tensor([3, 2, 1]), sorted_indices=tensor([0, 1, 2]), unsorted_indices=tensor([0, 1, 2]))
除了 padding 与裁剪将所有序列统一为定长以外,PyTorch 还提供了两个函数将不定长度序列打包和解包。
torch.nn.utils.rnn.pack\_padded\_sequence(input, lengths, batch\_first=False, enforce\_sorted=True)
将一个不定长度的序列进行打包,返回一个 PackedSequence
实例。有 4 个参数:
input
:一个T x B x *
尺寸的序列,T
为序列中最长的序列的长度,B
为 batch 的数量,*
为每个序列的维度(可以为 0);lengths
:单个序列长度的列表;batch\_first
:是否以 batch 为第一个维度;enforce\_sorted
:是否对序列以每个序列的长度进行降序排序。1
2
3
4
5
61,2,0], [3,0,0], [4,5,6]]) seq = torch.tensor([[
2, 1, 3] lens = [
True, enforce_sorted=False) packed = torch.nn.utils.rnn.pack_padded_sequence(seq, lens, batch_first=
packed
PackedSequence(data=tensor([4, 1, 3, 5, 2, 6]), batch_sizes=tensor([3, 2, 1]),
sorted_indices=tensor([2, 0, 1]), unsorted_indices=tensor([1, 2, 0]))
还有一个与之相反的解包函数:
torch.nn.utils.rnn.pad\_packed\_sequence(sequence, batch\_first=False, padding\_value=0.0, total\_length=None)
这个函数接受一个 PackedSequence
实例,有 4 个参数:
sequence
:需要进行解包的序列;batch\_first
:是否以 batch 为第一维;padding\_value
:解包后填充的值,默认为 0;total\_length
:将所有序列填充至total\_length
的长度。如果这个值小于最长序列的长度,将抛出异常。
这个函数返回两个张量,解包后的序列和原始序列的长度。
1 | True) seq_unpacked, lens_unpacked = torch.nn.utils.rnn.pad_packed_sequence(packed, batch_first= |
2. GPU 的使用
2.1 检查系统内 GPU 的状态
可以使用 nvidia-smi
命令。在 Jupyter Notebook 里要在命令前加上 !
。下面为 Google Colab 上的 GPU 状态:
1 | !nvidia-smi |
2.2 在 PyTorch 内检查可用 GPU
可以使用 torch.cuda.is\_available()
。
1 | torch.cuda.is_available() |
2.3 将神经网络与张量在 CPU 与 GPU 之间移动
有两种方法:
1 | # 第一种方法 |
以上仅为一张 GPU 的情况。GPU 上仅可以进行运算,其它操作需要将张量移动到 CPU 上完成。
3. 模型的保存与读取
保存的模型如果在 GPU 上,需要先转移到 CPU 上。保存模型既可以保存整个模型,也可以只保存模型的参数权重。
1 | # 保存整个模型 |
欢迎关注我的微信公众号“花解语 NLP”: