Pytorch

conda activate pytorch

注释:Ctrl + /

下一行:Shift + Enter

提示:Ctrl + P

read data

这段代码定义了一个自定义的数据集类MyData,继承自torch.utils.data.Dataset。该数据集类用于加载图像和标签,并返回一个元组(img, label),其中img是图像对象,label是标签字符串。

在__init__(initial)方法中,首先传入根目录root_dir和标签目录label_dir,然后拼接出完整的路径self.path。接着使用os.listdir()函数获取该路径下的所有文件名,存储在self.img_path列表中。

在__getitem__方法中,根据索引idx获取对应的图像文件名img_name,然后拼接出图像文件的完整路径img_item_path。接着使用PIL库中的Image.open()函数打开图像文件,并将标签字符串赋值给label变量。最后返回一个元组(img, label)。

在__len__方法中,直接返回self.img_path列表的长度,即数据集中的样本数量。

最后,创建两个MyData实例ants_dataset和bees_dataset,分别对应于"hymenoptera_data/train"目录下的"ants"和"bees"子目录。将这两个实例合并为一个新的数据集train_dataset。

# 导入所需的库和模块
from torch.utils.data import Dataset # 创建自定义的数据集
from PIL import Image # 导入PIL库中的Image模块,用于处理图像
import os # 导入os库,用于文件路径操作

class MyData(Dataset):
    # 定义数据集的构造函数,接收两个参数:root_dir和label_dir
    def __init__(self, root_dir, label_dir):
        # 加self使后面的变量在其他函数中也可使用
        self.root_dir = root_dir # 数据集根目录
        self.label_dir = label_dir # 标签目录
        self.path = os.path.join(root_dir,label_dir) # 将root_dir和label_dir拼接成完整的数据集路径
        self.img_path = os.listdir(self.path) # 获取该路径下的所有文件名,存储在img_path列表中

# 定义数据集的__getitem__方法,接收一个参数idx,返回一个元组,包含图像和标签
    def __getitem__(self, idx):
        img_name = self.img_path[idx] # 获取当前索引对应的图片名称
        img_item_path = os.path.join(self.root_dir,self.label_dir,img_name) # 获取图片完整路径
        img = Image.open(img_item_path) # 打开图片
        label = self.label_dir # 标签目录
        return img,label # 返回图片和标签

# 定义数据集的__len__方法,返回数据集中图像的数量
    def __len__(self):
        return len(self.img_path) # 返回图片数量

root_dir = "hymenoptera_data/train" # 数据集根目录
ants_label_dir = "ants" # 蚂蚁标签目录
bees_label_dir = "bees" # 蜜蜂标签目录
ants_dataset = MyData(root_dir,ants_label_dir) # 创建蚂蚁数据集对象
bees_dataset = MyData(root_dir,bees_label_dir) # 创建蜜蜂数据集对象

train_dataset = ants_dataset + bees_dataset # 将两个数据集合并为一个训练数据集对象

rename dataset

这段代码的作用是将标签信息写入到对应的txt文件中。

具体来说,代码首先定义了两个变量:root_dir和target_dir,分别表示原始数据集的根目录和目标目录。然后使用os.listdir()函数获取目标目录下的所有文件名,存储在img_path列表中。接着,通过split()函数将目标目录名按照下划线"_"进行分割,取第一部分作为标签信息,存储在label变量中。

接下来,遍历img_path列表中的每个文件名i,使用split()函数将其按照".jpg"进行分割,得到一个列表,其中包含了文件名中的所有部分。接着,通过索引[0]获取该列表的第一个元素,即文件名中不包含扩展名的部分(即原文件名),将其赋值给file_name变量。

然后,使用open()函数创建一个新的txt文件,并将标签信息写入到该文件中。具体来说,使用os.path.join()函数拼接出新文件的完整路径,格式为"root_dir, out_dir, file_name.txt"。然后,使用with语句打开文件,确保文件在使用完毕后能够自动关闭。最后,使用write()方法将标签信息写入到文件中。

