PyTorch入门——(黑马程序员视频学习笔记)

1. PyTorch基本语法

什么是PyTorch

  • PyTorch是基于Numpy的科学计算包,向它的使用者提供了两大功能.
    • 作为Numpy的代替者,向用户提供使用GPU强大功能的能力.
    • 作为一款深度学习的平台,向用户提供最大的灵活性和速度.

PyTorch的基本元素操作

  • Tensors张量:张量的概念类似于Numpy中的ndarray数据结构,最大的区别在于Tensor可以利用GPU的加速功能.
  • 使用PyTorch的时候,常规步骤是先将torch引用进来,如下所示:
from __future__ import print_function
import torch 
  • 创建矩阵的操作
  • 创建一个没有初始化的矩阵
x = torch.empty(5, 3)
print(x)
  • 输出结果
tensor([[9.2755e-39, 1.0837e-38, 8.4490e-39],
        [1.0653e-38, 9.2755e-39, 1.0837e-38],
        [9.9184e-39, 9.0000e-39, 1.0561e-38],
        [1.0653e-38, 4.1327e-39, 8.9082e-39],
        [9.8265e-39, 9.4592e-39, 1.0561e-38]])
  • 创建一个有初始化的矩阵
x =  torch.rand(5, 3)
print(x)
  • 输出结果
tensor([[0.7213, 0.5152, 0.8148],
        [0.8858, 0.1565, 0.2185],
        [0.2963, 0.1965, 0.9803],
        [0.3091, 0.7917, 0.9686],
        [0.8878, 0.2557, 0.5401]])

对比有无初始化的矩阵:当声明一个未初始化的矩阵时,它本身不包含任何确切的值。当创建一个未初始化的矩阵时,分配给内存中有什么数值就赋值给了这个矩阵,本质上是毫无意义的数据.

  • 创建一个全零矩阵并可指定数据元素的类型为long
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
  • 输出结果
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
  • 通过数据创建张量
x = torch.tensor([[2.5, 3.5], [1.2, 4.9]])
print(x)
  • 输出结果
tensor([[2.5000, 3.5000],
        [1.2000, 4.9000]])
  • 通过已有的一个张量创建相同尺寸的新张量
# 利用new_mothods方法得到一个张量
x = x.new_ones(5, 3, dtype=torch.double)  # x需要已定义
print(x)
# 利用rand_like方法得到相同张量尺寸的一个新张量,并且采用随机初始化来对其赋值
y = torch.rand_like(x, dtype=torch.float)
print(y)
  • 输出结果
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
tensor([[0.2379, 0.0495, 0.6053],
        [0.0042, 0.2097, 0.3830],
        [0.2966, 0.5169, 0.9339],
        [0.4348, 0.6332, 0.5082],
        [0.9846, 0.2941, 0.4086]])
  • 得到张量的尺寸
print(x.size())
  • 输出结果
torch.Size([5, 3])

torch.size函数本质上返回的是一个tuple,因此它支持一切元组的操作.

PyTorch的基本运算操作

  • 加法与减法
import torch

x = torch.tensor([1, 2, 3])
y = torch.tensor([4, 5, 6])

# 加法
z1 = x + y
# 减法
z2 = x - y
print(z1) # tensor([5, 7, 9])
print(z2) # tensor([-3, -3, -3])
  • 乘法与除法
# 乘法
z3 = x * y
# 除法
z4 = x / y
print(z3) # tensor([ 4, 10, 18])
print(z4) # tensor([0.2500, 0.4000, 0.5000])
  • 点乘(内积)
# 需要确保两个张量的维度相同
z5 = torch.dot(x, y)
print(z5) # tensor(32)
  • 矩阵乘法
# 可以学习线性代数相关知识
A = torch.tensor([[1, 2], [3, 4]])
B = torch.tensor([[2, 0], [1, 3]])

# 矩阵乘法
C = torch.mm(A, B)
print(C) 

-输出结果

tensor([[ 4,  6],
        [10, 12]])
  • 指数与对数
# 指数
z6 = torch.exp(x) # e**x
# 对数
z7 = torch.log(x) # log(x)
print(z6) # tensor([ 2.7183,  7.3891, 20.0855])
print(z7) # tensor([0.0000, 0.6931, 1.0986])
  • 切片与索引
# 获取索引为1的元素
z8 = x[1]
# 获取索引为0到2的元素
z9 = x[0:2]
print(z8) # tensor(2)
print(z9)# tensor([1, 2])
  • 改变张量的形状: .view()
