PyTorch基本使用

一、Pytorch的环境配置

1.1.安装CPU环境的pytorch

1.1.1.创建虚拟环境

创建虚拟环境命令格式:

conda create -n 环境名 python=版本号

比如我创建的存放cpu版本pytorch的conda为:

conda create -n cpupytorch python=3.9

大家可以根据自己的需要去下载,输入命令后点击回车,然后输入y,回车,下载完成!

1.1.2.进入虚拟环境

输入conda activate 环境名:

前面的括号里面的东西代表你现在处于哪个虚拟环境。

1.1.3.删除虚拟环境

如果你发现你的环境下错了,想删除这个环境,可以使用这句话:

conda remove -n 环境名 --all

1.1.4.安装CPU环境的pytorch

1.1.4.1. 进入Pytorch官网

进入官网以后往下滑,找到命令:

查看发现在历史版本中有对应的Anaconda的包安装命令:

直接复制命令,不指定版本,让安装最新的版本即可

conda install pytorch torchvision torchaudio cpuonly -c pytorch
1.1.4.2.安装Pytorch

打开anaconda命令行窗口,进入到你的虚拟环境中,输入命令,然后输入y,等待下载完成:

如果你觉得下载速度慢可以使用清华的镜像源安装(要么从清华的镜像源下载要么就从官网下载,二者选一个就好):

清华镜像源:Index of /anaconda/cloud/pytorch/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

找到适合你的镜像,点进去:

然后复制网址,把命令改成: 

conda install pytorch torchvision torchaudio cpuonly -c 刚才复制的链接

然后输入命令等待下载完成。

1.1.4.3.验证Pytorch

进入你的虚拟环境,输入conda list,如果有Pytorch说明安装成功:

另一种验证方法:在虚拟环境的命令行中输入python,然后输入import torch,等待导入完成,然后输入torch.cuda.is_available(),如果出现False,说明安装成功!

1.2.安装GPU环境的pytorch

1.2.1.准备虚拟环境

首先打开Anaconda的命令行,创建虚拟环境命令格式:

conda create -n 环境名 python=版本号

比如我创建的存放gpu版本pytorch的conda为:

conda create -n gpupytorch python=3.9

大家可以根据自己的需要去下载,输入命令后点击回车,然后输入y,回车,下载完成!

1.2.2.进入虚拟环境

输入conda activate 环境名:

conda activate gpupytorch

前面的括号里面的东西代表你现在处于哪个虚拟环境。

1.2.3.删除虚拟环境

如果你发现你的环境下错了,想删除这个环境,可以使用这句话:

conda remove -n 环境名 --all

1.2.4.安装GPU环境的pytorch

1.2.4.1.先检查自己的电脑所支持的CUDA版本是多少

桌面右键点击进去NVIDIA控制面板,找到左下角的系统信息,点击组件,出现如下界面。

从NVCUDA.DLL 这一行后面的CUDA 11.6说明我的电脑所支持的最高版本是11.6。

也可以通过下面方式查询支持的CUDA:安装前用以下命令查询机器上显卡最高支持的CUDA 版本,终端输入:

nvidia-smi

下图中CUDA Version是11.6。

如果你没有安装 cuda toolkit 或者需要升级,可以去官网下载:https://developer.nvidia.com/cuda-toolkit-archive

如果不显示此界面,请先更新显卡驱动,官网:官方驱动 | NVIDIA 

选择符合自己电脑配置的版本,然后安装即可:

1.2.4.2.官网下载相对应的CUDA: (https://developer.nvidia.com/cuda-toolkit-archive)

CUDA(Compute Unified Device Architecture)是NVIDIA推出的一个平行计算平台和编程模型,它允许开发者使用NVIDIA的GPU进行通用计算。CUDA将GPU视为一个可以执行复杂算术计算的协处理器,并与主机的CPU协同工作,从而使开发者能够利用GPU的并行计算能力来加速应用程序。

NVIDIA CUDA Toolkit Archive页面上,用户可以找到各个版本的CUDA Toolkit,这些工具包提供了开发CUDA应用程序所需的库、头文件、编译器、调试器等工具。开发者可以根据自己的需求,选择合适的CUDA Toolkit版本进行下载和使用。

CUDA的应用范围非常广泛,包括科学计算、图形处理、数据分析、人工智能等领域。通过使用CUDA,开发者可以显著提高应用程序的性能,尤其是在处理大规模数据和复杂计算任务时。

我所支持的版本是11.6,所以我下载的是红色箭头所标出的那行。点击以后出现如下页面,选择第一个下载即可。

下载完成后,在所在的文件夹下运行安装即可。

然后查看CUDA是否安装成功: cmd中运行到安装的文件目录下:

cd C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.6\bin

然后执行nvcc -V,查看是否出现如下信息,有则说明CUDA安装成功。

如果输入nvcc -V 出现错误,考虑是否将Anaconds加入环境变量PATH中。

1.2.4.3.确定cuDNN驱动版本

NVIDIA CUDA深度神经网络库 (cuDNN) 是一个 GPU 加速的深度神经网络基元库,能够以高度 优化的方式实现标准例程(如前向和反向卷积、池化层、归一化和激活层)。
全球的深度学习研究人员和框架开发者都依赖 cuDNN 来实现高性能 GPU 加速。借助 cuDNN, 研究人员和开发者可以专注于训练神经网络及开发软件应用,而不必花时间进行低层级的 GPU 性能调整。cuDNN 可加速广泛应用的深度学习框架,包括 Caffe2、Keras、MATLAB、 MxNet、PaddlePaddle、PyTorch和 TensorFlow。
下载地址:cuDNN Archive | NVIDIA Developer

用以下命令查询机器上显卡最高支持的CUDA 版本:

nvidia-smi

下载:

我的是CUDA11.6选择的对应版本。等待下载完成后进行解压,得到一个cuda文件夹,进入之后,全选,复制到之前CUDA所安装的文件夹下,有重复的进行替换即可。

CUDA安装默认路径:

  • Windows: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA
  • Linux: /usr/local/cuda

查看cuDNN是否安装成功: 步骤如下:(进入安装的路径)

出现如下Result = PASS 说明cuDNN安装成功。

可以再接着执行deviceQuery.exe,如果出现Result = PASS 说明CUDA和cuDNN都已经安装成功了。

1.2.4.4.安装Pytorch

打开pytorch安装指导网站,选择合适的系统平台,关键是在 compute platform 选择一个不高 于你电脑上的 CUDA Version ,复制命令安装。

  • pip install torch==版本号
  • conda install torch==版本号

安装命令:

# 使用conda安装
conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.6 -c pytorch -c nvidia # 使用pip安装 pip install torch torchvision torchaudio
--index-url https://download.pytorch.org/whl/cu117

注意:pytorch-cuda=11.6这个根据CUDA的版本决定

二、张量的使用

2.1.基本类型

  • 0维张量:标量(scalar)
scalar = torch.tensor(7)         
scalar.ndim
>>> 0
  • 1维张量:向量(vector)
vector = torch.tensor([7, 7])        
vector.ndim
>>> 1
  • 2维张量:矩阵(matrix)
MATRIX = torch.tensor([[7, 8], [9, 10]])
MATRIX.ndim
>>> 2
  • 多维张量
TENSOR = torch.tensor([[[1, 2, 3],[3, 6, 9],[2, 4, 5]]])
TENSOR.ndim
>>> 3

2.2.张量创建

  • 1.torch.tensor 根据指定数据创建张量
import torch
import numpy as np

# 1,创建张量标量
data = torch.tensor(10)
print(data)  # 输出tensor(10)

# 2. numpy 数组, 由于 data 为 float64, 下面代码也使用该类型
data = np.random.randn(2, 3)
data = torch.tensor(data)
print(data)

# 3. 列表, 下面代码使用默认元素类型  float32
data = [[10,20,30],[40,50,60]]
data = torch.tensor(data)
print(data)

输出如下:

tensor(10)
tensor([[-0.2179, -0.2428,  0.7984],
        [ 0.4661, -0.0081, -0.9212]], dtype=torch.float64)
tensor([[10, 20, 30],
        [40, 50, 60]])
  • 2.torch.Tensor 根据形状创建张量, 其也可用来创建指定数据的张量
import torch

# 1. 创建2行3列的张量, 默认  dtype 为  float32
data = torch.Tensor(2, 3)
print(data)

# 2. 注意: 如果传递列表, 则创建包含指定元素的张量
data = torch.Tensor([10])
print(data)
data = torch.Tensor([10, 20])
print(data)

输出如下:

tensor([[7.8675e+34, 4.6894e+27, 1.6217e-19],
        [1.4333e-19, 2.7530e+12, 7.5338e+28]])
tensor([10.])
tensor([10., 20.])
  • 3.torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量
import torch

# 1. 创建2行3列, dtype 为  int32 的张量
data = torch.IntTensor(2, 3)
print(data)

# 2. 注意: 如果传递的元素类型不正确, 则会进行类型转换
data = torch.IntTensor([2.5, 3.3])
print(data)

# 3. 其他的类型
data = torch.ShortTensor()  # int16
data = torch.LongTensor()   # int64 
data = torch.FloatTensor()  # float32 
data = torch.DoubleTensor() # float64

输出如下:

tensor([[2035577441,  541025648,  539767135],
        [1870225740,  541029493,  539767391]], dtype=torch.int32)
tensor([2, 3], dtype=torch.int32)
  • 4.创建线性和随机张量

torch.arange 和 torch.linspace 创建线性张量

import torch

# 1. 在指定区间按照步长生成元素  [start, end, step)
data = torch.arange(0, 10, 2)
print(data)

# 2. 在指定区间按照元素个数生成 [start, end, steps]
data = torch.linspace(0, 11, 10)
print(data)

输出:

tensor([0, 2, 4, 6, 8])
tensor([ 0.0000,  1.2222,  2.4444,  3.6667,  4.8889,  6.1111,  7.3333,  8.5556,
         9.7778, 11.0000])

torch.random.init_seed、 torch.random.manual_seed 随机种子设置和torch.randn 创建随机张量

import torch

# 1. 创建随机张量
data = torch.randn(2, 3)  # 创建2行3列张量
print(data)
# 查看随机数种子
print(f"随机数种子:{torch.random.initial_seed()}")

# 2. 随机数种子设置
torch.random.manual_seed(100)
data = torch.randn(2, 3)
print(data)
print(f"随机数种子:{torch.random.initial_seed()}")

输出:

tensor([[-0.2426, -0.4330, -0.8342],
        [ 1.2758,  0.3775,  1.3942]])
随机数种子:368228203513900
tensor([[ 0.3607, -0.2859, -0.3938],
        [ 0.2429, -1.3833, -2.3134]])
随机数种子:100
  • 5.创建0-1张量

torch.zeros 和 torch.zeros_like 创建全0张量

import torch

# 1. 创建指定形状全0张量
data = torch.zeros(2, 3)
print(data)

# 2. 根据张量形状创建全0张量
data = torch.zeros_like(data)
print(data)

输出:

tensor([[0., 0., 0.],
        [0., 0., 0.]])
tensor([[0., 0., 0.],
        [0., 0., 0.]])

torch.ones 和 torch.ones_like 创建全1张量

import torch
# 1. 创建指定形状全1张量
data = torch.ones(2, 3)
print(data)
# 2. 根据张量形状创建全1张量
data = torch.ones_like(data)
print(data)

输出如下:

tensor([[1., 1., 1.],
        [1., 1., 1.]])
tensor([[1., 1., 1.],
        [1., 1., 1.]])

torch.full 和 torch.full_like 创建全为指定值张量

import torch

# 1. 创建指定形状指定值的张量
data = torch.full([2, 3], 10)
print(data)
# 2. 根据张量形状创建指定值的张量
data = torch.full_like(data, 20)
print(data)

输出:

tensor([[10, 10, 10],
        [10, 10, 10]])
tensor([[20, 20, 20],
        [20, 20, 20]])
  • 6.张量元素类型转换

data.type(torch.DoubleTensor)

import torch

data = torch.full([2,3], 10)
print(data.dtype)
# 将 data 元素类型转换为 float64 类型
data = data.type(torch.DoubleTensor)
print(data.dtype)
# 转换为其他类型
# data = data.type(torch.ShortTensor)
# data = data.type(torch.IntTensor)
# data = data.type(torch.LongTensor)
# data = data.type(torch.FloatTensor)

输出:

torch.int64
torch.float64

data.double()

import torch

data = torch.full([2,3], 10)
print(data.dtype)
# 将 data 元素类型转换为 float64 类型
data = data.double()
print(data.dtype)
# 转换为其他类型
# data = data.short()
# data = data.int()
# data = data.long()
# data = data.float()

2.3.张量类型转换

2.3.1.张量转换为NumPy数组

使用 Tensor.numpy 函数可以将张量转换为 ndarray 数组,但是共享内存,可以使用 copy 函数避免共享。

import torch

# 1. 将张量转换为  numpy 数组
data_tensor = torch.tensor([1, 2, 3, 4, 5]) # 创建一个PyTorch张量:将Python列表 [1, 2, 3, 4, 5] 转换为PyTorch的Tensor对象
print(data_tensor)
# 使用张量对象中的  numpy 函数进行转换
data_numpy = data_tensor.numpy()
print(type(data_tensor))
print(type(data_numpy))
  • 输出:
tensor([1, 2, 3, 4, 5])
<class 'torch.Tensor'>
<class 'numpy.ndarray'>

可以使用copy()函数避免共享

import torch

# 1. 将张量转换为  numpy 数组
data_tensor = torch.tensor([1, 2, 3, 4, 5]) # 创建一个PyTorch张量:将Python列表 [1, 2, 3, 4, 5] 转换为PyTorch的Tensor对象
print(data_tensor)
# 使用张量对象中的  numpy 函数进行转换, 通过copy()方法拷贝对象
data_numpy = data_tensor.numpy().copy()
print(type(data_tensor))
print(type(data_numpy))
# 注意: data_tensor 和  data_numpy 此时不共享内存
# 修改其中的一个,另外一个不会发生改变
# data_tensor[0] = 100
data_numpy[0] = 100
print(data_tensor)
print(data_numpy)
  • 输出如下:
tensor([1, 2, 3, 4, 5])
<class 'torch.Tensor'>
<class 'numpy.ndarray'>
tensor([1, 2, 3, 4, 5])
[100   2   3   4   5]

2.3.2.NumPy数组转换为张量

使用 from_numpy 可以将 ndarray 数组转换为 Tensor,默认共享内存,使用 copy 函数避 免共享。

import numpy as np
import torch

data_numpy = np.array([2, 3, 4])
# 将 numpy 数组转换为张量类型
data_tensor = torch.from_numpy(data_numpy)
# nunpy 和  tensor 共享内存
# data_numpy[0] = 100
data_tensor[0] = 100
print(data_tensor)
print(data_numpy)
  • 输出:
tensor([100,   3,   4], dtype=torch.int32)
[100   3   4]

使用 torch.tensor 可以将 ndarray 数组转换为 Tensor,默认不共享内存。

import numpy as np
import torch

data_numpy = np.array([2, 3, 4])
data_tensor = torch.tensor(data_numpy)
# nunpy 和  tensor 不共享内存
data_numpy[0] = 100
# data_numpy[0] = 100
data_tensor[0] = 100
print(data_tensor) 
print(data_numpy)
  • 输出:
tensor([100,   3,   4], dtype=torch.int32)
[100   3   4]

2.3.3.标量张量和数字转换

对于只有一个元素的张量,使用item()函数将该值从张量中提取出来

import torch

# 当张量只包含一个元素时, 可以通过 item() 函数提取出该值
data = torch.tensor([30, ])
print(data.item())
data = torch.tensor(30)
print(data.item())
  • 输出:
30 
30

2.4.张量数值计算

2.4.1.掌握张量基本运算

加减乘除取负号:

  • add、sub、mul、div、neg
  • add_、sub_、mul_、div_、neg_(其中带下划线的版本会修改原数据)

案例:

import torch

data = torch.randint(0, 10, [2, 3])
print(data)
# 1. 不修改原数据
new_data = data.add(10)  # new_data = data+10
print(new_data)
# 2. 直接修改原数据 注意: 带下划线的函数为修改原数据本身
data.add_(10)
print(data)
# 3. 其他函数
print(data.sub(100)) # 减法
print(data.mul(100)) # 乘法
print(data.div(100)) # 除法
print(data.neg()) # 取负数

输出:

tensor([[5, 8, 3],
        [3, 7, 1]])
tensor([[15, 18, 13],
        [13, 17, 11]])
tensor([[15, 18, 13],
        [13, 17, 11]])
tensor([[-85, -82, -87],
        [-87, -83, -89]])
tensor([[1500, 1800, 1300],
        [1300, 1700, 1100]])
tensor([[0.1500, 0.1800, 0.1300],
        [0.1300, 0.1700, 0.1100]])
tensor([[-15, -18, -13],
        [-13, -17, -11]])

2.4.2.掌握张量点乘运算

点乘指(Hadamard)的是两个同维矩阵对应位置的元素相乘,使用mul 和运算符 * 实现。

案例:

import torch

data1 = torch.tensor([[1, 2], [3, 4]])
data2 = torch.tensor([[5, 6], [7, 8]])
print(data1)
print(data2)
# 第一种方式
data = torch.mul(data1, data2)
print(data)

# 第二种方式
data = data1 * data2
print(data)

输出:

tensor([[1, 2],
        [3, 4]])
tensor([[5, 6],
        [7, 8]])
tensor([[ 5, 12],
        [21, 32]])
tensor([[ 5, 12],
        [21, 32]])

2.4.3.掌握张量矩阵乘法运算

矩阵乘法运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。

  1. 运算符 @ 用于进行两个矩阵的乘积运算
  2. torch.matmul 对进行乘积运算的两矩阵形状没有限定.对数输入的 shape 不同的张量, 对应的最后几个维度必须符合 矩阵运算规则

案例:

import torch

# 点积运算
data1 = torch.tensor([[1, 2], [3, 4], [5, 6]])
data2 = torch.tensor([[5, 6], [7, 8]])
print(data1)
print(data2)
# 方式一
data3 = data1 @ data2
print(f"data3---->{data3}")
# 方式二
data4 = torch.matmul(data1, data2)
print(f"data4---->{data4}")

输出:

tensor([[1, 2],
        [3, 4],
        [5, 6]])
tensor([[5, 6],
        [7, 8]])
data3---->tensor([[19, 22],
        [43, 50],
        [67, 78]])
data4---->tensor([[19, 22],
        [43, 50],
        [67, 78]])

结果矩阵的第i行第j列元素 = data1第i行与data2第j列的点积,分析:

# 结果矩阵第一行
(1*5 + 2*7) = 19  (1*6 + 2*8) = 22

# 结果矩阵第二行 
(3*5 + 4*7) = 43  (3*6 + 4*8) = 50

# 结果矩阵第三行
(5*5 + 6*7) = 67  (5*6 + 6*8) = 78

关键点总结:

  1. 矩阵乘法要求第一个矩阵的列数等于第二个矩阵的行数

  2. 结果矩阵的行数=第一个矩阵的行数,列数=第二个矩阵的列数

  3. PyTorch中@运算符和torch.matmul()函数等价

  4. 这种运算在神经网络中非常重要,是全连接层的核心操作

2.5.张量运算函数

PyTorch 为每个张量封装很多实用的计算函数:

  • 均值
  • 平方根
  • 求和
  • 指数计算
  • 对数计算等等

案例:

import torch

data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
print(data)
# 1. 计算均值
# 注意: tensor 必须为 Float 或者 Double 类型
print(data.mean())
print(data.mean(dim=0))  # 按列计算均值
print(data.mean(dim=1))  # 按行计算均值

# 2.计算总和
print(data.sum())
print(data.sum(dim=0))  # 按列计算求和
print(data.sum(dim=1))  # 按行计算求和

# 3. 计算平方
print(torch.pow(data, 2))

# 4. 计算平方根
print(data.sqrt())

# 5. 指数计算, e^n 次方
# 计算张量 data 中每个元素的自然指数函数值(即以自然常数 e≈2.71828 为底的指数函数),并打印结果。
print(data.exp())

# 6. 对数计算
print(data.log())  # 以 e 为底, 计算张量 data 中每个元素的自然对数(以 *e* 为底,*e* ≈ 2.71828)
print(data.log2())  # 计算张量 data 中每个元素的以 2 为底的对数
print(data.log10())  # 计算张量 data 中每个元素的以 10 为底的对数

输出如下:

tensor([[6., 0., 5.],
        [2., 6., 6.]], dtype=torch.float64)
tensor(4.1667, dtype=torch.float64)
tensor([4.0000, 3.0000, 5.5000], dtype=torch.float64)
tensor([3.6667, 4.6667], dtype=torch.float64)
tensor(25., dtype=torch.float64)
tensor([ 8.,  6., 11.], dtype=torch.float64)
tensor([11., 14.], dtype=torch.float64)
tensor([[36.,  0., 25.],
        [ 4., 36., 36.]], dtype=torch.float64)
tensor([[2.4495, 0.0000, 2.2361],
        [1.4142, 2.4495, 2.4495]], dtype=torch.float64)
tensor([[403.4288,   1.0000, 148.4132],
        [  7.3891, 403.4288, 403.4288]], dtype=torch.float64)
tensor([[1.7918,   -inf, 1.6094],
        [0.6931, 1.7918, 1.7918]], dtype=torch.float64)
tensor([[2.5850,   -inf, 2.3219],
        [1.0000, 2.5850, 2.5850]], dtype=torch.float64)
tensor([[0.7782,   -inf, 0.6990],
        [0.3010, 0.7782, 0.7782]], dtype=torch.float64)

说明:

函数典型应用场景
.log() 概率模型、交叉熵损失、梯度下降
.log2() 信息熵、算法复杂度分析、信号处理
.log10() 数据标准化、科学计量(如分贝、地震级数)

2.6.张量索引操作

2.6.1.简单行列索引

在操作张量时,经常要去获取某些元素进行处理或者修改操作,在这里需要了解torch中的索引操作。 准备数据:

import torch

# 随机生成数据
data = torch.randint(0,10,[4,5])
print(data)

输出结果:

tensor([[2, 1, 3, 8, 9],
        [6, 7, 5, 7, 2],
        [2, 9, 4, 9, 1],
        [0, 7, 7, 9, 5]])

根据索引取值

import torch

# 随机生成数据
data = torch.randint(0,10,[4,5])
print(data)
print(data[0])
print(data[:,0])

注意:

tensor([[5, 8, 0, 4, 8],
        [2, 3, 5, 5, 1],
        [6, 6, 5, 6, 4],
        [2, 8, 0, 6, 6]])
tensor([5, 8, 0, 4, 8])
tensor([5, 2, 6, 2])

data[:, 0] 表示:

  • ::选择所有行(第一个维度,即行维度)

  • 0:选择第0列(第二个维度,即列维度)

2.6.2.列表索引

import torch

# 随机生成数据
data = torch.randint(0, 10, [4, 5])
print(data)

# 返回 (0, 1)、(1, 2) 两个位置的元素
print(data[[0, 1], [1, 2]])
# 返回  0、1 行的  1、2 列共4个元素
print(data[[[0], [1]], [1, 2]])

输出结果:

tensor([[6, 3, 9, 4, 0],
        [3, 8, 7, 4, 5],
        [1, 4, 1, 7, 9],
        [7, 6, 3, 8, 4]])
tensor([3, 7])
tensor([[3, 9],
        [8, 7]])

2.6.3.范围索引

import torch

# 随机生成数据
data = torch.randint(0, 10, [4, 5])
print(data)
# 前3行的前2列数据
print(data[:3,:2])

# 第2行到最后的前2列数据
print(data[2:, :2])

输出结果:

tensor([[2, 3, 6, 0, 9],
        [3, 6, 6, 5, 4],
        [5, 5, 9, 8, 9],
        [8, 4, 1, 4, 1]])
tensor([[2, 3],
        [3, 6],
        [5, 5]])
tensor([[5, 5],
        [8, 4]])

2.6.4.布尔索引

import torch

# 随机生成数据
data = torch.randint(0, 10, [4, 5])
print(data)
# 第三列大于5的行数据
# data[:, 2] 获取的是所有行,2表示索引,取的是第三列
print(data[data[:, 2] > 5])

输出结果:

tensor([[0, 7, 0, 4, 4],
        [9, 5, 8, 8, 8],
        [6, 8, 9, 8, 8],
        [0, 3, 3, 0, 0]])
tensor([[9, 5, 8, 8, 8],
        [6, 8, 9, 8, 8]])

2.6.5.多维索引

import torch

#  3 页纸,每页纸上有 4 行 5 列的数字
data = torch.randint(0, 10, [3, 4, 5])
print(data)
# 获取0轴上的第一个数据
print(data[0, :, :])
# 获取1轴上的第一个数据
print(data[:, 0, :])
# 获取2轴上的第一个数据
print(data[:, :, 0])

输出结果:

tensor([[[4, 5, 9, 8, 9],
         [0, 0, 4, 3, 5],
         [0, 0, 3, 6, 2],
         [8, 0, 2, 4, 0]],

        [[0, 0, 5, 3, 0],
         [1, 3, 9, 1, 2],
         [7, 8, 5, 0, 9],
         [1, 8, 4, 2, 4]],

        [[5, 6, 0, 4, 8],
         [2, 9, 5, 5, 6],
         [4, 2, 7, 3, 3],
         [2, 2, 2, 0, 5]]])
tensor([[4, 5, 9, 8, 9],
        [0, 0, 4, 3, 5],
        [0, 0, 3, 6, 2],
        [8, 0, 2, 4, 0]])
tensor([[4, 5, 9, 8, 9],
        [0, 0, 5, 3, 0],
        [5, 6, 0, 4, 8]])
tensor([[4, 0, 0, 8],
        [0, 1, 7, 1],
        [5, 2, 4, 2]])

2.7.张量形状操作

2.7.1.reshape()函数

reshape 函数可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状。

import torch

# 创建一个张量, 包含两个二维向量
data = torch.tensor([[10, 20, 30], [40, 50, 60]])

# 1.使用shape或者size属性获取张量的形状,分别输出 张量的形状、张量的维度和张量的元素个数
print(data.shape, data.shape[0], data.shape[1])
print(data.size(), data.size()[0], data.size()[1])

# 使用reshape函数修改张量的形状
data = data.reshape(1, 6)  # 将张量修改为1行6列
print(data.shape)

输出如下:

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

2.7.2.squeeze()和unsqueeze()函数

squeeze 函数删除形状为 1 的维度(降维),unsqueeze 函数添加形状为1的维度(升维)。

import torch

data1 = torch.tensor([1, 2, 3, 4, 5])
print("普通的一维数组的形状和数据为:", data1.shape, data1)

# dim=0 表示在第0维度上进行操作,这是PyTorch中维度索引的表示方法。
data2 = data1.unsqueeze(dim=0)
print("在0维度上,拓展维度:", data2.shape, data2) # 1*5

# dim=1 表示在第1维度上进行操作
data3 = data1.unsqueeze(dim=1)
print("在1维度上,拓展维度:", data3.shape, data3) # 5*1

data4 = data1.unsqueeze(dim=-1)
print("在-1维度上,拓展维度:", data4.shape, data4)

data5 = data4.squeeze()
print("将拓展的维度去掉:", data5.shape, data5)

输出结果:

普通的一维数组的形状和数据为: torch.Size([5]) tensor([1, 2, 3, 4, 5])
在0维度上,拓展维度: torch.Size([1, 5]) tensor([[1, 2, 3, 4, 5]])
在1维度上,拓展维度: torch.Size([5, 1]) tensor([[1],
        [2],
        [3],
        [4],
        [5]])
在-1维度上,拓展维度: torch.Size([5, 1]) tensor([[1],
        [2],
        [3],
        [4],
        [5]])
将拓展的维度去掉: torch.Size([5]) tensor([1, 2, 3, 4, 5])

2.7.3.transpose()和permute()函数

transpose 函数可以实现交换张量形状的指定维度, 例如: 一个张量的形状为 (2, 3, 4) 可以通过 transpose 函数把 3 和 4进行交换, 将张量的形状变为 (2, 4, 3) 。 permute 函数可以一次交换更多的维度。

import torch
import numpy as np

# 1.创建的是一个三维张
# - 第0维度(大小3) → 3个"页面"
# - 第1维度(大小4) → 每个页面有4行
# 第2维度(大小5) → 每行有5列
# 可以理解为:3个4×5的二维矩阵
data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print("data.shape",  data.shape)

# 2.交换1和2维度
data2 = torch.transpose(data, 1, 2)
print("data2.shape", data2.shape)

# 3.将data的形状修改为 (4, 5, 3) 需要变换多次
data3 = torch.transpose(data,0, 1) # (4, 3, 5)
data4 = torch.transpose(data3, 1, 2) # (4, 5, 3)
print("data4.shape", data4.shape)

# 3.使用permute函数将data的形状([3, 4, 5])修改为 (4, 5, 3)
# 3-1 方法1
data5 = data.permute(1, 2, 0)
print("data5.shape", data5.shape)

# 3-2 方法2
data6 = torch.permute(data,[1, 2, 0])
print("data6.shape", data6.shape)

输出结果:

data.shape torch.Size([3, 4, 5])
data2.shape torch.Size([3, 5, 4])
data4.shape torch.Size([4, 5, 3])
data5.shape torch.Size([4, 5, 3])
data6.shape torch.Size([4, 5, 3])

2.7.4.view()和contiguous()函数

view 函数也可以用于修改张量的形状,只能用于存储在整块内存中的张量。在 PyTorch 中,有些张量是由不同的数据块组成的,它们并没有存储在整块的内存中,view 函数无法对这样的张量进行变形处理,例如: 一个张量经过了transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。

import torch

# 创建一个张量
data = torch.tensor([[10, 20, 30], [40, 50, 60]])
print("data: ", data, data.shape)

# 1.判断是否使用整块内存
print("data.is_contiguous(): ", data.is_contiguous()) # True

# 2. view 函数,修改张量的形状
data2 = data.view(3, 2)
print("data2: ", data2, data2.shape)

# 3. 判断是否使用整块内存
print("data2.is_contiguous(): ", data2.is_contiguous()) # False

输出如下:

data:  tensor([[10, 20, 30],
        [40, 50, 60]]) torch.Size([2, 3])
data.is_contiguous():  True
data2:  tensor([[10, 20],
        [30, 40],
        [50, 60]]) torch.Size([3, 2])
data2.is_contiguous():  True

一个张量经过了transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。要使用 view 函数,需要使用contiguous()变成连续以后保证张量的数据是连续的。,在使用view函数,

import torch
import numpy as np

# 1.创建的是一个三维张
# - 第0维度(大小3) → 3个"页面"
# - 第1维度(大小4) → 每个页面有4行
# 第2维度(大小5) → 每行有5列
# 可以理解为:3个4×5的二维矩阵
data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print("data.shape", data.shape)

# 3-2 方法2
data6 = torch.permute(data, [1, 2, 0])
print("data6.shape", data6.shape)

# 4.使用view函数将data的形状([4, 5, 3])修改为 (4, 3, 5)
data6 = data6.contiguous()
data7 = data6.view(4, 3, 5)
print("data7.shape", data7.shape, data7)

2.8.张量拼接操作

torch.cat() 函数可以将两个张量根据指定的维度拼接起来,不改变维度数。

import torch

data1 = torch.randint(0, 10, [1, 2, 3])
data2 = torch.randint(0, 10, [1, 2, 3])
print(data1)
print(data2)

# 1.按0维度拼接  [1, 2, 3]+ [1, 2, 3] = [2, 2, 3]
new_data = torch.cat([data1, data2], dim=0)
print(new_data)
print(new_data.shape)

# 2.按1维度拼接    [1, 2, 3]+ [1, 2, 3] = [1, 4, 3]
new_data = torch.cat([data1, data2], dim=1)
print(new_data)
print(new_data.shape)

# 3.按2维度拼接 [1, 2, 3]+ [1, 2, 3] = [1, 2, 6]
new_data = torch.cat([data1, data2], dim=2)
print(new_data)
print(new_data.shape)

2.9.张量自动微分模块

训练神经网络时,最常用的算法就是反向传播。在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整。为了计算这些梯度,PyTorch内置了名为 torch.autograd 的微分引擎。

  • 自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。
  • 自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中,Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新。

它支持任意计算图的自动梯度计算:

image

2.9.1 梯度基本运算

  • 采用backward()方法可以进行自动微分
  • 采用backward()方法需要f是一个标量,如果不是标量就需要传入一个gradient参数,它是形状匹配的张量。

用生活案例理解:导数是 “变化快慢”:

案例 1:位移与速度(最经典的导数场景)

假设你开车时,“位移”(行驶的距离,记为函数 (s(t)))随 “时间”(记为 t)变化
  • 如果你 1 小时走了 60 公里,“平均速度” 是 60 公里 / 小时(这是 “平均变化率”);
  • 但仪表盘显示的 “瞬时速度”(比如某一秒的速度是 58 公里 / 小时),就是位移函数 (s(t)) 对时间 t 的导数(这是 “瞬时变化率”)。
此时,导数的物理意义就是 “瞬时速度”—— 它描述了 “时间每微小增加 1 单位,位移会增加多少”。

案例 2:函数图像的切线斜率

比如你熟悉的二次函数 y = x^2,它的图像是一条抛物线:
  • 在 x=0 处,抛物线的切线是水平的(斜率为 0),所以此时导数为 0—— 意味着 “当 x 在 0 附近微小变化时,y 几乎不变”;
  • 在 x=1 处,抛物线的切线斜率为 2,所以此时导数为 2—— 意味着 “当 x 在 1 附近每增加 0.001,y 会大约增加 2 * 0.001 = 0.002”;
  • 在 x=1处,切线斜率为 - 2,导数为 - 2—— 意味着 “当 x 在-1附近每增加 0.001,y 会大约减少 0.002”(负号表示 “反向变化”)。
这就是导数的几何意义:函数在某点的导数 = 该点切线的斜率
标量对应 “零维张量”(torch.Tensor 中没有维度的张量),可以通过 ndim 属性查看维度(标量的 ndim=0)。
代码示例:
import torch

# 标量张量(零维)
scalar_1 = torch.tensor(10.0)  # 数值10.0,无方向
scalar_2 = torch.tensor(3.14)  # 数值3.14,无方向

print("标量1的值:", scalar_1)       # 输出:tensor(10.)
print("标量1的维度数:", scalar_1.ndim)  # 输出:0(零维)
print("标量1的形状:", scalar_1.shape)  # 输出:torch.Size([])(空括号表示无维度)
向量对应 “一维张量”(torch.Tensor 中只有一个维度的张量),维度数 ndim=1,形状用 (n,) 表示(n 是向量中元素的个数,即 “向量的长度”)。
import torch

# 向量张量(一维)
vector_1 = torch.tensor([10, 20, 30, 40])  # 4个元素的向量,形状(4,)
vector_2 = torch.tensor([1.5, 2.5, 3.5])   # 3个元素的向量,形状(3,)

print("向量1的值:", vector_1)         # 输出:tensor([10, 20, 30, 40])
print("向量1的维度数:", vector_1.ndim)   # 输出:1(一维)
print("向量1的形状:", vector_1.shape)   # 输出:torch.Size([4])(4个元素的一维张量)
print("向量1的长度(元素个数):", len(vector_1))  # 输出:4

①.标量的梯度计算

目的是计算函数  y = x^2 + 20  在  x = 10 处的导数(梯度),让我们逐行解释它的作用:

import torch

# 实现标量的梯度计算, y=x**2 + 20

# 1.对于需要求导的张量需要设置 requires_grad = True
# requires_grad=True 表示需要跟踪 x 的所有操作,用于后续计算梯度
# dtype=torch.float64 是为了更高的计算精度(求导需要浮点类型)
x = torch.tensor(10.0, requires_grad=True, dtype=torch.float64)
print("x: ", x)

# 2.对x的中间计算,定义关于x的函数(此时 PyTorch 会自动构建计算图,记录 y 是如何从 x 计算得到的(即记录操作历史))
y = x ** 2 + 20

# 3.自动微分,调用backward()之后,会根据f进行求导
# 属于"触发求导" 的关键它会从y开始,沿着之前记录的计算图反向推导,计算y对所有requires_grad=True的张量(这里就是x)的导数
# 本质上是计算y对x的导数
y.backward()

# 4.访问梯度,求得方程式在x处的梯度(x.grad存储的是反向传播计算出的梯度结果,即x=10处的值)
print(x.grad)

输入如下:

x:  tensor(10., dtype=torch.float64, requires_grad=True)
tensor(20., dtype=torch.float64)

②.向量的梯度计算

import torch

# 向量梯度计算  y=x**2 + 20

# 创建包含 4 个元素的向量张量x(可以理解为 4 个自变量 x1=10, x2=20, x3=30, x4=40),并开启了梯度追踪。
x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)

