一、卷积神经网络CNN
图象在计算机中是一对按照顺序排列的数字,数字在0~255之间
1.卷积层
卷积是什么:对图像(不同的窗口数据)和卷积核(一组固定的权重:因为每个神经元的多个权重固定,所以又可以看做一个恒定的滤波器filter)做内积(逐个元素相乘再求和)的操作就是所谓的『卷积』操作,也是卷积神经网络的名字来源。
1)步长stride:每次滑动的位置步长。
2)卷积核的个数:决定输出的depth厚度。同时代表卷积核的个数。
3) 填充值zero-padding:在外围边缘补充若干圈0,方便从初始位置以步长为单位可以刚好滑倒末尾位置,通俗地讲就是为了总长能被步长整除。

2.池化层
一种降采样,减小数据的空间大小,因此参数的数量和计算量也会下降,这在一定程度上也控制了过拟合。
1)常见的池化层:最大池化、平均池化、全局平均池化、全局最大池化。
- 平均池化(average pooling):计算图像区域的平均值作为该区域池化后的值。
- 最大池化(max pooling):选图像区域的最大值作为该区域池化后的值。是最为常见的。

通常来说,CNN的卷积层之间都会周期性地插入池化层。
2)池化层的操作方法:
与卷积层类似,池化层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(有时称为 池化窗口)遍历的每个位置计算一个输出。 然而,不同于卷积层中的输入与卷积核之间的互相关计算,池化层不包含参数。
3.全连接层
当抓取到足以用来识别图片的特征后,接下来的就是如何进行分类。 全连接层(也叫前馈层)就可以用来将最后的输出映射到线性可分的空间。 通常卷积网络的最后会将末端得到的长方体平摊(flatten)成一个长长的向量,并送入全连接层配合输出层进行分类。
4.感受野
用3层3×3小卷积代替1层7×7大卷积,效果差不多但参数少很多。
小卷积叠着用比大卷积好,省内存,效果好,训练快
5.卷积神经网络的多种模型
- LeNet:第一个成功的卷积神经网络应用
- AlexNet:类似LeNet,但更深更大。使用了层叠的卷积层来抓取特征(通常是一个卷积层马上一个max pooling层)
- ZF Net:增加了中间卷积层的尺寸,让第一层的stride和filter size更小。
- GoogLeNet:减少parameters数量,最后一层用max pooling层代替了全连接层,更重要的是Inception-v4模块的使用。
- VGGNet:只使用3x3 卷积层和2x2 pooling层从头到尾堆叠。
- ResNet:引入了跨层连接和batch normalization。
- DenseNet:将跨层连接从头进行到尾。
二、搭建卷积神经网络
下面是全连接神经网络的构造
python
import torch
import torchvision
import torchaudio
print(torch.__version__)#打印pytorch版本
print(torchvision.__version__)#打印torchvision版本
print(torchaudio.__version__)#打印torchaudio版本
'''mnist数据集包含7万张手写数字图像:6万张用于训练,1万张用于测试
图像是灰度的,28x28像素的,并且居中的,以减少预处理和加快运行
'''
import torch
from torch import nn#神经网络模块,
from torch.utils.data import DataLoader#数据加载器
from torchvision import datasets #数据集
from torchvision.transforms import ToTensor#转成张量,就是把图片数据变成pytorch能认识的数字矩阵,就像把照片变成excel表格里的数字
'''下载训练数据集(包含训练图片+标签)'''
training_data= datasets.MNIST( #跳转到函数的内部源代码,pycharm 按下ctrl +鼠标点击
root="data",#保存到data文件中
train=True,#下载训练集
download=True,#如果你之前已经下载过了,就不用再下载,没下载就下载
transform=ToTensor(),#张量,图片是不能直接传入神经网络模型
)#对于pytorch库能够识别的数据一般是tensor张量
# datasets.MNIST的参数:
# root(string):表示数据集的根目录
# train(bool,optional):如果为true,则从trainingpt创建数据集,否则从test.pt创建数据集
# download(bool,optional):如果为true,则从internet下载数据集并将其放入根目录,如果数据集已经下载,则不会再次下载
# transform(callable,optional):接收pil图片并返回转换后版本图片的转换函数
'''下载测试数据集(包含训练图片+标签)'''
test_data = datasets.MNIST(
root="data",
train=False,#True是训练集,False就是测试集
download=True,
transform=ToTensor(),#Tensor是在深度学习中提出并广泛应用的数据类型,它与深度学习框架(如 PyTorch、TensorFlow)紧密集成,方便进行神经网络的训练和)
)#NumPy 数组只能在CPU上运行。Tensor可以在GPU上运行,这在深度学习应用中可以显著提高计算速度。
print(len(training_data))#打印训练集大小
'''展示9张手写字图片'''
from matplotlib import pyplot as plt
figue=plt.figure()
for i in range(9):
img,label=training_data[i+59000]#提取第59000张图片
figue.add_subplot(3,3,i+1)#图像窗口中创建多个小窗口,小窗口用于显示图片,三行三列显示
plt.title(label)#标题显示数字标签
plt.axis("off")#不显示坐标轴
plt.imshow(img.squeeze(),cmap="gray")#显示灰度图
# plt.show()#显示图片
'''创建dataloader(数据加载器)
batch_size:将数据集分成多分,每一份为batch------size个数据
优点:可以减少内存的使用,提高训练速度'''
train_dataloader = DataLoader(training_data, batch_size=64)#训练集,每批64张
test_dataloader = DataLoader(test_data, batch_size=64)#测试集每批64张
#测试数据形状
for X,y in test_dataloader:
#X是表示打包好的每一个数据包,图片数据,形状是[64,1,28,28],64张,1个颜色通道,28x28,y是标签数据,形状是[64],64个数字
print(f"Shape of X[N,C,H,W]:{X.shape}")
print(f"Shape of y:{y.shape} {y.dtype}")
break
'''判断当前设备是否支持GPU,无则用CPU,其中mps是苹果m系列芯片的GPU'''
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else 'cpu'
print(f"Using {device} device")
#GPU是图形处理器,算的快费电,适合大量计算,cpu是中央处理器,全能但很慢,适合日常用
'''定义神经网络 类的继承'''
class NeuralNetwork(nn.Module):#通过调用类的形式来使用神经网络,神经网络的模型,nn.module
def __init__(self):#python的基础关于类,self类自己本身
super().__init__()#继承的父类初始化
self.a = 10
self.flatten=nn.Flatten()#把图片展开,创建一个展开对象flatten
self.hidden1=nn.Linear(28*28,out_features=128)#第一层:第一个参数有多少个神经元传入进来,第二个参数有多少个数据
self.hidden2=nn.Linear(in_features=128,out_features=256)#第二层:128输入,256输出
self.hidden3=nn.Linear(in_features=256,out_features=512)#第三层:256输入,512输出
self.out=nn.Linear(in_features=512,out_features=10)#输出层:512输入,10输出,输出必须和标签的类别相同,输入必须是上一层的神经元个数
self.dropout=nn.Dropout(0.2)#随机丢弃20%神经圆,防过拟合
def forward(self,x):#前向传播,你要告诉他数据的流向,视神经网络层连接起来,函数名称不能改。
# x= self.flatten(x)#图像进行展开
# x= self.hidden1(x)
# x= torch.sigmoid(x)
# x= self.hidden2(x)
# x= torch.sigmoid(x)
# x= self.out(x)
x= self.flatten(x)#图像进行展开
x= torch.relu(self.hidden1(x))#第一层+激活
x= self.dropout(x)#随机丢弃
x= torch.relu(self.hidden2(x))
#第二层+激活,激活函数,经常使用的是relu函数,sigmoid适合二分类,tanh在-1和1之间,leaky relu是改进版relu
x= self.dropout(x)
x= torch.relu(self.hidden3(x))
x= self.out(x)#输出层
return x
model =NeuralNetwork().to(device)#把刚刚创建的模型传入到GpU/cpu
print(model)#打印模型结构
'''训练函数'''
def train(dataloader,model,loss_fn,optimizer):
model.train()#切换到训练模式:告诉模型,我要开始训练,模型中w进行随机化操作,已经更新w,在训练过程中,w会被修改的
#pytorch提供2种方式来切换训练和测试的模式,分别是:model.train()和model.eval()
#一般用法是:在训练开始之前写上model.trian(),在测试时写上model.eval()
batch_size_num=1#统计训练的batch数量
for X,y in dataloader:#其中batch为每一个数据的编号
X,y = X.to(device),y.to(device) # 把训练数据集和标签传入cpu或GPU
pred = model.forward(X)#预测
loss = loss_fn(pred,y)#计算损失
optimizer.zero_grad()#梯度清零
loss.backward()#反向传播
optimizer.step()#更新参数
loss_value = loss.item()
if batch_size_num %100==0:#每100批打印一次
print(f"loss:{loss_value:>7f} [number:{batch_size_num}]")
batch_size_num +=1
'''测试函数'''
def test (dataloader,model,loss_fn):
size=len(dataloader.dataset)#数据集大小
num_batchs=len(dataloader)#批次数量
model.eval()#切换到测试模式
test_loss, correct = 0,0
with torch.no_grad():#不计算梯度,加速
for X,y in dataloader:
X,y = X.to(device),y.to(device)
pred = model.forward(X)
test_loss += loss_fn(pred,y).item() # 累加损失
correct +=(pred.argmax(1)== y).type(torch.float).sum().item() # 统计正确个数
# a=(pred.argmax(1)== y) # dim=1表示每一行中的最大值对应的索引号,dim=日表示每一列中的最大值对应的索引号
# b=(pred.argmax(1)== y).type(torch.float)
test_loss /= num_batchs#平均损失
correct /= size#准确率
print(f"Test result: \n Accuracy :{(100*correct)}%,Avg loss:{test_loss}")
'''开始训练'''
loss_fn=nn.CrossEntropyLoss()#损失函数
optimizer = torch.optim.SGD(model.parameters(),lr=0.1)#优化器:调整神经网络参数,让预测更准
#SGD老方法,稳定但慢
#Adam最常用,快且好
epochs=10#训练10轮
for t in range(epochs):
print(f"Epoch{t+1}\n------")
train(train_dataloader,model,loss_fn,optimizer)#训练
print("Dnoe!")
test(test_dataloader,model,loss_fn)#测试
运行结果,可以将上述代码中,展示九张手写图那块最后画图代码注释,查看绘图结果

现在我们把神经网络换成卷积神经网络,cnn更适合图像,参数更少,准确率更高。
把上述代码定义神经网络部分全部替换为下面这串代码
python
class CNN(nn.Module):
def __init__(self):
super(CNN,self).__init__()
self.conv1=nn.Sequential(
nn.Conv2d(
in_channels=1,#输入通道为一个,全连接中28*28,128,输入时784
out_channels=16,
kernel_size=5,
stride=1,
padding=2,
),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
self.conv2=nn.Sequential(
nn.Conv2d(16,32,5,1,2),
nn.ReLU(),
nn.Conv2d(32,32,5,1,2),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.conv3=nn.Sequential(
nn.Conv2d(32,64,5,1,2),
nn.ReLU(),
)
self.out=nn.Linear(64*7*7,10)#输出3136
def forward(self,x):
x=self.conv1(x)
x=self.conv2(x)
x=self.conv3(x)
x=x.view(x.size(0),-1)
output=self.out(x)
return output
model=CNN().to(device)
print(model)
结果比之前高些
