pyTorch入门

一、张量

本文是个人的学习笔记,内容主要参考了图书《python深度学习——基于pyTorch》、极客时间的课程《pyTorch深度学习实战》以及B站上“深度之眼”的课程《pyTorch框架基础》等。

1.1 概述

张量(Tensor)是一个多维数组,它是标量(0维张量)、向量(1维张量)、矩阵(2维张量)的高维扩展。

Tensor在torch.Tensor中,有以下几个基本属性:

  • data: 存储了主要的数据
  • grad: data的梯度
  • grad_fn: 创建Tensor的Function,自动求导计算的依据
  • requires_grad: 是否需要计算梯度
  • is_leaf: 当前Tensor是否为计算图中的叶子节点
  • dtype: 张量的数据类型,如torch.FloatTensortorch.cuda.FloatTensor
  • shape: 张量的形状
  • device: 张量的位置,如GPU/CPU

非叶子节点的梯度会在计算梯度后被释放

1.2 张量的创建

1.2.1 直接创建

直接使用torch.tensor创建张量,默认在CPU上创建,定义为:

torch.tensor(data, 
            dtype=None, 
            device=None, 
            requires_grad=False, 
            pin_memory=False)
  • data: 数据,可以是ndarraylisttuplescalar
  • dtype: 张量的数据类型,默认为torch.float32
  • device: 张量的位置,默认为torch.device('cpu')
  • requires_grad: 是否需要计算梯度,默认为False
  • pin_memory: 是否使用pinned memory,默认为False

除此以外,还可以使用torch.Tensor进行张量的创建,torch.Tensor的创建和torch.tensor的创建基本相同,只是torch.Tensor是类,是默认张量类型torch.FloatTensor的别名;而torch.tensor()则是Python的函数。

import torch
t = torch.tensor([1,2,3])
print(t)

import numpy as np

arr = np.ones((3,3))
t = torch.tensor(arr, device='cuda', dtype=torch.int16)
print(t)
tensor([1, 2, 3], device='cuda:0')
tensor([[1, 1, 1],
        [1, 1, 1],
        [1, 1, 1]], device='cuda:0', dtype=torch.int16)

除此以外,还可以使用torch.Tensorfrom_numpy方法创建张量,需要注意的是,使用from_numpy创建的Tensornumpyndarray共享内存,修改一个,另一个也会改变:

import numpy as np
import torch

arr = np.array([[1,2,3],[4,5,6]])
t = torch.from_numpy(arr)
print('ndarray arr is {}'.format(arr))
print('Tensor t is {}'.format(t))

arr[1,2] = 10
print(t)
print('after modification arr is:\n {}'.format(arr))
print('After modify arr, t is:\n {}'.format(t))
ndarray arr is [[1 2 3]
 [4 5 6]]
Tensor t is tensor([[1, 2, 3],
        [4, 5, 6]], dtype=torch.int32)
tensor([[ 1,  2,  3],
        [ 4,  5, 10]], dtype=torch.int32)
after modification arr is:
 [[ 1  2  3]
 [ 4  5 10]]
After modify arr, t is:
 tensor([[ 1,  2,  3],
        [ 4,  5, 10]], dtype=torch.int32)

1.2.2 依据数值创建

torch.zeros()

创建一个指定形状的张量,并初始化为0,其定义为:

torch.zeros(size,
            out=None,
            dtype=None,
            layout=torch.strided,
            device=None,
            requires_grad=False)
  • size: 指定形状
  • out: 输出张量
  • dtype: 张量的数据类型,默认为torch.float32
  • layout: 张量的存储方式,默认为torch.strided
  • device: 张量的位置,默认为torch.device('cpu')
import torch
Y = torch.ones([1])
X = torch.zeros((3, 3), out=Y)
print(X, Y, id(X), id(Y), id(X) == id(Y))
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]]) tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]]) 2374969795552 2374969795552 True

可以看到X和Y是同一个内容

torch.ones_like()

依据内容形状创建全0张量,其定义为:

torch.ones_like(input,
                out=None,
                dtype=None,
                layout=None,
                device=None,
                requires_grad=False)

torch.full()

依据内容创建全为特定值的张量,其定义为:

torch.full(size, 
        fill_value, 
        out=None, 
        dtype=None, 
        layout=torch.strided, 
        device=None, 
        requires_grad=False)

torch.arange()

创建一个等差数列的张量(半开半闭区间,即[start, end)),其定义为:

torch.arange(start=0,
            end,
            step=1,
            out=None,
            dtype=None,
            layout=torch.strided,
            device=None,
            requires_grad=False)

torch.linspace()

创建一个等差张量,与arange的区别在于,该函数在一定数值范围内(闭区间)构建一个等差的张量,而不需要指定间隔的大小,其定义为:

torch.linspace(start, 
        end, 
        steps=100, 
        out=None, 
        dtype=None, 
        layout=torch.strided, 
        device=None, 
        requires_grad=False)

此处需要注意的是第三个参数step,该参数表示的是最后生成的张量的长度,而不是数列的间隔,因此实际的间隔为
sep = \frac {end - start} {steps - 1}

import torch
torch.linspace(0, 10, 5)
tensor([ 0.0000,  2.5000,  5.0000,  7.5000, 10.0000])

torch.logspace()

创建一个对数等比数列的张量,其定义为:

torch.logspace(start,
               end,
               steps=100,
               base=10.0,
               out=None,
               dtype=None,
               layout=torch.strided,
               device=None,
               requires_grad=False)

其中,

  • base: 表示对数数列的底数,默认为10
  • step: 表示最后生成的张量的长度,而不是数列的间隔,因此实际的间隔为

1.2.3 创建随机张量

torch.normal()

创建一个正态分布的张量,其定义为:

torch.normal(mean, std, out=None)

torch.normal(mean, std, size, out=None)

需要注意的是对于normal()函数而言,当mean和std均为标量时,必须指定size参数,否则会报错

import torch
# mean为张量,std为张量
mean = torch.arange(1, 5, dtype=torch.float)
std = torch.arange(1, 5, dtype=torch.float)
t_normal = torch.normal(mean, std)
print('mean is {}\nstd is {}'.format(mean, std))
print('t_normal is {}'.format(t_normal))

# mean为标量,std为标量
t_normal = torch.normal(0.0, 1.0, size=(1,))
print('t_normal is {}'.format(t_normal))

# mean为张量,std为标量
mean = torch.arange(1, 5, dtype=torch.float)
std = 1
t_normal = torch.normal(mean, std)
print("mean is {}\nstd is {}".format(mean, std))
print('t_normal is {}'.format(t_normal))
mean is tensor([1., 2., 3., 4.])
std is tensor([1., 2., 3., 4.])
t_normal is tensor([0.1857, 1.8819, 5.5086, 6.1349])
t_normal is tensor([1.3543])
mean is tensor([1., 2., 3., 4.])
std is 1
t_normal is tensor([2.2058, 0.9800, 3.6500, 3.6093])

torch.randn()

创建一个标准正态分布的张量,其定义为:

torch.randn(size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
  • size: 指定张量的形状

torch.rand()和torch.rand_like()

在区间[0,1)创建一个均匀分布的张量,其定义为:

torch.rand(size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
torch.rand_like(input, dtype=None, layout=None, device=None, requires_grad=False, memory_format=None)

torch.randint()和torch.randint_like()

在一个区间[low, high)上,创建一个均匀分布的整数张量,其定义为:

torch.randint(low=0, 
            high, 
            size, 
            out=None, 
            dtype=None, 
            layout=torch.strided, 
            device=None, 
            requires_grad=False)

参考官方文档:https://pytorch.org/docs/stable/generated/torch.randint.html#torch-randint

torch.randint_like(input,
                  low=0,
                  high=None,
                  size=None,
                  out=None,
                  dtype=None,
                  layout=None,
                  device=None,
                  requires_grad=False,
                  memory_format=torch.preserve_format)

参考官方文档:https://pytorch.org/docs/stable/generated/torch.randint_like.html#torch-randint-like

torch.randperm()

创建一个随机排列的整数张量,常常用来生成一个随机索引,其定义为:

torch.randperm(n, 
            out=None, 
            dtype=torch.int64, 
            layout=torch.strided, 
            device=None, 
            requires_grad=False)

torch.bernoulli()

input的值作为概率,生成一个服从伯努利分布的张量,其定义为:

torch.bernoulli(input,
                *
                generator=None, 
                out=None)
import torch
t = torch.bernoulli(torch.tensor([0.1, 0.2, 0.3, 0.4, 0.5]))
t
tensor([0., 1., 0., 1., 1.])

1.3 张量的操作

1.3.1 张量的拼接

torch.cat()

将输入的张量按维度dim拼接起来,其定义为:

torch.cat(tensors, dim=0, out=None)
  • tensors:输入的张量列表
  • dim:在哪个维度上拼接,默认为0

dim可以理解为:操作后会发生变化的维度

torch.stack()

将输入的张量在新创建的维度dim拼接起来,其定义为:

torch.stack(tensors, dim=0, out=None)
  • tensors:输入的张量列表
  • dim:在哪个维度上增加,默认为0
# cat的操作
import torch
t = torch.arange(1,7)
t = torch.reshape(t, (2,3))
t_0 = torch.cat([t, t], dim=0)
t_1 = torch.cat([t, t], dim=1)
print('tensor t is {}'.format(t))
print(t.shape)
print('在dim=0上执行操作cat的结果为{}'.format(t_0))
print(t_0.shape)
print('在dim=1上执行操作cat的结果为{}'.format(t_1))
print(t_1.shape)
tensor t is tensor([[1, 2, 3],
        [4, 5, 6]])
torch.Size([2, 3])
在dim=0上执行操作cat的结果为tensor([[1, 2, 3],
        [4, 5, 6],
        [1, 2, 3],
        [4, 5, 6]])
torch.Size([4, 3])
在dim=1上执行操作cat的结果为tensor([[1, 2, 3, 1, 2, 3],
        [4, 5, 6, 4, 5, 6]])
torch.Size([2, 6])
# stack的操作
t_stack_0 = torch.stack([t, t], dim=0)
t_stack_1 = torch.stack([t, t], dim=2)
print(t_stack_0)
print(t_stack_0.shape)
print(t_stack_1)
print(t_stack_1.shape)
tensor([[[1, 2, 3],
         [4, 5, 6]],

        [[1, 2, 3],
         [4, 5, 6]]])
torch.Size([2, 2, 3])
tensor([[[1, 1],
         [2, 2],
         [3, 3]],

        [[4, 4],
         [5, 5],
         [6, 6]]])
torch.Size([2, 3, 2])

1.3.2 张量的切分

torch.chunk()

将输入的张量在dim维度上按照chunks个大小切分,其定义为:

torch.chunk(input, chunks, dim=0)
  • input:输入的张量
  • chunks:切分的份数,必须为整数
  • dim:在哪个维度上切分,默认为0

若不能整除,chunk函数是先做除法,然后再向上取整得到每组的数量。例如原始tensor中有10个元素时,当chunks=3时, 10/3=3.3333, 向上取整为4,所以最后得到每组张量有4个元素,最后剩下的两个元素自动归为1组。

import torch

A=torch.arange(1,11)
B = torch.chunk(A, 2, 0)
print(B)

# 不能整除的情况 
B = torch.chunk(A, 3, 0)
print(B)
(tensor([1, 2, 3, 4, 5]), tensor([ 6,  7,  8,  9, 10]))
(tensor([1, 2, 3, 4]), tensor([5, 6, 7, 8]), tensor([ 9, 10]))

torch.split()

将输入的张量在dim维度上按照split_size的大小切分,其定义为:

torch.split(tensor, split_size_or_sections, dim=0)
  • tensor:输入的张量
  • split_size_or_sections:切分的份数,若为整数,则会在指定维度上将整个张量切分为split_size_or_sections大小(不能整除时,最后剩下的为1组);若为list,则按照list中元素的数量来切分
  • dim:在哪个维度上切分,默认为0
import torch

A = torch.rand(4, 4)
print('原始tensor为\n{}'.format(A))
print('1.split_size_or_sections为整数,且可以整除时:')
B = torch.split(A, 2, 0)
print(B)

print('2.split_size_or_sections为整数,且不可以整除时:')
B = torch.split(A, 3, 0)
print(B)

print('3.split_size_or_sections为列表时:')
B = torch.split(A, [1, 3], 0)
print(B)
原始tensor为
tensor([[0.3747, 0.0479, 0.1082, 0.7263],
        [0.8332, 0.2020, 0.7699, 0.0615],
        [0.8753, 0.0584, 0.9294, 0.3031],
        [0.6463, 0.2581, 0.3250, 0.5398]])
1.split_size_or_sections为整数,且可以整除时:
(tensor([[0.3747, 0.0479, 0.1082, 0.7263],
        [0.8332, 0.2020, 0.7699, 0.0615]]), tensor([[0.8753, 0.0584, 0.9294, 0.3031],
        [0.6463, 0.2581, 0.3250, 0.5398]]))
2.split_size_or_sections为整数,且不可以整除时:
(tensor([[0.3747, 0.0479, 0.1082, 0.7263],
        [0.8332, 0.2020, 0.7699, 0.0615],
        [0.8753, 0.0584, 0.9294, 0.3031]]), tensor([[0.6463, 0.2581, 0.3250, 0.5398]]))
3.split_size_or_sections为列表时:
(tensor([[0.3747, 0.0479, 0.1082, 0.7263]]), tensor([[0.8332, 0.2020, 0.7699, 0.0615],
        [0.8753, 0.0584, 0.9294, 0.3031],
        [0.6463, 0.2581, 0.3250, 0.5398]]))

1.3.3 张量的索引

torch.index_select()

将输入的张量在dim维度上按照index索引进行选择,其定义为:

torch.index_select(
    input, 
    dim, 
    index, 
    out)
  • input:输入的张量
  • dim:在哪个维度上选择,默认为0
  • index:索引,数据类型为tensor.long
import torch

t = torch.arange(1, 10).view((3, 3 ))
print(t)
idx = torch.tensor([0, 2], dtype=torch.long)
t_select = torch.index_select(t, dim=0, index=idx)
print(t_select)
tensor([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]])
tensor([[1, 2, 3],
        [7, 8, 9]])

tensor.masked_select()

将输入的张量在dim维度上按照mask索引进行选择,其定义为:

torch.masked_select(
    input,
    mask,
    out=None)
  • input:输入的张量
  • mask:索引,数据类型为tensor.bool的列表,大小与input相同

返回值为满足条件的1维张量

import torch
t = torch.arange(1, 10).view((3, 3 ))
mask = t.ge(5)
t_select = t.masked_select(mask)
print('mask is: \n{}'.format(mask))
print(t_select)

# 也可以简化为如下形式:
t_select = torch.masked_select(t, t>=5)
print(t_select)
mask is: 
tensor([[False, False, False],
        [False,  True,  True],
        [ True,  True,  True]])
tensor([5, 6, 7, 8, 9])
tensor([5, 6, 7, 8, 9])

1.3.4 张量变换

torch.reshape()

将输入的张量按照shape重新排列,其定义为:

torch.reshape(input, shape)
  • input 要变换的张量
  • shape 新的形状,如果一个维度为-1,则这个维度会自动计算

注意:当张量在内存中是连续的时候,新得到的张量与input共享内存。

import torch
t = torch.randperm(8)
print('原始数据t为:\n{}'.format(t))
t_reshape = torch.reshape(t, (-1, 4))
print('reshape得到的数据为:\n{}'.format(t_reshape))

# 共享内存验证
t[0] = 1024
print('修改了t的第一个元素为1024后,t的值变为:\n{}'.format(t))
print('reshape得到的数据变为:\n{}'.format(t_reshape))

原始数据t为:
tensor([4, 5, 0, 2, 7, 3, 1, 6])
reshape得到的数据为:
tensor([[4, 5, 0, 2],
        [7, 3, 1, 6]])
修改了t的第一个元素为1024后,t的值变为:
tensor([1024,    5,    0,    2,    7,    3,    1,    6])
reshape得到的数据变为:
tensor([[1024,    5,    0,    2],
        [   7,    3,    1,    6]])

torch.transpose()

将输入的张量按照dim0dim1进行转置,其定义为:

torch.transpose(input, dim0, dim1)
t.transpose(dim0, dim1)

torch.t()

2维张量的转置,相当于torch.transpose(input, 0, 1)

import torch
t = torch.rand((2,3,4))
# 也可以写成t_tranpose = torch.transpose(t, 0, 1)
t_transpose = t.transpose(dim0=0, dim1=1)
print('t的转置为:\n{}'.format(t_transpose.shape))
t的转置为:
torch.Size([3, 2, 4])

torch.squeeze()

dimNone,则将输入的张量中所有维度为1的维度删除;若指定了dim,且指定维度的长度为1,则移除该维度,其定义为:

torch.squeeze(input, 
            dim=None,
            out=None)

torch.unsqueeze()

将输入的张量中指定维度增加一个维度,其定义为:

torch.unsqueeze(input,
            dim, 
            out=None)

依据dim扩展维度

import torch

t = torch.rand((2,1,4))
t_squeeze = torch.squeeze(t)
print('t的形状为:\n{}'.format(t.shape))
print('squeeze后t的形状为:\n{}'.format(t_squeeze.shape))

t_unsqueeze = torch.unsqueeze(t, 3)
print('unsqueeze后的形状为:\n{}'.format(t_unsqueeze.shape))
t的形状为:
torch.Size([2, 1, 4])
squeeze后t的形状为:
torch.Size([2, 4])
unsqueeze后的形状为:
torch.Size([2, 1, 4, 1])

1.4 张量的计算

1.4.1 加法

torch.add()

对输入的张量进行加法操作(先乘后加),其定义为:

torch.add(input, 
        other, 
        *, 
        alpha=1, 
        out=None)
  • input:第一个的张量
  • alpha:加法操作的系数,默认为1
  • other:第二个的张量

整个运算完成的操作公式为:
result = input + alpha * other

import torch
t1 = torch.ones((3,3)) * 2
t2 = torch.ones((3,3))

print(torch.add(t1, t2, alpha=10))
tensor([[12., 12., 12.],
        [12., 12., 12.],
        [12., 12., 12.]])
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容