import os
# 设置根目录,存放训练数据
root_dir = "hymenoptera_data/train"
# 设置目标目录
target_dir = "ants_image"
# 获取目标目录下的所有文件路径
img_path = os.listdir(os.path.join(root_dir, target_dir))
# 按照下划线"_"进行分割,并取分割后的第一个部分ants作为标签信息
label = target_dir.split('_')[0]
# 设置输出目录,存放标签信息
out_dir = "ants_label"

# 遍历所有图片文件
for i in img_path:
    # 提取出不包含后缀的文件名
    file_name = i.split('.jpg')[0]
''' 打开一个文件,并将文件名取为文件名加上 .txt 后缀
    root_dir 和 out_dir 是文件路径的两个部分, {} 是格式化字符串的占位符,用于将 file_name 的值代入文件名中。'w'参数表示以写入模式打开文件。
    format() 是一个字符串方法,用于进行字符串的格式化。它允许我们将变量的值插入到字符串中的指定位置。
    把 file_name 的值插入到 {} 中,并在其后追加 .txt 后缀。结果将得到类似于 file_name.txt 的字符串。
    with 语句用于在代码块结束时自动关闭文件,以确保文件的正确关闭和释放资源。
    as f 是将打开的文件对象赋值给一个变量 f 的一种写法。这个 f 变量可以用来引用该文件对象,以便对文件进行读取或写入操作。'''
    with open(os.path.join(root_dir, out_dir, "{}.txt".format(file_name)), 'w') as f:
        # 变量 label 的值写入该文件中
        f.write(label)

TensorBoard

终端输入

(base) PS D:\Software\Pycharm\study> conda activate pytorch
(pytorch) PS D:\Software\Pycharm\study> tensorboard --logdir logs

指定端口

​tensorboard --logdir=logs --port=6008

from torch.utils.tensorboard import SummaryWriter

## 创建一个名为"logs"的SummaryWriter对象,用于记录TensorBoard日志
writer = SummaryWriter("logs")

# y = 2x

for i in range(100):
    writer.add_scalar("y=2x", 2*i, i)  # tag,y轴,x轴

# 关闭SummaryWriter对象,不再写入日志
writer.close()

神经网络 nn.Conv2d

这段代码定义了一个简单的神经网络模型,并使用PyTorch的nn.Module类进行封装。该模型接受一个输入张量x,将其乘以3后返回输出张量output。

具体来说,首先定义了一个名为ModelName的类,继承自nn.Module类。在__init__()方法中,调用了父类的构造函数,用于初始化模型的参数。

然后定义了forward()方法,该方法是神经网络模型的核心部分,用于实现前向传播过程。在这个例子中,forward()方法接收一个输入张量input,将其乘以3后返回输出张量output。

接下来创建了一个ModelName类的实例Name,并将一个值为1的张量x作为输入传递给Name对象。最后打印输出结果output,即x乘以3的结果。

# 导入PyTorch库
import torch
from torch import nn

# 定义一个名为ModelName的类,继承自nn.Module
class ModelName(nn.Module):
    def __init__(self):
        # 调用父类的构造函数,用于初始化模型的参数
        super(ModelName, self).__init__()

    def forward(self, input):
        # 对输入进行乘法操作,将每个元素乘以3
        output = input * 3
        # 返回输出结果
        return output

# 创建一个ModelName实例
Name = ModelName()
# 创建一个大小为1的张量x
x = torch.tensor(1)
# 将x作为参数传递给ModelName实例,得到输出结果output
output = Name(x)
# 打印输出结果
print(output)

