目录
一、应用场景
计算机视觉领域
图像检测
图像分类检索
检索
:输入一个商品,返回相似物品。类似淘宝识图
超分辨率重构
让图像更加清晰
医学任务
细胞检测
字体识别等
。。。
二、问题引入
为何不用全连接神经网络处理图像,解决图像数据问题的2个思想,引入CNN
参数过多
全连接层中一个神经元需要将输入数据的每个分量都要连接到自己身上,
对于图像来说
当参数较多时,模型表达能力非常强容易导致过拟合
,对于训练数据有非常好的结果,但对于测试数据并不能有好的结果。所以普通全连接神经网络不能满足图像处理
解决:
1、局部连接
图像具有很强的区域性
,每个部分和其余部分关系不大,所以将全连接改为局部链接,降低参数量
图像内一共100个10*10的小范围,100个小范围每个都与10^6个神经元相连接,所以权重参数w1,w2…wn一共100 ×10 ^ 6个
2、参数共享
图像特征与位置无关
,每个神经单元局部连接都使用同样的参数
那么 w1=w2=…=wn,参数只有100个
三、网络细节
输入的是三维数据,直接对图像特征向量做提取
二维->三维
1、输入层
图像的三个维度
(height、weight、C(RGB通道数))
通常,我们认为图像的RGB通道数有红绿蓝三个维度,因此图像的RGB通道数通常为3
2、卷积层
用处
:通过卷积核提取图像特征得到特征图
卷积核
卷积核就是局部连接的权重参数
卷积核在输入图像上从左到右,从上到下滑动(局部连接思想、参数共享思想)
动图理解
输出size=输入size-卷积核size+1(3=5-3+1)
步长
步长小:细粒度提取特征
步长大:粗粒度提取特征(文本任务常用)
padding
卷积核在图像划过后输出相对于输入是变小的,CNN不止一个卷积层,如果卷积层越来越小,最终图像的大小就会变为1出现问题
padding使卷积输入和输出size不变
卷积处理多通道
图像都是三元通道,我们上步只演示一个通道卷积操作,三个通道需要分别加在一起
三元通道像素不一致,我们需要各自卷积,对应位置相加最后结果加上偏置项(最终得到的特征图左上角3)
注意:这里的步长设定为了2
我们可以有很多卷积核提取图像特征得到特征图,让特征更丰富(例如上图的filterw0与filterw1得到了两个绿色矩阵,最后堆叠起来)
堆叠卷积层
一个卷积核产生一个特征图,特征提取一次是不够的,我们要在特征图的基础上继续做卷积
卷积参数计算
例如这是一个卷积核,他的参数有3×3×3=27个
3、池化层
对卷积后的特征图压缩(下采样),不是每个特征都非常重要。选择重要的留下
这里只讲最大池化,因为效果最好
四、整体架构
五、代码实战
手写数字识别
import numpy
import torch
from torch import nn
from PIL import Image
import matplotlib.pyplot as plt
import os
from torchvision import datasets, transforms,utils
#神经网路模型
import torch.nn.functional as F
#优化函数
import torch.optim as optim
#路径
import os
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=[0.5],std=[0.5])])
#导入训练数据
train_data = datasets.MNIST(root = "./data/",
transform=transform,
train = True,
download =False)
#导入测试数据
test_data = datasets.MNIST(root="./data/",
transform = transform,
train = False)
# print(len(train_data)) #60000
# print(len(test_data)) #10000
#打包放入data_loader 设置了batch大小,加快训练速度,加载器中的基本单为是一个batch的数据
train_loader = torch.utils.data.DataLoader(train_data,batch_size=64,
shuffle=True,num_workers=2)
test_loader = torch.utils.data.DataLoader(test_data,batch_size=64,
shuffle=True,num_workers=2)
# print(len(train_loader)) #938
# print(len(test_loader)) #157
#一个样本的格式为[data,label],第一个存放数据,第二个存放标签
# oneimg,label=train_data[0] #这里是三维
# plt.imshow(oneimg.squeeze(0)) #需要压缩一个维度才能将像素矩阵绘制成图片
# plt.show()
#配置网络
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1=nn.Conv2d(1,32,kernel_size=3,padding=1,stride=1)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
#初始尺寸:28*28*1 池化一次 14*14 池化二次 7*7 向量化 7*7*通道数(64)
self.fc1 = nn.Linear(64 * 7 * 7, 1024) # 两个池化,所以是7*7而不是14*14
self.fc2 = nn.Linear(1024, 512)
self.fc3 = nn.Linear(512, 10)
def forward(self,x):
x= self.pool(F.relu(self.conv1(x))) #第一次卷积+池化
x = self.pool(F.relu(self.conv2(x))) #第二次卷积+池化
x = x.view(-1, 64 * 7 * 7) # 将数据平整为一维的
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net=CNN() #实例化
criterion = nn.CrossEntropyLoss()#交叉熵损失函数
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) #随机梯度下降优化函数
def train():
train_accs = []
train_loss = []
test_accs = []
for epoch in range(3):
running_loss=0.0
for i,data in enumerate(train_loader,0):
inputs, labels = data[0], data[1]
optimizer.zero_grad()
# 前向+后向+优化
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# loss 的输出,每个一百个batch输出,平均的loss
running_loss += loss.item()
if i % 100 == 0:
print('[%d,%5d] loss :%.3f' %
(epoch, i, running_loss / 100))
running_loss = 0.0
train_loss.append(loss.item())
if i % 100==0:
torch.save(net.state_dict(),"./CNNmodel/model.pkl")
torch.save(optimizer.state_dict(),"./CNNmodel/optimizer.pkl")
def test():
loss_list = []
acc_list = []
for idx, (input, target) in enumerate(test_loader):
with torch.no_grad():
output = net(input)
cur_loss = criterion(output, target)
loss_list.append(cur_loss)
pred = output.max(dim=-1)[-1] # 获取每一行上最大值
cur_acc = pred.eq(target).float().mean() # 准确率
acc_list.append(cur_acc)
print("平均准确率,平均损失", numpy.mean(acc_list), numpy.mean(loss_list))
if __name__ == '__main__':
train()
test()