0基础跟德姆(dom)一起学AI 深度学习02-Pytorch基本使用

1 基本介绍

(1)什么是Pytorch?

  • PyTorch是一个开源机器学习和深度学习框架 。PyTorch 允许您使用 Python 代码操作和处理数据并编写深度学习算法能够在强大的GPU加速基础上实现张量和动态神经网络

  • PyTorch是一个基于 Python 的科学计算包,使用 Tensor 作为其核心数据结构,类似于 Numpy 数组,不同的是,PyTorch 可以用GPU来处理数据,提供许多深度学习的算法。

  • PyTorch提供了完整的使用文档、循序渐进的用户指南,作者亲自维护PyTorch论坛,方便用户交流和解决问题。

  • Meta(前Facebook)人工智能研究院FAIR 对PyTorch的推广提供了大力支持。作为当今排名前三的深度学习研究机构,FAIR的支持足以确保PyTorch获得持续开发、更新的保障,不至于像一些个人开发的框架那样昙花一现。如有需要,我们也可以使用Python软件包(如NumPy、SciPy和Cython)来扩展 PyTorch。

  • 相对于TensorFlow,PyTorch的一大优点是它的图是动态的,而TensorFlow框架是静态图,不利于扩展。同时,PyTorch非常简洁,方便使用。

  • 如果说TensorFlow的设计是"Make it complicated",Keras的设计是"Make it complicated and hide it",那么PyTorch的设计则真正做到了**"Keep it simple,stupid"**。

(2)为什么使用Pytorch

  • 机器学习研究人员喜欢使用 PyTorch。截至 2022 年 2 月,PyTorch 是 Papers With Code 上最常用的深度学习框架,该网站用于跟踪机器学习研究论文及其附带的代码存储库。

  • PyTorch 易于学习和使用,足够灵活,可以在各种应用中使用,高效,以便我们可以处理巨大的现实数据集,并且足够准确,即使在输入数据存在不确定性的情况下也能提供正确的结果。

  • 许多深度学习的应用使用了PyTorch,比如:

  • 文字生成图片的Stable Diffusion:

  • 用于对象检测和语义分割的YOLOv5系列(现在已经更新到YOLOv10):

  • 实时语音克隆Real-Time Voice Cloning

  • 除上述通用领域的应用外,在一些专业领域的应用也在快速发展,比如医学领域,用PyTorch开发的开箱即用的解决方案nnU-Net

  • 在GIS领域,ESRI官方提供了许多解决方案和预训练模型,有提取建筑物轮廓的模型:

2 环境安装

(1)PyTorch安装环境配置

  • 创建一个虚拟python环境:

    conda create -n DL_Pytorch python=3.9

    conda activate DL_Pytorch

  • 查看虚拟环境列表:

    conda info --envs

    conda info -e

    conda env list

  • 激活虚拟环境

    conda旧版本:4.3及更早的版本

    source activate DL_Pytorch

    conda新版本:4.4及以后

    conda activate DL_Pytorch

  • 删除虚拟环境

    conda remove -n DL_Pytorch --all

(2)Pytorch-CUDA版本环境配置

电脑如果是Windows平台下的Nvidia GPU的用户,需配置Pytorch的CUDA版本,分为三步:

1. 安装或更新NVIDA显卡驱动

官方驱动下载地址:下载 NVIDIA 官方驱动 | NVIDIA

2. 安装CUDA Toolkit + cudnn
1)CUDA安装

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

终端输入:

nvidia-smi

下图中CUDA Version是12.2。

如果你没有安装cuda toolkit或者需要升级,可以去官网下载: CUDA Toolkit Archive | NVIDIA Developer

2)CUDNN安装

NVIDIA CUDA深度神经网络库 (cuDNN) 是一个 GPU 加速深度神经网络基元库,能够以高度优化的方式实现标准例程(如前向和反向卷积、池化层、归一化和激活层)。

全球的深度学习研究人员和框架开发者都依赖 cuDNN 来实现高性能 GPU 加速。借助 cuDNN,研究人员和开发者可以专注于训练神经网络及开发软件应用,而不必花时间进行低层级的 GPU 性能调整。cuDNN 可加速广泛应用的深度学习框架,包括 Caffe2、Keras、MATLAB、MxNet、PaddlePaddle、PyTorch和 TensorFlow。

下载地址: cuDNN Archive | NVIDIA Developer

(1)下载并解压文件

(2)复制内容到CUDA安装路径

CUDA安装默认路径:

  • Windows:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA
  • Linux:/usr/local/cuda
3. 安装Pytorch

(1)在线安装

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

(2)离线安装

注意:

1)PYTORCH与TORCHVISION版本对应问题

Pytorch与torchvision版本配套

如果你的conda解决环境很慢,可以试一试pip安装。

2)使用镜像源
3)安装验证
import torch
# 打印出正在使用的PyTorch和CUDA版本。
print(torch.__version__)
print(torch.version.cuda)

# 测试GPU是否生效
print(torch.cuda.is_available())

(3)导入PyToch

导入 PyTorch 并检查正在使用的版本。

import torch
torch.__version__
>>> '2.0.1'

(4)Pycharm配置Anaconda环境

  1. 打开Pycharm,点击File-->New Project,例如新建项目工程名字为:Pycharm_conda
  2. 选择新建工程所在文件位置,并命名,点击create。选择New Window。
  1. 在创建好的新工程窗口下,点击File-->Settings
  1. Settings-->Project :Pycharm_conda-->Python Interpreter, 然后点击右边齿轮状图标或者"Add Interpreter",点击Add ,添加解释器。
  1. 打开后选择Conda Environment,然后选中Existing environment,选择自己创建的环境,点击OK,低版本可勾选Make available to all projects。
  1. 等待加载完毕后,会看到在Python Interpreter页面多了许多包。表示在该环境下创建的工程就可以使用anaconda中已有的库了。
  1. 点击OK,配置完成,在主界面的右下角会显示当前环境处于刚配置好的环境中,等待加载完毕后即可正常使用
  1. 使用terminal安装依赖

3 基本使用

(1)张量简介

张量 ,英文为Tensor,是机器学习的基本构建模块,是以数字方式表示数据的形式。

例如,图像可以表示为形状为 [3, 224, 224] 的张量,这意味着 [colour_channels, height, width] ,因为图像具有 3 颜色通道(红色、绿色、蓝色),高度为 224 像素,宽度为 224 像素。

在张量语言(用于描述张量的语言)中,张量将具有三个维度,一个维度表示 colour_channelsheightwidth

(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. 张量的创建
1)张量的基本创建方式
  • torch.tensor 根据指定数据创建张量
  • torch.Tensor 根据形状创建张量, 其也可用来创建指定数据的张量
  • torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量

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)
>>> tensor([[ 0.1345,  0.1149,  0.2435],
            [ 0.8026, -0.6744, -1.0918]], dtype=torch.float64)

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

2、torch.Tensor() 根据指定形状创建张量,也可以用来创建指定数据的张量

# 1. 创建2行3列的张量, 默认 dtype 为 float32
data = torch.Tensor(2, 3)
print(data)
>>> tensor([[0.0000e+00, 3.6893e+19, 2.2018e+05],
            [4.6577e-10, 2.4158e-12, 1.1625e+33]])

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

data = torch.Tensor([10, 20])
print(data)
>>> tensor([10., 20.])

3、torch.IntTensor()、torch.FloatTensor()、torch.DoubleTensor() 创建指定类型的张量

# 1. 创建2行3列, dtype 为 int32 的张量
data = torch.IntTensor(2, 3)
print(data)
>>> tensor([[ 0, 1610612736, 1213662609],
            [ 805308409,  156041223,  1]], dtype=torch.int32)

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

# 3. 其他的类型
data = torch.ShortTensor()  # int16
data = torch.LongTensor()   # int64
data = torch.FloatTensor()  # float32
data = torch.DoubleTensor() # float64
2)创建线性张量和随机张量
  • torch.arange 和 torch.linspace 创建线性张量
  • torch.random.init_seed 和 torch.random.manual_seed 随机种子设置
  • torch.randn 创建随机张量

1、torch.arange()、torch.linspace() 创建线性张量

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

# 2. 在指定区间按照元素个数生成 [start, end, steps]
data = torch.linspace(0, 11, 10)
print(data)
>>> tensor([0.0000, 1.2222, 2.4444, 3.6667, 4.8889, 6.1111, 7.3333, 8.5556, 9.7778,                     11.0000])

2、随机种子操作

  • torch.random.initial_seed()查看随机种子

  • torch.random.manual_seed() 设置随机数种子

  • torch.randn() 创建随机张量

    1. 创建随机张量

    data = torch.randn(2, 3) # 创建2行3列张量
    print(data)

    tensor([[-0.5209, -0.2439, -1.1780],
    [ 0.8133, 1.1442, 0.6790]])

    2.查看随机数种子

    print('随机数种子:', torch.random.initial_seed())

    随机数种子: 4508475192273306739

    3.设置随机数种子

    torch.random.manual_seed(100)
    data = torch.randn(2, 3)
    print(data)
    print('随机数种子:', torch.random.initial_seed())

    tensor([[ 0.3607, -0.2859, -0.3938],
    [ 0.2429, -1.3833, -2.3134]])
    随机数种子: 100

