0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

13个你一定来看看的PyTorch特性!

深度学习自然语言处理 来源:ronghuaiyang(AI公园) 作者:MARCIN ZABŁOCKIMARCI 2021-04-19 09:08 次阅读

作者:MARCIN ZABŁOCKIMARCIN ZABŁOCKI

编译:ronghuaiyang(AI公园)

导读

PyTorch使用上的13个特性,确实非常的有用。

PyTorch在学术界和工业界的应用研究中都获得了很多关注。它是一个具有很大灵活性的深度学习框架,使用了大量的实用工具和函数来加快工作速度。PyTorch的学习曲线并不是那么陡峭,但在其中实现高效和干净的代码可能会很棘手。在使用它超过2年之后,以下是我最喜欢的PyTorch功能,我希望我一开始学习它就知道。

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)行,它们将数据从CPUGPU转移到其他地方。这样的语句通常会出现在大量的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)

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

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》)

11. 在给定的位置给张量填入值(Tensor.scatter)

这个函数的用例如下,你想用给定位置下另一个张量的值填充一个张量。一维张量更容易理解,所以我将先展示它,然后继续更高级的例子。

data = torch.tensor([1, 2, 3, 4, 5])

index = torch.tensor([0, 1])

values = torch.tensor([-1, -2, -3, -4, -5])

data.scatter(0, index, values)

tensor([-1, -2, 3, 4, 5])

上面的例子很简单,但是现在看看如果将index改为index = torch.tensor([0, 1, 4])会发生什么:

data = torch.tensor([1, 2, 3, 4, 5])

index = torch.tensor([0, 1, 4])

values = torch.tensor([-1, -2, -3, -4, -5])

data.scatter(0, index, values)

tensor([-1, -2, 3, 4, -3])

为什么最后一个值是-3,这是反直觉的,对吧?这是PyTorch scatter函数的中心思想。index变量表示data张量的第i个值应该放在values张量的哪个位置。我希望下面的简单python版的这个操作能让你更明白:

data_orig = torch.tensor([1, 2, 3, 4, 5])

index = torch.tensor([0, 1, 4])

values = torch.tensor([-1, -2, -3, -4, -5])

scattered = data_orig.scatter(0, index, values)

data = data_orig.clone()

for idx_in_values, where_to_put_the_value in enumerate(index):

what_value_to_put = values[idx_in_values]

data[where_to_put_the_value] = what_value_to_put

data, scattered

(tensor([-1, -2, 3, 4, -3]), tensor([-1, -2, 3, 4, -3]))

2D数据的PyTorch scatter例子

始终记住,index的形状与values的形状相关,而index中的值对应于data中的位置。

data = torch.zeros((4, 4)).float()

index = torch.tensor([

[0, 1],

[2, 3],

[0, 3],

[1, 2]

])

values = torch.arange(1, 9).float().view(4, 2)

values, data.scatter(1, index, values)

(tensor([[1., 2.],

[3., 4.],

[5., 6.],

[7., 8.]]),

tensor([[1., 2., 0., 0.],

[0., 0., 3., 4.],

[5., 0., 0., 6.],

[0., 7., 8., 0.]]))

12. 在网络中进行图像插值 (F.interpolate)

当我学习PyTorch时,我惊讶地发现,实际上可以在前向传递中调整图像(或任何中间张量),并保持梯度流。这种方法在使用CNN和GANs时特别有用。

# image from https://commons.wikimedia.org/wiki/File:A_female_British_Shorthair_at_the_age_of_20_months.jpg

img = Image.open(“。/cat.jpg”)

img

