10个你一定要知道的Pytorch特性

简介: 10个你一定要知道的Pytorch特性

目录


1.DatasetFolder


2.尽量少用.todevicedevice,用zeros\_like/ones\_like之类的代替


3.Register Buffer ( nn.Module.register_buffer)


4.Built-in Identity()


5.Pairwise distances: torch.cdist


6.Cosine similarity: F.cosine_similarity


7.归一化向量: F.normalize


8.线性层 + 分块技巧 (torch.chunk)


9.Masked select (torch.masked_select)


10.使用 torch.where来对tensors加条件


1.DatasetFolder

当学习PyTorch时,人们首先要做的事情之一是实现自己的某种Dataset 。这是一个低级错误,没有必要浪费时间写这样的东西。通常,数据集要么是数据列表(或者是numpy数组),要么磁盘上的文件。所以,把数据在磁盘上组织好,要比写一个自定义的Dataset来加载某种奇怪的格式更好。分类器最常见的数据格式之一,是有一个带有子文件夹的目录,子文件夹表示类,子文件夹中的文件表示样本,如下所示:

folder/class_0/file1.txt
folder/class_0/file2.txt
folder/class_0/...
folder/class_1/file3.txt
folder/class_1/file4.txt
folder/class_2/file5.txt
folder/class_2/...

有一个内置的方式来加载这类数据集,不管你的数据是图像,文本文件或其他什么,只要使用'DatasetFolder就可以了。令人惊讶的是,这个类是torchvision包的一部分,而不是核心PyTorch。这个类非常全面,你可以从文件夹中过滤文件,使用自定义代码加载它们,并动态转换原始文件。例子:

from torchvision.datasets import DatasetFolder
from pathlib import Path
# I have text files in this folder
ds = DatasetFolder("/Users/marcin/Dev/tmp/my_text_dataset", 
    loader=lambda path: Path(path).read_text(),
    extensions=(".txt",), #only load .txt files
    transform=lambda text: text[:100], # only take first 100 characters
)
# Everything you need is already there
len(ds), ds.classes, ds.class_to_idx
(20, ['novels', 'thrillers'], {'novels': 0, 'thrillers': 1})

如果你在处理图像,还有一个torchvision.datasets.ImageFolder类,它基于DatasetLoader,它被预先配置为加载图像。

2.尽量少用.to\(device\),用zeros\_like/ones\_like之类的代替

       很多GitHub仓库的PyTorch代码,几乎在每个repo中都有许多*.to(device)行,它们将数据从CPU或GPU转移到其他地方。这样的语句通常会出现在大量的repos或初学者教程中。个人建议尽可能少地实现这类操作,并依赖内置的PyTorch功能自动实现这类操作。到处使用.to(device)通常会导致性能下降,还会出现异常:  

Expected object of device type cuda but got device type cpu

       显然,有些情况下你无法回避它,但大多数情况(如果不是全部)都在这里。其中一种情况是初始化一个全0或全1的张量,这在深度神经网络计算损失的的时候是经常发生的,模型的输出已经在cuda上了,你需要另外的tensor也是在cuda上,这时,你可以使用*_like操作符:

my_output # on any device, if it's cuda then my_zeros will also be on cuda
my_zeros = torch.zeros_like(my_output_from_model)

在内部,PyTorch所做的是调用以下操作:

my_zeros = torch.zeros(my_output.size(), dtype=my_output.dtype, layout=my_output.layout, device=my_output.device)

所以所有的设置都是正确的,这样就减少了代码中出现错误的概率。类似的操作包括:

torch.zeros_like()
torch.ones_like()
torch.rand_like()
torch.randn_like()
torch.randint_like()
torch.empty_like()
torch.full_like()

3.Register Buffer ( nn.Module.register_buffer)

       这将是建议不要到处使用 .to(device) 的下一步。有时,你的模型或损失函数需要有预先设置的参数,并在调用forward时使用,例如,它可以是一个“权重”参数,它可以缩放损失或一些固定张量,它不会改变,但每次都使用。对于这种情况,请使用nn.Module.register_buffer 方法,它告诉PyTorch将传递给它的值存储在模块中,并将这些值随模块一起移动。如果你初始化你的模块,然后将它移动到GPU,这些值也会自动移动。此外,如果你保存模块的状态,buffers也会被保存!