3)创建0-1张量
  • torch.ones 和 torch.ones_like 创建全1张量
  • torch.zeros 和 torch.zeros_like 创建全0张量
  • torch.full 和 torch.full_like 创建全为指定值张量

1、torch.ones()、torch.ones_like() 创建全1张量

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

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

2、torch.zeros()、torch.zeros_like() 创建全0张量

# 1. 创建指定形状全1张量
data = torch.ones(2, 3)
print(data)
>>> tensor([[1., 1., 1.],
            [1., 1., 1.]])

# 2. 根据张量形状创建全1张量
data = torch.ones_like(data)
print(data)
>>> tensor([[1., 1., 1.],
            [1., 1., 1.]])

3、torch.full()、torch.full_like() 创建全为指定值张量

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

# 2. 根据张量形状创建指定值的张量
data = torch.full_like(data, 20)
print(data)
>>> tensor([[20, 20, 20],
            [20, 20, 20]])
4)张量元素类型转换
  • data.type(torch.DoubleTensor)
  • data.double()

1、data.type(torch.DoubleTensor)

data = torch.full([2, 3], 10)
print(data.dtype)
>>> torch.int64

# 将 data 元素类型转换为 float64 类型
data = data.type(torch.DoubleTensor)
print(data.dtype)
>>> torch.float64

# 转换为其他类型
# data = data.type(torch.ShortTensor)   # int16
# data = data.type(torch.IntTensor)    # int32
# data = data.type(torch.LongTensor)   # int64
# data = data.type(torch.FloatTensor)  # float32

2、data.double()

data = torch.full([2, 3], 10)
print(data.dtype)
>>> torch.int64

# 将 data 元素类型转换为 float64 类型
data = data.double()
print(data.dtype)
>>> torch.float64

# 转换为其他类型
# data = data.short()
# data = data.int()
# data = data.long()
# data = data.float()
5)总结

<1> 创建张量的方式

  • torch.tensor() 根据指定数据创建张量
  • torch.Tensor() 根据形状创建张量, 其也可用来创建指定数据的张量
  • torch.IntTensor()、torch.FloatTensor()、torch.DoubleTensor() 创建指定类型的张量

<2> 创建线性和随机张量

  • torch.arrange() 和 torch.linspace() 创建线性张量
  • torch.random.initial_seed() 和 torch.random.manual_seed() 随机种子设置
  • torch.randn() 创建随机张量

<3> 创建01张量

  • torch.ones() 和 torch.ones_like() 创建全1张量
  • torch.zeros() 和 torch.zeros_like() 创建全0张量
  • torch.full() 和 torch.full_like() 创建全为指定值张量

<4> 张量元素类型转换

  • data.type(torch.DoubleTensor)
  • data.double()
3. 张量的类型转换
1)张量转换为NUMPY数组
  • 使用 Tensor.numpy 函数可以将张量转换为 ndarray 数组,但是共享内存,可以使用 copy 函数避免共享。

    1. 将张量转换为 numpy 数组

    data_tensor = torch.tensor([2, 3, 4])

    使用张量对象中的 numpy 函数进行转换

    data_numpy = data_tensor.numpy()
    print(type(data_tensor))

    <class 'torch.Tensor'>

    print(type(data_numpy))

    <class 'numpy.ndarray'>

    注意: data_tensor 和 data_numpy 共享内存

    修改其中的一个,另外一个也会发生改变

    data_tensor[0] = 100

    data_numpy[0] = 100
    print(data_tensor)

    tensor([100, 3, 4])

    print(data_numpy)

    [100 3 4]

    2. 对象拷贝避免共享内存

    data_tensor = torch.tensor([2, 3, 4])

    使用张量对象中的 numpy 函数进行转换,通过copy方法拷贝对象

    data_numpy = data_tensor.numpy().copy()
    print(type(data_tensor))

    <class 'torch.Tensor'>

    print(type(data_numpy))

    <class 'numpy.ndarray'>

    注意: data_tensor 和 data_numpy 此时不共享内存

    修改其中的一个,另外一个不会发生改变

    data_tensor[0] = 100

    data_numpy[0] = 100
    print(data_tensor)

    tensor([2, 3, 4])

    print(data_numpy)

    [100 3 4]

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

    data_numpy = np.array([2, 3, 4])

    将 numpy 数组转换为张量类型

    1. from_numpy

    2. torch.tensor(ndarray)

    data_tensor = torch.from_numpy(data_numpy)

    nunpy 和 tensor 共享内存

    data_numpy[0] = 100

    data_tensor[0] = 100
    print(data_tensor)

    tensor([100, 3, 4], dtype=torch.int32)

    print(data_numpy)

    [100 3 4]

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

    data_numpy = np.array([2, 3, 4])
    data_tensor = torch.tensor(data_numpy)

    nunpy 和 tensor 不共享内存

    data_numpy[0] = 100

    data_tensor[0] = 100
    print(data_tensor)

    tensor([100, 3, 4], dtype=torch.int32)

    print(data_numpy)

    [2 3 4]

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

    当张量只包含一个元素时, 可以通过 item() 函数提取出该值

    data = torch.tensor([30,])
    print(data.item())

    30

    data = torch.tensor(30)
    print(data.item())

    30