torch.nn.functional.conv2d(inputweightbias=Nonestride=1padding=0dilation=1groups=1

input – input tensor of shape (minibatch , in_channels, iH , iW)     4个元素

卷积层需要输入张量的形状为(batch_size, channels, height, width),其中batch_size表示一批数据的大小,channels表示每个样本的通道数,height和width表示每个样本的高度和宽度。因此,在使用卷积层之前,需要将输入张量的形状调整为适合卷积操作的形状。


# 导入PyTorch库
import torch

# 导入卷积操作函数
import torch.nn.functional as F

# 定义输入数据
input = torch.tensor([[1,2,0,3,1],
                      [0,1,2,3,1],
                      [1,2,1,0,0],
                      [5,2,3,1,1],
                      [2,1,0,1,1]])

# 定义卷积核
kernel = torch.tensor([[1,2,1],
                       [0,1,0],
                       [2,1,0]])

# 对输入数据进行尺寸变换
input = torch.reshape(input,(1,1,5,5)) # 将输入数据调整为(batch_size, channel, height, width)的形状
kernel = torch.reshape(kernel,(1,1,3,3)) 

# 打印输入数据的形状
print(input.shape)
# 打印卷积核的形状
print(kernel.shape)

# 使用F.conv2d函数进行卷积操作
output = F.conv2d(input, kernel, stride=1) # 步长为1
print(output)

# 使用F.conv2d函数进行卷积操作
output2 = F.conv2d(input, kernel, stride=2) # 步长为2
print(output2)

# 使用F.conv2d函数进行卷积操作,并添加padding参数以防止卷积后图像边缘出现黑边
output3 = F.conv2d(input, kernel, stride=1, padding=1) # 步长为1,padding为1
print(output3)

 

 

卷积层 nn.Conv2d

transforms.ToTensor()           为什么我们需要 Tensor 数据类型

Tensor 数据类型包装了反向神经网络所需要的一些理论基础的参数,如:_backward_hooks、_grad等(先转换成Tensor数据类型,再训练)

​​​​​​​ 

这段代码实现了一个简单的卷积神经网络模型,并使用PyTorch的DataLoader和SummaryWriter对数据进行处理和可视化。

# 导入所需的库
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 加载CIFAR-10数据集,不进行训练,转换为张量,并下载数据集
dataset = torchvision.datasets.CIFAR10("data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
# 创建一个DataLoader实例,用于批量加载数据
dataloader = DataLoader(dataset, batch_size=64)

# 定义一个名为ModelName的神经网络模型类,继承自nn.Module
class ModelName(nn.Module):
    def __init__(self):
        super(ModelName, self).__init__()
        # 初始化卷积层,输入通道数为3(彩色图像),输出通道数为6,卷积核大小为3x3,步长为1,填充为0
        self.Conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)

    def forward(self, x):
        # 对输入数据x进行卷积操作
        x = self.Conv1(x)
        return x

# 实例化模型并打印模型结构
ow = ModelName()
print(ow)       # ModelName((Conv1): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1)))

# 创建一个SummaryWriter对象用于记录日志
writer = SummaryWriter("logs")
step = 0

# 遍历数据加载器中的每个批次数据
for data in dataloader:
    imgs, targets = data
    output = ow(imgs)
    print(imgs.shape)
    # torch.Size([64, 3, 32, 32]),表示64个样本,batch_size=64,每个样本有3个通道(彩色图像),大小为32x32像素
    # print(output.shape)
    # torch.Size([64, 6, 30, 30]),表示经过卷积操作后的输出形状,通道数变为6(6个卷积核),大小为30x30像素

    # 将输入图像和输出图像分别添加到SummaryWriter中,用于后续可视化分析
    writer.add_images("input", imgs, step)
    
    # 对输出结果进行reshape操作,将通道数变为3,批量大小变大
    output = torch.reshape(output, (-1, 3, 30, 30)) # [64, 6, 30, 30] ->[*, 3, 30, 30]        
    print(output.shape)  # [128, 3, 30, 30],表示批量大小为128,通道数为3,大小为30x30像素
    
    # 将输出结果添加到日志中
    writer.add_images("output", output, step)
    
    # 每处理完一个批次的数据,步数加1
    step = step + 1

最大池化 nn.MaxPool2d

池化stride默认值是kernel_size的值

默认ceil_mode=False 不允许出界

import torch
import torchvision.datasets
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader

input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]], dtype=torch.float32)
input = torch.reshape(input,(-1, 1, 5, 5)) # input必须是四维,-1自动计算batch_size
print(input.shape)

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)

    def forward(self, input):
        output = self.maxpool1(input)
        return output

ezio = Model()
output = ezio(input)
print(output)  # output = 2
import torch
import torchvision.datasets
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)

    def forward(self, input):
        output = self.maxpool1(input)
        return output

ezio = Model()

writer = SummaryWriter("logs")
step = 0

for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, step)
    output = ezio(imgs) # 池化不用reshape改变通道数
    writer.add_images("output", output, step)
    step = step + 1

writer.close()

非线性激活 Non-linear Activations 