一旦注册,这些值就可以在forward函数中访问,就像其他模块的属性一样。

from torch import nn
import torch
class ModuleWithCustomValues(nn.Module):
    def __init__(self, weights, alpha):
        super().__init__()
        self.register_buffer("weights", torch.tensor(weights))
        self.register_buffer("alpha", torch.tensor(alpha))
    def forward(self, x):
        return x * self.weights + self.alpha
m = ModuleWithCustomValues(
    weights=[1.0, 2.0], alpha=1e-4
)
m(torch.tensor([1.23, 4.56]))
tensor([1.2301, 9.1201])

4.Built-in Identity()

       有时候,当你使用迁移学习时,你需要用1:1的映射替换一些层,可以用nn.Module来实现这个目的,只返回输入值。PyTorch内置了这个类。例子,你想要在分类层之前从一个预训练过的ResNet50获取图像表示。以下是如何做到这一点:

from torchvision.models import resnet50
model = resnet50(pretrained=True)
model.fc = nn.Identity()
last_layer_output = model(torch.rand((1, 3, 224, 224)))
last_layer_output.shape
torch.Size([1, 2048])

5.Pairwise distances: torch.cdist

       下次当你遇到计算两个张量之间的欧几里得距离(或者一般来说:p范数)的问题时,请记住torch.cdist。它确实做到了这一点,并且在使用欧几里得距离时还自动使用矩阵乘法,从而提高了性能。

points1 = torch.tensor([[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]])
points2 = torch.tensor([[0.0, 0.0], [-1.0, -1.0], [-2.0, -2.0], [-3.0, -3.0]]) # batches don't have to be equal
torch.cdist(points1, points2, p=2.0)
tensor([[0.0000, 1.4142, 2.8284, 4.2426],
        [1.4142, 2.8284, 4.2426, 5.6569],
        [2.8284, 4.2426, 5.6569, 7.0711]])

没有矩阵乘法或有矩阵乘法的性能,在我的机器上使用mm时,速度快了2倍以上。

%%timeit
points1 = torch.rand((512, 2))
points2 = torch.rand((512, 2))
torch.cdist(points1, points2, p=2.0, compute_mode="donot_use_mm_for_euclid_dist")

867µs±142µs per loop (mean±std. dev. of 7 run, 1000 loop each)

%%timeit
points1 = torch.rand((512, 2))
points2 = torch.rand((512, 2))
torch.cdist(points1, points2, p=2.0)

6.Cosine similarity: F.cosine_similarity

       与上一点相同,计算欧几里得距离并不总是你需要的东西。当处理向量时,通常余弦相似度是选择的度量。PyTorch也有一个内置的余弦相似度实现。

import torch.nn.functional as F
vector1 = torch.tensor([0.0, 1.0])
vector2 = torch.tensor([0.05, 1.0])
print(F.cosine_similarity(vector1, vector2, dim=0))
vector3 = torch.tensor([0.0, -1.0])
print(F.cosine_similarity(vector1, vector3, dim=0))
tensor(0.9988)
tensor(-1.)

PyTorch中批量计算余弦距离

import torch.nn.functional as F
batch_of_vectors = torch.rand((4, 64))
similarity_matrix = F.cosine_similarity(batch_of_vectors.unsqueeze(1), batch_of_vectors.unsqueeze(0), dim=2)
similarity_matrix
tensor([[1.0000, 0.6922, 0.6480, 0.6789],
        [0.6922, 1.0000, 0.7143, 0.7172],
        [0.6480, 0.7143, 1.0000, 0.7312],
        [0.6789, 0.7172, 0.7312, 1.0000]])

7.归一化向量: F.normalize

       最后一点仍然与向量和距离有松散的联系,那就是归一化:通常是通过改变向量的大小来提高计算的稳定性。最常用的归一化是L2,可以在PyTorch中按如下方式应用:

vector = torch.tensor([99.0, -512.0, 123.0, 0.1, 6.66])
normalized_vector = F.normalize(vector, p=2.0, dim=0)
normalized_vector
tensor([ 1.8476e-01, -9.5552e-01,  2.2955e-01,  1.8662e-04,  1.2429e-02])

在PyTorch中执行归一化的旧方法是:

vector = torch.tensor([99.0, -512.0, 123.0, 0.1, 6.66])
normalized_vector = vector / torch.norm(vector, p=2.0)
normalized_vector
tensor([ 1.8476e-01, -9.5552e-01,  2.2955e-01,  1.8662e-04,  1.2429e-02])

在PyTorch中批量进行L2归一化

batch_of_vectors = torch.rand((4, 64))
normalized_batch_of_vectors = F.normalize(batch_of_vectors, p=2.0, dim=1)
normalized_batch_of_vectors.shape, torch.norm(normalized_batch_of_vectors, dim=1) # all vectors will have length of 1.0
(torch.Size([4, 64]), tensor([1.0000, 1.0000, 1.0000, 1.0000]))

8.线性层 + 分块技巧 (torch.chunk)

     假设你想把你的输入映射到N个不同的线性投影中。你可以通过创建N个nn.Linear来做到这一点。或者你也可以创建一个单一的线性层,做一个向前传递,然后将输出分成N块。这种方法通常会带来更高的性能,所以这是一个值得记住的技巧。

d = 1024
batch = torch.rand((8, d))
layers = nn.Linear(d, 128, bias=False), nn.Linear(d, 128, bias=False), nn.Linear(d, 128, bias=False)
one_layer = nn.Linear(d, 128 * 3, bias=False)
%%timeit
o1 = layers[0](batch)
o2 = layers[1](batch)
o3 = layers[2](batch)


289 µs ± 30.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%%timeit
o1, o2, o3 = torch.chunk(one_layer(batch), 3, dim=1)

202 µs ± 8.09 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

9.Masked select (torch.masked_select)

       有时你只需要对输入张量的一部分进行计算。给你一个例子:你想计算的损失只在满足某些条件的张量上。为了做到这一点,你可以使用torch.masked_select,注意,当需要梯度时也可以使用这个操作。

data = torch.rand((3, 3)).requires_grad_()
print(data)
mask = data > data.mean()
print(mask)
torch.masked_select(data, mask)
tensor([[0.0582, 0.7170, 0.7713],
        [0.9458, 0.2597, 0.6711],
        [0.2828, 0.2232, 0.1981]], requires_grad=True)
tensor([[False,  True,  True],
        [ True, False,  True],
        [False, False, False]])
tensor([0.7170, 0.7713, 0.9458, 0.6711], grad_fn=<MaskedSelectBackward>)

直接在tensor上应用mask

类似的行为可以通过使用mask作为输入张量的 “indexer”来实现。

data[mask]
tensor([0.7170, 0.7713, 0.9458, 0.6711], grad_fn=<IndexBackward>)

有时,一个理想的解决方案是用0填充mask中所有的False值,可以这样做:

data * mask
tensor([[0.0000, 0.7170, 0.7713],
        [0.9458, 0.0000, 0.6711],
        [0.0000, 0.0000, 0.0000]], grad_fn=<MulBackward0>)

10.使用 torch.where来对tensors加条件

当你想把两个张量结合在一个条件下这个函数很有用,如果条件是真,那么从第一个张量中取元素,如果条件是假,从第二个张量中取元素。

x = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0], requires_grad=True)
y = -x
condition_or_mask = x <= 3.0
torch.where(condition_or_mask, x, y)
tensor([ 1.,  2.,  3., -4., -5.], grad_fn=<SWhereBackward>)