4)总结

1. 张量转换为 numpy 数组

  • data_tensor.numpy()

  • data_tensor.numpy().copy()

2. numpy 转换为张量

  • torch.from_numpy(data_numpy)

  • torch.tensor(data_numpy)

3. 标量张量和数字转换

  • data.item()
4. 张量的数值计算
1)张量基本运算
  • 加减乘除取负号:

  • add、sub、mul、div、neg

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

    data = torch.randint(0, 10, [2, 3])
    print(data)

    tensor([[3, 7, 4],
    [0, 0, 6]])

    1. 不修改原数据

    new_data = data.add(10) # 等价 new_data = data + 10
    print(new_data)

    tensor([[13, 17, 14],
    [10, 10, 16]])

    2. 直接修改原数据 注意: 带下划线的函数为修改原数据本身

    data.add_(10) # 等价 data += 10
    print(data)

    tensor([[13, 17, 14],
    [10, 10, 16]])

    3. 其他函数

    print(data.sub(100))

    tensor([[-87, -83, -86],
    [-90, -90, -84]])

    print(data.mul(100))

    tensor([[1300, 1700, 1400],
    [1000, 1000, 1600]])

    print(data.div(100))

    tensor([[0.1300, 0.1700, 0.1400],
    [0.1000, 0.1000, 0.1600]])

    print(data.neg())

    tensor([[-13, -17, -14],
    [-10, -10, -16]])

2)张量点乘运算
  • 点乘指(Hadamard)的是两个同维矩阵对应位置的元素相乘,使用mul 和运算符 * 实现。
data1 = torch.tensor([[1, 2], [3, 4]])
data2 = torch.tensor([[5, 6], [7, 8]])
# 第一种方式
data = torch.mul(data1, data2)
print(data)
>>> tensor([[ 5, 12],
            [21, 32]])

# 第二种方式
data = data1 * data2
print(data)
>>> tensor([[ 5, 12],
            [21, 32]])
3)张量矩阵乘法运算
  • 矩阵乘法运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。

  • 运算符 @ 用于进行两个矩阵的乘积运算

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

    点积运算

    data1 = torch.tensor([[1, 2], [3, 4], [5, 6]])
    data2 = torch.tensor([[5, 6], [7, 8]])

    方式一:

    data3 = data1 @ data2
    print("data3-->", data3)

    data3--> tensor([[19, 22],
    [43, 50],
    [67, 78]])

    方式二:

    data4 = torch.matmul(data1, data2)
    print("data4-->", data4)

    data4--> tensor([[19, 22],
    [43, 50],
    [67, 78]])

4)总结

<1> 张量基本运算函数

  • add、sub、mul、div、neg等函数

  • add_、sub_、mul_、div_、neg_等函数

<2> 张量的点乘运算

  • mul 和运算符 *

<3> 点积运算

  • 运算符@用于进行两个矩阵的点乘运算

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