将神经网络的线性模型变换成非线性的,非线性激活作用是提高泛化能力

非线性是非线性,激活是激活,激活是只输入到了一定阈值才会触发输出,非线性是指将输入输出作一个非线性映射,增加整个网络的非线性拟合能力

nn.ReLU

inplace=True:会更改传入的数据(更改器方法),原位操作,改变变量本身的值,反之不会更改原数据(访问器方法)

Input = -1, ReLU(Input, inplace = True)  ->  Input = 0

Input = -1, Output = ReLU(Input, inplace = False)  ->  Input = -1, Output = 0

import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1, -0.5],
                      [-1, 3]])

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.ReLU1 = ReLU() # inplace默认= False

    def forward(self, input):
        output = self.ReLU1(input)
        return output

ezio = Model()
output = ezio(input)
print(output)
"""output = tensor([[1., 0.],
                    [0., 3.]])"""

nn.Sigmoid

import torch
import torchvision
from torch import nn
from torch.nn import Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

input = torch.tensor([[1, -0.5],
                      [-1, 3]])

dataset = torchvision.datasets.CIFAR10("data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.sigmoid1 = Sigmoid()

    def forward(self, input):
        output = self.sigmoid1(input)
        return output

ezio = Model()

writer = SummaryWriter("logs")
step = 0

for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, step)
    output = ezio(imgs)
    writer.add_images("output", output, step)
    step += 1

writer.close()

线性层  Linear Layers

nn.Linear

torch.nn.Linear(in_featuresout_featuresbias=Truedevice=Nonedtype=None)

in_features = x1 ~ Xd 的个数 = d

out_features = g1 ~ gL 的个数 = L        d代表特征数,L代表神经元个数

k*x+b     k为权重     +b与否却决于bias

对k、b调整,达到合理的预测

线性层的作用是变换特征维度,将特征表示整合成一个值,其优点在于减少特征位置对于分类结果的影响,提高了整个网络的鲁棒性。

reshape():可以指定尺寸进行变换

flatten():变成1行,摊平

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64, drop_last=True)

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = Linear(196608, 10)

    def forward(self, input):
         output = self.linear1(input)
         return output

ezio = Model()
for data in dataloader:
    imgs,targets = data
    print(imgs.shape)  #torch.Size([64, 3, 32, 32])
    # 每个batch里面只有一个,单通道,1行n列的矩阵
    output = torch.reshape(imgs,(1, 1, 1, -1))
    print(output.shape)  # torch.Size([1, 1, 1, 196608])
    output = tudui(output)
    print(output.shape)  # torch.Size([1, 1, 1, 10])
 
for data in dataloader:
    imgs,targets = data
    print(imgs.shape)  #torch.Size([64, 3, 32, 32])
    output = torch.flatten(imgs)   #摊平
    print(output.shape)   #torch.Size([196608])
    output = tudui(output)
    print(output.shape)   #torch.Size([10])

搭建小实战

CIFAR 10:根据图片内容,识别其究竟属于哪一类(10代表有10个类别)

卷积层中卷积核个数=输出特征图的通道数

首先加了 padding(图像大小不变,还是32x32),然后卷积了32次

same卷积:输出和输入大小一样的卷积,padding大小一般为p=(f-1)/2        (f=过滤器的尺寸=卷积核的大小):p=(5-1)/2=2

奇数卷积核(一般在计算机视觉中卷积核大小为奇数) padding 也可直接用kernel_size //(地板除) 2计算:5//2 = 2

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = Conv2d(3, 32, 5, padding=2)  # in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2
        self.maxpool1 = MaxPool2d(2)  # kernel_size=2
        self.conv2 = Conv2d(32, 32, 5, padding=2)  # 维持尺寸不变,所以padding仍为2
        self.maxpool2 = MaxPool2d(2)
        self.conv3 = Conv2d(32, 64, 5, padding=2)
        self.maxpool3 = MaxPool2d(2)
        self.flatten = Flatten()  # 展平为64x4x4=1024个数据
        self.linear1 = Linear(1024, 64)
        self.linear2 = Linear(64, 10)  # 10个类别

    def forward(self, x):
        x = self.conv1(x)
        x = self.maxpool1(x)
        x = self.conv2(x)
        x = self.maxpool2(x)
        x = self.conv3(x)
        x = self.maxpool3(x)
        x = self.flatten(x)
        x = self.linear1(x)
        x = self.linear2(x)
        return x

