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()
相关推荐
dwjf3214 分钟前
机器学习(三)-多项式线性回归
人工智能·机器学习·线性回归
数据小小爬虫5 分钟前
如何利用Python爬虫获取商品历史价格信息
开发语言·爬虫·python
Gao_xu_sheng7 分钟前
Java程序打包成exe,无Java环境也能运行
java·开发语言
葡萄爱10 分钟前
OpenCV图像分割
人工智能·opencv·计算机视觉
NiNg_1_23412 分钟前
Python的sklearn中的RandomForestRegressor使用详解
开发语言·python·sklearn
谢家小布柔15 分钟前
java中的继承
java·开发语言
黑色叉腰丶大魔王16 分钟前
《基于 Python 的网页爬虫详细教程》
开发语言·爬虫·python
l1384942745121 分钟前
Java每日一题(2)
java·开发语言·游戏
晓纪同学29 分钟前
QT创建一个模板槽和信号刷新UI
开发语言·qt·ui
WANGWUSAN6635 分钟前
Python高频写法总结!
java·linux·开发语言·数据库·经验分享·python·编程