1.卷积层设计
卷积层的具体设计参数如下。
其中对于2维得数据,也是最常用的是使用卷积函数torch.nn.conv2d()。代码中TuDui网络仅仅构建了一层卷积层
'''
注意本文件与P16的区别
P16中使用的是torch.nn.functional.conv2d(),这为了更加理解函数内部结构
而本节中讲解的是更常使用、也更加方便的torch.nn.conv2d()
torch.nn.Conv2d(in_channels, 输入层通道数
out_channels, 输出层通道数
kernel_size, 卷积核形状大小
stride=1, 步长
padding=0) 填充大小
'''
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10(root="./dataset/CIFAR10",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=64,shuffle=True)
class TuDui(nn.Module):
def __init__(self) -> None:
super().__init__()
#定义一个卷积层
self.conv1 = nn.Conv2d(in_channels=3,#彩色图像,输入需要3个通道
out_channels=6,#自定义的
kernel_size=3,#卷积核定义为3*3
stride=1,
padding=0)
def forward(self,x):
x = self.conv1(x)
return x
tudui = TuDui()
#print(tudui)
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs,targets = data
output = tudui(imgs)
print(imgs.shape) #torch.Size([64, 3, 32, 32])
print(output.shape) #torch.Size([64, 6, 30, 30])
writer.add_images("input",imgs,step)
output = torch.reshape(output,(-1,3,30,30)) #重新改变形状时,-1的值可以自动根据重新改变后的大小进行自动赋值
writer.add_images("output",output,step)
step = step+1
writer.close()
2.最大池化层设计
为什么要进行最大池化?最大池化的作用是什么?
目的就是为了保留数据的输入特征,并将数据量减少
本例中:
池化前:torch.Size([1, 1, 5, 5])
池化后:torch.Size([1, 1, 2, 2])
池化时也需要一个池化核,与卷积核得使用嘞是,只不过这里是将与池化核相对应得最大值保留,即保留最具有特征值。
其中池化时也会遇到一个问题是边缘问题,ceil_model=True要保留把边缘,为False则不保留边缘。
import torch
from torch import nn
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.shape)
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
self.maxpool1 = nn.MaxPool2d(kernel_size=3,
ceil_mode=True)
def forward(self,input):
output = self.maxpool1(input)
return output
print(input.shape)
tudui = Tudui()
output = tudui(input)
print(output)
print(output.shape)
3.非线性激活层设计
给神经网络当中引入一些非线性的特质,增强拟合能力(滤波器)。
其中有几个常用的激活函数:ReLU,Sigmoid等,区别就是几个处理公式不同而已
'''
ReLU
Non-linear Activations
给神经网络当中引入一些非线性的特质,增强拟合能力(滤波器)
有很多非线性激活函数,ReLU,Sigmoid等,区别就是几个处理公式不同而已
'''
import torch
import torchvision.datasets
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
input = torch.tensor([[1,-0.5],
[-1,3]])
input = torch.reshape(input,(-1,1,2,2))
print(input.shape)
dataset = torchvision.datasets.CIFAR10(root="./dataset//CIFAR10",
train=False,
transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size=64,shuffle=True)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
#创建ReLU
self.relu1 = nn.ReLU() #ReLU激活函数
self.sigmoid1 = nn.Sigmoid() #sigmoid激活函数
def forward(self,input):
output = self.sigmoid1(input)
return output
tudui = Tudui()
output = tudui(input)
print(output)
writer = SummaryWriter("logs")
step=0
for data in dataloader:
imgs, targets = data
writer.add_images("input",imgs,global_step=step)
output = tudui(imgs)
writer.add_images("output",output,global_step=step)
step += 1
writer.close()
3.线性层设计
本层就是一个线性的拟合,其中输入的为输入维度和输出维度,最终训练的是直线(线性)的斜率k和截距b。即训练的是参数k和b。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("./dataset/CIFAR10",train=False,
transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size=64,drop_last=True)#舍去最后不完整的一组数据
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
self.liner1 = nn.Linear(196608,10)
def forward(self,input):
output = self.liner1(input)
return output
#writer = SummaryWriter("logs")
tudui = Tudui()
for data in dataloader:
imgs, targets = data
print(imgs.shape)
input = torch.flatten(imgs) #摊平,即变成一维
print(input.shape)
output = tudui(input)
print(output.shape)
4.完整的神经网络模型实战
依据下图的一个网络模型进行构造一个神经网络框架。
未使用Sequential
import torch
import torchvision
from torch import nn
from torch.utils.tensorboard import SummaryWriter
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
self.conv1 = nn.Conv2d(3,32,5,padding=2)
self.maxpool1 = nn.MaxPool2d(2)
self.conv2 = nn.Conv2d(32,32,5,padding=2)
self.maxpool2 = nn.MaxPool2d(2)
self.conv3 = nn.Conv2d(32,64,5,padding=2)
self.maxpool3 = nn.MaxPool2d(2)
self.flatten = nn.Flatten()
self.linear1 = nn.Linear(1024,64)
self.linear2 = nn.Linear(64,10)
def forward(self,input):
input = self.conv1(input)
input = self.maxpool1(input)
input = self.conv2(input)
input = self.maxpool2(input)
input = self.conv3(input)
input = self.maxpool3(input)
input = self.flatten(input)
input = self.linear1(input)
input = self.linear2(input)
return input
tudui = Tudui()
print(tudui)
#以下利用torch.ones数据来验证神经网络模型的正确性,输入输出维度等
input = torch.ones((64,3,32,32))
output = tudui(input)
print(output.shape)
#利用tensorboard来显示网络结构
writer = SummaryWriter("logs")
writer.add_graph(tudui,input)
writer.close()
使用了Sequential
import torch
import torchvision
from torch import nn
from torch.utils.tensorboard import SummaryWriter
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
#Sequential的作用
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,input):
input = self.model1(input)
return input
tudui = Tudui()
print(tudui)
#以下利用torch.ones数据来验证神经网络模型的正确性,输入输出维度等
input = torch.ones((64,3,32,32))
output = tudui(input)
print(output.shape)
#利用tensorboard来显示网络结构
writer = SummaryWriter("logs")
writer.add_graph(tudui,input)
writer.close()
5.损失函数和反向传播
损失函数作用:计算实际输出和目标输出之间的差距,为我们更新输出一定的依据。
这里使用了一些常见的损失函数,损失和nn.L1Loss(reduction=“sum”)、损失平均nn.L1Loss(reduction=“mean”)、差的平方求和再去平均nn.MSELoss()、交叉熵nn.CrossEntropyLoss()
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))
loss1 = nn.L1Loss(reduction="sum")#损失和
loss2 = nn.L1Loss(reduction="mean")#损失平均
loss_mse = nn.MSELoss()#差的平方求和再去平均
result1 = loss1(inputs,targets)
result2 = loss2(inputs,targets)
result3 = loss_mse(inputs,targets)
print(result1,result2,result3)
x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])#只有1个batch_size,所以元素为一个,又因为dog位于label下表1,所以数值是1
x = torch.reshape(x,(1,3))#修改成CrossEntropyLoss满足的输入类型,1代表batch_size
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)
1.当利用交叉熵损失函数进行对神经网络的损失值进行计算。
2.另外对于计算反向传播使用函数loss_cross.backward(),求出每个需要调节参数的梯度(获取每层的梯度)。如果不适用该函数,则梯度无法计算。
3.对于神经网络中的参数修改需要使用优化器,以达到损失减少的目的,本例使用的为梯度下降法。
大致流程为首先1进行正向计算出损失值,2利用这些损失值进行反向传播计算出梯度,3利用梯度更新参数
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10(root="./dataset/CIFAR10",train=False,
transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size=64)
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
#Sequential的作用
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,input):
input = self.model1(input)
return input
tudui = Tudui()
loss = nn.CrossEntropyLoss()
optim = torch.optim.SGD(tudui.parameters(),lr=0.01)#优化器
for epoch in range(10):
running_loss = 0.0
'''
由于通过计算对整个数据一轮次的学习,并没有看到weight变化多少,
需要多轮次,重复之前数据时,与之前该数据时对比loss的减小程度才明显。
因为对于同一轮次来说,数据不同,对比不同数据的loss变化并没有什么意义
要想不同轮次的对比明显,则使用running_loss来记录每轮的loss总数,对比每轮的running_loss即可
'''
for data in dataloader:
imgs, targets = data
outputs = tudui(imgs)#outputs为神经网络输出
loss_cross = loss(outputs,targets)#计算神经网络输出和真实target输出的误差
optim.zero_grad()#首先将梯度调零
loss_cross.backward()#反向传播,求出每个需要调节参数的梯度(获取每层的梯度)
optim.step()#依据backward计算出来的梯度,来进行更新网络中的权重weight
running_loss += loss_cross
print(running_loss)
'''
由于电脑性能,所有仅计算了轮次,我就结束了
tensor(360.4226, grad_fn=<AddBackward0>)
tensor(356.6039, grad_fn=<AddBackward0>)
tensor(344.1981, grad_fn=<AddBackward0>)
tensor(321.3732, grad_fn=<AddBackward0>)
tensor(311.2141, grad_fn=<AddBackward0>)
'''
6.现有模型的使用和修改
主要在PyTroch的官网,可以找到与torchaudio语音相关、torchtext文字相关、torchvision图像相关的训练好的模型,本例中使用的是vgg16模型
import scipy
import torchvision.datasets
'''
vgg中参数pretrain参数
1.当为True时代表该模型不仅有网络模型框架,还有已经对框架中的参数进行了预训练,即参数已经训练好了
2.当为False时代表该模型仅仅加载网络模型框架,并未进行预训练参数,参数是默认的
'''
# vgg16_false = torchvision.models.vgg16(pretrained=False)
# vgg16_true = torchvision.models.vgg16(pretrained=True)
7.模型的保存和加载
import torch
import torchvision
from torch import nn
vgg16 = torchvision.models.vgg16(pretrained=False)
#保存方式1
torch.save(vgg16,"vgg16_method1.pdh")
#对应保存方式2的加载方式
vgg16_load1 = torch.load("vgg16_method1.pdh")
print(vgg16_load1)
#保存方式2 (官方推荐的保存方式)
#将vgg16保存为字典形式,并不保存网络的结构,而只是保存网络的参数
torch.save(vgg16.state_dict(),"vgg16_method2.pdh")
#对应保存方式2的加载方式
'''
vgg16_load2 = torch.load("vgg16_method2.pdh")
print(vgg16_load2)#保存的是一个字典的形式
'''
vgg16_load2 = torchvision.models.vgg16(pretrained=False)#先加载框架
vgg16_load2.load_state_dict(torch.load("vgg16_method2.pdh"))#再导入参数
#通过以上两个步骤才算加载成功
print(vgg16_load2)
8.模型的完整训练套路
首先构建一个网络模型,放在model.py文件中
import torch
from torch import nn
#搭建神经网络
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,stride=1,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,stride=1,padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
x = self.model1(x)
return x
if __name__ =='__main__':
tudui = Tudui()
input = torch.ones(64,3,32,32)
output = tudui(input)
print(output.shape)
再完成训练套路train.py
import torch
from torch import nn
#搭建神经网络
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,stride=1,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,stride=1,padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
x = self.model1(x)
return x
if __name__ =='__main__':
tudui = Tudui()
input = torch.ones(64,3,32,32)
output = tudui(input)
print(output.shape)
以上训练好之后并保存了模型,则接下来就是使用训练好的模型了
import torch
import torchvision
from PIL import Image
from torch import nn
img_path = "./dataset/dog.png"
image = Image.open(img_path)
print(image)
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
image = transform(image)
print(image)
class Tudui(nn.Module):
def __init__(self) -> None:
super().__init__()
#Sequential的作用
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,input):
input = self.model1(input)
return input
model = torch.load("./models/tudui_2.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))
完成训练之后,利用训练好的模型进行预测train.py
import torch
import torchvision.transforms
from PIL import Image
img_path = "./dataset/dog.png"
image = Image.open(img_path)
print(image)
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
image = transform(image)
print(image)
model = torch.load("./models/tudui_6.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))