5. 张量的运算函数

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

  • 均值

  • 平方根

  • 求和

  • 指数计算

  • 对数计算等等

    import torch

    data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
    print(data)

    tensor([[4., 0., 7.],
    [6., 3., 5.]], dtype=torch.float64)

    1. 计算均值

    注意: tensor 必须为 Float 或者 Double 类型

    print(data.mean())

    tensor(4.1667, dtype=torch.float64)

    print(data.mean(dim=0)) # 按列计算均值

    tensor([5.0000, 1.5000, 6.0000], dtype=torch.float64)

    print(data.mean(dim=1)) # 按行计算均值

    tensor([3.6667, 4.6667], dtype=torch.float64)

    2. 计算总和

    print(data.sum())

    tensor(25., dtype=torch.float64)

    print(data.sum(dim=0))

    tensor([10., 3., 12.], dtype=torch.float64)

    print(data.sum(dim=1))

    tensor([11., 14.], dtype=torch.float64)

    3. 计算平方

    print(torch.pow(data,2))

    tensor([[16., 0., 49.],
    [36., 9., 25.]], dtype=torch.float64)

    4. 计算平方根

    print(data.sqrt())

    tensor([[2.0000, 0.0000, 2.6458],
    [2.4495, 1.7321, 2.2361]], dtype=torch.float64)

    5. 指数计算, e^n 次方

    print(data.exp())

    tensor([[5.4598e+01, 1.0000e+00, 1.0966e+03],
    [4.0343e+02, 2.0086e+01, 1.4841e+02]], dtype=torch.float64)

    6. 对数计算

    print(data.log()) # 以 e 为底

    tensor([[1.3863, -inf, 1.9459],
    [1.7918, 1.0986, 1.6094]], dtype=torch.float64)

    print(data.log2())

    tensor([[2.0000, -inf, 2.8074],
    [2.5850, 1.5850, 2.3219]], dtype=torch.float64)

    print(data.log10())

    tensor([[0.6021, -inf, 0.8451],
    [0.7782, 0.4771, 0.6990]], dtype=torch.float64)

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

准备数据:

import torch
# 随机生成数据
data = torch.randint(0, 10, [4, 5])
print(data)
>>> tensor([[0, 7, 6, 5, 9],
            [6, 8, 3, 1, 0],
            [6, 3, 8, 7, 3],
            [4, 9, 5, 3, 1]])
1)简单行列索引的使用
print(data[0])
>>> tensor([0, 7, 6, 5, 9])

print(data[:, 0])
>>> tensor([0, 6, 6, 4])
2)列表索引的使用
# 返回 (0, 2)、(1, 3) 两个位置的元素
print(data[[0, 1], [2, 3]])
>>> tensor([7, 3])

# 返回 0、1 行的 1、2 列共4个元素
print(data[[[0], [1]], [1, 2]])
>>> tensor([[7, 6],
            [8, 3]])
3)范围索引的使用
# 前3行的前2列数据
print(data[:3, :2])
>>> tensor([[0, 7],
            [6, 8],
            [6, 3]])

# 第2行到最后的前2列数据
print(data[2:, :2])
>>> tensor([[6, 3],
            [4, 9]])

# 第0行、第2行的第0、1两列数据
print(data[0:3:2, :2])
>>>tensor([[0, 7],
            [6, 3]])
4)布尔索引的使用
# 第三列大于5的行数据
print(data[data[:, 2] > 5])
>>> tensor([[0, 7, 6, 5, 9],
            [6, 3, 8, 7, 3]])

# 第二行大于5的列数据
print(data[:, data[1] > 5])
>>> tensor([[0, 7],
            [6, 8],
            [6, 3],
            [4, 9]])
5)多维索引的使用
data = torch.randint(0, 10, [3, 4, 5])
print(data)
>>> tensor([[[2, 4, 1, 2, 3],
             [5, 5, 1, 5, 0],
             [1, 4, 5, 3, 8],
             [7, 1, 1, 9, 9]],

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

            [[0, 7, 3, 5, 6],
             [2, 4, 6, 4, 3],
             [2, 0, 3, 7, 9],
             [9, 6, 4, 4, 4]]])

# 获取0轴上的第一个数据
print(data[0, :, :])
>>> tensor([[2, 4, 1, 2, 3],
            [5, 5, 1, 5, 0],
            [1, 4, 5, 3, 8],
            [7, 1, 1, 9, 9]])

# 获取1轴上的第一个数据
print(data[:, 0, :])
>>> tensor([[2, 4, 1, 2, 3],
            [9, 7, 5, 3, 1],
            [0, 7, 3, 5, 6]])

# 获取2轴上的第一个数据
print(data[:, :, 0])
>>> tensor([[2, 5, 1, 7],
            [9, 8, 6, 9],
            [0, 2, 2, 9]])
7.张量的形状操作

有重塑、堆叠、挤压和解压:

方法 单行描述
torch.reshape(input, shape) 重塑 input 到 shape (如果兼容),也可以使用 torch.Tensor.reshape()。
tensor.view(shape) 返回不同 shape 中的原始张量视图,但与原始张量共享相同的数据。
tensor.contiguous() 将张量转换到整块内存上
torch.stack(tensors, dim=0) 沿着新的维度(dim)连接 tensors 的序列,所有 tensors 必须具有相同的大小。
torch.squeeze(input) 挤压 input 以移除值为 1 的所有尺寸。
torch.unsqueeze(input, dim) 返回在 dim 处添加了维度值 1 的 input。
torch.transpose(input,dim1,dim2) 实现交换张量形状的指定维度
torch.permute(input, dims) 返回原始 input 的视图,其尺寸被置换(重新排列)为 dims。