print(x, x.shape)

# 定义函数关系:对 x 中的每个元素做平方后加 20。由于 x 是向量,y1 也会是一个向量(和 x 同形状)
# y1得到的是一个向量,需要将其转为标量,才能使用backward()进行自动微分
y1 = x ** 2 + 20

# autograd 的 backward() 方法默认只能对 “标量” 求导(因为向量对向量的导数是 “矩阵”,情况更复杂)。
# 这里用 mean() 求平均值 ((10**2+20)+(20**2+20)+(30**2+20)+(40**2+20))/4,把向量 y1 转成了标量 y2,
y2 = y1.mean()

# 自动微分反向传播,计算 y2 对 x 中每个元素的偏导数(因为 x 是向量,求导结果也是向量)。
y2.backward()

# 打印梯度, 梯度计算的结果会报错到x.grad中
print(x.grad)

输出如下:

tensor([10., 20., 30., 40.], dtype=torch.float64, requires_grad=True) torch.Size([4])
tensor([ 5., 10., 15., 20.], dtype=torch.float64)

③.多标量梯度计算

import torch

# 多标量梯度计算

# 1. 对于需要求导的张量需要设置 requires_grad = True
x1 = torch.tensor(10.0, requires_grad=True, dtype=torch.float64)
x2 = torch.tensor(20.0, requires_grad=True, dtype=torch.float64)