ezio = Model()
print(ezio)

网络结构:

 对网络结构进行检验:

# 创建一个形状为(batch_size=64,3通道,32x32)的全1张量作为模拟图像数据输入
input = torch.ones((64,3,32,32))  
output = tudui(input)
print(output.shape)  # torch.Size([64, 10])

Sequential 简化模型代码:

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential


class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10),
        )

    def forward(self, x):
        x = self.model1(x)
        return x

ezio = Model()
print(ezio)
input = torch.ones((64, 3, 32, 32))
output = ezio(input)
print(output.shape)

TensorBoard 可视化:

writer = SummaryWriter("logs")
writer.add_graph(ezio, input)
writer.close()

损失函数与反向传播

loss 衡量实际神经网络输出 output 与真实想要结果 target 的差距,越小越好

作用:

  1. 计算实际输出和目标之间的差距
  2. 为我们更新输出提供一定的依据(反向传播):给每一个卷积核中的参数提供了梯度 grad,采用反向传播时,每一个要更新的参数都会计算出对应的梯度,优化过程中根据梯度对参数进行优化,最终达到整个 loss 进行降低的目的

nn.L1Loss、nn.MSELoss均方误差

import torch
from torch.nn import L1Loss, MSELoss

input = torch.tensor([1, 2 ,3], dtype=torch.float32)
target = torch.tensor([1, 2, 5], dtype=torch.float32)

loss = L1Loss()
result = loss(input, target)

loss_mse = MSELoss()
result1 = loss_mse(input, target)

print(result) # tensor(0.6667) = [0+0+(5-3)]/3
print(result1) # tensor(1.3333) = [0+0+(5-3)^2]/3

交叉熵 nn.CrossEntropyLoss

x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))  # 1batch-size,3类
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)  # tensor(1.1019)

 在神经网络中用到 Loss Function:

import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=1)

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10),
        )

    def forward(self, x):
        x = self.model1(x)
        return x

loss = nn.CrossEntropyLoss()
ow = Model()

for data in dataloader:
    imgs, targets = data
    output = ow(imgs)
    result_loss = loss(output, targets)
  # result_loss.backward()
    print(result_loss)

损失函数一定要经过 .backward() 后才能反向传播,才能有每个需要调节的参数的grad的值

选择合适的优化器,利用梯度对网络中的参数进行优化更新,以达到整个loss最低的目的

优化器

当使用损失函数时,可以调用损失函数的 backward,得到反向传播,反向传播可以求出每个需要调节的参数对应的梯度,有了梯度就可以利用优化器,优化器根据梯度对参数进行调整,以达到整体误差降低的目的

torch.optim

(1)构造

# Example:
# SGD为构造优化器的算法,Stochastic Gradient Descent 随机梯度下降
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)  #模型参数、学习速率、特定优化器算法中需要设定的参数
optimizer = optim.Adam([var1, var2], lr=0.0001)

(2)调用优化器的step方法

利用之前得到的梯度对参数进行更新

for input, target in dataset:
    optimizer.zero_grad() #把上一步训练的每个参数的梯度清零
    output = model(input)
    loss = loss_fn(output, target)  # 输出跟真实的target计算loss
    loss.backward() #调用反向传播得到每个要更新参数的梯度
    optimizer.step() #每个参数根据上一步得到的梯度进行优化

算法

如Adadelta、Adagrad、Adam、RMSProp、SGD等等,不同算法前两个参数:params、lr 都是一致的,后面的参数不同

CLASS torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
# params为模型的参数、lr为学习速率(learning rate)
# 后续参数都是特定算法中需要设置的

学习速率不能太大(太大模型训练不稳定)也不能太小(太小模型训练慢),一般建议先采用较大学习速率,后采用较小学习速率
以 SGD(随机梯度下降法)为例:

import torch
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
 
# 加载数据集并转为tensor数据类型
dataset = torchvision.datasets.CIFAR10("data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=1)
 
# 定义一个名为Overwatch的神经网络模型类
class Overwatch(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model1 = Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32,64,5,padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024,64),
            Linear(64,10)
        )
 
    def forward(self,x):   # x为input,forward前向传播
        x = self.model1(x)
        return x
 