深度学习模型(神经网络)都是以某种方式操纵张量。由于矩阵乘法的规则,如果形状不匹配,就会遇到错误。这些方法可帮助您确保张量的正确元素与其他张量的正确元素混合。

举例说明:

import torch
x = torch.arange(1., 8.)
print(x)
>>> tensor([1., 2., 3., 4., 5., 6., 7.])

print(x.shape)
>>> torch.Size([7])
1)RESHAPE

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

使用 torch.reshape() 增加一个维度。

# 增加一个维度
x_reshaped = x.reshape(1, 7)
print(x_reshaped)
>>> tensor([[1., 2., 3., 4., 5., 6., 7.]])

print(x_reshaped.shape)
>>> torch.Size([1, 7])

使用 torch.reshape() 改变张量的形状。

import torch

data = torch.tensor([[10, 20, 30], [40, 50, 60]])
# 1. 使用 shape 属性或者 size 方法都可以获得张量的形状
print(data.shape, data.shape[0], data.shape[1])
>>> torch.Size([2, 3]) 2 3

print(data.size(), data.size(0), data.size(1))
>>> torch.Size([2, 3]) 2 3

# 2. 使用 reshape 函数修改张量形状
new_data = data.reshape(1, 6)
print(new_data.shape)
>>> torch.Size([1, 6])
2)VIEW / CONTIGUOUS
  • view 函数也可以用于修改张量的形状,只能用于存储在整块内存中的张量

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

  • 此时需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数。

    1 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作

    若要使用view函数, 需要使用contiguous() 变成连续以后再使用view函数

    2 判断张量是否使用整块内存

    data = torch.tensor( [[10, 20, 30],[40, 50, 60]])
    print('data--->', data, data.shape)

    data---> tensor([[10, 20, 30],
    [40, 50, 60]]) torch.Size([2, 3])

    1 判断是否使用整块内存

    print(data.is_contiguous())

    True

    2 view

    mydata2 = data.view(3, 2)
    print('mydata2--->', mydata2, mydata2.shape)

    mydata2---> tensor([[10, 20],
    [30, 40],
    [50, 60]]) torch.Size([3, 2])

    3 判断是否使用整块

    print('mydata2.is_contiguous()--->', mydata2.is_contiguous())

    mydata2.is_contiguous()---> True

    4 使用 transpose 函数修改形状

    mydata3 = torch.transpose(data, 0, 1)
    print('mydata3--->', mydata3, mydata3.shape)

    mydata3---> tensor([[10, 40],
    [20, 50],
    [30, 60]]) torch.Size([3, 2])

    print('mydata3.is_contiguous()--->', mydata3.is_contiguous())

    mydata3.is_contiguous()---> False

    5 需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数

    print (mydata3.contiguous().is_contiguous())

    True

    mydata4 = mydata3.contiguous().view(2, 3)
    print('mydata4--->', mydata4.shape, mydata4)

    mydata4---> torch.Size([2, 3]) tensor([[10, 40, 20],
    [50, 30, 60]])

3)STACK

如果想将新张量堆叠五次,使用 torch.stack() 来实现。

# Stack tensors on top of each other
x_stacked = torch.stack([x, x, x, x], dim=0) # 同pandas的axis,按行堆叠
>>>tensor([[5., 2., 3., 4., 5., 6., 7.],
           [5., 2., 3., 4., 5., 6., 7.],
           [5., 2., 3., 4., 5., 6., 7.],
           [5., 2., 3., 4., 5., 6., 7.]])
4)SQUEEZE / UNSQUEEZE

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

mydata1 = torch.tensor([1, 2, 3, 4, 5])             
print('mydata1--->', mydata1.shape, mydata1) # 一个普通的数组 1维数据
>>> mydata1---> torch.Size([5]) tensor([1, 2, 3, 4, 5])

mydata2 = mydata1.unsqueeze(dim=0)
print('在0维度上 拓展维度:', mydata2, mydata2.shape)  #1*5
>>> 在0维度上 拓展维度: tensor([[1, 2, 3, 4, 5]]) torch.Size([1, 5])

mydata3 = mydata1.unsqueeze(dim=1)
print('在1维度上 拓展维度:', mydata3, mydata3.shape)  #5*1
>>> 在1维度上 拓展维度: tensor([[1],
                              [2],
                              [3],
                              [4],
                              [5]]) torch.Size([5, 1])


mydata4 = mydata1.unsqueeze(dim=-1)
print('在-1维度上 拓展维度:', mydata4, mydata4.shape) #5*1
>>> 在-1维度上 拓展维度: tensor([[1],
                               [2],
                               [3],
                               [4],
                               [5]]) torch.Size([5, 1])

