深度学习笔记一 PyTorch Tensor Optimisation GPU

深度学习课堂实验学习笔记一: 包括PyTorch介绍,Tensor,梯度下降法(包含代码),Backpropagation,Juypter Notebook的GPU使用等
实验环境:Juypter Notebook

💡PyTorch介绍

一个用于自动计算梯度的软件包,更广泛地用于深度学习。

If you don’t already have the PyTorch package installed you will need to follow the
PyTorch Get Started
instructions for your platform. For example with Anaconda, if your platform doesn’t have a GPU use:

conda install pytorch torchvision torchaudio cpuonly -c pytorch

If you have a GPU, use

conda install pytorch torchvision torchaudio cudatoolkit=10.2 -c pytorch
Replacing the version number of CUDA as appropriate - see
PyTorch Get Started

There is a short section on using a GPU at the end of this notebook.

documentation
and
tutorials
are available on the
PyTorch website.

import numpy as np
import torch
import matplotlib.pyplot as plt

#matplotlib inline
%matplotlib notebook

Tensor

  1. 在torch中,类似数组的数据被称为张量(tensor),并且以torch.Tensor类的实例的形式存储。张量类似于NumPy的ndarray。许多函数名称都是相同的。在下面的示例中,我们创建了一个包含四个浮点值的一维张量和一个包含整数值的二维张量。

    x = torch.tensor([5.0, 3, 6.6, 1])    # a 1-D tensor (vector),一维张量(向量),包含了四个元素
    print(x)
    
    y = torch.tensor([[1, 2],[4, 5]])    # a 2-D tensor (matrix),二维张量(矩阵),包含了两个行和两个列
    print(y)
    
    print(y*y)    # element-wise (Hadamard) product (unlike Matlab, where this would be matrix multiplication)
    #进行了逐元素相乘的操作,也称为Hadamard积。这与矩阵乘法不同,它是对应位置上元素的相乘。
    print(torch.matmul(y,y))#矩阵乘法操作,即矩阵y与自身的乘积
    #tensor([5.0000, 3.0000, 6.6000, 1.0000])
    #tensor([[1, 2],
    #        [4, 5]])
    #tensor([[ 1,  4],
    #        [16, 25]])
    #tensor([[ 9, 12],
    #        [24, 33]])
    
  2. 然而,torch在计算输出相对于输入的梯度时超越了NumPy,使得优化变得更加容易。这使得torch非常适合优化问题(optimisation)。要启用这种功能,可以在创建张量时告诉torch。在这个示例中,我们从标量值x创建了一个张量,并计算了z=x^2。根据微积分,我们知道

    d z d x = 2 x \frac{dz}{dx}=2x dxdz=2x

    算术操作z=x**2会即时构建一个计算图,并且x.backward实现了从输出变量x进行反向传播。因此,grad属性包含了梯度 - 在这个示例中,是一个包含单个标量的张量。请注意,需要梯度的张量必须是浮点数(或复数)数据类型。

    x = torch.tensor(3.0, requires_grad=True)
    # 创建了一个张量 x,其值为 3.0,并且指定了 requires_grad=True,表示我们需要计算相对于 x 的梯度
    z = x**2
    # 计算了 x 的平方,并将结果赋给了变量 z。在这里,PyTorch会自动构建一个计算图,其中包含了这个平方运算
    # 计算图(Computational Graph)是一种用来描述数学运算的有向图,其中节点表示操作(或函数),边表示数据流动。
    z.backward()
    # 反向传播的过程。PyTorch会根据计算图自动计算出相对于输入变量 x 的梯度,即求导
    print(x.grad)
    # 输出:tensor(6.)
    

    在这里插入图片描述

  3. 对于一个多变量函数,我们如何计算其各个变量的偏导数(partial derivatives)

    z = 3 x y + y 2 z=3xy+y^2 z=3xy+y2

    ∂ z ∂ x = 3 y \frac{\partial z}{\partial x}=3y xz=3y

    ∂ z ∂ y = 3 x + 2 y \frac{\partial z}{\partial y}=3x+2y yz=3x+2y

    x = torch.tensor(3.0, requires_grad=True)
    y = torch.tensor(5.0, requires_grad=True)
    
    z = 3*x*y + y**2
    z.backward()
    
    print(x.grad)
    print(y.grad)
    # 输出:tensor(15.)
    # tensor(19.)
    

    在构建计算图时,PyTorch会为每个节点添加一个名为 grad_fn 的属性。这个属性包含了创建该节点的基本函数,这些函数用于反向传播过程。对于 z 这个节点来说,它是由将 p 和 q 相加得到的最终结果。

    因此,在计算图中,z 节点的 grad_fn 属性将包含一个函数,这个函数用于将 p 和 q 相加并产生 z。这个函数实际上记录了前向传播过程中执行的操作,以便在反向传播时能够正确地计算梯度。

    通过 grad_fn 属性,PyTorch能够追踪整个计算图的结构,并在反向传播时根据计算图中的每个节点的操作,自动计算出相对于输入节点的梯度。这种自动微分的机制使得深度学习中的梯度计算变得非常方便和高效。

  4. PyTorch能够处理任意维度的张量,即包含多个标量值的张量,这些张量被用来表示复杂的数据结构,比如图像、文本等。PyTorch能够为n维张量计算梯度,这些梯度表示了函数相对于张量中每个标量值的变化率。张量在计算图中的节点上进行操作,每个操作构成了计算图中的一个节点。当我们执行操作(例如张量运算)时,PyTorch会自动在计算图中添加相应的节点。反向传播算法是深度学习中的核心技术之一,它用于计算损失函数相对于模型参数的梯度。PyTorch通过计算图来实现反向传播,可以高效地处理复杂的计算图结构,从而使得反向传播的计算过程更加高效。需要注意的是,PyTorch要求函数的输出必须是标量(即单个数字),才能够计算其相对于输入的梯度。这是因为梯度表示了一个标量值相对于另一个标量值的变化率。因此,在使用PyTorch进行深度学习时,我们需要确保函数的输出是一个标量(scalar)。

    x = torch.tensor([1., 3., 2., 5.], requires_grad=True)
    # 创建了一个张量 x,其中包含四个标量值 [1., 3., 2., 5.]。设置 requires_grad=True 表示我们要对这个张量进行梯度计算。
    z = x**2
    # 对张量 x 中的每个元素进行平方操作,得到一个新的张量 z
    z = z.sum()    # sums the outputs, producing a scalar result
    # 对张量 z 中的所有元素进行求和操作,得到一个标量结果,并将其赋值给变量 z。这里得到的标量是所有元素平方值的总和。
    print(z)
    z.backward()
    # 调用 backward() 方法,执行反向传播算法,计算相对于张量 x 的梯度
    print(x.grad)
    # 输出:tensor(39., grad_fn=<SumBackward0>)
    # tensor([ 2.,  6.,  4., 10.])
    
    x = torch.tensor([[1., 3.], [2., 5.]], requires_grad=True)
    # 创建了一个二维张量 x,其中包含两行两列的数据。设置 requires_grad=True 表示我们要对这个张量进行梯度计算。
    z = x**2
    z = z.sum()    # sums the outputs, producing a scalar result
    # 对张量 z 中的所有元素进行求和操作,得到一个标量结果,并将其赋值给变量 z。这里得到的标量是所有元素平方值的总和
    print(z)
    z.backward()
    print(x.grad)
    # 输出:tensor(39., grad_fn=<SumBackward0>)
    # tensor([[ 2.,  6.],
    #         [ 4., 10.]])
    
  5. 在单变量函数中,导数表示函数在某一点上的变化率或斜率。而在多元函数中,梯度是一个向量,它包含了函数在每个变量方向上的偏导数,表示函数在某一点上的变化率和方向。因此,梯度告诉我们函数在给定点上沿着哪个方向增长最快,以及增长的速率是多少。在深度学习中,我们经常需要计算损失函数相对于模型参数的梯度。这些梯度告诉我们如何调整模型参数,以便最小化损失函数。通过梯度下降等优化算法,我们可以根据梯度的方向和大小来更新模型参数,从而不断地优化模型,使其在训练数据上表现更好。

  6. 有时候从NumPy数组创建一个张量是很方便的。在深度学习中,我们通常会使用NumPy进行数据处理和准备,然后将其转换为PyTorch张量以便在PyTorch中进行模型训练和操作。PyTorch提供了将NumPy数组转换为张量的方法,这使得在两个框架之间进行数据交互变得更加方便。

    x = np.array([[1.,2.,3.],[4.,5.,6.]])
    # 建了一个二维NumPy数组 x,其中包含两行三列的数据。
    print(x)
    y = torch.tensor(x, requires_grad=True)
    # 将NumPy数组 x 转换为PyTorch张量 y。设置 requires_grad=True 表示我们要对这个张量进行梯度计算。
    print(y)
    
    输出:
    [[1. 2. 3.]
     [4. 5. 6.]]
    tensor([[1., 2., 3.],
            [4., 5., 6.]], dtype=torch.float64, requires_grad=True)
    

Optimisation

  1. 梯度下降法:https://www.bilibili.com/video/BV18P4y1j7uH/?spm_id_from=333.337.search-card.all.click&vd_source=0b01d46b5ffcde6bf7c9dc1429a00d7b

  2. 使用梯度下降法,从 x=0, y=0 出发,考虑一下函数

    z = ( x − 5 ) 2 + ( y − 7 ) 2 + 20 z = (x-5)^2+(y-7)^2 + 20 z=(x5)2+(y7)2+20

    1. 首先,我们创建一个包含初始值的输入张量 p,并设置 requires_grad=True,以便后续计算梯度。

    2. 设定迭代次数为50次,并创建一个 NumPy 数组 history,用于记录每次迭代后的参数值和目标函数值。

    3. 设置步长为0.1。

    4. 开始迭代优化的循环,每次迭代的过程如下:

      a. 计算当前输入的目标函数值 z = (x-5)^2 + (y-7)^2 + 20,其中 (x, y) 是参数张量 p 的当前值。

      b. 执行梯度的反向传播,计算相对于参数张量 p 的梯度。

      c. 根据梯度下降法的更新规则,更新参数张量 p 的值。

      d. 将更新后的参数值和目标函数值记录到 history 数组中,并打印出来。

      e. 将参数张量 prequires_grad 属性重新设置为 True,以便下一次迭代时能够计算梯度。

    5. 最后,算法结束,history 数组中存储了每次迭代后的参数值和目标函数值,可以用于分析优化的过程。

    # create an input tensor with initial values 创建一个包含初始值的输入张量
    p = torch.tensor([0., 0.], requires_grad=True)
    # 创建一个包含初始值 [0., 0.] 的张量 p,并设置 requires_grad=True,表示我们要对这个张量进行梯度计算。
    
    iterations = 50
    # 设定迭代次数为50次,即我们将执行50次优化步骤
    
    # create and ndarray to record the trajectory 
    history = np.zeros((iterations,3))
    # 创建一个形状为 (iterations, 3) 的NumPy数组 history,用于记录每次迭代后的变量值和函数值
    stepsize = 0.1
    # 设置步长为0.1,即每次迭代中参数更新的幅度
    for i in range(iterations): #开始进行迭代优化的循环,循环次数为 iterations
        
        # forward step (i.e. computing the function for current input)
        z = sum((p - torch.tensor([5,7]))**2) + 20#计算当前输入的函数值 z。这里我们使用的是目标函数 $z = (x-5)^2+(y-7)^2 + 20$。
        
        # backpropagation of gradients
        z.backward()#执行梯度的反向传播,计算相对于张量 p 的梯度
        
        # disable gradient computations to perform update
        with torch.no_grad():#进入一个上下文管理器,该管理器中的操作将不会被追踪或记录,也不会影响梯度的计算。
            p = p - stepsize*p.grad#根据梯度下降法的更新规则,更新参数 p。即用当前的参数值减去学习率乘以梯度值。
                 
            z = sum((p - torch.tensor([5,7]))**2) + 20  # new value for display and history
            #计算目标函数 $z = (x-5)^2+(y-7)^2 + 20$ 的值
            print(f"iteration {i+1}: x ={p[0].numpy(): .2f}, y ={p[1].numpy(): .2f}, z ={z: .2f}")
            #打印出当前迭代步骤的参数值和函数值
            history[i,0:2] = p.data#将当前参数值记录到 history 数组中
            history[i,2] = z#将当前函数值记录到 history 数组中
           
        # new p requires_grad=False so turn on
        p = p.requires_grad_(True)#将参数张量 p 的 requires_grad 属性重新设置为 True,以便下一次迭代时能够计算梯度。
        
    # Alternative update that works in-place on p这是一种在原地更新参数的替代方法
    # Disable gradient compututations and update p in-place
    #    with torch.no_grad():
    #        p += -0.1*p.grad
    #   zero out p.grad (in-place) prior to next iteration
    #   p.grad.zero_()
    
    iteration 1: x = 1.00, y = 1.40, z = 67.36
    iteration 2: x = 1.80, y = 2.52, z = 50.31
    iteration 3: x = 2.44, y = 3.42, z = 39.40
    iteration 4: x = 2.95, y = 4.13, z = 32.42
    iteration 5: x = 3.36, y = 4.71, z = 27.95
    iteration 6: x = 3.69, y = 5.16, z = 25.09
    iteration 7: x = 3.95, y = 5.53, z = 23.25
    iteration 8: x = 4.16, y = 5.83, z = 22.08
    iteration 9: x = 4.33, y = 6.06, z = 21.33
    iteration 10: x = 4.46, y = 6.25, z = 20.85
    iteration 11: x = 4.57, y = 6.40, z = 20.55
    iteration 12: x = 4.66, y = 6.52, z = 20.35
    iteration 13: x = 4.73, y = 6.62, z = 20.22
    iteration 14: x = 4.78, y = 6.69, z = 20.14
    iteration 15: x = 4.82, y = 6.75, z = 20.09
    iteration 16: x = 4.86, y = 6.80, z = 20.06
    iteration 17: x = 4.89, y = 6.84, z = 20.04
    iteration 18: x = 4.91, y = 6.87, z = 20.02
    iteration 19: x = 4.93, y = 6.90, z = 20.02
    iteration 20: x = 4.94, y = 6.92, z = 20.01
    iteration 21: x = 4.95, y = 6.94, z = 20.01
    iteration 22: x = 4.96, y = 6.95, z = 20.00
    iteration 23: x = 4.97, y = 6.96, z = 20.00
    iteration 24: x = 4.98, y = 6.97, z = 20.00
    iteration 25: x = 4.98, y = 6.97, z = 20.00
    iteration 26: x = 4.98, y = 6.98, z = 20.00
    iteration 27: x = 4.99, y = 6.98, z = 20.00
    iteration 28: x = 4.99, y = 6.99, z = 20.00
    iteration 29: x = 4.99, y = 6.99, z = 20.00
    iteration 30: x = 4.99, y = 6.99, z = 20.00
    iteration 31: x = 5.00, y = 6.99, z = 20.00
    iteration 32: x = 5.00, y = 6.99, z = 20.00
    iteration 33: x = 5.00, y = 7.00, z = 20.00
    iteration 34: x = 5.00, y = 7.00, z = 20.00
    iteration 35: x = 5.00, y = 7.00, z = 20.00
    iteration 36: x = 5.00, y = 7.00, z = 20.00
    iteration 37: x = 5.00, y = 7.00, z = 20.00
    iteration 38: x = 5.00, y = 7.00, z = 20.00
    iteration 39: x = 5.00, y = 7.00, z = 20.00
    iteration 40: x = 5.00, y = 7.00, z = 20.00
    iteration 41: x = 5.00, y = 7.00, z = 20.00
    iteration 42: x = 5.00, y = 7.00, z = 20.00
    iteration 43: x = 5.00, y = 7.00, z = 20.00
    iteration 44: x = 5.00, y = 7.00, z = 20.00
    iteration 45: x = 5.00, y = 7.00, z = 20.00
    iteration 46: x = 5.00, y = 7.00, z = 20.00
    iteration 47: x = 5.00, y = 7.00, z = 20.00
    iteration 48: x = 5.00, y = 7.00, z = 20.00
    iteration 49: x = 5.00, y = 7.00, z = 20.00
    iteration 50: x = 5.00, y = 7.00, z = 20.00
    
  3. 显示梯度下降算法在优化过程中的路径和目标函数的曲面,即要求绘制出优化过程中参数 (x, y) 的轨迹以及目标函数曲面。

    1. history[:,0] 包含了优化过程中每一步的参数 x 的值,history[:,1] 包含了每一步的参数 y 的值。由于梯度下降算法是在二维空间中进行优化的,所以我们可以将参数 xy 的变化路径绘制在二维平面上。

    2. 而在三维曲面图中,0 表示 z 轴的位置。因此,ax.plot(history[:,0], history[:,1], 0, color='green', marker='.', markeredgecolor='red') 将在三维曲面图中绘制出参数 xy 的变化路径,通过这个路径我们可以看到梯度下降算法在曲面上移动的轨迹。 color='green' 设置轨迹的颜色为绿色, marker='.' 表示使用圆点作为标记, markeredgecolor='red' 表示圆点的边缘颜色为红色。

    3. ax.plot(history[:,0], history[:,1], 0, color='green', marker='.', markeredgecolor='red') 中的第三个参数 0 表示将路径绘制在 z=0 这个平面上。在二维空间中,我们只能绘制两个维度的数据,而曲面图中的 xy 对应于二维平面上的横坐标和纵坐标。因此,我们无法直接在曲面图中绘制三维数据(包括 z 轴)的路径。为了可视化梯度下降算法在二维平面上的路径,我们将 z 设置为一个固定的值,通常选择 0 或其他合适的值,以便路径能够清晰可见。

      # Make data.
      X = np.arange(-1,13, 0.1)#生成一个从-1到12.9的数组,步长为0.1,用于表示x轴上的数据。
      Y = np.arange(-1,13, 0.1)#生成一个从-1到12.9的数组,步长为0.1,用于表示y轴上的数据。
      X, Y = np.meshgrid(X, Y)
      #使用 np.meshgrid 函数创建网格,将 X 和 Y 的值组合成二维平面上的坐标点。
      
      Z = ((X-5)**2 + (Y-7)**2) + 20#计算二元函数 $z = (x-5)^2 + (y-7)^2 + 20$ 在每个坐标点上的函数值,得到一个二维数组 Z
      
      # Plot the surface.
      fig = plt.figure()#创建一个新的图形窗口
      ax = plt.axes(projection='3d')#在图形窗口上创建一个三维坐标轴
      ax.plot_surface(X,Y,Z, cmap='jet', antialiased=True, alpha=0.5)
      #绘制三维曲面图,横坐标为 X,纵坐标为 Y,高度为 Z,使用彩色图谱 jet,开启抗锯齿效果,设置透明度为0.5。
      ax.set_xlabel('x'); ax.set_ylabel('y'); ax.set_zlabel('z')#设置坐标轴的标签。
      ax.plot(history[:,0], history[:,1], 0, color='green', marker='.', markeredgecolor='red')
      #在曲面图上绘制梯度下降算法的路径。history[:,0] 和 history[:,1] 表示参数 x 和 y 的变化轨迹,0表示 z 值为0,
      # color='green' 表示颜色为绿色,marker='.' 表示使用圆点作为标记,markeredgecolor='red' 表示圆点的边缘颜色为红色。
      
      plt.show()
      

      在这里插入图片描述

  4. 反向传播Backpropagation:https://www.bilibili.com/video/BV1vA4y1o7ax/?spm_id_from=333.337.search-card.all.click&vd_source=0b01d46b5ffcde6bf7c9dc1429a00d7b

    在这里插入图片描述
    在这里插入图片描述
    对于任何树状结构的计算图,我们都可以以这种方式反向传播梯度,使用链规则沿着从输出(根)到输入(叶)的唯一路径反向传播梯度
    在这里插入图片描述
    在这里插入图片描述

使用GPU

  1. if torch.cuda.is_available(): device = torch.device('cuda'): 这行代码首先检查是否有GPU可用。torch.cuda.is_available() 函数会返回一个布尔值,表示当前环境是否有CUDA可用,如果有,则将 device 设置为 'cuda',即GPU设备。

    #GPUs operations
    #First check devices available (gpus or cpu), 'cuda' stands for gpus
    if torch.cuda.is_available(): device = torch.device('cuda')#检查是否有GPU可用
    else: device = torch.device('cpu')#如果没有GPU可用,则将 device 设置为 'cpu',表示使用CPU设备。
    
    print(device)
    
  2. 这里使用 to 函数将涉及到的张量移动到当前设备(GPU或CPU)上。
    注意:在训练网络时,需要将模型和输入数据都发送到正确的设备上。通常使用 model.to(device)inputs.to(device) 将模型和输入数据发送到设备上。

    #Use function 'to' to move the tensors (those involved on the operation) to the current device
    #When training your network don't forget to send the model (model.to(device)) 
    #and the inputs (inputs.to(device)) to the correct device
    
    tensor1 = torch.ones([2, 2]).to(device)#创建了一个形状为 [2, 2] 的全为1的张量 tensor1,并将其移动到当前设备上。
    tensor2 = torch.ones([2, 2]).to(device)#创建了另一个形状为 [2, 2] 的全为1的张量 tensor2,并将其移动到当前设备上。
    
    operation = tensor1 + tensor2#执行了一个张量操作,将 tensor1 和 tensor2 相加,由于它们都在同一个设备上,因此可以进行相加操作。
    print(operation)#打印出 operation,即相加后的结果张量
    
    #输出
    tensor([[2., 2.],
            [2., 2.]], device='cuda:0')
    
  3. 也可以直接在设备上创建张量。如下:使用 torch.ones 函数创建一个形状为 [3, 3] 的张量,并将其所有元素初始化为1。通过指定 device=device,将该张量直接创建在指定的设备上。

    #Alternatively you can create the tensors directly on the device
    tensorondevice = torch.ones([3, 3], device=device)
    # 在当前设备(GPU或CPU)上创建一个形状为 [3, 3] 的张量,并将其所有元素初始化为1。
    

Reference

来自学校ppt和实验资料

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值