# 中间计算过程
y = x1**2 + x2**2 + x1*x2

# 自动微分,求导
y.backward()

# 打印梯度
print(x1.grad)
print(x2.grad)

输出结果:

tensor(40., dtype=torch.float64)
tensor(50., dtype=torch.float64)

④.多向量的梯度计算

import torch

# 多向量的梯度计算

x1 = torch.tensor([10.0, 20.0], requires_grad=True, dtype=torch.float64)
x2 = torch.tensor([30.0, 40.0], requires_grad=True, dtype=torch.float64)

# 定义中间计算过程
y = x1**2 + x2**2 + x1*x2

# 将输出结果变为变量
y = y.sum()

# 自动微分
y.backward()

#  打印张量的梯度
print(x1.grad)
print(x2.grad)

输出结果:

tensor([50., 80.], dtype=torch.float64)
tensor([ 70., 100.], dtype=torch.float64)

2.9.2.梯度的控制计算

模型训练的时候需要进行梯度计算,模型训练完成,进入到另一个阶段之后,不需要进行梯度计算,由此需要控制梯度的计算

①.控制梯度计算

可以通过一定的方式控制是否需要对每个函数进行梯度计算

import torch

# 创建张量x
x = torch.tensor(10.0, requires_grad=True, dtype=torch.float64)
print(x.requires_grad)