mydata5 = mydata4.squeeze()
print('压缩维度:', mydata5, mydata5.shape)  #1*5
>>> 压缩维度: tensor([1, 2, 3, 4, 5]) torch.Size([5])
5)TRANSPOSE/ PERMUTE

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

data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print('data shape:', data.size())
>>> data shape: torch.Size([3, 4, 5])

# 1 交换1和2维度
mydata2 = torch.transpose(data, 1, 2)
print('mydata2.shape--->', mydata2.shape)
>>> mydata2.shape---> torch.Size([3, 5, 4])

# 2 将data 的形状修改为 (4, 5, 3), 需要变换多次
mydata3 =  torch.transpose(data, 0, 1)
mydata4 = torch.transpose(mydata3, 1, 2)
print('mydata4.shape--->', mydata4.shape)
>>> mydata4.shape---> torch.Size([4, 5, 3])

# 3 使用 permute 函数将形状修改为 (4, 5, 3)
# 3-1 方法1
mydata5 = torch.permute(data, [1, 2, 0])
print('mydata5.shape--->', mydata5.shape)
>>> mydata5.shape---> torch.Size([4, 5, 3])

# 3-2 方法2
mydata6 = data.permute([1, 2, 0])
print('mydata6.shape--->', mydata6.shape)
>>> mydata6.shape---> torch.Size([4, 5, 3])
6)总结

<1> reshape 函数可以在保证张量数据不变的前提下改变数据的维度

<2> squeeze 和 unsqueeze 函数可以用来增加或者减少维度

<3> transpose 函数可以实现交换张量形状的指定维度, permute 可以一次交换更多的维度

<4> view 函数也可以用于修改张量的形状, 但是它要求被转换的张量内存必须连续,所以一般配合 contiguous 函数使用

8.张量的拼接操作
  • torch.cat()

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

    tensor([[[7, 8, 7],
    [6, 3, 6]]])

    print(data2)

    tensor([[[3, 6, 5],
    [7, 5, 0]]])

    1. 按0维度拼接

    new_data = torch.cat([data1, data2], dim=0)
    print(new_data)

    tensor([[[7, 8, 7],
    [6, 3, 6]],

              [[3, 6, 5],
               [7, 5, 0]]])
    

    print(new_data.shape)

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

    2. 按1维度拼接

    new_data = torch.cat([data1, data2], dim=1)
    print(new_data)

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

    print(new_data.shape)

    torch.Size([1, 4, 3])

    3. 按2维度拼接

    new_data = torch.cat([data1, data2], dim=2)
    print(new_data)

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

    print(new_data.shape)

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

9.自动微分模块

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

使用 backward 方法、grad 属性来实现梯度的计算和访问

  • 输入为标量时的梯度计算

    import torch

    1. 当X为标量时梯度的计算

    def test01():
    x = torch.tensor(5)
    # 目标值
    y = torch.tensor(0.)
    # 设置要更新的权重和偏置的初始值
    w = torch.tensor(1., requires_grad=True, dtype=torch.float32)
    b = torch.tensor(3., requires_grad=True, dtype=torch.float32)
    # 设置网络的输出值
    z = x * w + b # 矩阵乘法
    # 设置损失函数,并进行损失的计算
    loss = torch.nn.MSELoss()
    loss = loss(z, y)
    # 自动微分
    loss.backward()
    # 打印 w,b 变量的梯度
    # backward 函数计算的梯度值会存储在张量的 grad 变量中
    print("W的梯度:", w.grad)
    print("b的梯度", b.grad)

    X是标量时的结果

    输出结果:

    W的梯度: tensor(80.)
    b的梯度 tensor(16.)

  • 输入为多维张量时的梯度计算

    import torch
    def test02():
    # 输入张量 25
    x = torch.ones(2,5)
    # 目标值是 2
    3
    y = torch.zeros(2,3)
    # 设置要更新的权重和偏置的初始值
    w = torch.randn(5, 3,requires_grad=True)
    b = torch.randn(3, requires_grad=True)
    # 设置网络的输出值
    z = torch.matmul(x, w) + b # 矩阵乘法
    # 设置损失函数,并进行损失的计算
    loss = torch.nn.MSELoss()
    loss = loss(z, y)
    # 自动微分
    loss.backward()
    # 打印 w,b 变量的梯度
    # backward 函数计算的梯度值会存储在张量的 grad 变量中
    print("W的梯度:", w.grad)
    print("b的梯度", b.grad)

    输出结果:

    W的梯度: tensor(
    [[ 0.0757, 0.6087, -0.6538],
    [ 0.0757, 0.6087, -0.6538],
    [ 0.0757, 0.6087, -0.6538],
    [ 0.0757, 0.6087, -0.6538],
    [ 0.0757, 0.6087, -0.6538]])
    b的梯度 tensor([ 0.0757, 0.6087, -0.6538])