import torch
x = torch.randn(4, 4)
# tensor.view()操作需要保证数据元素的总数量不变
y = x.view(16)
# -1代表自动匹配个数
z = x.view(-1, 8)
print(x.size(), y.size(), z.size()) # torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
  • 如果张量中只有一个元素,可以用.item()将值取出,作为一个python number
a = torch.randn(1)
print(a)  # tensor([0.3608])
print(a.item())  # 0.360826313495636

关于Torch Tensor和Numpy array的相互转换

  • Torch Tensor和Numpy array共享底层的内存空间,因此改变其中一个的值,另一个也会随之改变.
import torch

a = torch.ones(5)
print(a)  # tensor([1., 1., 1., 1., 1.]) 
  • 将Torch Tensor转换为Numpy array
b = a.numpy()
print(b)  # [1. 1. 1. 1. 1.]
  • 对其中一个进行加法操作,另一个也随之改变:
# a中的每个元素加1
a.add_(1)
print(a)  # tensor([2., 2., 2., 2., 2.])
print(b)  # [2. 2. 2. 2. 2.]
  • 将Numpy array转换为Torch Tensor:
import numpy as np

a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)  # [2. 2. 2. 2. 2.]
print(b)  # tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

所有在CPU上的Tensors,除了CharTensor,都可以转换为Numpy array并可以反向转换.

  • 关于Cuda Tensors可以用.to()方法来将其移动到任意设备上.
x = torch.randn(5, 3)
if torch.cuda.is_available():
    # 定义一个设备对象,这里指定成CUDA,即使用GPU
    device = torch.device("cuda")
    # 直接在GPU上创建一个Tensor
    y = torch.ones_like(x, device=device)
    # 将在CPU上面的x张量移动到GPU上面
    x = x.to(device)
    # x和y都在GPU上面,才能支持加法运算
    z = x + y
    # 此处的张量z在GPU上面
    print(z)
    # 也可以将z转移到CPU上面,并同时指定张量元素的数据类型
    print(z.to("cpu", torch.double))
  • 输出结果
tensor([[ 2.1871,  0.6287,  0.8492],
        [-0.1629,  0.8709,  1.3267],
        [-0.5498,  0.3387, -0.1716],
        [ 0.6034,  1.1825,  2.5378],
        [ 0.4426,  0.0858,  1.3867]], device='cuda:0')
tensor([[ 2.1871,  0.6287,  0.8492],
        [-0.1629,  0.8709,  1.3267],
        [-0.5498,  0.3387, -0.1716],
        [ 0.6034,  1.1825,  2.5378],
        [ 0.4426,  0.0858,  1.3867]], dtype=torch.float64)

PyTorch中的autograd

  • 在整个PyTorch框架中,所有神经网络的本质都是一个autograd package(自动求导工具包)
    • autograd package提供了一个对Tensors所有的操作进行自动微分的功能.

关于torch.Tensor

  • torch.Tensor是整个package的核心类,如果将属性.requires_grad设置为True,它将追踪在这个类上定义的所有操作. 当代码要进行反向传播的时候,直接调用.backward()就可以自动计算所有的梯度,在这个Tensor上的所有梯度将被累加进属性.grad中.
  • 如果想终止一个Tensor在计算图中的追踪回溯,只需要执行.detach()就可以将该Tensor从计算图中撤下,在未来的回溯计算中也不会再计算该Tensor.
  • 除了.detach(),如果想终止计算图的回溯,也就是不再进行方向传播求导数的过程,也可以采用代码块的方式with torch.no_grad():,这种方式非常适用于对模型进行预测的时候,因为预测阶段不再需要对梯度进行计算.
  • 关于torch.Function:
    • Function类是和Tensor类同等重要的一个核心类,它和Tensor共同构建了一个完整的类,每一个Tensor拥有一个.grad_fn属性,代表引用了哪个具体的Function创建了该Tensor.
    • 如果某个张量Tensor是用户自定义的,则其对应的grad_fn is None.

关于Tensor的操作

import torch
x1 = torch.ones(3, 3)
print(x1)
x2 = torch.ones(2, 2, requires_grad=True)
print(x2)
  • 输出结果
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
可以看出,在使用requires_grad=True之后,在输出结果中出现了该属性
  • 在具有requires_grad=True的Tensor上执行一个加法操作
y = x2 + 2
print(y)
  • 输出结果
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)
  • 打印Tensor的grad_fn属性
print(x2.grad_fn)  # None
print(y.grad_fn)  # <AddBackward0 object at 0x000001D734C09E80> -> 类对象,Add表示进行了加法操作
  • 在Tensor上执行更复杂的操作:
x3 = y * y * 2
out = x3.mean()
print(x3)
print(out)
  • 输出结果:
tensor([[18., 18.],
        [18., 18.]], grad_fn=<MulBackward0>) # 表示乘法操作
tensor(18., grad_fn=<MeanBackward0>) # 表示求平均值操作
  • 关于方法.requires_grad_(): 该方法可以原地改变Tensor的属性.requires_grad的值,如果没有主动设定默认为True.
a = torch.randn(2, 2)
a = ((a*3)/(a-1))
print(a.requires_grad)  # False
a.requires_grad_(True)
print(a.requires_grad)  # True
b = (a * a).sum()
print(b.grad_fn)  # <SumBackward0 object at 0x0000021ADBCAB8E0> 求和操作
关于梯度Gradients
  • 在PyTorch中,反向传播是靠.backward()实现的.
import torch

x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
print(z.grad_fn)

out.backward()
print(x.grad)

为了理解这段代码中的求导过程,我们可以逐步推导出每个操作的导数。首先,让我们定义一些符号:

  • x x x 是一个 2x2 的张量, x i j x_{ij} xij 表示它的第 i i i 行第 j j j 列的元素。
  • y = x + 2 y = x + 2 y=x+2 是对 x x x 中每个元素加 2 得到的张量。
  • z = y × y × 3 z = y \times y \times 3 z=y×y×3 y y y 的平方乘以 3。
  • out \text{out} out z z z 的元素平均值。
    求导的目标是计算 ∂ out ∂ x i j \frac{\partial \text{out}}{\partial x_{ij}} xijout
  1. 计算 y y y的导数:
    y i j = x i j + 2 y_{ij} = x_{ij} + 2 yij=xij+2
    因此,
    ∂ y i j ∂ x i j = 1 \frac{\partial y_{ij}}{\partial x_{ij}} = 1 xijyij=1
  2. 计算 z z z 的导数:
    z i j = ( x i j + 2 ) 2 × 3 z_{ij} = (x_{ij} + 2)^2 \times 3 zij=(xij+2)2×3
    使用链式法则,我们有
    ∂ z i j ∂ x i j = ∂ ∂ x i j [ ( x i j + 2 ) 2 × 3 ] = 2 ( x i j + 2 ) × 3 \frac{\partial z_{ij}}{\partial x_{ij}} = \frac{\partial}{\partial x_{ij}} \left[ (x_{ij} + 2)^2 \times 3 \right] = 2(x_{ij} + 2) \times 3 xijzij=xij[(xij+2)2×3]=2(xij+2)×3
  3. 计算 out \text{out} out 的导数:
    out = 1 4 ∑ i = 1 2 ∑ j = 1 2 z i j \text{out} = \frac{1}{4} \sum_{i=1}^{2} \sum_{j=1}^{2} z_{ij} out=41i=12j=12zij
    因此,
    ∂ out ∂ x i j = 1 4 ∂ z i j ∂ x i j \frac{\partial \text{out}}{\partial x_{ij}} = \frac{1}{4} \frac{\partial z_{ij}}{\partial x_{ij}} xijout=41xijzij
  4. 结合所有部分:
    将上述导数代入,我们得到
    ∂ out ∂ x i j = 1 4 × 2 ( x i j + 2 ) × 3 = 3 2 ( x i j + 2 ) \frac{\partial \text{out}}{\partial x_{ij}} = \frac{1}{4} \times 2(x_{ij} + 2) \times 3 = \frac{3}{2}(x_{ij} + 2) xijout=41×2(xij+2)×3=23(xij+2)
    这就是 out \text{out} out x i j x_{ij} xij 的导数。在PyTorch中,out.backward() 会自动计算这个导数,并将结果存储在 x.grad 中。由于 $ x $ 是一个 2x2 的张量,x.grad 也会是一个 2x2 的张量,其每个元素都是通过上述公式计算得到的。

  • 关于自动求导的属性设置:可以通过设置.requires_grad=True来执行自动求导,也可以通过代码块的限制来停止自动求导.
print(x.requires_grad)  # True
print((x**2).requires_grad)  # True

with torch.no_grad():
    print((x**2).requires_grad)  # False
  • 可以通过.detach()获得一个新的Tensor,拥有相同的内容但不需要自动求导.
print(x.requires_grad)  # True
y = x.detach()
print(y.requires_grad)  # False
print(x.eq(y).all())  # tensor(True)

2. PyTorch初步应用

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值