目录
一. 从全连接到卷积
1.不变性
平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”。
局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,在后续神经网络,整个图像级别上可以集成这些局部特征用于预测.
总结
图像的平移不变性使我们以相同的方式处理局部图像,而不在乎它的位置。
局部性意味着计算相应的隐藏表示只需一小部分局部图像像素。
在图像处理中,卷积层通常比全连接层需要更少的参数,但依旧获得高效用的模型。
卷积神经网络(CNN)是一类特殊的神经网络,它可以包含多个卷积层。
多个输入和输出通道使模型在每个空间位置可以获取图像的多方面特征。
卷积(convolution)
图像的卷积就是让卷积核(卷积模板)在原图像上依次滑动,在两者重叠的区域中,把对应位置的像素值和卷积模板值相乘,最后累加求和得到新图像(卷积结果)中的一个像素值,卷积核每滑动一次获得一个新值,当完成原图像的全部遍历,便完成原图像的一次卷积。
传统数字图像处理方法中,一般输入图像为灰度图像,通道为1,因此图像卷积操作中卷积核深度为1,为一个(s×s)的二维矩阵,而在CNN中,由于每一层(包含彩色图像输入层)图像存在多个通道(feature map),每一个通道图像均需要一个卷积核,分别对各自通道进行卷积,然后将各个通道卷积的结果线性叠加形成新的图像作为卷积结果(feature map),例:设卷积输入层图像为[128×128×12],输出层图像为[128×128×24](假设padding,stride,dilation设置满足需求),卷积核size为5,则卷积核的规模为[5×5×12],卷积核的个数为24。
padding:原图像的补0边数,卷积操作中,为了满足原图像边缘位置在卷积后的图像中存在一个对应位置,给原图像增加几条补0边。第一个卷积示意图中虚线边。
stride:卷积操作中每次卷积核滑动的步长
dilation:卷积核的间隔,如一个[3×3]的卷积核,如果dilation为1,则实际卷积过程中卷积核覆盖的区域为[5×5]
图像卷积输出大小计算:不同的卷积核参数导致输出的图像(feature map)大小存在多种可能(指图像的H和W,C不受影响),以PyTorch框架下的设置为例,PyTorch中卷积核需要设置的参数有size,padding,stride和dilation,每个参数包括H,W两个方向。
注意,输出大小略小于输入大小。这是因为卷积核的宽度和高度大于1, 而卷积核只与图像中每个大小完全适合的位置进行互相关运算。 所以,输出大小等于输入大小 nh×nw 减去卷积核大小 kh×kw,即:(nh−kh+1)×(nw−kw+1).
二.卷积层
卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出。 所以,卷积层中的两个被训练的参数是卷积核权重和标量偏置。
三.图像中目标的边缘检测
通过找到像素变化的位置,来检测图像中不同颜色的边缘。
四.学习卷积核
这个没有看懂。。。
五.卷积神经网络
卷积神经网络(CNN)是一类使用卷积层的网络。
在卷积神经网络中,我们组合使用卷积层、非线性激活函数和汇聚层。
为了构造高性能的卷积神经网络,我们通常对卷积层进行排列,逐渐降低其表示的空间分辨率,同时增加通道数。
在传统的卷积神经网络中,卷积块编码得到的表征在输出之前需由一个或多个全连接层进行处理。LeNet是最早发布的卷积神经网络之一。
猫狗大战
1.导包
import numpy as np
import matplotlib.pyplot as plt
import os
import torch
import torch.nn as nn
import torchvision
from torchvision import models,transforms,datasets
import time
import json
2.下载并处理数据集
! wget http://fenggao-image.stor.sinaapp.com/dogscats.zip
! unzip dogscats.zip
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
vgg_format = transforms.Compose([
transforms.CenterCrop(224),
transforms.ToTensor(),
normalize,
])
data_dir = './dogscats'
dsets = {x: datasets.ImageFolder(os.path.join(data_dir, x), vgg_format)
for x in ['train', 'valid']}
dset_sizes = {x: len(dsets[x]) for x in ['train', 'valid']}
dset_classes = dsets['train'].classes
loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=64, shuffle=True, num_workers=6)
loader_valid = torch.utils.data.DataLoader(dsets['valid'], batch_size=5, shuffle=False, num_workers=6)
3.导入VGG模型,修改并进行训练
model_vgg = models.vgg16(pretrained=True)
model_vgg_new = model_vgg;
for param in model_vgg_new.parameters():
param.requires_grad = False
model_vgg_new.classifier._modules['6'] = nn.Linear(4096, 2)
model_vgg_new.classifier._modules['7'] = torch.nn.LogSoftmax(dim = 1)
model_vgg_new = model_vgg_new.to(device)
'''
第一步:创建损失函数和优化器
损失函数 NLLLoss() 的 输入 是一个对数概率向量和一个目标标签.
它不会为我们计算对数概率,适合最后一层是log_softmax()的网络.
'''
criterion = nn.NLLLoss()
# 学习率
lr = 0.001
# 随机梯度下降
optimizer_vgg = torch.optim.SGD(model_vgg_new.classifier[6].parameters(),lr = lr)
'''
第二步:训练模型
'''
def train_model(model,dataloader,size,epochs=1,optimizer=None):
model.train()
for epoch in range(epochs):
running_loss = 0.0
running_corrects = 0
count = 0
for inputs,classes in dataloader:
inputs = inputs.to(device)
classes = classes.to(device)
outputs = model(inputs)
loss = criterion(outputs,classes)
optimizer = optimizer
optimizer.zero_grad()
loss.backward()
optimizer.step()
_,preds = torch.max(outputs.data,1)
# statistics
running_loss += loss.data.item()
running_corrects += torch.sum(preds == classes.data)
count += len(inputs)
print('Training: No. ', count, ' process ... total: ', size)
epoch_loss = running_loss / size
epoch_acc = running_corrects.data.item() / size
print('Loss: {:.4f} Acc: {:.4f}'.format(
epoch_loss, epoch_acc))
path = './models/model'+ '.pth'
torch.save(model, path)
# 模型训练
train_model(model_vgg_new,loader_train,size=dset_sizes['train'], epochs=1,
optimizer=optimizer_vgg)
4.测试
def test_model(model,dataloader,size):
model.eval()
predictions = np.zeros(size)
all_classes = np.zeros(size)
all_proba = np.zeros((size,2))
i = 0
running_loss = 0.0
running_corrects = 0
for inputs,classes in dataloader:
inputs = inputs.to(device)
classes = classes.to(device)
outputs = model(inputs)
loss = criterion(outputs,classes)
_,preds = torch.max(outputs.data,1)
# statistics
running_loss += loss.data.item()
running_corrects += torch.sum(preds == classes.data)
predictions[i:i+len(classes)] = preds.to('cpu').numpy()
all_classes[i:i+len(classes)] = classes.to('cpu').numpy()
all_proba[i:i+len(classes),:] = outputs.data.to('cpu').numpy()
i += len(classes)
print('Testing: No. ', i, ' process ... total: ', size)
epoch_loss = running_loss / size
epoch_acc = running_corrects.data.item() / size
print('Loss: {:.4f} Acc: {:.4f}'.format(
epoch_loss, epoch_acc))
return predictions, all_proba, all_classes
predictions, all_proba, all_classes = test_model(model_vgg_new,loader_valid,size=dset_sizes['valid'])
测试结果