4 案例-线性回归案例

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

(1)导入工具包

# 导入相关模块
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  # 用来正常显示负号

(2)数据集构建

def create_dataset():
    x, y, coef = make_regression(n_samples=100,
                                 n_features=1,
                                 noise=10,
                                 coef=True,
                                 bias=1.5,
                                 random_state=0)

    # 将构建数据转换为张量类型
    x = torch.tensor(x)
    y = torch.tensor(y)

    return x, y, coef

if __name__ == "__main__": 
    # 生成的数据 
    x,y,coef=create_dataset()
    # 绘制数据的真实的线性回归结果
    plt.scatter(x, y)
    x = torch.linspace(x.min(), x.max(), 1000) 
    y1 = torch.tensor([v * coef + 1.5 for v in x]) 
    plt.plot(x, y1, label='real') 
    plt.grid() 
    plt.legend() 
    plt.show()

(3)数据加载器构建及模型构建

# 构造数据集
x, y, coef = create_dataset()
# 构造数据集对象
dataset = TensorDataset(x, y)
# 构造数据加载器
# dataset=:数据集对象
# batch_size=:批量训练样本数据
# shuffle=:样本数据是否进行乱序
dataloader = DataLoader(dataset=dataset, batch_size=16, shuffle=True)
# 构造模型
# in_features指的是输入张量的大小size
# out_features指的是输出张量的大小size
 model = nn.Linear(in_features=1, out_features=1)

(4)训练参数设置

# 构造平方损失函数
criterion = nn.MSELoss()
# 构造优化函数
optimizer = optim.SGD(params=model.parameters(), lr=1e-2)

(5)模型训练

epochs = 100
# 损失的变化
loss_epoch = []
total_loss = 0.0
train_sample = 0.0
for _ in range(epochs):
    for train_x, train_y in dataloader:
        # 将一个batch的训练数据送入模型
        y_pred = model(train_x.type(torch.float32))
        # 计算损失值
        loss = criterion(y_pred, train_y.reshape(-1, 1).type(torch.float32))
        total_loss += loss.item()
        train_sample += len(train_y)
        # 梯度清零
        optimizer.zero_grad()
        # 自动微分(反向传播)
        loss.backward()
        # 更新参数
        optimizer.step()
    # 获取每个batch的损失
    loss_epoch.append(total_loss / train_sample)

(6)构建训练模型函数

# 绘制损失变化曲线
plt.plot(range(epochs), loss_epoch)
plt.title('损失变化曲线')
plt.grid()
plt.show()
# 绘制拟合直线
plt.scatter(x, y)
x = torch.linspace(x.min(), x.max(), 1000)
y1 = torch.tensor([v * model.weight + model.bias for v in x])
y2 = torch.tensor([v * coef + 1.5 for v in x])
plt.plot(x, y1, label='训练')
plt.plot(x, y2, label='真实')
plt.grid()
plt.legend()
plt.show()
相关推荐
初遇你时动了情1 分钟前
uniapp 城市选择插件
开发语言·javascript·uni-app
sp_fyf_20245 分钟前
【大语言模型】ACL2024论文-19 SportsMetrics: 融合文本和数值数据以理解大型语言模型中的信息融合
人工智能·深度学习·神经网络·机器学习·语言模型·自然语言处理
CoderIsArt8 分钟前
基于 BP 神经网络整定的 PID 控制
人工智能·深度学习·神经网络
编程修仙16 分钟前
Collections工具类
linux·windows·python
开源社21 分钟前
一场开源视角的AI会议即将在南京举办
人工智能·开源
FreeIPCC22 分钟前
谈一下开源生态对 AI人工智能大模型的促进作用
大数据·人工智能·机器人·开源
芝麻团坚果32 分钟前
对subprocess启动的子进程使用VSCode python debugger
linux·ide·python·subprocess·vscode debugger
机器之心40 分钟前
全球十亿级轨迹点驱动,首个轨迹基础大模型来了
人工智能·后端
z千鑫41 分钟前
【人工智能】PyTorch、TensorFlow 和 Keras 全面解析与对比:深度学习框架的终极指南
人工智能·pytorch·深度学习·aigc·tensorflow·keras·codemoss
EterNity_TiMe_41 分钟前
【论文复现】神经网络的公式推导与代码实现
人工智能·python·深度学习·神经网络·数据分析·特征分析