【Pytorch教程】迅速入门Pytorch深度学习框架

news/2024/11/14 13:08:00/文章来源:https://www.cnblogs.com/UnderTurrets/p/18380855

@

目录
  • 前言
  • 1.tensor基础操作
    • 1.1 tensor的dtype类型
    • 1.2 创建tensor(建议写出参数名字)
      • 1.2.1 空tensor(无用数据填充)
        • API
        • 示例
      • 1.2.2 全一tensor
      • 1.2.3 全零tensor
      • 1.2.4 随机值[0,1)的tensor
      • 1.2.5 随机值为整数且规定上下限的tensor
        • API
        • 示例
      • 1.2.6 随机值均值0方差1的tensor
      • 1.2.7 从列表或numpy数组创建tensor
    • 1.3 tensor常用成员函数和成员变量
      • 1.3.1 转为numpy数组
      • 1.3.2 获得单元素tensor的值item
      • 1.3.3 获取维度个数
      • 1.3.4 获取数据类型
      • 1.3.5 获取形状
      • 1.3.6 浅拷贝与深拷贝
        • detach函数浅拷贝
        • 深拷贝
      • 1.3.7 形状变换
        • 转置
        • cat堆叠
        • stack堆叠
        • view改变形状
        • reshape改变形状
      • 1.3.8 数学运算
      • 1.3.9 使用指定设备计算tensor
  • 2.线性回归模型
    • 2.1 自动求导机制
    • 2.2 nn.Module的继承(from torch import nn)
      • 2.2.1 概述
      • 2.2.2 实例
    • 2.3 优化器类(from torch import optim)
      • 2.3.1 概述
      • 2.3.2 流程
      • 2.3.3 动态学习率(import torch.optim.lr_scheduler)
    • 2.4 代价函数(from torch import nn)
    • 2.5 评估模型
    • 2.6 线性回归模型的建立
      • 2.6.1 流程
      • 2.6.2 示例
  • 3.数据集和数据加载器 (from torch.utils.data import Dataset,DataLoader)
    • 3.1 Dataset类的继承(from torch.utils.data import Dataset)
      • 3.1.1 概述
      • 3.1.2 实例
    • 3.2 DataLoader类
      • 3.2.1 API
      • 3.2.2 示例
  • 4.图像处理:手写数字识别
    • 4.1 torchvision模块
      • 4.1.1 transforms.ToTensor类(仿函数)
      • 4.1.2 transforms.Normalize类(仿函数)
      • 4.1.3 transforms.Compose类(仿函数)
      • 4.1.4 示例
    • 4.2 网络构建
      • 4.2.1 激活函数大全
      • 4.2.2 演示代码(在gpu上)
  • 5.制作图片数据集(以flower102为例)
    • 5.1 建立数据集骨架
    • 5.2 建立从名称到数字标签的映射
    • 5.3 建立csv数据
    • 5.4 完善成员函数和transform过程
    • 5.5 DataLoader检验
  • 6.迁移学习
    • 6.1 现有模型的保存和加载
      • 6.1.1 保存(torch.save函数)
        • 把数据加载进网络
        • 把数据加载进优化器
      • 6.1.3 示例
    • 6.2 使用预训练的模型(以resnet50为例)
      • 6.2.1 确定初始化参数
    • 6.3 开始训练


前言

本文只是对于pytorch深度学习框架的使用方法的介绍,如果涉及算法中复杂的数学原理,本文将不予阐述,敬请读者自行阅读相关论文或者文献。


1.tensor基础操作

1.1 tensor的dtype类型

代码 含义
float32 32位float
float floa
float64 64位float
double double
float16 16位float
bfloat16 比float范围大但精度低
int8 8位int
int16 16位int
short short
int32 32位int
int int
int64 64位int
long long
complex32 32位complex
complex64 64位complex
cfloat complex float
complex128 128位complex float
cdouble complex double

1.2 创建tensor(建议写出参数名字)

创建tensor时,有很多参数可以选择,为节省篇幅,本文在列举API时只列举一次,不列举重载的API。

1.2.1 空tensor(无用数据填充)

API

@overload
def empty(size: Sequence[Union[_int, SymInt]], *, memory_format: Optional[memory_format]=None, out: Optional[Tensor]=None, dtype: Optional[_dtype]=None, layout: Optional[_layout]=None, device: Optional[Union[_device, str, None]]=None, pin_memory: Optional[_bool]=False, requires_grad: Optional[_bool]=False) -> Tensor: ...

size:[行数,列数]

dtype(deepth type):数据类型

device:选择运算设备

requires_grad:是否进行自动求导,默认为False

示例

       gpu=torch.device("cuda")empty_tensor=torch.empty(size=[3,4],device=gpu,requires_grad=True)print(empty_tensor)

输出

tensor([[0., 0., 0., 0.],[0., 0., 0., 0.],[0., 0., 0., 0.]], device='cuda:0', requires_grad=True)

1.2.2 全一tensor

@overload
def ones(size: _size, *, names: Optional[Sequence[Union[str, ellipsis, None]]], dtype: Optional[_dtype]=None, layout: Optional[_layout]=None, device: Optional[Union[_device, str, None]]=None, pin_memory: Optional[_bool]=False, requires_grad: Optional[_bool]=False) -> Tensor: ...

size:[行数,列数]

dtype(deepth type):数据类型

device:选择运算设备

requires_grad:是否进行自动求导,默认为False

1.2.3 全零tensor

@overload
def zeros(size: _size, *, names: Optional[Sequence[Union[str, ellipsis, None]]], dtype: Optional[_dtype]=None, layout: Optional[_layout]=None, device: Optional[Union[_device, str, None]]=None, pin_memory: Optional[_bool]=False, requires_grad: Optional[_bool]=False) -> Tensor: ...

1.2.4 随机值[0,1)的tensor

@overload
def rand(size: _size, *, generator: Optional[Generator], names: Optional[Sequence[Union[str, ellipsis, None]]], dtype: Optional[_dtype]=None, layout: Optional[_layout]=None, device: Optional[Union[_device, str, None]]=None, pin_memory: Optional[_bool]=False, requires_grad: Optional[_bool]=False) -> Tensor: ...

1.2.5 随机值为整数且规定上下限的tensor

API

@overload
def randint(low: _int, high: _int, size: _size, *, generator: Optional[Generator]=None, dtype: Optional[_dtype]=None, device: Device=None, requires_grad: _bool=False) -> Tensor: ...

示例

   int_tensor=torch.randint(low=0,high=20,size=[5,6],device=gpu)print(int_tensor)

输出

tensor([[18,  0, 14,  7, 18, 14],[17,  0,  2,  0,  0,  3],[16, 17,  5, 15,  1, 14],[ 7, 12,  8,  6,  4, 11],[12,  4,  7,  5,  3,  3]], device='cuda:0')

1.2.6 随机值均值0方差1的tensor

@overload
def randn(size: _size, *, generator: Optional[Generator], names: Optional[Sequence[Union[str, ellipsis, None]]], dtype: Optional[_dtype]=None, layout: Optional[_layout]=None, device: Optional[Union[_device, str, None]]=None, pin_memory: Optional[_bool]=False, requires_grad: Optional[_bool]=False) -> Tensor: ...

1.2.7 从列表或numpy数组创建tensor

def tensor(data: Any, dtype: Optional[_dtype]=None, device: Device=None, requires_grad: _bool=False) -> Tensor: ...
  • 如果使用torch.from_numpy(),返回的tensor与ndarray共享内存。

1.3 tensor常用成员函数和成员变量

1.3.1 转为numpy数组

