我们将通过pytorch提供的模型学习深度学习,在此之前,我们需要配置pytorch环境以及jupyter环境(没有搭建的可以看我上一篇文章),我也是初学者,谨以此记录一下学习的笔记,通过接下来的学习,我可能会不断的补充笔记。
首先,我们要先进入到pytorch的官网,再点击导航栏中Docs下的PyTorch,如下图所示:
然后,在右侧的导航栏中,选中我们要用的nn(Neural network)框架,如下图所示:
我们先进入到containers这里面,会看到有对其里面内容的基本介绍:
我们在选中其中的第一个属性Module,进入到里面,我们可以看到这个的代码与介绍:
这里看代码就需要对继承等编程知识有一定的基础(在此,继承我就不多讲了,默认有一定的编程能力)。在新的类中,我们重写了一些方法,这里是重写了初始化与前向传播的方法。通过前向传播方法,我们可以看到模型的运行流程是输入->卷积(conv1)->非线性(relu)->卷积(conv2)->非线性(relu)->输出。
前向传播:就是给模型一个输入,通过一个关系表达式或函数表达式的运算,最终得到一个输出。
打开pycharm,创建一个工程,创建一个python文件,输入以下代码:
import torch
from torch import nn
class DeepMd(nn.Module):
def __init__(self):
super().__init__()
def forward(self,input):
output = input + 1
return output
# 创建一个对象
deepMd = DeepMd()
x = torch.tensor(1.0)
output = deepMd(x)
print(output)
我们可以得到结果tensor(2.),如下图所示:
到此,我们框架就有了大概的了解。
我们还是在nn(Neural network)框架中点击 Convolution Layers跳转到卷积层中的一些信息。
我们主要是用的是nn.Conv2d这个卷积。因此,我们需要借用torch.nn.functional这里面的nn.Conv2d。其实你不必太过于纠结他们两个之间的关系,你只要明白我们是通过 torch.nn.functional来理解torch.nn。
在调用torch.nn.functional.conv2d()这个方法的时候,我们需要输入一些参数:
在卷积的时候,我们需要一个输入,一个卷积核,才能得到我们的卷积结果,如下图所示:
当我们进行卷积的时候,我们从输入图像的最左上方开始框出一个与卷积核大小相等的区域,将其与卷积核的对应位置相乘,再相加,我们会得到结果的的一个输出。如下图所示,简单的理解就是一个(5X5)的输入,卷积核是(3X3),那么我们的输出就是(3X3)的结果。(这里我们默认卷积步是1,此时你不需要懂结果为什么是(3X3)。你要理解的是每卷积一次,输出结果对应在(3X3)的位置上)。那么我将从输入图像的最左上方开始框出一个(3X3)的区域与卷积核进行对应位置相乘,再相加,最后的计算结果会放在输出(3X3)结果的“a11”位置。
接下来,我们理解一下stride这个参数。当我们设置stride为一个整数时(向上面那样),那么我们就是理解成每卷积完一次后,首先要向有走整数个单位后,进行卷积。当我们移动到右边的边缘时(也就是卷积核的右侧与输入图像的右侧重合),我们需要回到这一行的起始位置,另其向下移动整数个单位进行卷积。依次重复,直到卷积核移动到输入图像的右下角,卷积结束。
上面是最简单的理解,我们也可以参考pytorch官网的参数理解,可以自由设置左移的距离和下移的距离,即(sH,sW)。不管你设置的stride是多少,都要遵循先左移,再下移,以此循环往复。
再接下来,我们看一下padding的理解,padding的是指可以理解为在我们的输入图像的外面添加多宽的边距,通过下面这张图可以更好地理解padding。
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]])
print(input.shape)
print(kernel.shape)
# 结果为:
# torch.Size([5, 5])
# torch.Size([3, 3])
# 根据pytorch的文档,这并不符合conv2d的卷积输入的格式,我们需要进行修改
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
print(input.shape)
print(kernel.shape)
output = F.conv2d(input, kernel, stride=1)
print(output)
通过运行,我们能得到最终的卷积结果,与我们上面理解里面的运算结果是一致的。
你可以修改stride的值,看看与你自己的计算结果是否一致。
下面代码是padding = 1的程序:
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]])
print(input.shape)
print(kernel.shape)
# 结果为:
# torch.Size([5, 5])
# torch.Size([3, 3])
# 根据pytorch的文档,这并不符合输入的格式,我们需要进行修改
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
print(input.shape)
print(kernel.shape)
output = F.conv2d(input, kernel, stride=1)
print(output)
output1 = F.conv2d(input, kernel, stride=2)
print(output1)
output3 = F.conv2d(input, kernel, stride=1, padding=1)
print(output3)
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("../data", tran=False, transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset, batch_size=64)
# 搭建简单神经网络
class DeepMd(nn.Module):
def __init__(self):
super(DeepMd, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
def forward(self, x):
x = self.conv1(x)
return x
deepMd = DeepMd()
writer = SummaryWriter("../logs")
step = 0
for data in dataloader:
imgs, targets = data
output = deepMd(imgs)
# print(output.shape)
# 输入的大小
print(imgs.shape)
print(output.shape)
# torch.Size([64, 3, 32, 32])
writer.add_images("input", imgs, step)
# torch.Size([64, 6, 30, 30])
output = torch.reshape(output, (-1, 3, 30, 30))
writer.add_images("output", output, step)
step = step + 1
运行完此程序后,我们可以通过pycharm打开我们的终端,进入到我们所在的pytorch环境下,输入如下命令,则会给我们一个地址,我们进入到此地址会看到我们的图片。
tensorboard --logsdir=logs
我们还是在nn(Neural network)框架中点击 Padding Layers跳转到池化层中的一些信息。
我们主要是用的是nn.MaxPool2d这个池化。在调用torch.nn.MaxPool2d()这个方法的时候,我们需要输入一些参数:
普通实现:
import torch
from torch import nn
from torch.nn import MaxPool2d
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))
print(input)
class DeepMd(nn.Module):
def __init__(self):
super(DeepMd, self).__init__()
# ceil_mode = True表示边缘取最大值
# ceil_mode = False表示边缘取值使舍去,不取
# self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)
def forward(self, input):
output = self.maxpool1(input)
return output
deepMd = DeepMd()
output = deepMd(input)
print(output)
利用数据集实现:
import torch
import torchvision
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, download=True, transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)
class DeepMd(nn.Module):
def __init__(self):
super(DeepMd, self).__init__()
# ceil_mode = True表示边缘取最大值
# ceil_mode = False表示边缘取值使舍去,不取
# self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)
def forward(self, input):
output = self.maxpool1(input)
return output
deepMd = DeepMd()
writer = SummaryWriter("../logs_maxpool")
step = 0
for data in dataloader:
imgs, targets = data
print(imgs.shape)
writer.add_images("input", imgs, step)
output = deepMd(imgs)
writer.add_images("output", output, step)
step = step + 1
writer.close()
运行完此程序后,我们还是通过pycharm打开我们的终端,进入到我们所在的pytorch环境下,输入如下命令,则会给我们一个地址,我们进入到此地址会看到我们的图片。
tensorboard --logsdir=logs_maxpool
我们还是在nn(Neural network)框架中点击Non-linear Activations (weighted sum, nonlinearity)跳转到非线性激活中的一些信息。
普通实现:
我们选用ReLU进行代码实验。
import torch
from torch import nn
from torch.nn import ReLU
input = torch.tensor([[1, -0.5],
[-1, 3]])
input = torch.reshape(input, (-1, 1, 2, 2))
print(input.shape)
class DeepMd(nn.Module):
def __init__(self):
super().__init__()
# inplace=False表示对原来的位置不改变
self.relu1 = ReLU()
def forward(self, input):
output = self.relu1(input)
return output
deepMd = DeepMd()
output = deepMd(input)
print(output)
利用数据集实现:
由于ReLU对图像的处理不是很明显,因此我们选用Sigmoid激活函数。
import torch
import torchvision.datasets
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("../data", train=False, download=True, transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)
class DeepMd(nn.Module):
def __init__(self):
super().__init__()
# inplace=False表示对原来的位置不改变
self.relu1 = ReLU()
self.sigmoid1 = Sigmoid()
def forward(self, input):
output = self.sigmoid1(input)
return output
deepMd = DeepMd()
writer = SummaryWriter("../logs_relu")
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images("input", imgs, global_step=step)
output = deepMd(imgs)
writer.add_images("output", output, global_step=step)
step = step + 1
writer.close()
运行完此程序后,我们还是通过pycharm打开我们的终端,进入到我们所在的pytorch环境下,输入如下命令,则会给我们一个地址,我们进入到此地址会看到我们的图片。
tensorboard --logsdir=logs_relu
我们还是在nn(Neural network)框架中点击Linear Layers跳转到线性层中的一些信息。
线性层要做的事情其实如下图所示,将一个(5X5)的输入变成一个(1X25),再变成(1X3)。
利用数据集实现:
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)
class DeepMd(nn.Module):
def __init__(self):
super().__init__()
self.linear1 = Linear(196608, 10)
def forward(self, input):
output = self.linear1(input)
return output
deepMd = DeepMd()
for data in dataloader:
imgs, targets = data
print(imgs.shape) # torch.Size([64, 3, 32, 32])
# 这一行output = torch.reshape(imgs, (1, 1, 1, -1)) 生成torch.Size([1, 1, 1, 196608])的结果,
# 其作用个下面这一行的作用是一样的
output = torch.flatten(imgs) # torch.Size([196608])
print(output.shape)
output = deepMd(output)
print(output.shape) # torch.Size([1, 1, 1, 10])
这是一个简单的CIFAR10的网络结构,我们需要通过代码搭建这么一个结构。
其中,我们需要通过pytorch官网中卷积所提供的的公式,计算出stride和padding的数目。其中,dilation默认为1。
这是我们按照前面所说的方式一步步写的模型,并没有用到Sequential。
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
class DeepMd(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = Conv2d(3, 32, 5, stride=1, padding=2)
self.maxPool1 = MaxPool2d(2)
self.conv2 = Conv2d(32, 32, 5, stride=1, padding=2)
self.maxPool2 = MaxPool2d(2)
self.conv3 = Conv2d(32, 64, 5, stride=1, padding=2)
self.maxPool3 = MaxPool2d(2)
self.flatten = Flatten()
self.linear1 = Linear(1024, 64)
self.linear2 = Linear(64, 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
deepMd = DeepMd()
print(deepMd)
# 检查网络正确性
input = torch.ones(64, 3, 32, 32)
output = deepMd(input)
print(output.shape)
当我们使用Sequential的时候,会发现简化了很多。
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
class DeepMd(nn.Module):
def __init__(self):
super().__init__()
self.module1 = Sequential(
Conv2d(3, 32, 5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
deepMd = DeepMd()
print(deepMd)
# 检查网络正确性
input = torch.ones((64, 3, 32, 32))
output = deepMd(input)
print(output.shape)
我们还是在nn(Neural network)框架中点击Loss Functions跳转到损失函数中的一些信息。
损失函数的作用有两个,一个就是计算实际输出和目标之间的差距,另一个是为我们更新输出提供一定的依据(反向传播),这个依据就是梯度。
这是几个不同的损失函数计算结果的代码。
import torch
from torch import nn
from torch.nn import L1Loss, MSELoss
# dtype=torch.float32表示浮点数
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 = L1Loss(reduction='sum')
result = loss(inputs, targets)
loss_mse = MSELoss()
result_mse = loss_mse(inputs, targets)
print(result)
print(result_mse)
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)
这里我们通过搭建损失函数网络。
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
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 DeepMd(nn.Module):
def __init__(self):
super().__init__()
self.module1 = Sequential(
Conv2d(3, 32, 5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
loss = nn.CrossEntropyLoss()
deepMd = DeepMd()
for data in dataloader:
imgs, targets = data
outputs = deepMd(imgs)
result_loss = loss(outputs, targets)
# result_loss.backward()
其实,上面代码就是一个有自己搭建的网络以及自己设定的损失函数,最后一行代码是经过损失函数后要再进行反向传播,我们可以通过断点调试的方法,可以看到当我们没有反向传播的时候,梯度grad是没有的,一旦我们经过反向传播,我们会获得梯度的一些值。如下图所示:
查找路径是自己的网络deepMd–>module1–>Protected Attributes–>_modules–>‘0’–>weight–>grad
import torch.optim
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
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 DeepMd(nn.Module):
def __init__(self):
super().__init__()
self.module1 = Sequential(
Conv2d(3, 32, 5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
# 损失函数
loss = nn.CrossEntropyLoss()
deepMd = DeepMd()
# 优化器
optim = torch.optim.SGD(deepMd.parameters(), lr=0.01)
for epoc in range(20):
running_loss = 0.0
# 这是对这个网络进行一层的学习
for data in dataloader:
imgs, targets = data
outputs = deepMd(imgs)
result_loss = loss(outputs, targets)
# 将梯度清零
optim.zero_grad()
# 反向传播
result_loss.backward()
# 对参数调优
optim.step()
running_loss = running_loss + result_loss
print(running_loss)
这段代码其实就算一个相对完整的代码,有前向传播,后向传播,参数优化等。
我们通过模型vgg16进行修改。
import torchvision.datasets
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)
最后,经过线性层的输出是1000个分类,如果我们想要对其进行修改,让其最后的输出是10。(我们此处是对vgg_true进行的修改。)
from torch import nn
import torchvision.datasets
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)
# 因为vgg16的输出是1000,我们需要添加一个线性层使其输出为10
# add_linear是我们添加的名字,nn.Linear是我们添加的线性层
vgg16_true.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)
这里可以看出,我们添加的线性层是在整个vgg16里面添加的,如果我们想要添加到Sequential里面的话,需要进行如下操作:
from torch import nn
import torchvision.datasets
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)
# 因为vgg16的输出是1000,我们需要添加一个线性层使其输出为10
# add_linear是我们添加的名字,nn.Linear是我们添加的线性层
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)
import torchvision.datasets
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_false)
我们想要将其classifier中的第六层的out_features修改成10,则代码如下:
from torch import nn
import torchvision.datasets
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_false)
# 要注意这个classifier[?]里面的数要与模型中的数对应
vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)
保存
这种方式不仅将模型保存下来,还将模型参数进行了保存。
import torch
import torchvision
# pretrained=False表示我们没有对模型进行训练,而是使用了他原来训练的一些参数
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1
# 这种方式不仅将模型保存下来,还将模型参数进行了保存
torch.save(vgg16, "vgg16_method1.pth")
import torch
import torchvision.models
# 用保存的方式一的形式加载模型
model = torch.load("vgg16_method1.pth")
print(model)
保存
这种方式保存的不再是模型的结构,而是模型的参数。
import torch
import torchvision
# pretrained=False表示我们没有对模型进行训练,而是使用了他原来训练的一些参数
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式2(官方推荐)
# 这种方式保存的不再是模型的结构,而是模型的参数
# state_dict()是将其保存成一个字典的python形式
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
import torch
import torchvision.models
model = torch.load("vgg16_method2.pth")
print(model)
你会发现打印出的是一个字典形式。
恢复
当我们在这种情况下存储的是字典的形式,我们要恢复成网络模型,需要以下操作:
import torch
import torchvision.models
# model存储的是字典形式,需要load_state_dict加载字典形式的数据
model = torch.load("vgg16_method2.pth")
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(model)
print(vgg16)
我们打开该目录下的终端命令窗口,输入ls或者dir都可以(如果输入后不成功,多敲几下回车,再输入命令试试,我也不知道什么原因),查看所有问价的大小:
ls
我们可以看到第二种方法的存储方式比第一种的小,此处表现得不是很明显,当我们模型特别的大时,这能帮助我们节省存储空间。
在讲完整的训练模型之前,我们先要了解一下针对分类的最后处理。
我们以上面这个图片为基础,我们在每次测试的时候,经过训练后的模型得到的outputs输出都要取得分类的结果,将其与我们的目标结果进行比较,得到正确的个数。例如:上图有两个图片,经过训练模型后,我们达到输出的概率。第一张图片的结果是[0.1, 0.2],即这个模型认为他是第一类的概率是0.1,第二类的概率是0.2,所以第二类的概率相对而言比较大,因此,我们就可以认为这张图片是第二类,那么我们怎么得到他是第二类呢?,这时候我们就需要经过argmax()函数后会得到最大概率的下标,也就是得到结果1。同理,第二张图片的结果也是1。
接下来,我们需要将这个预测的结果与实际的目标结果进行比较,是否正确。例如图中,我们目标结果是第一张图片分类是0,第二张图片分类是1,也就是[0, 1]。但是预测的是[1, 1],所以经过preds == inputs target 这个判断后,会返回[False, True]。在经过sum()函数后,会统计正确的个数。
import torch
outputs = torch.tensor([[0.1, 0.2],
[0.3, 0.4]])
print(outputs.argmax(1))
preds = outputs.argmax(1)
targets = torch.tensor([0, 1])
print((preds == targets).sum())
我们完整的训练模型时,需要有非常好的规范,因此,我们需要建立两个文件,一个是 model.py 文件用来存储网络模型,另一个是 train.py 文件用来做一些其他的设置。我们要保证将这两个python文件放在同一个根目录下。
model.py
import torch
from torch import nn
# 搭建神经网络
class DeepMd(nn.Module):
def __init__(self):
super().__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(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 测试我们的网络模型
if __name__ == '__main__':
deepMd = DeepMd()
input = torch.ones((64, 3, 32, 32))
output = deepMd(input)
print(output.shape)
train.py
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
# 准备数据集
train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
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)
# 创建网络模型
deepMd = DeepMd()
# 损失函数
loss_fn = nn.CrossEntropyLoss()
# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(deepMd.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))
# 训练步骤开始
deepMd.train() # 可有可没有
for data in train_dataloader:
imgs, targets = data
outputs = deepMd(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
# .item()是将tensor数据类型变成一个真实的数字
if total_train_step % 100 == 0:
print("训练次数:{},loss:{}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 测试步骤开始
deepMd.eval() # 可有可没有
total_test_loss = 0
total_accuracy = 0
# 让模型的梯度消除
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
outputs = deepMd(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
# 当我们进行分类问题的时候,我们就需要下面这一行代码,如果是目标检测或者自然语言处理的时候,就暂时不需要。
# argmax(1)指的是横向对比大小,返回最大值的下标
# argmax(0)指的是纵向对比大小,返回最大值的下标
# == 值的是输出概率结果与目标的结果是否一致,是返回True,否则返回False
# sum()是用来计算统计True的个数
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(deepMd, "deepMd_{}.pth".format(i + 1))
# torch.save(deepMd.state_dict(), "deepMd_{}.pth".format(i+1))
print("模型已保存")
writer.close()
这段代码可能会运行出错,查看一下是不是存储模型的时候报的错,如果是,那么就换一种存储方式。(我第一遍运行的时候没有问题,但是第三四次运行时就报“attribute lookup DeepMd on _main _failed”错误)
当我们使用GPU进行训练和测试的时候,我们需要在原来的代码上对网络模型、数据(输入、标注)以及损失函数进行修改,判断他们是否有GPU。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
# from model import *
# 准备数据集
train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
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 DeepMd(nn.Module):
def __init__(self):
super(DeepMd, 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(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
deepMd = DeepMd()
if torch.cuda.is_available():
deepMd = deepMd.cuda()
# 损失函数
loss_fn = nn.CrossEntropyLoss()
if torch.cuda.is_available():
loss_fn = loss_fn.cuda()
# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(deepMd.parameters(), lr=learning_rate)
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
# 添加tensorboard
writer = SummaryWriter("../logs_train")
start_time = time.time()
for i in range(epoch):
print("--------第{} 轮训练开始---------".format(i + 1))
# 训练步骤开始
deepMd.train() # 可有可没有
for data in train_dataloader:
imgs, targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
outputs = deepMd(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
# .item()是将tensor数据类型变成一个真实的数字
if total_train_step % 100 == 0:
end_time = time.time()
print(end_time - start_time)
print("训练次数:{},loss:{}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 测试步骤开始
deepMd.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 = deepMd(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
# 当我们进行分类问题的时候,我们就需要下面这一行代码,如果是目标检测或者自然语言处理的时候,就暂时不需要。
# argmax(1)指的是横向对比大小,返回最大值的下标
# argmax(0)指的是纵向对比大小,返回最大值的下标
# == 值的是输出概率结果与目标的结果是否一致,是返回True,否则返回False
# sum()是用来计算统计True的个数
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(DeepMd(), "deepMd_{}.pth".format(i+1))
torch.save(deepMd.state_dict(), "deepMd_{}.pth".format(i+1))
print("模型已保存")
writer.close()
如果你电脑上没有GPU,你可以访问谷歌的Google Colab,它给你提供的GPU进行试验,一周大概可以免费试用30个小时。
pytorch还给我们提供了第二种方法来利用GPU训练,主要就是通过“.to(device)”这个函数对网络模型、数据(输入,标注)以及损失函数进行修改。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
# from model import *
# 定义一个设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 准备数据集
train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
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 DeepMd(nn.Module):
def __init__(self):
super(DeepMd, 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(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
deepMd = DeepMd()
deepMd = deepMd.to(device)
# 损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)
# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(deepMd.parameters(), lr=learning_rate)
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
# 添加tensorboard
writer = SummaryWriter("../logs_train")
start_time = time.time()
for i in range(epoch):
print("--------第{} 轮训练开始---------".format(i + 1))
# 训练步骤开始
deepMd.train() # 可有可没有
for data in train_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = deepMd(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
# .item()是将tensor数据类型变成一个真实的数字
if total_train_step % 100 == 0:
end_time = time.time()
print(end_time - start_time)
print("训练次数:{},loss:{}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 测试步骤开始
deepMd.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 = deepMd(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
# 当我们进行分类问题的时候,我们就需要下面这一行代码,如果是目标检测或者自然语言处理的时候,就暂时不需要。
# argmax(1)指的是横向对比大小,返回最大值的下标
# argmax(0)指的是纵向对比大小,返回最大值的下标
# == 值的是输出概率结果与目标的结果是否一致,是返回True,否则返回False
# sum()是用来计算统计True的个数
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(DeepMd(), "deepMd_{}.pth".format(i+1))
torch.save(deepMd.state_dict(), "deepMd_{}.pth".format(i+1))
print("模型已保存")
writer.close()
前面我们讲个两种存储模型的方式,那么我们就会有两种加载的方式。我们采用一狗的照片进行测试。
不知道什么原因,我在计算机中运行存储方式一的代码总是报错(前面成功过一两次),因此我在Google Colab中运行。
import torch
import torchvision
from PIL import Image
from torch import nn
image_path = "../imgs/dog.png"
image = Image.open(image_path)
print(image)
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)
# 搭建神经网络
class DeepMd(nn.Module):
def __init__(self):
super(DeepMd, 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(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 第一种加载方式
deepMd = DeepMd()
model = torch.load("deepMd_9.pth")
print(model)
image = torch.reshape(image, (1, 3, 32, 32))
model.eval()
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1))
原来的分类:
运行的最终结果可以得到:
运行的结果是第5类,所以对应的分类是dog,这个结果是正确的。
import torch
import torchvision
from PIL import Image
from torch import nn
image_path = "../imgs/dog.png"
image = Image.open(image_path)
print(image)
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)
# 搭建神经网络
class DeepMd(nn.Module):
def __init__(self):
super().__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(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 如果在GPU训练的模型,想在CPU上测试,那么需要加入下面一行代码
# model = torch.load("deepMd_10.pth", map_location=torch.device('cpu'))
model = torch.load("deepMd_10.pth")
deepMd = DeepMd()
deepMd.load_state_dict(model)
print(model)
image = torch.reshape(image, (1, 3, 32, 32))
print(image.shape)
deepMd.eval()
with torch.no_grad():
output = deepMd(image)
print(output)
print(output.argmax(1))
原来的分类:
运行的最终结果可以得到:
运行的结果是第5类,所以对应的分类是dog,这个结果是正确的,与第一种方式对应了。
如果你的结果验证的不对,可能与你训练的次数和学习速率有关,你可以更改这两个参数从新训练,再用训练的模型验证你的结果是否正确。
以上我是结合着视频学习的,如果你有时间,你可以结合PyTorch深度学习的快速入门视频一起学习。