to_pil_image(

F.interpolate(to_tensor(img).unsqueeze(0), # batch of size 1

mode=“bilinear”,

scale_factor=2.0,

align_corners=False).squeeze(0) # remove batch dimension

看看梯度流是如何保存的:

F.interpolate(to_tensor(img).unsqueeze(0).requires_grad_(),

mode=“bicubic”,

scale_factor=2.0,

align_corners=False)

tensor([[[[0.9216, 0.9216, 0.9216, 。。., 0.8361, 0.8272, 0.8219],

[0.9214, 0.9214, 0.9214, 。。., 0.8361, 0.8272, 0.8219],

[0.9212, 0.9212, 0.9212, 。。., 0.8361, 0.8272, 0.8219],

。。.,

[0.9098, 0.9098, 0.9098, 。。., 0.3592, 0.3486, 0.3421],

[0.9098, 0.9098, 0.9098, 。。., 0.3566, 0.3463, 0.3400],

[0.9098, 0.9098, 0.9098, 。。., 0.3550, 0.3449, 0.3387]],

[[0.6627, 0.6627, 0.6627, 。。., 0.5380, 0.5292, 0.5238],

[0.6626, 0.6626, 0.6626, 。。., 0.5380, 0.5292, 0.5238],

[0.6623, 0.6623, 0.6623, 。。., 0.5380, 0.5292, 0.5238],

。。.,

[0.6196, 0.6196, 0.6196, 。。., 0.3631, 0.3525, 0.3461],

[0.6196, 0.6196, 0.6196, 。。., 0.3605, 0.3502, 0.3439],

[0.6196, 0.6196, 0.6196, 。。., 0.3589, 0.3488, 0.3426]],

[[0.4353, 0.4353, 0.4353, 。。., 0.1913, 0.1835, 0.1787],

[0.4352, 0.4352, 0.4352, 。。., 0.1913, 0.1835, 0.1787],

[0.4349, 0.4349, 0.4349, 。。., 0.1913, 0.1835, 0.1787],

。。.,

[0.3333, 0.3333, 0.3333, 。。., 0.3827, 0.3721, 0.3657],

[0.3333, 0.3333, 0.3333, 。。., 0.3801, 0.3698, 0.3635],

[0.3333, 0.3333, 0.3333, 。。., 0.3785, 0.3684, 0.3622]]]],

grad_fn=《UpsampleBicubic2DBackward1》)

13. 将图像做成网格 (torchvision.utils.make_grid)

当使用PyTorch和torchvision时,不需要使用matplotlib或一些外部库来复制粘贴代码来显示图像网格。只要使用torchvision.utils.make_grid就行了。

from torchvision.utils import make_grid

from torchvision.transforms.functional import to_tensor, to_pil_image

from PIL import Image

img = Image.open(“。/cat.jpg”)

to_pil_image(

make_grid(

[to_tensor(i) for i in [img, img, img]],

nrow=2, # number of images in single row

padding=5 # “frame” size

英文原文:https://zablo.net/blog/post/pytorch-13-features-you-should-know/

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6876

    浏览量

    88810
  • 图像
    +关注

    关注

    2

    文章

    1083

    浏览量

    40410
  • pytorch
    +关注

    关注

    2

    文章

    803

    浏览量

    13143

原文标题:13个你一定要知道的PyTorch特性

文章出处:【微信号:zenRRan,微信公众号:深度学习自然语言处理】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    PyTorch 数据加载与处理方法

    PyTorch流行的开源机器学习库,它提供了强大的工具来构建和训练深度学习模型。在构建模型之前,重要的步骤是加载和处理数据。 1
    的头像 发表于 11-05 17:37 291次阅读

    使用PyTorch在英特尔独立显卡上训练模型

    PyTorch 2.5重磅更新:性能优化+新特性》中的特性就是:正式支持在英特尔独立显卡上训练模型!
    的头像 发表于 11-01 14:21 215次阅读
    使用<b class='flag-5'>PyTorch</b>在英特尔独立显卡上训练模型

    Pytorch深度学习训练的方法

    掌握这 17 种方法,用最省力的方式,加速Pytorch 深度学习训练。
    的头像 发表于 10-28 14:05 140次阅读
    <b class='flag-5'>Pytorch</b>深度学习训练的方法

    pytorch怎么在pycharm中运行

    部分:PyTorch和PyCharm的安装 1.1 安装PyTorch PyTorch
    的头像 发表于 08-01 16:22 1209次阅读

    pycharm如何调用pytorch

    引言 PyTorch开源的机器学习库,广泛用于计算机视觉、自然语言处理等领域。PyCharm是流行的Python集成开发环境(ID
    的头像 发表于 08-01 15:41 511次阅读

    PyTorch中搭建最简单的模型

    PyTorch中搭建最简单的模型通常涉及几个关键步骤:定义模型结构、加载数据、设置损失函数和优化器,以及进行模型训练和评估。
    的头像 发表于 07-16 18:09 1775次阅读

    PyTorch神经网络模型构建过程

    PyTorch,作为广泛使用的开源深度学习库,提供了丰富的工具和模块,帮助开发者构建、训练和部署神经网络模型。在神经网络模型中,输出层是尤为关键的部分,它负责将模型的预测结果以合适的形式输出。以下将详细解析
    的头像 发表于 07-10 14:57 439次阅读

    PyTorch的介绍与使用案例

    PyTorch基于Python的开源机器学习库,它主要面向深度学习和科学计算领域。PyTorch由Meta Platforms(原Facebook)的人工智能研究团队开发,并逐渐
    的头像 发表于 07-10 14:19 352次阅读

    tensorflow和pytorch哪个更简单?

    PyTorch更简单。选择TensorFlow还是PyTorch取决于您的具体需求和偏好。如果您需要易于使用、灵活且具有强大社区支持的框架,Py
    的头像 发表于 07-05 09:45 772次阅读

    PyTorch特性和使用方法

    PyTorch开源的Python机器学习库,由Meta Platforms(前身为Facebook)的人工智能研究团队开发,并于2017年1月正式推出。PyTorch基于Torc
    的头像 发表于 07-02 14:27 476次阅读

    PyTorch如何训练自己的数据集

    PyTorch广泛使用的深度学习框架,它以其灵活性、易用性和强大的动态图特性而闻名。在训练深度学习模型时,数据集是不可或缺的组成部分。然而,很多时候,我们可能需要使用自己的数据集
    的头像 发表于 07-02 14:09 1308次阅读

    如何使用PyTorch建立网络模型

    PyTorch基于Python的开源机器学习库,因其易用性、灵活性和强大的动态图特性,在深度学习领域得到了广泛应用。本文将从PyTorch
    的头像 发表于 07-02 14:08 355次阅读

    使用PyTorch构建神经网络

    PyTorch流行的深度学习框架,它以其简洁的API和强大的灵活性在学术界和工业界得到了广泛应用。在本文中,我们将深入探讨如何使用PyTorch构建神经网络,包括从基础概念到高级
    的头像 发表于 07-02 11:31 654次阅读

    TorchFix:基于PyTorch的代码静态分析

    TorchFix是我们最近开发的新工具,旨在帮助PyTorch用户维护健康的代码库并遵循PyTorch的最佳实践。首先,我想要展示些我
    的头像 发表于 12-18 15:20 1034次阅读

    详解PyTorch在MPS后端的新特性

    大家好,我叫Kulinseth,我在苹果的MPS团队工作,今天我将讨论PyTorch中MPS后端的改进。接下来,我将介绍MPS后端进入Beta Stage的新功能。我们添加了些新功能,如支持分析器、自定义内核和MPS开发者API,这些都是MPS后端的新
    的头像 发表于 12-15 10:57 2221次阅读
    详解<b class='flag-5'>PyTorch</b>在MPS后端的新<b class='flag-5'>特性</b>