def numpy(self,*args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__ pass
  • 只有在CPU上运算的tensor才可以转为numpy数组
  • tensor.requires_grad属性为True的tensor不能转为numpy数组

1.3.2 获得单元素tensor的值item

def item(self): # real signature unknown; restored from __doc__...
  • 如果tensor只有一个元素,就返回它的值
  • 如果tensor有多个元素,抛出ValueError

1.3.3 获取维度个数

def dim(self): #real signature unknown; restored from __doc__return 0
  • 返回一个int表示维度个数

1.3.4 获取数据类型

dtype = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

1.3.5 获取形状

def size(self,dim=None): # real signature unknown; restored from __doc__pass
  • 使用.shape效果相同

1.3.6 浅拷贝与深拷贝

detach函数浅拷贝

假设有模型A和模型B,我们需要将A的输出作为B的输入,但训练时我们只训练模型B. 那么可以这样做:

input_B = output_A.detach()

它可以使两个计算图的梯度传递断开,从而实现我们所需的功能。

返回一个新的tensor,新的tensor和原来的tensor共享数据内存,但不涉及梯度计算,即requires_grad=False。修改其中一个tensor的值,另一个也会改变,因为是共享同一块内存。

   sequence_tensor=torch.tensor(np.array([[[1,2,3],[4,5,6]],[[9,8,7],[6,5,4]]]),dtype=torch.float,device=gpu,)sequence_tensor_shallowCp=sequence_tensor.detach()sequence_tensor_shallowCp+=1print(sequence_tensor)print(sequence_tensor_shallowCp.requires_grad)

输出

tensor([[[ 2.,  3.,  4.],[ 5.,  6.,  7.]],[[10.,  9.,  8.],[ 7.,  6.,  5.]]], device='cuda:0')
False

深拷贝

  • 法一:.clone().detach()
  • 法二:.new_tensor()

1.3.7 形状变换

转置

向量或矩阵转置

def t(self): # real signature unknown; restored from __doc__"""t() -> TensorSee :func:`torch.t`"""return _te.Tensor(*(), **{})
  • 返回值与原tensor共享内存!

指定两个维度进行转置:

def permute(self, dims: _size) -> Tensor: r"""permute(*dims) -> TensorSee :func:`torch.permute`"""...
  • 返回值与原tensor共享内存!
  • 对矩阵来说,.t()等价于.permute(0, 1)

多维度同时转置

def permute(self, *dims): # real signature unknown; restored from __doc__"""permute(*dims) -> TensorSee :func:`torch.permute`"""return _te.Tensor(*(), **{})
  • 把要转置的维度放到对应位置上,比如对于三维tensor,x、y、z分别对应0、1、2,如果想要转置x轴和z轴,则输入2、1、0即可
  • 返回值与原tensor共享内存!

cat堆叠

cat可以把两个或多个tensor沿着指定的维度进行连接,连接后的tensor维度个数不变,指定维度上的大小改变,非指定维度上的大小不变。譬如,两个shape=(3,)行向量按dim=0连接,变成1个shape=(6,)的行向量;2个3阶方阵按dim=0连接,就变成1个(6, 3)的矩阵。

cat在使用时对输入的这些tensor有要求:除了指定维度,其他维度的大小必须相同。譬如,1个shape=(1, 6)的矩阵可以和1个shape=(2, 6)的矩阵在dim=0连接。

例子可以参考下面的定义和注释。

def cat(tensors: Union[Tuple[Tensor, ...], List[Tensor]], dim: _int = 0, *, out: Optional[Tensor] = None) -> Tensor: r"""cat(tensors, dim=0, *, out=None) -> TensorConcatenates the given sequence of :attr:`seq` tensors in the given dimension.All tensors must either have the same shape (except in the concatenatingdimension) or be a 1-D empty tensor with size ``(0,)``.:func:`torch.cat` can be seen as an inverse operation for :func:`torch.split`and :func:`torch.chunk`.:func:`torch.cat` can be best understood via examples... seealso:::func:`torch.stack` concatenates the given sequence along a new dimension.Args:tensors (sequence of Tensors): any python sequence of tensors of the same type.Non-empty tensors provided must have the same shape, except in thecat dimension.dim (int, optional): the dimension over which the tensors are concatenatedKeyword args:out (Tensor, optional): the output tensor.Example::>>> x = torch.randn(2, 3)>>> xtensor([[ 0.6580, -1.0969, -0.4614],[-0.1034, -0.5790,  0.1497]])>>> torch.cat((x, x, x), 0)tensor([[ 0.6580, -1.0969, -0.4614],[-0.1034, -0.5790,  0.1497],[ 0.6580, -1.0969, -0.4614],[-0.1034, -0.5790,  0.1497],[ 0.6580, -1.0969, -0.4614],[-0.1034, -0.5790,  0.1497]])>>> torch.cat((x, x, x), 1)tensor([[ 0.6580, -1.0969, -0.4614,  0.6580, -1.0969, -0.4614,  0.6580,-1.0969, -0.4614],[-0.1034, -0.5790,  0.1497, -0.1034, -0.5790,  0.1497, -0.1034,-0.5790,  0.1497]])"""...
  • 返回值与原tensor不共享内存

stack堆叠

stackcat有很大的区别,stack把两个或多个tensor在dim创建一个全新的维度进行连接,非指定维度个数不变,创建的维度的大小取决于这次连接使用了多少个tensor。譬如,3个shape=(3,)行向量按dim=0连接,会变成一个shape=(3, 3)的矩阵;两个3阶方阵按dim=-1连接,就变成一个(3, 3, 2)的tensor。

def stack(tensors: Union[Tuple[Tensor, ...], List[Tensor]], dim: _int = 0, *, out: Optional[Tensor] = None) -> Tensor: r"""stack(tensors, dim=0, *, out=None) -> TensorConcatenates a sequence of tensors along a new dimension.All tensors need to be of the same size... seealso:::func:`torch.cat` concatenates the given sequence along an existing dimension.Arguments:tensors (sequence of Tensors): sequence of tensors to concatenatedim (int, optional): dimension to insert. Has to be between 0 and the numberof dimensions of concatenated tensors (inclusive). Default: 0Keyword args:out (Tensor, optional): the output tensor.Example::>>> x = torch.randn(2, 3)>>> xtensor([[ 0.3367,  0.1288,  0.2345],[ 0.2303, -1.1229, -0.1863]])>>> x = torch.stack((x, x)) # same as torch.stack((x, x), dim=0)>>> xtensor([[[ 0.3367,  0.1288,  0.2345],[ 0.2303, -1.1229, -0.1863]],[[ 0.3367,  0.1288,  0.2345],[ 0.2303, -1.1229, -0.1863]]])>>> x.size()torch.Size([2, 2, 3])>>> x = torch.stack((x, x), dim=1)tensor([[[ 0.3367,  0.1288,  0.2345],[ 0.3367,  0.1288,  0.2345]],[[ 0.2303, -1.1229, -0.1863],[ 0.2303, -1.1229, -0.1863]]])>>> x = torch.stack((x, x), dim=2)tensor([[[ 0.3367,  0.3367],[ 0.1288,  0.1288],[ 0.2345,  0.2345]],[[ 0.2303,  0.2303],[-1.1229, -1.1229],[-0.1863, -0.1863]]])>>> x = torch.stack((x, x), dim=-1)tensor([[[ 0.3367,  0.3367],[ 0.1288,  0.1288],[ 0.2345,  0.2345]],[[ 0.2303,  0.2303],[-1.1229, -1.1229],[-0.1863, -0.1863]]])"""...
  • 返回值与原tensor不共享内存

view改变形状

view先把数据变成一维数组,然后再转换成指定形状。变换前后的元素个数并不会改变,所以变换前后的shape的乘积必须相等。详细例子如下:

def view(self, *shape): # real signature unknown; restored from __doc__"""Example::>>> x = torch.randn(4, 4)>>> x.size()torch.Size([4, 4])>>> y = x.view(16)>>> y.size()torch.Size([16])>>> z = x.view(-1, 8)  # the size -1 is inferred from other dimensions>>> z.size()torch.Size([2, 8])>>> a = torch.randn(1, 2, 3, 4)>>> a.size()torch.Size([1, 2, 3, 4])>>> b = a.transpose(1, 2)  # Swaps 2nd and 3rd dimension>>> b.size()torch.Size([1, 3, 2, 4])>>> c = a.view(1, 3, 2, 4)  # Does not change tensor layout in memory>>> c.size()torch.Size([1, 3, 2, 4])>>> torch.equal(b, c)False """return _te.Tensor(*(), **{})
  • 返回值与原tensor共享内存

reshape改变形状

reshapeview的区别如下:

  • view只能改变连续(.contiguous())的tensor,如果已经对tensor进行了permute、transpose等操作,tensor在内存中会变得不连续,此时调用view会报错。且view方法与原来的tensor共享内存。
  • reshape再调用时自动检测原tensor是否连续,如果是,则等价于view;如果不是,先调用.contiguous(),再调用view,此时返回值与原来tensor不共享内存
    def reshape(self, shape: Sequence[Union[_int, SymInt]]) -> Tensor: ...

1.3.8 数学运算

在这里插入图片描述

    def mean(self, dim=None, keepdim=False, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__ ...def sum(self, dim=None, keepdim=False, dtype=None): # real signature unknown; restored from __doc__...def median(self, dim=None, keepdim=False): # real signature unknown; restored from __doc__...def mode(self, dim=None, keepdim=False): # real signature unknown; restored from __doc__...def dist(self, other, p=2): # real signature unknown; restored from __doc__...def std(self, dim, unbiased=True, keepdim=False): # real signature unknown; restored from __doc__...def var(self, dim, unbiased=True, keepdim=False): # real signature unknown; restored from __doc__...def cumsum(self, dim, dtype=None): # real signature unknown; restored from __doc__...def cumprod(self, dim, dtype=None): # real signature unknown; restored from __doc__...

在这里插入图片描述

1.3.9 使用指定设备计算tensor

to可以把tensor转移到指定设备上。

    def to(self, *args, **kwargs): # real signature unknown; restored from __doc__"""Example::>>> tensor = torch.randn(2, 2)  # Initially dtype=float32, device=cpu>>> tensor.to(torch.float64)tensor([[-0.5044,  0.0005],[ 0.3310, -0.0584]], dtype=torch.float64)>>> cuda0 = torch.device('cuda:0')>>> tensor.to(cuda0)tensor([[-0.5044,  0.0005],[ 0.3310, -0.0584]], device='cuda:0')>>> tensor.to(cuda0, dtype=torch.float64)tensor([[-0.5044,  0.0005],[ 0.3310, -0.0584]], dtype=torch.float64, device='cuda:0')>>> other = torch.randn((), dtype=torch.float64, device=cuda0)>>> tensor.to(other, non_blocking=True)tensor([[-0.5044,  0.0005],[ 0.3310, -0.0584]], dtype=torch.float64, device='cuda:0')"""return _te.Tensor(*(), **{})

2.线性回归模型

2.1 自动求导机制

  • 在pytorch中,如果设置一个 tensor 的属性 requires_grad 为 True,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 tensor.backward 函数,来自动计算所有的梯度。这个张量的所有梯度将会自动累加到 grad 属性。
  • 由于是累加,因此在进行线性回归模型的计算时,每轮都要用 tensor.zero_ 函数清空一次 grad 属性

示例

   sequence_tensor=torch.tensor(np.array([[[1,2,3],[4,5,6]],[[9,8,7],[6,5,4]]]),dtype=torch.float,device=gpu,requires_grad=True)multi_tensor=sequence_tensor*3+1multi_tensor_mean=multi_tensor.mean()multi_tensor_mean.backward()print(sequence_tensor.grad)

输出

tensor([[[0.2500, 0.2500, 0.2500],[0.2500, 0.2500, 0.2500]],[[0.2500, 0.2500, 0.2500],[0.2500, 0.2500, 0.2500]]], device='cuda:0')

2.2 nn.Module的继承(from torch import nn)

2.2.1 概述

nn.Module是torch.nn提供的一个类,是pytorch中定义网络的必要的一个父类,在这个类中定义了很多有用的方法,使我们非常方便地计算。在我们进行网络的定义时,有两个地方需要特别注意:

  • 在定义成员变量时必须调用super函数,继承父类__init__参数,即,在__init__中必须调用super(<the name of the variable>,self)函数
  • 通常还会在__init__中定义网络的结构
  • 必须定义forward函数,表示网络中前向传播的过程

2.2.2 实例

    class lr(nn.Module):def __init__(self):super(lr,self).__init__()self.linear=nn.Linear(1,1)def forward(self,x):y_predict=self.linear(x)return y_predict

其中,nn.Linear函数的参数为:输入的特征量,输出的特征量。

2.3 优化器类(from torch import optim)

2.3.1 概述

优化器(optimizer),用来操纵参数的梯度以更新参数,常见的方法有随机梯度下降(stochastic gradient descent)(SGD)等。

  • torch.optim.SGD(参数,float 学习率)
  • torch.optim.Adam(参数,float 学习率)

2.3.2 流程

  • 调用Module.parameters函数获取模型参数,并定义学习率,进行实例化
  • 用实例化对象调同 .zero_grad 函数,将参数重置为0
  • 调用tensor.backward函数反向传播,获得梯度
  • 用实例化对象调用 .step 函数更新参数

2.3.3 动态学习率(import torch.optim.lr_scheduler)

lr_scheduler允许模型在训练的过程中动态更新学习率,且提供了许多种策略可供选择,以下列举一些常用的:

指数衰减:在训练的过程中,学习率以设定的gamma参数进行指数的衰减。

class ExponentialLR(LRScheduler):"""Decays the learning rate of each parameter group by gamma every epoch.When last_epoch=-1, sets initial lr as lr.Args:optimizer (Optimizer): Wrapped optimizer.gamma (float): Multiplicative factor of learning rate decay.last_epoch (int): The index of last epoch. Default: -1.verbose (bool): If ``True``, prints a message to stdout foreach update. Default: ``False``."""def __init__(self, optimizer, gamma, last_epoch=-1, verbose=False):self.gamma = gammasuper().__init__(optimizer, last_epoch, verbose)

固定步长衰减:在固定的训练周期后,以指定的频率进行衰减。

class StepLR(LRScheduler):"""Decays the learning rate of each parameter group by gamma everystep_size epochs. Notice that such decay can happen simultaneously withother changes to the learning rate from outside this scheduler. Whenlast_epoch=-1, sets initial lr as lr.Args:optimizer (Optimizer): Wrapped optimizer.step_size (int): Period of learning rate decay.gamma (float): Multiplicative factor of learning rate decay.Default: 0.1.last_epoch (int): The index of last epoch. Default: -1.verbose (bool): If ``True``, prints a message to stdout foreach update. Default: ``False``.Example:>>> # xdoctest: +SKIP>>> # Assuming optimizer uses lr = 0.05 for all groups>>> # lr = 0.05     if epoch < 30>>> # lr = 0.005    if 30 <= epoch < 60>>> # lr = 0.0005   if 60 <= epoch < 90>>> # ...>>> scheduler = StepLR(optimizer, step_size=30, gamma=0.1)>>> for epoch in range(100):>>>     train(...)>>>     validate(...)>>>     scheduler.step()"""def __init__(self, optimizer, step_size, gamma=0.1, last_epoch=-1, verbose=False):self.step_size = step_sizeself.gamma = gammasuper().__init__(optimizer, last_epoch, verbose)
  • 用法:创建scheduler的时候绑定optimizer对象,然后在调用optimizer.step()后面跟着scheduler.step()即可。

2.4 代价函数(from torch import nn)

在torch.nn中已经定义好了很多代价函数,只需要调用它们并且传入真实值、预测值,就可以返回结果,例如:

  • 均方误差:nn.MSELoss()
  • 交叉熵误差:nn.CrossEntropyLoss()

当然,也可以自己定义loss的计算过程。

2.5 评估模型

  • Module.eval()表示设置模型为评估模式,即预测模式
  • Module.train(mdoe=True)表示设置模型为训练模式

2.6 线性回归模型的建立

2.6.1 流程

  1. 定义网络,注意:实现super函数和forward函数
  2. 准备数据
  3. 实例化网络、代价函数、优化器
  4. 进行循环,调用Module.forward函数前向传播,调用代价函数进行计算,调用优化器类进行参数更新
  5. 使用pyplot进行模型评估

2.6.2 示例

if __name__=="__main__":import torchimport numpy as npfrom torch import nnfrom torch import optimfrom matplotlib import pyplotgpu=torch.device("cuda")cpu="cpu"#定义网络class lr(nn.Module):def __init__(self):#继承成员变量super(lr,self).__init__()self.linear=nn.Linear(1,1)#定义前向传播函数def forward(self,x):y_predict=self.linear(x)return y_predict#准备数据x_train=torch.rand([200,1],device=gpu)y_train=torch.matmul(x_train,torch.tensor([[3]],dtype=torch.float32,requires_grad=True,device=gpu))+8#实例化model_lr=lr().to(gpu)optimizer=optim.SGD(model_lr.parameters(),0.02)cost_fn=nn.MSELoss()#开始计算for i in range(1000):y_predict=model_lr.forward(x_train)cost=cost_fn(y_predict,y_train)optimizer.zero_grad()cost.backward(retain_graph=True)optimizer.step()if i%20==0:print(cost.item())print(list(model_lr.parameters()))#进行预测与评估model_lr.eval()y_predict_numpy=model_lr.forward(x_train).to(cpu).detach().numpy()x_train_numpy=x_train.to(cpu).detach().numpy()y_train_numpy=y_train.to(cpu).detach().numpy()pyplot.scatter(x_train_numpy,y_predict_numpy,c="r")pyplot.plot(x_train_numpy,y_train_numpy)pyplot.show()

输出

4.7310328227467835e-05
[Parameter containing:
tensor([[3.0237]], device='cuda:0', requires_grad=True), Parameter containing:
tensor([7.9876], device='cuda:0', requires_grad=True)]

绘制图
在这里插入图片描述

3.数据集和数据加载器 (from torch.utils.data import Dataset,DataLoader)

3.1 Dataset类的继承(from torch.utils.data import Dataset)

3.1.1 概述

在pytorch中提供了数据集的父类torch.utils.data.Dataset,继承这个父类,我们可以非常快速地实现对数据的加载,与继承nn.Module类一样,我们同样必须定义一些必要的成员函数

  • __getitem__(self,index),用来进行索引,可以用 [ ]
  • __len__(self),用来获取元素个数

3.1.2 实例

    SMSData_path="D:\Desktop\PycharmProjects\exercise\SMSSpamCollection"#数据来源:http://archive.ics.uci.edu/ml/machine-learning-databases/00228/class SMSData(Dataset):def __init__(self):self.data=open(SMSData_path,"r",encoding="utf-8").readlines()def __getitem__(self, index):current_line=self.data[index].strip()label=current_line[:4].strip()content=current_line[4:].strip()return [label,content]def __len__(self):return len(self.data)SMSex=SMSData()print(SMSex.__getitem__(5))print(SMSex.__len__())

输出

['spam', "FreeMsg Hey there darling it's been 3 week's now and no word back! I'd like some fun you up for it still? Tb ok! XxX std chgs to send, £1.50 to rcv"]
5574

3.2 DataLoader类

3.2.1 API

class DataLoader(Generic[T_co]):def __init__(self, dataset: Dataset[T_co], batch_size: Optional[int] = 1,shuffle: Optional[bool] = None, sampler: Union[Sampler, Iterable, None] = None,batch_sampler: Union[Sampler[Sequence], Iterable[Sequence], None] = None,num_workers: int = 0, collate_fn: Optional[_collate_fn_t] = None,pin_memory: bool = False, drop_last: bool = False,timeout: float = 0, worker_init_fn: Optional[_worker_init_fn_t] = None,multiprocessing_context=None, generator=None,*, prefetch_factor: int = 2,persistent_workers: bool = False,pin_memory_device: str = ""):#只列出参数表,以下详细内容不再列出

dataset:以Dataset类为父类的自定义类的实例化对象

batch_size:批处理的个数

shuffle:bool类型,若为True则表示提前打乱数据

num_workers:加载数据时用到的线程数

drop_last :bool类型,若为True:这个是对最后的未完成的batch来说的,比如你的batch_size设置为64,而一个训练集只有100个样本,那么训练的时候后面的36个就被扔掉了。如果为False(默认),那么会继续正常执行,只是最后的batch_size会小一点。

timeout:如果是正数,表明等待从worker进程中收集一个batch等待的时间,若超出设定的时间还没有收集到,那就不收集这个内容了。这个numeric应总是大于等于0,默认为0

3.2.2 示例

import torch
from torch.utils.data import Dataset,DataLoader
import chardet
gpu = torch.device("cuda")
cpu="cpu"
try:SMSData_path="SMSSpamCollection"#获取文件编码方式with open(SMSData_path,"rb") as file:file_format=chardet.detect(file.read())["encoding"]class SMSData(Dataset):def __init__(self):self.data=open(SMSData_path,"r",encoding=file_format).readlines()def __getitem__(self, index):current_line=self.data[index].strip()origin=current_line[:4].strip()content=current_line[4:].strip()return [origin,content]def __len__(self):return len(self.data)SMSex=SMSData()SMSData_loader=DataLoader(dataset=SMSex,batch_size=2,shuffle=False,num_workers=2)if __name__=='__main__':#如果设置多线程,一定要加这句话,否则会报错for i in SMSData_loader:print("遍历一:",i)breakfor i in enumerate(SMSData_loader):print("遍历二:",i)breakfor batch_index,(label,content) in enumerate(SMSData_loader):print("遍历三:",batch_index,label,content)breakexcept BaseException as error:print(error)

输出

遍历一: [('ham', 'ham'), ('Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...', 'Ok lar... Joking wif u oni...')]
遍历二: (0, [('ham', 'ham'), ('Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...', 'Ok lar... Joking wif u oni...')])
遍历三: 0 ('ham', 'ham') ('Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...', 'Ok lar... Joking wif u oni...')
  • 可见,DataLoader是一个可遍历对象,每轮中返回的数据以列表的方式存储,且列表中每个元素都是一个元组,列表的长度等于Dataset.__getitem__返回的列表长度,元组的长度等于batch_size参数的大小

4.图像处理:手写数字识别

4.1 torchvision模块

4.1.1 transforms.ToTensor类(仿函数)

class ToTensor:def __init__(self) -> None:_log_api_usage_once(self)
  • 将原始的PILImage数据类型或者numpy.array数据类型化为tensor数据类型。
  • 如果 PIL Image 属于 (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)中的一种图像类型,或者 numpy.ndarray 格式数据类型是 np.uint8 ,则将 [0, 255] 的数据转为 [0.0, 1.0] ,也就是说将所有数据除以 255 进行归一化。

4.1.2 transforms.Normalize类(仿函数)

class Normalize(torch.nn.Module):def __init__(self, mean, std, inplace=False):super().__init__()_log_api_usage_once(self)self.mean = meanself.std = stdself.inplace = inplace

mean:数据类型为元组,元组的长度取决于通道数

std:数据类型为元组,元组的长度取决于通道数

  • 此函数可以将tensor进行标准化,使其在每个通道上都转化为均值为mean,标准差为std的高斯分布。

4.1.3 transforms.Compose类(仿函数)

class Compose:def __init__(self, transforms):if not torch.jit.is_scripting() and not torch.jit.is_tracing():_log_api_usage_once(self)self.transforms = transforms

transforms:数据类型为列表,列表中每个元素都是transforms模块中的一个类,如ToTensor和Normalize(隐式构造)。

  • 此函数可以将许多transforms类结合起来同时使用。

4.1.4 示例

import torchvision
if __name__ == '__main__':MNIST=torchvision.datasets.MNIST(root="./data",train=True,download=False,transform=None)MNIST_normalize=torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Normalize((0),(1))])(MNIST[0][0])print(MNIST_normalize)

4.2 网络构建

4.2.1 激活函数大全

在这里插入图片描述

  • 在pytorch中已经实现了上述很多的激活函数,下面我们将使用ReLU激活函数进行网络构建。

4.2.2 演示代码(在gpu上)

import torchvision
import torch
from torch.utils.data import DataLoader
from torch import nn
from torch import optim
from torch.nn import functional as Activate
from matplotlib import pyplot# 定义所用网络
class ExNet(nn.Module):def __init__(self):# super函数调用super(ExNet, self).__init__()# 卷积层1self.conv1 = nn.Conv2d(1, 15, 5)'''输入通道数1,输出通道数15,核的大小5,输入必须为1,输出可以自定义'''# 卷积层2self.conv2 = nn.Conv2d(15, 30, 3)'''输入通道数15,输出通道数30,核的大小3,输入必须与上层的输出一致,输出可以自定义'''# 全连接层1self.fully_connected_1 = nn.Linear(30 * 10 * 10, 40)'''MNIST原始图像是1*28*28,输入为batch_size*1*28*28,经过卷积层1后,变为batch_size*15*24*24经过池化层后,变为batch_size*15*12*12经过卷积层2后,变为batch_size*30*10*10这个全连接层的第一层输入个数就是这么来的'''# 全连接层2self.fully_connected_2 = nn.Linear(40, 10)'''输入与上层保持一致由于要鉴别十个数字,因此输出层的神经元个数必须是10'''# 定义前向传播def forward(self, x):in_size = x.size(0)  # 在本例中in_size,也就是BATCH_SIZE的值。输入的x可以看成是batch_size*1*28*28的张量。# 卷积层1out = self.conv1(x)  # batch*1*28*28 -> batch*15*24*24out = Activate.relu(out)  # 调用ReLU激活函数# 池化层out = Activate.max_pool2d(out, 2, 2)  # batch*15*24*24 -> batch*15*12*12(2*2的池化层会减半)# 卷积层2out = self.conv2(out)  # batch*15*12*12 -> batch*30*10*10out = Activate.relu(out)  # 调用ReLU激活函数# flatten处理out = out.view(in_size, -1)# 全连接层1out = self.fully_connected_1(out)out = Activate.relu(out)# 全连接层2out = self.fully_connected_2(out)# 归一化处理,以便进行交叉熵代价函数的运算out = Activate.log_softmax(out, dim=1)return out# 开始训练
def train(the_model, the_device, train_loader, the_optimizer, the_epoch):# 模型相关设置the_model=the_model.to(device=the_device)the_model.train(mode=True)# 用来绘制图像的变量list_times = []list_cost = []# 每轮循环for batch_idx, (data, target) in enumerate(train_loader):# 转移到指定设备上计算data = data.to(the_device);target = target.to(the_device)# 优化器参数重置the_optimizer.zero_grad()# 向前计算output = the_model.forward(data)# 计算误差cost = Activate.nll_loss(output, target)# 反向传播cost.backward()# 参数更新the_optimizer.step()# 打印信息if batch_idx % 10 == 0:print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(the_epoch, batch_idx * len(data), len(train_loader.dataset),100. * batch_idx / len(train_loader), cost.item()))print(batch_idx, cost.item())list_times.append(batch_idx)list_cost.append(cost.item())# 绘制图像pyplot.scatter(list_times, list_cost)pyplot.savefig("costImage.jpg")pyplot.show()returndef test(the_model, the_device, the_test_loader):# 设置训练模式the_model=the_model.to(device=the_device)the_model.eval()# 测试的结果集acc_vector = []cost_vector = []#开始测试with torch.no_grad():for index, (data, target) in enumerate(the_test_loader):# 转移到指定设备上计算data = data.to(the_device);target = target.to(the_device)# 向前计算output = the_model.forward(data)# 计算误差cost = Activate.nll_loss(output, target)cost_vector.append(cost)pred = output.max(dim=1)[-1]  # output的尺寸是[batch_size,10],对每行取最大值,返回索引编号,即代表模型预测手写数字的结果cur_acc = pred.eq(target).float().mean()  # 均值代表每组batch_size中查准率acc_vector.append(cur_acc)# 打印结果print("平均查准率:{}".format(sum(acc_vector)/len(acc_vector)))print("average cost:{}".format(sum(cost_vector)/len(cost_vector)))returnif __name__ == '__main__':gpu = torch.device("cuda")cpu = "cpu"# 准备数据transAndNorm = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Normalize((0), (1))])MNISTData = torchvision.datasets.MNIST(root="./data", train=True, download=False, transform=transAndNorm)MNISTtest = torchvision.datasets.MNIST(root="./data", train=False, download=False, transform=transAndNorm)MNISTData_loader = DataLoader(dataset=MNISTData, batch_size=10, shuffle=True)MNISTtest_loader = DataLoader(dataset=MNISTtest, batch_size=10, shuffle=True)# 实例化网络和优化器MNISTnet_Ex = ExNet()MNIST_optimizer = optim.Adam(MNISTnet_Ex.parameters(), lr=0.001)  # lr(learning rate)是学习率for i in range(1,2):train(the_model=MNISTnet_Ex, the_device=gpu, train_loader=MNISTData_loader, the_optimizer=MNIST_optimizer, the_epoch=i)test(the_model=MNISTnet_Ex, the_device=gpu, the_test_loader=MNISTtest_loader)

输出

平均查准率:0.9804015159606934
average cost:0.061943911015987396

散点图
在这里插入图片描述

5.制作图片数据集(以flower102为例)

在刚刚的MNIST手写数字识别分类任务中,我们使用的数据集是pytorch官方内置的图片数据集。现在,我们要从零开始,尝试制作我们自己的数据集。

Oxford 102 Flower 是一个图像分类数据集,由 102 个花卉类别组成。被选为英国常见花卉的花卉。每个类别由 40 到 258 张图像组成。图像具有大尺度、姿势和光线变化。此外,还有一些类别在类别内有很大的变化,还有几个非常相似的类别。这里是flower102数据集的下载地址。解压后的文件目录如下:
在这里插入图片描述

5.1 建立数据集骨架

如第三章一样建立即可,如下:

import torch
from torch.utils.data import Dataset
import os
gpu = torch.device("cuda")
cpu = "cpu"
class flower102(Dataset):def __init__(self,root,resize,mode):super(flower102,self).__init__()passdef __len__(self):passdef __getitem__(self, item):pass

5.2 建立从名称到数字标签的映射

在这里插入图片描述
在训练集中,这102种花的类别名称如上图所示(我这里是经过重命名的),我们定义名称flower1数字标签1,这样我们就建立了一个映射。接下来,稍微修改一下构造函数,就可以实现全部的映射。如下:

import csv
import glob
import random
import os
from PIL import Imageimport torch
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transformsgpu = torch.device("cuda")
cpu = "cpu"class flower102(Dataset):def __init__(self, root, resize, mode):super(flower102, self).__init__()self.root = rootself.train_root = os.path.join(self.root, "train")self.val_root = os.path.join(self.root, "valid")self.test_root = os.path.join(self.root, "test")self.resize = resizeself.mode = modeself.mean = [0.485, 0.456, 0.406]self.std = [0.229, 0.224, 0.225]self.cat2label = {}  # 创建一个空字典,用于存储映射关系。for name in sorted(os.listdir(os.path.join(self.train_root))):  # 遍历训练集目录下的文件和文件夹,并按照名称排序。if not os.path.isdir(os.path.join(self.train_root, name)):  # 如果遍历到的是文件而不是文件夹,则跳过该项继续遍历下一项。continueelif not (name in self.cat2label):self.cat2label[name] = len(self.cat2label.keys())  # 将文件夹名称与类别标签对应,类别标签为字典长度(每次循环增加1)。print(self.cat2label)  # 打印映射关系字典。def __len__(self):passdef __getitem__(self, idx):pass# 创建数据集实例
db = flower102(r"D:\Desktop\Datasets\flower102\dataset", resize=224, mode="train")

结果如下:

{'flower1': 0, 'flower10': 1, 'flower100': 2, 'flower101': 3, 'flower102': 4, 'flower11': 5, 'flower12': 6, 'flower13': 7, 'flower14': 8, 'flower15': 9, 'flower16': 10, 'flower17': 11, 'flower18': 12, 'flower19': 13, 'flower2': 14, 'flower20': 15, 'flower21': 16, 'flower22': 17, 'flower23': 18, 'flower24': 19, 'flower25': 20, 'flower26': 21, 'flower27': 22, 'flower28': 23, 'flower29': 24, 'flower3': 25, 'flower30': 26, 'flower31': 27, 'flower32': 28, 'flower33': 29, 'flower34': 30, 'flower35': 31, 'flower36': 32, 'flower37': 33, 'flower38': 34, 'flower39': 35, 'flower4': 36, 'flower40': 37, 'flower41': 38, 'flower42': 39, 'flower43': 40, 'flower44': 41, 'flower45': 42, 'flower46': 43, 'flower47': 44, 'flower48': 45, 'flower49': 46, 'flower5': 47, 'flower50': 48, 'flower51': 49, 'flower52': 50, 'flower53': 51, 'flower54': 52, 'flower55': 53, 'flower56': 54, 'flower57': 55, 'flower58': 56, 'flower59': 57, 'flower6': 58, 'flower60': 59, 'flower61': 60, 'flower62': 61, 'flower63': 62, 'flower64': 63, 'flower65': 64, 'flower66': 65, 'flower67': 66, 'flower68': 67, 'flower69': 68, 'flower7': 69, 'flower70': 70, 'flower71': 71, 'flower72': 72, 'flower73': 73, 'flower74': 74, 'flower75': 75, 'flower76': 76, 'flower77': 77, 'flower78': 78, 'flower79': 79, 'flower8': 80, 'flower80': 81, 'flower81': 82, 'flower82': 83, 'flower83': 84, 'flower84': 85, 'flower85': 86, 'flower86': 87, 'flower87': 88, 'flower88': 89, 'flower89': 90, 'flower9': 91, 'flower90': 92, 'flower91': 93, 'flower92': 94, 'flower93': 95, 'flower94': 96, 'flower95': 97, 'flower96': 98, 'flower97': 99, 'flower98': 100, 'flower99': 101}

5.3 建立csv数据

在建立了从名称到数字标签的映射后,我们希望有一个csv文件,里面存储了所有的图片路径及其数字标签,接下来,我们将定义一个load_csv函数去完成这件事,如下:

import csv
import glob
import random
import os
from PIL import Imageimport torch
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transformsgpu = torch.device("cuda")
cpu = "cpu"class flower102(Dataset):def __init__(self, root, resize, mode):super(flower102, self).__init__()self.root = rootself.train_root = os.path.join(self.root, "train")self.val_root = os.path.join(self.root, "valid")self.test_root = os.path.join(self.root, "test")self.resize = resizeself.mode = modeself.mean = [0.485, 0.456, 0.406]self.std = [0.229, 0.224, 0.225]self.cat2label = {}  # 创建一个空字典,用于存储映射关系。for name in sorted(os.listdir(os.path.join(self.train_root))):  # 遍历训练集目录下的文件和文件夹,并按照名称排序。if not os.path.isdir(os.path.join(self.train_root, name)):  # 如果遍历到的是文件而不是文件夹,则跳过该项继续遍历下一项。continueelif not (name in self.cat2label):self.cat2label[name] = len(self.cat2label.keys())  # 将文件夹名称与类别标签对应,类别标签为字典长度(每次循环增加1)。print(self.cat2label)  # 打印映射关系字典。if mode == "train":self.images, self.labels = self.load_csv("images_train.csv")elif mode == "valid":self.images, self.labels = self.load_csv("images_valid.csv")else:raise Exception("invalid mode!", self.mode)# 加载CSV文件并返回图像路径和标签列表def load_csv(self, filename):# 如果CSV文件不存在,则根据训练集目录和映射关系生成CSV文件if not os.path.exists(os.path.join(self.root, filename)):images = []for name in self.cat2label.keys():images += glob.glob(os.path.join(self.root, self.mode, name, "*.png"))images += glob.glob(os.path.join(self.root, self.mode, name, "*.jpg"))images += glob.glob(os.path.join(self.root, self.mode, name, "*.jpeg"))random.shuffle(images)with open(os.path.join(self.root, filename), mode="w", newline="") as f:writer = csv.writer(f)for img in images:label = self.cat2label[img.split(os.sep)[-2]]writer.writerow([img, label])print("written into csv file:", filename)# 从CSV文件中读取图像路径和标签images = []labels = []with open(os.path.join(self.root, filename)) as f:reader = csv.reader(f)for row in reader:img, label = rowlabel = int(label)images.append(img)labels.append(label)assert len(images) == len(labels)return images, labels# 反归一化def denormalize(self, x_hat):passdef __len__(self):passdef __getitem__(self, idx):pass# 创建数据集实例
db = flower102(r"D:\Desktop\Datasets\flower102\dataset", resize=224, mode="train")

然后,我们获得了一个如下的csv文件:
在这里插入图片描述

5.4 完善成员函数和transform过程

在完成了load_csv函数后,这个数据集基本制作完成,接下来只需要完善__len__函数和__getitem__函数,并定义transform过程即可。

