PyTorch入门(1)—— Tensor基本数据操作

  • 参考:
    1. 《深度学习框架PyTorch入门与实践》—— 陈云
    2. 《动手学深度学习(PyTorch版)》—— 李沐
  • 注意:由于本文是jupyter文档转换来的,代码不一定可以直接运行,有些注释是jupyter给出的交互结果,而非运行结果!!

  • PyTorch中,张量torch.Tensor 是存储和变换数据的主要工具
  • Tensor和NumPy的多维数组ndarray非常类似,与之相比,Tensor提供GPU计算自动求梯度等更多功能,使其更加适合深度学习。

1. 创建 Tensor

  • 创建方式和 NumPy 的ndarray大同小异,一些常见的 API 如下
    在这里插入图片描述
  • 示例:
    import torch 
    
    print('创建一个 5x3 的未初始化tensor')
    x = torch.empty(5,3)  
    print(x,'\n')
    
    print('创建一个 5x3 的随机初始化tensor,数据是整型')
    x = torch.IntTensor(5,3)  	# rnadn() 生成指定维度浮点型数据,数据服从均值为0,方差为1的正态分布
    print(x,'\n')
    
    print('创建一个 5x3 的随机初始化tensor,数据是浮点型')
    x = torch.FloatTensor(5,3)  # rnadn() 生成指定维度浮点型数据,数据服从均值为0,方差为1的正态分布
    print(x,'\n')
    
    
    print('创建一个 5x3 的随机初始化tensor,数据是浮点型,服从 0-1 区间均匀分布')
    x = torch.rand(5,3)  		# rnad() 生成指定维度浮点型数据,数据服从 0-1 区间均匀分布
    print(x,'\n')
    
    print('创建一个 5x3 的随机初始化tensor,数据是浮点型,服从均值为0,方差为1的正态分布')
    x = torch.rand(5,3)  		# rnadn() 生成指定维度浮点型数据,数据服从均值为0,方差为1的正态分布
    print(x,'\n')
    
    print('根据起、止、步长创建一个左闭右开的 range 序列,类似Numpy中的 arange()')
    x = torch.arange(0,11,2,dtype = torch.int)  # rnadn() 生成指定维度浮点型数据,数据服从均值为0,方差为1的正态分布
    print(x,'\n')
    
    print('创建一个 5x3 的 long 型全零 Tensor')
    x = torch.zeros(5,3,dtype=torch.long)
    print(x,'\n')
    
    print('直接根据pythons数据创建,可以指定数据类型')
    x = torch.tensor([5.5,3],dtype = torch.int32)
    print(x,'\n')
    
    print('通过现有的tensor来创建,默认重用数据类型等属性,除非自定义这些属性')
    x = torch.ones(5,3,dtype=torch.float64)
    print(x)
    print("借用形状并且指定新的数据类型")
    x = torch.randn_like(x,dtype=torch.float)
    print(x)
    
  • 可以通过.shape或者.size()来获取Tensor的形状,返回的torch.Size是一个tuple, 支持所有tuple的操作
    x = torch.zeros(5,5)
    print(x.size())	# torch.Size([5, 5])
    print(x.shape)	# torch.Size([5, 5])
    

2. 操作

  • PyTorch中的 Tensor 支持超过一百种操作,包括转置、索引、切片、数学运算、线性代数、随机数等等,可参考 官方文档

2.1 算数操作

  • 在Pytorch中,同一种操作可能有很多形式,以加法为例

    x = torch.tensor([[5,3],[4,6]])
    y = torch.tensor([[5,3],[4,6]])
    
    # 方法1:直接用运算符号
    print(x+y,'\n')
    
    # 方法2:使用torch.add静态方法,可以通过 out 参数指定输出(要求尺寸一致)
    res = torch.empty(2,2)
    torch.add(x,y,out = res)
    print(res,'\n')
    
    # 方法3:inplace形式(这种形式会修改y的值)
    y.add_(x)
    print(y,'\n')
    
  • 注:Pytorch 操作的 inplace 版本都有后缀 _,如 y.add_(x)y.copy_(y)y.t_() 等,inplace 方式会修改 y 的值,可以避免重新开辟内存,速度较快

2.2 索引

  • 可以使用类似 NumPy 的索引操作来访问 Tensor 的一部分,需要注意的是:索引出来的结果与原数据共享内存,也即修改一个,另一个会跟着修改
    x = torch.tensor([[5,3],[4,6]])
    print(x)
    
    y = x[0,:][0]
    y += 1
    x[0,:][1] -= 1
    print(x)
    # 输出
    tensor([[5, 3],
        	[4, 6]])
    tensor([[6, 2],
            [4, 6]])
    
  • 还有很多特殊索引,可以等用到的时候再查
    在这里插入图片描述
    x = torch.tensor([[1,2],[3,4],[5,6]])
    print(torch.index_select(x,1,torch.tensor([1])))
    print(torch.masked_select(x,x>4))
    # 输出
    tensor([[2],
            [4],
            [6]])
    tensor([5, 6])
    

2.3 改变Tensor形状

  • 使用 tensor.view() 来改变 Tensor 的形状。注意 view() 返回的Tensor与源Tensor是共享data的,即更改其中一个,另外一个也会跟着改变。(顾名思义,view仅仅是改变了对这个张量的观察角度,内部数据并未改变)

    x = torch.randn(5,3)
    y = x.view(15)
    z = x.view(-1,5)  # -1 所指的维度可以由其他维度的值推导出来
    
    print(x.size(),y.size(),z.size())	# torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5])
    
  • 如果想要返回真正的副本,可以先使用 Tensor.clone() 方法创建副本,再用 Tensor.view() 方法修改形状,如下

    x = torch.rand(5,3)
    y = x.clone().view(15)
    
    y[y<0.5] = 0
    print(x)
    print(y)
    
    # 输出
    tensor([[0.2987, 0.1410, 0.2219],
                [0.3753, 0.8129, 0.4814],
                [0.1228, 0.0604, 0.6366],
                [0.1716, 0.1970, 0.1454],
                [0.8926, 0.3561, 0.2427]])
    tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.8129, 0.0000, 0.0000, 0.0000, 0.6366,
            0.0000, 0.0000, 0.0000, 0.8926, 0.0000, 0.0000])
    

    注:使用 clone 还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor

  • 另一个常用函数是 Tensor.item(),它将一个 标量 Tensor转为 Python member

    x = torch.FloatTensor(1)
    print(x)
    print(x.item())
    
    # 输出
    tensor([1.4013e-45])
        1.401298464324817e-45
    

2.4 线性代数

  • 常用方法,可以具体用时再具体查
    在这里插入图片描述

3. 广播机制

  • 两个形状相同的Tensor,可以直观地进行按元素计算

  • 两个形状不同的Tensor,在进行按元素计算时,可能触发 广播(boardcasting) 机制:先适当地复制元素使得两个 Tensor 形状相同,然后按元素运算,例如

    x = torch.arange(1, 3).view(1, 2)  # size = 2 -> size = (1,2)
    print(x)
    y = torch.arange(1, 4).view(3, 1)  # size = 3 -> size = (3,1)
    print(y)
    print(x + y)
    
    # 输出
    tensor([[1, 2]])
    tensor([[1],
            [2],
            [3]])
    tensor([[2, 3],
            [3, 4],
            [4, 5]])
    

    这里 xy 分别是 (1,2) 和 (3,1) 的矩阵,如果要计算 x + y,那么

    1. x 中第一行的2个元素广播(复制)到第二行和第三行
    2. y 中第一列的3个元素广播(复制)到第二列

    如此,就可以对2个 (3,2) 的矩阵按元素相加。

4. 运算的内存开销

  • 索引操作不会开辟新内存,而像 y = x + y 这样的运算是会新开内存的,然后将 y 指向新内存

  • 我们可以使用Python自带的 id 函数判断是否开辟了新内存:如果两个实例的 ID 一致,那么它们所对应的内存地址相同;反之则不同(说明开辟了新内存)

    x = torch.tensor([1, 2])
    y = torch.tensor([3, 4])
    id_before = id(y)
    y = y + x
    print(id(y) == id_before) # False 
    
  • 如果想指定结果到原来的内存中,比如把 x + y 的结果写进 y 对应的内存中,不开新空间,有以下方法

    1. 使用索引进行替换操作y[:] = x + y
    2. 使用运算函数中的 out 参数进行设置:torch.add(x, y, out=y)
    3. 使用自加运算符 += 等:y += x
    4. 使用 inplace 版本的运算函数:y.add_(x)

    注:虽然 view 返回的Tensor与源Tensor是共享data的,但是依然是一个新的Tensor(因为Tensor除了包含data外还有一些其他属性),二者 id(内存地址)并不一致。

5. Tensor 和 ndarray 的相互转换

  • PyTorch中的Tensor和NumPy中的ndarray可以很方便地相互转换,这样我们就可以把 NumPy 强大的矩阵运算能力和Tensor支持GPIU加速的优势相结合,所有在CPU上的Tensor(除了CharTensor)都支持与NumPy ndarray相互转换

  • 常用的转换方法

    1. Tensor -> ndarray:ndarray = Tensor.numpy()
    2. ndarray -> Tensor:Tensor = torch.from_numpy(ndarray)

    需要注意的一点是:这两个函数所产生的的 Tensor 和 ndarray 共享相同的内存(所以他们之间的转换很快),改变其中一个时另一个也会改变!!!

    # Tensor -> ndarray
    a = torch.ones(5)
    b = a.numpy()
    print(a, b)		#  tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
    
    a += 1
    print(a, b)		# tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
    b += 1
    print(a, b)		# tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
    
    # ndarray -> Tensor
    import numpy as np
    a = np.ones(5)
    b = torch.from_numpy(a)
    print(a, b)			# [1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
    
    a += 1			
    print(a, b)			# [2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
    b += 1
    print(a, b)			# [3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
    
  • 还有一个常用的 ndarray -> Tensor 的方法是直接使 ndarray 构造 Tensor,即使用 torch.tensor() 方法。此方法总是会进行数据拷贝(就会消耗更多的时间和空间),所以返回的Tensor和原来的数据不再共享内存

    a = np.ones(5)
    c = torch.tensor(a)
    a += 1
    print(a, c)			# [2. 2. 2. 2. 2.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
    

6. Tensor on GPU

  • 在创建 Tensor 时,默认是在CPU处理的,通过设置 device 参数可以将其创建为在GPU处理的Tensor

  • Tensor.to() 方法,可以将Tensor在CPU与GPU之间相互移动

  • 使用 Tensor.cuda() 方法,可以把CPU Tensor转换为GPU Tensor

    x = torch.tensor([1, 2])                   # 这样直接创建的Tensor是在CPU处理的
    
    # 以下代码只有在PyTorch GPU版本上才会执行
    if torch.cuda.is_available():
        device = torch.device("cuda")          # GPU
        y = torch.ones_like(x, device=device)  # 通过设置 device 参数,直接创建在GPU处理的 Tensor
        x = x.to(device)                       # 等价于 .to("cuda")
        z = x + y
        print(z)
        print(z.to("cpu", torch.double))       # to()还可以同时更改数据类型
    
    # 输出
    tensor([2, 3], device='cuda:0')
    tensor([2., 3.], dtype=torch.float64)
    
    x = torch.tensor([1, 2]) 
    y = torch.tensor([2, 3]) 
    if torch.cuda.is_available():
        x = x.cuda()
        y = y.cuda()
        z = x + y
        print(z)		# tensor([3, 5], device='cuda:0')
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值