# 导入交叉熵损失函数
loss = nn.CrossEntropyLoss()
 
# 实例化模型
OW = Overwatch()
 
# 设置优化器
optim = torch.optim.SGD(tudui.parameters(),lr=0.01)  # SGD随机梯度下降法

# 进行20轮训练
for epoch in range(20):
    running_loss = 0.0  # 在每一轮开始前将loss设置为0
    for data in dataloader:  # 该循环相当于只对数据进行了一轮学习
        imgs,targets = data  # imgs为输入,放入神经网络中
        outputs = OW(imgs)  # outputs为输入通过神经网络得到的输出,targets为实际输出
        result_loss = loss(outputs,targets)
        optim.zero_grad()  # 清空梯度缓存:把网络模型中每一个可以调节的参数对应梯度设置为0
        result_loss.backward()  # 反向传播,计算梯度:backward反向传播求出每一个节点的梯度,是对result_loss,而不是对loss
        optim.step()  # 对每个参数进行调优
        running_loss = running_loss + result_loss  # 每一轮所有loss的和
    print(running_loss)

在 data 循环外又套一层 epoch 循环,一次 data 循环相当于对数据训练一次,加了 epoch 循环相当于对数据训练 20 次

现有网络模型

查看VGG16 模型:

import torchvision

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)

print(vgg16_true)
  • pretrained 为 False 的情况下,只是加载网络模型,参数都为默认参数,不需要下载
  • 为 True 时需要从网络中下载,卷积层、池化层对应的参数等等(在ImageNet数据集中训练好的)

改动

CIFAR10 把数据分成了10类,而 vgg16 模型把数据分成了 1000 类,如何应用这个网络模型呢?

  1. 把最后线性层的 out_features 从1000改为10
  2. 在最后的线性层下面再加一层,in_features为1000,out_features为10

利用现有网络去改动它的结构,避免写 vgg16

很多框架会把 vgg16 当做前置的网络结构,提取一些特殊的特征,再在后面加一些网络结构,实现功能
 

(1)添加

vgg16_true.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)

如果想添加至 classifier 里:

vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)

 修改:

vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)

保存与加载

方式1:不仅保存了网络模型的结构,也保存了网络模型的参数

vgg16 = torchvision.models.vgg16(pretrained=False)
torch.save(vgg16,"vgg16_method1.pth")

加载: 打印出的是网络模型的结构

model = torch.load("vgg16_method1.pth")
print(model)

方式2:网络模型的参数保存为字典,不保存网络模型的结构(官方推荐,占用空间小)

vgg16 = torchvision.models.vgg16(pretrained=False)
torch.save(vgg16.state_dict(),"vgg16_method2.pth")

加载: 打印出的是参数的字典形式

model = torch.load("vgg16_method2.pth")
print(model)

如何恢复网络模型结构?

vgg16 = torchvision.models.vgg16(pretrained=False)  # 预训练设置为False
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))  # vgg16通过字典形式,加载状态即参数
print(vgg16)

方式1的陷阱(自己定义网络结构时)

# save_model.py
class OVERWATCH(nn.Module):
    def __init__(self):
        super(OVERWATCH, self).__init__()
        self.conv = nn.Conv2d(3, 64, kernel_size=3)

    def forward(self, x):  # x为输入
        x = self.conv(x)
        return x

OW = OVERWATCH()
torch.save(OW, "OW_method1.pth")
# load_model.py
model = torch.load('OW_method1.pth')
print(model)

此时会报错不能得到模型属性

解决办法:加上网络模型,但无需实例化

class OVERWATCH(nn.Module):
    def __init__(self):
        super(OVERWATCH, self).__init__()
        self.conv = nn.Conv2d(3, 64, kernel_size=3)

    def forward(self, x):  
        x = self.conv(x)
        return x

model = torch.load('OW_method1.pth')
print(model)

实际写项目中,直接定义在一个单独的文件中(如save_model.py),再在 load_model.py中:

from save_model import *

 所以:

from save_model import *
model = torch.load('OW_method1.pth')
print(model)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值