import csv
import glob
import random
import os
from PIL import Imageimport torch
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transformsgpu = torch.device("cuda")
cpu = "cpu"class flower102(Dataset):def __init__(self, root, resize, mode):super(flower102, self).__init__()self.root = rootself.train_root = os.path.join(self.root, "train")self.val_root = os.path.join(self.root, "valid")self.test_root = os.path.join(self.root, "test")self.resize = resizeself.mode = modeself.mean = [0.485, 0.456, 0.406]self.std = [0.229, 0.224, 0.225]self.cat2label = {}  # 创建一个空字典,用于存储映射关系。for name in sorted(os.listdir(os.path.join(self.train_root))):  # 遍历训练集目录下的文件和文件夹,并按照名称排序。if not os.path.isdir(os.path.join(self.train_root, name)):  # 如果遍历到的是文件而不是文件夹,则跳过该项继续遍历下一项。continueelif not (name in self.cat2label):self.cat2label[name] = len(self.cat2label.keys())  # 将文件夹名称与类别标签对应,类别标签为字典长度(每次循环增加1)。print(self.cat2label)  # 打印映射关系字典。if mode == "train":self.images, self.labels = self.load_csv("images_train.csv")elif mode == "valid":self.images, self.labels = self.load_csv("images_valid.csv")else:raise Exception("invalid mode!", self.mode)# 加载CSV文件并返回图像路径和标签列表def load_csv(self, filename):# 如果CSV文件不存在,则根据训练集目录和映射关系生成CSV文件if not os.path.exists(os.path.join(self.root, filename)):images = []for name in self.cat2label.keys():images += glob.glob(os.path.join(self.root, self.mode, name, "*.png"))images += glob.glob(os.path.join(self.root, self.mode, name, "*.jpg"))images += glob.glob(os.path.join(self.root, self.mode, name, "*.jpeg"))random.shuffle(images)with open(os.path.join(self.root, filename), mode="w", newline="") as f:writer = csv.writer(f)for img in images:label = self.cat2label[img.split(os.sep)[-2]]writer.writerow([img, label])print("written into csv file:", filename)# 从CSV文件中读取图像路径和标签images = []labels = []with open(os.path.join(self.root, filename)) as f:reader = csv.reader(f)for row in reader:img, label = rowlabel = int(label)images.append(img)labels.append(label)assert len(images) == len(labels)return images, labels# 反归一化def denormalize(self, x_hat):# x_hat = (x - mean) / std# x = x_hat * std + mean# x.size(): [c, h, w]# mean.size(): [3] => [3, 1, 1]mean = torch.tensor(self.mean).unsqueeze(1).unsqueeze(1)std = torch.tensor(self.std).unsqueeze(1).unsqueeze(1)x = x_hat * std + meanreturn xdef __len__(self):# 返回数据集中样本的数量return len(self.images)def __getitem__(self, idx):# 根据索引获取图像和标签img, label = self.images[idx], self.labels[idx]# 定义数据的预处理操作tf = transforms.Compose([lambda x: Image.open(x).convert("RGB"),  # 以RGB格式打开图像transforms.Resize((int(self.resize * 1.25), int(self.resize * 1.25))),  # 调整图像大小为resize的1.25倍transforms.RandomRotation(15),  # 随机旋转图像(最大旋转角度为15度)transforms.CenterCrop(self.resize),  # 将图像中心裁剪为resize大小transforms.ToTensor(),  # 将图像转换为Tensor类型transforms.Normalize(mean=self.mean, std=self.std),  # 归一化图像])# 对图像进行预处理操作img = tf(img)label = torch.tensor(label)return img, label# 创建数据集实例
db = flower102(r"D:\Desktop\Datasets\flower102\dataset", resize=224, mode="train")

5.5 DataLoader检验

if __name__=='__main__' :loader = DataLoader(dataset=db, shuffle=True,num_workers=1,batch_size=8)import matplotlib.pyplot as pltdata,target=next(iter(db))print(data.shape)plt.imshow(transforms.ToPILImage()(db.denormalize(data)))plt.show()

成功显示:
在这里插入图片描述

6.迁移学习

6.1 现有模型的保存和加载

6.1.1 保存(torch.save函数)

我们要保存的是:

  • 实例化的网络的数据
  • 实例化的优化器的数据
def save(obj: object,f: FILE_LIKE,pickle_module: Any = pickle,pickle_protocol: int = DEFAULT_PROTOCOL,_use_new_zipfile_serialization: bool = True
) -> None:...
  • 我们只需要把string类型的文件名作为参数输入即可

把数据加载进网络

  • Module.load_state_dict函数,我们只需要用torch.load函数的返回值作为参数即可

把数据加载进优化器

  • optim.load_state_dict函数,我们只需要用torch.load函数的返回值作为参数即可

6.1.3 示例

    torch.save(MNISTnet_Ex.state_dict(),"MNIST.pt")torch.save(optimzer.state_dict(),"optimizer.pt")MNISTnet_Ex.load_state_dict(torch.load("MNIST.pt"))optimzer.load_state_dict(torch.load("optimizer.pt"))

6.2 使用预训练的模型(以resnet50为例)

pytoch官方提供了不少与训练的模型可供使用,如下:

model
AlexNet
ConvNeXt
DenseNet
EfficientNet
EfficientNetV2
GoogLeNet
Inception V3
MaxVit
MNASNet
MobileNet V2
MobileNet V3
RegNet
ResNet
ResNeXt
ShuffleNet V2
SqueezeNet
SwinTransformer
VGG
VisionTransformer
Wide ResNet

关于这些模型的详细用途,可以自行前往pytorch官网查阅相关资料,具体原理本文不再涉及。

6.2.1 确定初始化参数

在使用预训练模型的过程中,最重要的一步是,确定这个预训练模型中哪些参数是需要训练的,哪些参数是不需要训练的,哪些参数是要修改的

首先,查看一下resnet50的网络结构:

import torchvision.models as models
print(models.resnet50(pretrained=True))
Resnet(
...
(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))(fc): Linear(in_features=2048, out_features=1000, bias=True)
)

看到最后一层是一个1000分类的全连接层,而我们第五章制作的数据集里,只需要102分类,因此,我们选择只修改最后一层的参数并训练。如下所示:

import torchvision.models as models
import torch.nn as nndef set_parameter_requires_grad(model,need_train):if not need_train:for para in model.parameters():para.requires_grad = Falsereturndef initalize_resnet50(num_classes,need_train=False,pretrained=True):trained_model=models.resnet50(pretrained=pretrained)input_size=224set_parameter_requires_grad(trained_model, need_train)trained_model.fc = nn.Sequential(nn.Linear(trained_model.fc.in_features, num_classes),nn.LogSoftmax(dim=1),)# trained_model.fc = nn.Sequential(#     nn.Linear(trained_model.fc.in_features, num_classes),#     nn.Flatten(),# )return trained_model,input_sizeresnet50,input_size=initalize_resnet50(num_classes=102,need_train=False,pretrained=True)

6.3 开始训练

训练的流程和记录如第四章所示即可,如下:

import copy  # 导入copy模块,用于深拷贝对象
import os.path  # 导入os.path模块,用于操作文件路径
import time  # 导入time模块,用于计时def train(model, dataLoader, criterion, optimzer, num_epoch, device, filename):"""训练函数Args:model: 模型对象dataLoader: 数据加载器criterion: 损失函数optimzer: 优化器num_epoch: 迭代次数device: 计算设备filename: 保存模型的文件名Returns:model: 训练后的模型train_acc_history: 训练集准确率历史train_losses: 训练集损失历史l_rs: 优化器学习率历史"""since = time.time()  # 获取当前时间best_epoch = {"epoch": -1,"acc": 0}  # 存储最佳模型的epoch和准确率model.to(device)  # 将模型移动到计算设备上train_acc_history = []  # 存储训练集准确率历史train_losses = []  # 存储训练集损失历史l_rs = [optimzer.param_groups[0]['lr']]  # 存储优化器学习率历史best_model_wts = copy.deepcopy(model.state_dict())  # 深拷贝当前模型的权重作为最佳模型权重for epoch in range(num_epoch):  # 迭代训练print("Epoch {}/{}".format(epoch, num_epoch - 1))print('*' * 10)running_loss = 0.0  # 初始化损失总和running_correct = 0.0  # 初始化正确预测的样本数总和for data, target in dataLoader:  # 遍历数据加载器中的每个批次data = data.to(device)  # 将输入数据移动到计算设备上target = target.to(device)  # 将目标数据移动到计算设备上optimzer.zero_grad()  # 清零梯度output = model.forward(data)  # 前向传播loss = criterion(output, target)  # 计算损失pred = output.argmax(dim=1)  # 获取预测结果loss.backward()  # 反向传播optimzer.step()  # 更新参数running_loss += loss.item() * data.size(0)  # 累加损失running_correct += torch.eq(pred, target).sum().float().item()  # 累加正确预测的样本数epoch_loss = running_loss / len(dataLoader.dataset)  # 计算平均损失epoch_acc = running_correct / len(dataLoader.dataset)  # 计算准确率time_elapsed = time.time() - since  # 计算训练时间print("Time elapsed {:.0f}m {:.0f}s".format(time_elapsed // 60, time_elapsed % 60))print("Loss: {:4f} Acc:{:.4f}".format(epoch_loss, epoch_acc))train_acc_history.append(epoch_acc)  # 将准确率添加到历史列表中train_losses.append(epoch_loss)  # 将损失添加到历史列表中if (epoch_acc > best_epoch["acc"]):  # 更新最佳模型信息best_epoch = {"epoch": epoch,"acc": epoch_acc}best_model_wts = copy.deepcopy(model.state_dict())  # 深拷贝当前模型权重作为最佳模型权重state = {"state_dict": model.state_dict(),"best_acc": best_epoch["acc"],"optimzer": optimzer.state_dict(),}torch.save(state, filename)  # 保存最佳模型的状态字典到文件print("Optimzer learning rate : {:.7f}".format(optimzer.param_groups[0]['lr']))  # 打印当前优化器学习率l_rs.append(optimzer.param_groups[0]['lr'])  # 将当前优化器学习率添加到历史列表中print()time_elapsed = time.time() - since  # 计算总训练时间print("Training complete in {:.0f}m {:.0f}s".format(time_elapsed // 60, time_elapsed % 60))print("Best epoch:", best_epoch)model.load_state_dict(best_model_wts)  # 加载最佳模型权重return model, train_acc_history, train_losses, l_rsif __name__ == "__main__":import torchimport Netimport torch.nn as nnimport torch.optim as optimoptimzer = optim.Adam(params=Net.resnet50.parameters(), lr=1e-2)  # 创建Adam优化器sche = optim.lr_scheduler.StepLR(optimizer=optimzer, step_size=10, gamma=0.5)  # 创建学习率调度器criterion = nn.NLLLoss()  # 创建负对数似然损失函数#criterion=nn.CrossEntropyLoss()import flower102from torch.utils.data import DataLoaderdb = flower102.flower102(r"D:\Desktop\Datasets\flower102\dataset", resize=Net.input_size, mode="train")  # 创建数据集对象loader = DataLoader(dataset=db, shuffle=True, num_workers=1, batch_size=5)  # 创建数据加载器model = Net.resnet50  # 创建模型对象filename = "checkpoint.pth"  # 模型保存文件名if os.path.exists(filename):  # 如果存在模型文件checkpoint = torch.load(filename)  # 加载模型状态字典model.load_state_dict(checkpoint["state_dict"])  # 加载模型权重model, train_acc_history, train_loss, LRS = train(model=model, dataLoader=loader, criterion=criterion,optimzer=optimzer, num_epoch=5,device=torch.device("cuda"), filename=filename)

下面是我训练5轮的结果:

Epoch0/4
**********
Time elapsed 0m 37s
Loss: 11.229704 Acc:0.3515
Optimzer learning rate : 0.0100000
Epoch1/4
**********
Time elapsed 1m 12s
Loss: 8.165128 Acc:0.5697
Optimzer learning rate : 0.0100000
Epoch2/4
**********
Time elapsed 2m 4s
Loss: 7.410833 Acc:0.6363
Optimzer learning rate : 0.0100000
Epoch3/4
**********
Time elapsed 2m 60s
Loss: 6.991850 Acc:0.6822
Optimzer learning rate : 0.0100000
Epoch4/4
**********
Time elapsed 3m 44s
Loss: 6.482804 Acc:0.7128
Optimzer learning rate : 0.0100000
Training complete in 3m 44s
Best epoch: {'epoch': 4, 'acc': 0.7127594627594628}

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/787497.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AMD显卡VGA转HDMI花屏或者雪花屏解决办法

本以为是显卡或者显示屏或者转接线有问题,查阅资料后得出VGA转HDMI花屏或者雪花屏解决办法: 进入AMD控制面板。点击设置 - > 显示器 - > 覆盖 - > 禁用HDCP 即可完美解决问题。

AIGC时代,如何为“数据飞轮”提速

本文从技术角度,具体拆解DataLeap-找数助手、开发助手的实现方式,详AIGC如何为企业数智化转型赋能。更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 企业通过数智化转型实现降本增效,已经成为行业共识。而随着AIGC时代到来,企业的…

windows 安装nginx

1.进入下载页面下载window版本 安装 2.解压, 直接运行 exe就行 3.使用nssm 将exe文件 作为服务运行 4.配置conf/nginx.conf 的配置文件

使用yum命令报错

报错如下Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock error was 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"One of the configured repositories fai…

EXITS:基于极值点低标注成本弱监督实例分割 | CVPR 2024

EXITS将极值点视为真实实例掩模的一部分并将它们传播以识别潜在前景和背景点,所有这些都用于训练伪标签生成器,然后由生成器给出的伪标签反过来用于最终模型的监督学习。 来源:晓飞的算法工程笔记 公众号论文: Extreme Point Supervised Instance Segmentation论文地址:htt…

Swahili-text:华中大推出非洲语言场景文本检测和识别数据集 | ICDAR 2024

论文提出了一个专门针对斯瓦希里语自然场景文本检测和识别的数据集,这在当前研究中是一个未充分开发的语言领域。数据集包括976张带标注的场景图像,可用于文本检测,以及8284张裁剪后的图像用于识别。 来源:晓飞的算法工程笔记 公众号论文: The First Swahili Language Scen…

Codeforces Round 968 (Div. 2)

良心出题人给了中文题解!!! A. Turtle and Good Strings 长度为 \(n\) 的字符串至少分成两段,使 \(\forall i < j\) ,第 \(i\) 段的首字符不等于第 \(j\) 段的尾字符 第一个字符一定作为首字符,最后一个字符一定作为尾字符,只要判断这两个字符是否相等即可 相等的话一…

【日记】这个月花了好多钱(1317 字)

正文这几天都好热。热到人不想动,只想睡觉。今天写文章发现自己有个很显著的特点,就是在有个框架之后,具体细节完全没有预设。我只能像马尔可夫链一样,形成一个比较窄的窗口,接着这个窗口里的情节往下写,否则我就会宕机,写不出来。整个故事情节看起来也就比较散。马尔可…

个人博客导航如何创建更有个性化?

个人博客导航如何创建更有个性化?创建个性化博客导航是一个既实用又能体现个人风格的过程。以下是一些步骤和建议,帮助你完成这一过程:一、确定导航需求和内容明确目的:首先,你需要明确博客导航的主要目的是什么,比如是为了方便读者查找特定内容、展示你的博客结构,还是…

线程基础

生命周期创建线程 class MyThread extends Thread{@Overridepublic void run(){ System.out.println("Hello World"); } }class MyRunnable implements Runnable{@Overridepublic void run() { System.out.println("Hello World"); } }public class Test1 …

网站提示413 Payload Too Large:请求实体过大怎么办

当遇到“413 Payload Too Large”错误时,这意味着客户端发送的请求实体(通常是请求体)超过了服务器允许的最大大小。这种错误通常出现在上传文件或发送大量数据时。 解决方案减小请求体大小检查请求体中的数据量是否过大。 如果是文件上传,考虑减小文件大小或压缩文件。增加…

网站提示500 - 服务器遇到了意外的错误,无法完成请求,可以刷新重试怎么办

当网站提示 500 Internal Server Error 时,这意味着服务器遇到了一个错误,无法完成请求。这种错误通常是由服务器端的问题引起的,可能是由于配置错误、脚本错误、数据库连接问题等。以下是解决 500 Internal Server Error 的一些常见方法: 常见原因服务器配置错误:服务器的…