目录
相关文章
|
1月前
|
机器学习/深度学习 监控 PyTorch
深度学习工程实践:PyTorch Lightning与Ignite框架的技术特性对比分析
在深度学习框架的选择上,PyTorch Lightning和Ignite代表了两种不同的技术路线。本文将从技术实现的角度,深入分析这两个框架在实际应用中的差异,为开发者提供客观的技术参考。
42 7
|
5月前
|
机器学习/深度学习 PyTorch 编译器
Pytorch的编译新特性TorchDynamo的工作原理和使用示例
PyTorch的TorchDynamo是一个即时编译器,用于优化动态图执行,提高运行效率。它在运行时分析和转换代码,应用优化技术,如操作符融合,然后编译成高效机器码。通过一个包含特征工程、超参数调整、交叉验证的合成数据集示例,展示了TorchDynamo如何减少训练时间并提高模型性能。它易于集成,只需对现有PyTorch代码进行小改动,即可利用其性能提升。TorchDynamo的优化包括动态捕获计算图、应用优化和编译,适用于实时应用和需要快速响应的场景。
80 11
|
7月前
|
机器学习/深度学习 数据可视化 PyTorch
TensorFlow与PyTorch框架的深入对比:特性、优势与应用场景
【5月更文挑战第4天】本文对比了深度学习主流框架TensorFlow和PyTorch的特性、优势及应用场景。TensorFlow以其静态计算图、高性能及TensorBoard可视化工具适合大规模数据处理和复杂模型,但学习曲线较陡峭。PyTorch则以动态计算图、易用性和灵活性见长,便于研究和原型开发,但在性能和部署上有局限。选择框架应根据具体需求和场景。
|
7月前
|
缓存 PyTorch 编译器
使用PyTorch II的新特性加快LLM推理速度
Pytorch团队提出了一种纯粹通过PyTorch新特性在的自下而上的优化LLM方法,包括:
249 1
|
机器学习/深度学习 并行计算 PyTorch
Pytorch 2.0来了!来看看新特性怎么应用到自己的代码里
Pytorch 2.0来了!来看看新特性怎么应用到自己的代码里
|
2月前
|
算法 PyTorch 算法框架/工具
Pytorch学习笔记(九):Pytorch模型的FLOPs、模型参数量等信息输出(torchstat、thop、ptflops、torchsummary)
本文介绍了如何使用torchstat、thop、ptflops和torchsummary等工具来计算Pytorch模型的FLOPs、模型参数量等信息。
300 2
|
2月前
|
机器学习/深度学习 自然语言处理 监控
利用 PyTorch Lightning 搭建一个文本分类模型
利用 PyTorch Lightning 搭建一个文本分类模型
63 8
利用 PyTorch Lightning 搭建一个文本分类模型
|
2月前
|
机器学习/深度学习 自然语言处理 数据建模
三种Transformer模型中的注意力机制介绍及Pytorch实现:从自注意力到因果自注意力
本文深入探讨了Transformer模型中的三种关键注意力机制:自注意力、交叉注意力和因果自注意力,这些机制是GPT-4、Llama等大型语言模型的核心。文章不仅讲解了理论概念,还通过Python和PyTorch从零开始实现这些机制,帮助读者深入理解其内部工作原理。自注意力机制通过整合上下文信息增强了输入嵌入,多头注意力则通过多个并行的注意力头捕捉不同类型的依赖关系。交叉注意力则允许模型在两个不同输入序列间传递信息,适用于机器翻译和图像描述等任务。因果自注意力确保模型在生成文本时仅考虑先前的上下文,适用于解码器风格的模型。通过本文的详细解析和代码实现,读者可以全面掌握这些机制的应用潜力。
96 3
三种Transformer模型中的注意力机制介绍及Pytorch实现:从自注意力到因果自注意力
|
3月前
|
机器学习/深度学习 PyTorch 调度
在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型
在深度学习中,学习率作为关键超参数对模型收敛速度和性能至关重要。传统方法采用统一学习率,但研究表明为不同层设置差异化学习率能显著提升性能。本文探讨了这一策略的理论基础及PyTorch实现方法,包括模型定义、参数分组、优化器配置及训练流程。通过示例展示了如何为ResNet18设置不同层的学习率,并介绍了渐进式解冻和层适应学习率等高级技巧,帮助研究者更好地优化模型训练。
182 4
在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型