从LeNet到AlexNet:
更大的核窗口和步长(图片更大了);更大的池化窗口,使用最大池化层;更多的输出通道;新加了3层卷积层;从FC(120)增加到FC(4096);采用1000类输出。
更多细节:
激活函数从sigmoid变到了ReLU(减缓梯度消失);
隐藏全连接层后加入了丢弃层;
数据增强。
总结
AlexNet是更大更深的LeNet,10×参数个数,260×计算复杂度;
新加入了丢弃法,ReLU,最大池化层和数据增强。
VGG块: 3×3卷积(填充1)(n层,m通道);2×2最大池化层(步幅2)。
**VGG架构:**多个VGG块后接全连接层;不同次数的重复块得到不同的架构VGG-16、VGG-19等等。
总结
VGG使用可重复使用的卷积块来构建深度卷积神经网络;
不同的卷积块个数和超参数可以得到不同复杂度的变种。
NiN块以一个普通卷积层开始,后面是两个1×1的卷积层。这两个1×1卷积层充当带有ReLU激活函数的逐像素全连接层。第一层的卷积窗口形状通常由用户设置。随后的卷积窗口形状固定为1×1。
总结
NiN块使用卷积层加两个1×1卷积层(后者对每个像素增加了非线性);
NiN使用全局平均池化层来替代VGG和AlexNet中的全连接层(不容易过拟合,更少的参数个数)。
Inception块
跟单3×3或5×5卷积层比,Inception块有更少的参数个数和计算复杂度。
总结
Inception块用4条不同超参数的卷积层和池化层的路来抽取不同的信息(主要优点:模型参数小,计算复杂度低);
GoogleNet使用9个Inception块,是第一个达到上百层的网络。
μB 是样本均值, σB 是小批量B的样本标准差。 应用标准化后,生成的小批量的平均值为0和单位方差为1。由于单位方差是一个任意的选择,因此我们通常包含拉伸参数(scale)γ 和 偏移参数(shift)β ,它们的形状与x 相同。 请注意,γ和β是需要与其他模型参数一起学习的参数。
批量归一化层
可学习的参数为γ和β;
作用在①全连接层和卷积层输出上,激活函数前②全连接层和卷积层输入上;
对全连接层,作用在特征维;
对于卷积层,作用在通道维。
总结
批量归一化固定小批量中的均值和方差,然后学习出适合的偏移和缩放;
可以加速收敛速度,但一般不改变模型精度。
正常块:在 use_1x1conv=False 、应用 ReLU 非线性函数之前,将输入添加到输出。
残差块:在 use_1x1conv=True 时,添加通过 1×1 卷积调整通道和分辨率。
ResNet模型
ResNet 的前两层跟之前介绍的GoogLeNet中的一样:在输出通道数为 64、步幅为2的7×7卷积层后,接步幅为2的3×3的最大汇聚层。不同之处在于ResNet每个卷积层后增加了批量归一化层。
总结
残差块使得很深的网络更加容易训练;
残差网络对随后的深度神经网络(卷积类网络、全连接类网络)设计产生了深远影响。
ResNet解决梯度消失问题
梯度消失:
ResNet解决:
1.导包与数据集
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torchvision
from torchvision import models,transforms,datasets
import torch.nn.functional as F
from PIL import Image
import torch.optim as optim
import json, random
import os
import random
# 判断是否存在GPU设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('Using gpu: %s ' % torch.cuda.is_available())
!wget https://static.leiphone.com/cat_dog.rar
!unrar x cat_dog.rar
train_path = '/content/cat_dog/train/'
2.创建数据集(采用齐昊代码)
def get_data(file_path):
file_lst = os.listdir(file_path) #获得所有文件名称 xxxx.jpg
data_lst = []
for i in range(len(file_lst)):
clas = file_lst[i][:3] #cat和dog在文件名的开头
img_path = os.path.join(file_path,file_lst[i])#将文件名与路径合并得到完整路径,以备读取
if clas == 'cat':
data_lst.append((img_path, 0))
else:
data_lst.append((img_path, 1))
return data_lst
class catdog_set(torch.utils.data.Dataset):
def __init__(self, path, transform):
super(catdog_set).__init__()
self.data_lst = get_data(path)#调用刚才的函数获得数据列表
self.trans = torchvision.transforms.Compose(transform)
def __len__(self):
return len(self.data_lst)
def __getitem__(self,index):
(img,cls) = self.data_lst[index]
image = self.trans(Image.open(img))
label = torch.tensor(cls,dtype=torch.float32)
return image,label
train_loader = torch.utils.data.DataLoader(
catdog_set(train_path, [transforms.Resize((224,224)),transforms.ToTensor()]),
batch_size=128, shuffle=True)
3.定义网络
model_ResNet = torchvision.models.resnet18(pretrained=True)
model_ResNet.fc = nn.Linear(512, 2,bias=True)
#将网络放在GPU上
model_ResNet = model_ResNet.to(device)
#定义优化器
optimizer = torch.optim.SGD(model_ResNet.parameters(), lr=0.001, momentum=0.9, weight_decay=5e-4) # 设置训练细节
#定义损失函数
criterion = nn.CrossEntropyLoss()
4.网络训练
def accurate(y_hat,y):
pred = torch.argmax(y_hat,axis=-1).view(-1,1)
y = y.view(-1,1)
pred.type(y.dtype)
return torch.sum(pred==y)
for epoch in range(30): # 重复多轮训练
hits = 0
total = 0
for i, (inputs, labels) in enumerate(train_loader):
inputs = inputs.to(device)
labels = labels.to(device)
# 优化器梯度归零
optimizer.zero_grad()
# 正向传播 + 反向传播 + 优化
outputs = model_ResNet(inputs)
loss = criterion(outputs, labels.long())
loss.backward()
optimizer.step()
print('Epoch: %d loss: %.6f' %(epoch + 1, loss.item()))
with torch.no_grad():
for X,y in train_loader:
X = X.to(device)
y = y.to(device)
out = model_ResNet(X)
hit = accurate(out,y)
hits += hit
total += len(y)
#print(out)
print(hits/total)
print('Finished Training')
test_datapath='/content/test/'
resfile = open('/content/res.csv', 'w')
for i in range(0,2000):
img_PIL = Image.open(test_datapath+str(i)+'.jpg')
img_tensor = transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor()])(img_PIL)
img_tensor = img_tensor.reshape(-1, img_tensor.shape[0], img_tensor.shape[1], img_tensor.shape[2])
img_tensor = img_tensor.to(device)
out = model_ResNet(img_tensor).cpu().detach().numpy()
if out[0, 0] < out[0, 1]:
resfile.write(str(i)+','+str(1)+'\n')
else:
resfile.write(str(i)+','+str(0)+'\n')
resfile.close()
问题
1.第一次使用2000张小数据集时,上传结果只有50,改用原始数据集后得分与准确率相符,准确率较高有运气成分,具体网络细节仍需进一步研究。
2.训练网络部分代码遇到过困难,怀疑与transfrom.Compose()有关,需要继续搞清楚。
感想
跟第一次摸不到头绪相比,这次对于整体思路更加清晰,网络设计部分搜集了不少资料,对课程内容有了更加具体的理解,代码仍需加强,框架明晰后细节也不容忽视。