# 方式1:只计算y=x**2的数值,而不计算这个函数的梯度
with torch.no_grad():
    y = x ** 2
print(y.requires_grad)  # False, 表示y的梯度计算被禁止了


# 方式2:主要针对函数
@torch.no_grad()
def my_func(x):
    y = x ** 2
    return y
y = my_func(x)
print(y.requires_grad)  # False, 表示不对这个函数进行梯度计算

# 方式3:全局的方式
torch.set_grad_enabled(False)
y = x ** 2
print(y.requires_grad)

②.累计梯度和梯度清零

import torch

# 2.累计梯度和梯度清零
x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)

# 当我们重复对x进行梯度计算的时候,是会将历史的梯度值累加到 x.grad 属性中
# 希望不要去累加历史梯度
for _ in range(10):  # 10次梯度计算
    # 对x进行梯度计算
    f1 = x ** 2 + 20

    # 将向量装换位标量
    f2 = f1.mean()

    # 梯度清零,防止梯度累加
    # if x.grad is not None:
    #     x.grad.data.zero_()

    # 自动微分
    f2.backward()

没有设置x.grad.data.zero(),重复对x进行梯度计算的时候,是会将历史的梯度值累加到 x.grad 属性中

image

 添加清零后

image

 

三、案例一:线性回归

使用 PyTorch 的各个组件来构建线性回归的实现。在pytorch中进行模型构建的整个流程一般分为四个步骤:

  1. 准备训练集数据;
  2. 构建要使用的模型;
  3. 设置损失函数和优化器;
  4. 模型训练;

image

要使用的API

  • 使用 PyTorch 的 nn.MSELoss() 代替自定义的平方损失函数
  • 使用 PyTorch 的 data.DataLoader 代替自定义的数据加载器
  • 使用 PyTorch 的 optim.SGD 代替自定义的优化器
  • 使用 PyTorch 的 nn.Linear 代替自定义的假设函数

导入工具包

# 导入相关模块
import torch
from torch.utils.data import TensorDataset # 构造数据集对象
from torch.utils.data import DataLoader # 数据加载器
from torch import nn # nn模块中有平方损失函数和假设函数
from torch import optim # optim模块中有优化器函数
from sklearn.datasets import make_regression # 创建线性回归模型数据集
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

数据集构建

 

posted @ 2025-06-02 19:04  酒剑仙*  阅读(315)  评论(0)    收藏  举报