神经网络–线性层及其他层介绍
正则化层
num_features中的c主要来源于图像中的通道,也就是使用shape方法的时候中表示通道的值
其他参数值采用默认的值即可
线性层
点进去linear:
可以看到此处提供了三个参数,in_features就好比下方图片中的input layer,out_features好比于我们Hidden layer,然后bias的值可以设置为true或者false.true代表我们箭头,input layer与Hidden layer之间有很多个箭头,每个箭头上都有函数来进行计算,可以看到式子有例如y = k1x1+b1这样的式子,当bias设置为true的时候,代表此时加这个b1,当设置为false的时候,代表不加这个b1.
代码示例
import torch import torchvision from torch import nn from torch.nn import Linear from torch.utils.data import DataLoader dataset = torchvision.datasets.CIFAR10("datasets", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=64) class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() # 这里定义我们的input_feature,即输入样本的大小,其值为196608,定义我们想获得的输出样本的大小,其值为output_feature为10 self.linear1 = Linear(196608, 10) def forward(self, input): output = self.linear1(input) return output tudui = Tudui() for data in dataloader: imgs, targets = data # torch.Size([64, 3, 32, 32]) print(imgs.shape) # 这里引入了flatten函数, output = torch.flatten(imgs) # torch.Size([196608]) print(output.shape) # 对我们的output的结果进行linear操作 output = tudui(output) # torch.Size([10]) print(output.shape)
抓爆层
其目的是为了防止过拟合
神经网络搭建实战
现在我们想要构建如下的神经网络:
原代码长这样:
当我们使用了Sequential之后的代码如下所示:
""" 在这里引入我们的seq(序列化)操作 """ import torch from torch import nn from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential from torch.utils.tensorboard import SummaryWriter class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() # 注意如果一个神经网络中有很多个操作的时候,我们就需要 self.model1 = Sequential( # 第一次输入的时候,根据图我们可以知道in_channel是3,out_channel是32,卷积核的大小是5 # 但是在第一次卷积的时候图像大小没有变化,说明我们进行了图像的填充,使用了padding # 除了padding之外我们还需要计算我们的步长stride 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的输入值的话,建议可以打印一下flatten最后计算出来的值就是我们Linear的输入值 Linear(1024, 64), Linear(64, 10) ) def forward(self, x): x = self.model1(x) return x tudui = Tudui() # 在这里我们可以自定义我们的输入的图像为(64, 3, 32, 32) input = torch.ones((64, 3, 32, 32)) # 然后通过我们自定义的网络 output = tudui(input) # 最终的输出结果为torch.Size([64, 10]) print(output.shape) writer = SummaryWriter("logs_seq") # 注意这里使用的是add_graph方法,计算图的意思,第一个需要穿的参数是我们的model,也就是tudui,第二个参数是给这个model传一个input writer.add_graph(tudui, input) writer.close()
输出结果展示:
注意事项:
我们先来看这段代码:
Conv2d(3, 32, 5, padding=2)
这段代码对应图片中的这个过程:
此时对于这个卷积函数来说,输入为3,输出为32,然后卷积核为5,
在第一次进行卷积的时候我们的图像大小没有发生改变,所以此时我们知道图像一定进行了填充,使用到了padding,同时针对这次过程我们还需要对步长stride进行计算,下面给出一张图来显示计算过程:
最终我们可以得出步长stride等于1,padding为2,计算过程如上.
损失函数与反向传播
损失函数
L1Loss函数
这里默认redution = “mean”,意思是按照平均值计算
l1loss定义的loss函数如下所示:
默认的ln的计算方式为|xn-yn|
代码示例
reduction='mean’的情况,这里默认redution = “mean”,意思是按照平均值计算
import torch from torch.nn import L1Loss inputs = torch.tensor([1, 2, 3], dtype=torch.float32) targets = torch.tensor([1, 2, 5], dtype=torch.float32) inputs = torch.reshape(inputs, (1, 1, 1, 3)) targets = torch.reshape(targets, (1, 1, 1, 3)) # 注意这里的reduction='mean'默认取mean,意思就是取平均值 loss = L1Loss() result = loss(inputs, targets) # 最终的结果为tensor(0.6667) # 计算过程为(1-1)+(2-2)+(5-3)/3 = 0.667 print(result)
reduction='sum’的情况
import torch from torch.nn import L1Loss inputs = torch.tensor([1, 2, 3], dtype=torch.float32) targets = torch.tensor([1, 2, 5], dtype=torch.float32) inputs = torch.reshape(inputs, (1, 1, 1, 3)) targets = torch.reshape(targets, (1, 1, 1, 3)) # 注意这里的reduction='sum',意思就是取和 loss = L1Loss(reduction="sum") result = loss(inputs, targets) # 最终的结果为tensor(2.) # 计算过程为(1-1)+(2-2)+(5-3) = 2.0 print(result)
MSELoss
MSELoss定义的loss函数为ln
这里默认redution = “mean”,意思是按照平均值计算
然后ln默认的计算方式为(xn - yn)的平方
代码示例
import torch from torch import nn inputs = torch.tensor([1, 2, 3], dtype=torch.float32) targets = torch.tensor([1, 2, 5], dtype=torch.float32) inputs = torch.reshape(inputs, (1, 1, 1, 3)) targets = torch.reshape(targets, (1, 1, 1, 3)) loss_mse = nn.MSELoss() result_mse = loss_mse(inputs, targets) # 结果为tensor(1.3333) # 计算过程为(1-1)^2 + (2-2)^2 + (5-3)^2 = 4 ===> 4/3 = 1.333 print(result_mse)
交叉熵函数(CROSSENTROPYLOSS)
可以看到交叉熵函数对于分类问题是非常有用的
交叉熵函数规定的loss函数如下所示:
其输入输出如下所示:
loss函数与network的结合
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("datasets", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=1) class Tudui(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 = self.model1(x) return x loss = nn.CrossEntropyLoss() tudui = Tudui() for data in dataloader: imgs, targets = data outputs = tudui(imgs) result_loss = loss(outputs, targets) # 反向传播能计算出每个需要调节的参数,这个参数对应的梯度,有了这个梯度我们就可以利用我们的优化器,让这个优化器根据这个梯度对这个参数进行 调整,以达到整体误差降低的效果 # 注意我们进行反向传播的一定是经过loss之后的 result_loss.backward() print("ok")
注意这里我们再loss之后引入了我们的反向传播:
反向传播的作用:反向传播能计算出每个需要调节的参数,这个参数对应的梯度,有了这个梯度我们就可以利用我们的优化器,让这个优化器根据这个梯度对这个参数进行调整,以达到整体误差降低的效果,下面我们来讲讲优化器的使用
优化器
优化器都在torch.optim下面:
核心部分在这个优化器所提供的这些算法:
并且我们点进去一个算法会发现:必有的两个参数是params和lr,params是我们的参数,lr是学习速率
优化器的套路其实就是定义一个优化器,然后先把优化器中每个参数对应的梯度清零,然后调用损失函数的backward,就是他的反向传播求出每一个节点的梯度,然后调用step方法对模型参数进行调优
代码示例
import torch import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear from torch.optim.lr_scheduler import StepLR from torch.utils.data import DataLoader dataset = torchvision.datasets.CIFAR10("datasets", train=False, transform=torchvision.transforms.ToTensor(), download=True) dataloader = DataLoader(dataset, batch_size=1) class Tudui(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 = self.model1(x) return x loss = nn.CrossEntropyLoss() tudui = Tudui() # 这里的优化器我们选择随机梯度下降 # 其第一个参数传的是模型参数,第二个参数传的是学习率,一般建议一开始选择学习率大的进行学习,后面采用学习率小的来学习 optim = torch.optim.SGD(tudui.parameters(), lr=0.01) # epoch就是一轮一轮的意思,意思就是进行多轮的训练,这里只是进行20轮的训练 for epoch in range(20): # 定义最开始的整体误差为0 running_loss = 0.0 for data in dataloader: imgs, targets = data # 把数据存到我们自定义的tudui网络当中,去计算网络的输出 outputs = tudui(imgs) # 然后进行loss操作 result_loss = loss(outputs, targets) # 首先第一步我们要将网络模型中每个可以调节的参数的对应的一个梯度调整为0 # 循环每进行到这一步都会对上一步的梯度进行一个清零 optim.zero_grad() # 接下来我们的优化器要对参数进行优化,但是优化器需要每一个参数的梯度 # 所以我们需要针对loss的结果result_loss使用反向传播算法,然后得到了每一个可调节参数对应的梯度 result_loss.backward() # 优化器.step会对每个参数进行调优 optim.step() # 这里的loss就是整体的误差的求和,每一轮的误差都会进行求和 running_loss = running_loss + result_loss print(running_loss)
torchvision中的VGG模型
代码示例
import torchvision from torch import nn vgg16_false = torchvision.models.vgg16(pretrained=False) # 设置为true的时候说明我们使用的网络模型中的参数是经过训练的 vgg16_true = torchvision.models.vgg16(pretrained=True) """ 底下打印的就是vgg16的模型结构 (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) ) 可以看到我们的out_features的结果为1000 """ print(vgg16_true) train_data = torchvision.datasets.CIFAR10("datasets", train=True, transform=torchvision.transforms.ToTensor(), download=True) # 为我们的vgg16模型中的classifier加一层linear层,把我们最终的输出变为10,起名字为add_linear vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10)) """ 打印后的结果为: (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) (add_linear): Linear(in_features=1000, out_features=10, bias=True) 这里可以看到我们新加的 ) """ print(vgg16_true) # 接下来演示我们修改模型中的某一行的内容,假设我们要修改的是classifier的第六行,将其linear修改为输入4096,输出10 """ 这里我们就先打印一下没有修改前的classifier: (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) ) """ print(vgg16_false) # 修改我们classifier的第六行 vgg16_false.classifier[6] = nn.Linear(4096, 10) """ 修改过后为: (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=10, bias=True) ) """ print(vgg16_false)
模型的保存与读取
使用现成的vgg16模型
模型的保存有两种方式,下面来看代码:
方式1:
torch.save(vgg16, "vgg16_method1.pth")
之后会在这里面生成一个v9916_method1.pth
加载一下模型:
注意:load中放入的路径是vgg16_method1.pth这个文件在python中的相对路径,并不是绝对路径
model = torch.load("vgg16_method1.pth") print(model)
加载结果为:
VGG( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU(inplace=True) (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU(inplace=True) (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (6): ReLU(inplace=True) (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (8): ReLU(inplace=True) (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): ReLU(inplace=True) (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (13): ReLU(inplace=True) (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (15): ReLU(inplace=True) (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (18): ReLU(inplace=True) (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (20): ReLU(inplace=True) (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (22): ReLU(inplace=True) (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (25): ReLU(inplace=True) (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (27): ReLU(inplace=True) (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (29): ReLU(inplace=True) (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(7, 7)) (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) ) )
可以看到打印出来的不但有结构,还有参数
方式2:
此方式只保存模型参数,是官方推荐的:
这里的意思将vgg16中的参数保存成python中的字典结构
注意这里就不再保存网络结构了
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
我们来试着加载一下:
vgg16 = torchvision.models.vgg16(pretrained=False) vgg16.load_state_dict(torch.load("vgg16_method2.pth")) print(vgg16)
加载结果如下所示:
VGG( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU(inplace=True) (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU(inplace=True) (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (6): ReLU(inplace=True) (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (8): ReLU(inplace=True) (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): ReLU(inplace=True) (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (13): ReLU(inplace=True) (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (15): ReLU(inplace=True) (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (18): ReLU(inplace=True) (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (20): ReLU(inplace=True) (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (22): ReLU(inplace=True) (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (25): ReLU(inplace=True) (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (27): ReLU(inplace=True) (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (29): ReLU(inplace=True) (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(7, 7)) (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) ) )
自定义网络模型存取
这里有一个陷阱是需要大家注意的,假设此时我们自定义了一个网络结构并保存到我们的一个python文件中,如下所示:
import torch from torch import nn # 陷阱 class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=3) def forward(self, x): x = self.conv1(x) return x tudui = Tudui() torch.save(tudui, "tudui_method1.pth")
然后在另一个python文件中此时我们要加载这个网络模型,使用了以下语句
import torch model = torch.load('tudui_method1.pth') print(model)
最终结果如下所示:
Tudui( (conv1): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1)) )
完整的训练模型套路
# -*- coding: utf-8 -*- # 作者:小土堆 # 公众号:土堆碎念 import torch import torchvision from torch.utils.tensorboard import SummaryWriter # 准备数据集 from torch import nn from torch.utils.data import DataLoader # CIFAR10数据集是一个分类数据集 train_data = torchvision.datasets.CIFAR10(root="datasets", train=True, transform=torchvision.transforms.ToTensor(), download=True) test_data = torchvision.datasets.CIFAR10(root="datasets", train=False, transform=torchvision.transforms.ToTensor(), download=True) # length 长度 train_data_size = len(train_data) test_data_size = len(test_data) # 如果train_data_size=10, 训练数据集的长度为:10 # 这里的写法是字符串格式化写法,意思会把format中的变量把{}这部分替换掉 print("训练数据集的长度为:{}".format(train_data_size)) print("测试数据集的长度为:{}".format(test_data_size)) # 利用 DataLoader 来加载数据集 train_dataloader = DataLoader(train_data, batch_size=64) test_dataloader = DataLoader(test_data, batch_size=64) class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model = nn.Sequential( nn.Conv2d(3, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, 1, 2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(64 * 4 * 4, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model(x) return x # 创建网络模型 tudui = Tudui() # 损失函数,因为是分类问题,可以使用交叉熵函数 loss_fn = nn.CrossEntropyLoss() # 优化器 # learning_rate = 0.01 # 0.01的另一种写法:1e-2=1 x (10)^(-2) = 1 /100 = 0.01 learning_rate = 1e-2 optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate) # 设置训练网络的一些参数 # 记录训练的次数 total_train_step = 0 # 记录测试的次数 total_test_step = 0 # 训练的轮数 epoch = 10 # 添加tensorboard writer = SummaryWriter("logs_train") for i in range(epoch): print("-------第 {} 轮训练开始-------".format(i + 1)) # 训练步骤开始 tudui.train() for data in train_dataloader: imgs, targets = data outputs = tudui(imgs) loss = loss_fn(outputs, targets) # 优化器优化模型 optimizer.zero_grad() loss.backward() optimizer.step() # 统计的全局训练次数加1 total_train_step = total_train_step + 1 # 统计每训练100次的时候的loss值是多少 if total_train_step % 100 == 0: print("训练次数:{}, Loss: {}".format(total_train_step, loss.item())) # 统计完成后将其通过tensorboard显示每一次的变化 writer.add_scalar("train_loss", loss.item(), total_train_step) # 底下是测试步骤,其是为了测试我们的这个训练有没有训练好,接下来 测试步骤开始 tudui.eval() total_test_loss = 0 total_accuracy = 0 # 注意这里因为是测试,所以要用no_grad方法,代表是没有了梯度了,这样就不会进行调优了 with torch.no_grad(): for data in test_dataloader: imgs, targets = data outputs = tudui(imgs) # 注意这里的loss只是我们测试数据集中的loss,并不是整体的loss loss = loss_fn(outputs, targets) # 把每次的loss都加到total_test_loss里面 # 注意因为loss是tensor数据类型,例如我们打印loss的结果就是tensor(5),但是使用item后就是5,即取他的数字 total_test_loss = total_test_loss + loss.item() accuracy = (outputs.argmax(1) == targets).sum() total_accuracy = total_accuracy + accuracy print("整体测试集上的Loss: {}".format(total_test_loss)) print("整体测试集上的正确率: {}".format(total_accuracy / test_data_size)) writer.add_scalar("test_loss", total_test_loss, total_test_step) writer.add_scalar("test_accuracy", total_accuracy / test_data_size, total_test_step) # 每次测试完毕后测试次数加1 total_test_step = total_test_step + 1 torch.save(tudui, "tudui_{}.pth".format(i)) print("模型已保存") writer.close()
利用GPU训练
GPU训练的第一种方式
方法很简单:找到上述三种变量,调用.cuda就可以啦
1:网络模型
2:数据
3:损失函数:
代码示例
import torch import torchvision from torch.utils.tensorboard import SummaryWriter # from model import * # 准备数据集 from torch import nn from torch.utils.data import DataLoader train_data = torchvision.datasets.CIFAR10(root="datasets", train=True, transform=torchvision.transforms.ToTensor(), download=True) test_data = torchvision.datasets.CIFAR10(root="datasets", train=False, transform=torchvision.transforms.ToTensor(), download=True) # length 长度 train_data_size = len(train_data) test_data_size = len(test_data) # 如果train_data_size=10, 训练数据集的长度为:10 print("训练数据集的长度为:{}".format(train_data_size)) print("测试数据集的长度为:{}".format(test_data_size)) # 利用 DataLoader 来加载数据集 train_dataloader = DataLoader(train_data, batch_size=64) test_dataloader = DataLoader(test_data, batch_size=64) # 创建网络模型 class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model = nn.Sequential( nn.Conv2d(3, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, 1, 2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(64 * 4 * 4, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model(x) return x tudui = Tudui() if torch.cuda.is_available(): # 这里调用.cuda tudui = tudui.cuda() # 损失函数 loss_fn = nn.CrossEntropyLoss() if torch.cuda.is_available(): loss_fn = loss_fn.cuda() # 优化器 # learning_rate = 0.01 # 1e-2=1 x (10)^(-2) = 1 /100 = 0.01 learning_rate = 1e-2 optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate) # 设置训练网络的一些参数 # 记录训练的次数 total_train_step = 0 # 记录测试的次数 total_test_step = 0 # 训练的轮数 epoch = 10 # 添加tensorboard writer = SummaryWriter("../logs_train") for i in range(epoch): print("-------第 {} 轮训练开始-------".format(i + 1)) # 训练步骤开始 tudui.train() for data in train_dataloader: imgs, targets = data if torch.cuda.is_available(): imgs = imgs.cuda() targets = targets.cuda() outputs = tudui(imgs) loss = loss_fn(outputs, targets) # 优化器优化模型 optimizer.zero_grad() loss.backward() optimizer.step() total_train_step = total_train_step + 1 if total_train_step % 100 == 0: print("训练次数:{}, Loss: {}".format(total_train_step, loss.item())) writer.add_scalar("train_loss", loss.item(), total_train_step) # 测试步骤开始 tudui.eval() total_test_loss = 0 total_accuracy = 0 with torch.no_grad(): for data in test_dataloader: imgs, targets = data if torch.cuda.is_available(): imgs = imgs.cuda() targets = targets.cuda() outputs = tudui(imgs) loss = loss_fn(outputs, targets) total_test_loss = total_test_loss + loss.item() accuracy = (outputs.argmax(1) == targets).sum() total_accuracy = total_accuracy + accuracy print("整体测试集上的Loss: {}".format(total_test_loss)) print("整体测试集上的正确率: {}".format(total_accuracy / test_data_size)) writer.add_scalar("test_loss", total_test_loss, total_test_step) writer.add_scalar("test_accuracy", total_accuracy / test_data_size, total_test_step) total_test_step = total_test_step + 1 torch.save(tudui, "tudui_{}.pth".format(i)) print("模型已保存") writer.close()
GPU训练的第二种方式
1:首先定义设备
2:其次定义模型
3:然后定义损失函数
4:最后定义数据
代码示例
import torch import torchvision from torch.utils.tensorboard import SummaryWriter # from model import * # 准备数据集 from torch import nn from torch.utils.data import DataLoader # 定义训练的设备 device = torch.device("cuda") train_data = torchvision.datasets.CIFAR10(root="datasets", train=True, transform=torchvision.transforms.ToTensor(), download=True) test_data = torchvision.datasets.CIFAR10(root="datasets", train=False, transform=torchvision.transforms.ToTensor(), download=True) # length 长度 train_data_size = len(train_data) test_data_size = len(test_data) # 如果train_data_size=10, 训练数据集的长度为:10 print("训练数据集的长度为:{}".format(train_data_size)) print("测试数据集的长度为:{}".format(test_data_size)) # 利用 DataLoader 来加载数据集 train_dataloader = DataLoader(train_data, batch_size=64) test_dataloader = DataLoader(test_data, batch_size=64) # 创建网络模型 class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model = nn.Sequential( nn.Conv2d(3, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, 1, 2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(64*4*4, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model(x) return x tudui = Tudui() tudui = tudui.to(device) # 损失函数 loss_fn = nn.CrossEntropyLoss() loss_fn = loss_fn.to(device) # 优化器 # learning_rate = 0.01 # 1e-2=1 x (10)^(-2) = 1 /100 = 0.01 learning_rate = 1e-2 optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate) # 设置训练网络的一些参数 # 记录训练的次数 total_train_step = 0 # 记录测试的次数 total_test_step = 0 # 训练的轮数 epoch = 10 # 添加tensorboard writer = SummaryWriter("../logs_train") for i in range(epoch): print("-------第 {} 轮训练开始-------".format(i+1)) # 训练步骤开始 # train是训练的时候可以写 tudui.train() for data in train_dataloader: imgs, targets = data imgs = imgs.to(device) targets = targets.to(device) outputs = tudui(imgs) loss = loss_fn(outputs, targets) # 优化器优化模型 optimizer.zero_grad() loss.backward() optimizer.step() total_train_step = total_train_step + 1 if total_train_step % 100 == 0: print("训练次数:{}, Loss: {}".format(total_train_step, loss.item())) writer.add_scalar("train_loss", loss.item(), total_train_step) # 测试步骤开始 # eval是测试的时候可以写 tudui.eval() total_test_loss = 0 total_accuracy = 0 with torch.no_grad(): for data in test_dataloader: imgs, targets = data imgs = imgs.to(device) targets = targets.to(device) outputs = tudui(imgs) loss = loss_fn(outputs, targets) total_test_loss = total_test_loss + loss.item() accuracy = (outputs.argmax(1) == targets).sum() total_accuracy = total_accuracy + accuracy print("整体测试集上的Loss: {}".format(total_test_loss)) print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size)) writer.add_scalar("test_loss", total_test_loss, total_test_step) writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step) total_test_step = total_test_step + 1 torch.save(tudui, "tudui_{}.pth".format(i)) print("模型已保存") writer.close()
完整的模型验证套路
也可以说是测试,demo套路,即利用已经训练好的模型,然后给他提供输入
# -*- coding: utf-8 -*- # 作者:小土堆 # 公众号:土堆碎念 import torch import torchvision from PIL import Image from torch import nn image_path = "image/dog.jpg" image = Image.open(image_path) # image是PIL类型 print(image) # 注意要加上这句话 image = image.convert('RGB') # 这里指定我们的图片大小为32*32 transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), torchvision.transforms.ToTensor()]) # 此时的image是tensor数据类型 image = transform(image) # 打印结果为torch.Size([3, 32, 32]) print(image.shape) class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model = nn.Sequential( nn.Conv2d(3, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, 1, 2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, 1, 2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(64*4*4, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model(x) return x # map_location=torch.device('cpu')出现的原因是假设我们此时加载的文件是采用gpu训练的, # 假设我们的文件是在gpu上保存的,那么此时要在cpu上运行的话要从gpu映射到cpu上 # 就需要使用map_location model = torch.load("tudui_0.pth", map_location=torch.device('cpu')) """ 打印的model的结果为: Tudui( (model): Sequential( (0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (6): Flatten(start_dim=1, end_dim=-1) (7): Linear(in_features=1024, out_features=64, bias=True) (8): Linear(in_features=64, out_features=10, bias=True) ) ) """ print(model) # 因为没有指定输入的batch_size,所以要reshape以下 image = torch.reshape(image, (1, 3, 32, 32)) # 代表测试 model.eval() with torch.no_grad(): output = model(image) # tensor([[-1.3097, 0.4625, 0.3236, 0.8633, 0.7195, 0.9539, 0.9882, 0.6664, # -1.7272, -0.2835]]) print(output) # 打印结果为tensor([6]),因为tudui_0.pth对应的模型结构是一个分类问题,所以最终得到的依然是看我们所给图片是那个的类别的概率最大 # 最终跟所给的类别概率最大的那个下标将会被返回 print(output.argmax(1))
注意事项:
为什么要加image = image.convert(‘RGB’)这句话的原因: