一、AlexNet网络论文总结

一、AlexNet网络

资料推荐

PyTorch

官方文档:https://pytorch.org/docs/stable/index.html

PyTorch模型训练实用教程:https://github.com/TingsongYu/PyTorch_Tutorial

代码:

paper with code:https://paperswithcode.com

SOTA代码:https://www.jiqizhixin.com/sota

OpenCV:

文档:https://docs.opencv.org/3.4/d6/d00/tutorial_py_root.html

论文导读

1、一些数据集介绍

  • Mnist:

    手写识别数据集,类似于编程语言学习中的HelloWorld,是入门CV领域的最常用数据集;训练数据有5万张,测试有1万张,图片格式为Gray,分辨率为28x28;

  • Cifar-10

    十分类的数据集,入门数据集之一;训练数据有5万张,测试数据有1万张,图片格式为RGB,分辨率为32x32;

  • ILSVRC-2012

    大规模图像识别挑战赛2012年数据集,类别有一千类,训练数据为120万,测试数据为15万,图片为RGB格式,分辨率也来到100以上;

  • ImageNet数据集

    CV领域中整合的大型类别数据集,有21841个类别,其中总共有1400多万张图片;

Top-5 error评价指标:只要模型输出的5个类别中有一个是正确的,那么不惩罚模型;

论文概要

论文原文地址

  • 摘要

1、在ILSVRC-2010的120万张图片上训练的网络,获得top-1和top-5 error分别是37.5%和17%;

2、该网络(AlexNet)由5个卷积层和3个全连接层组成,共6000万参数,65万神经元;

3、为加快训练,采用非饱和激活函数——ReLU,采用GPU训练;

4、为减轻过拟合,采用Dropout;

  • 网络结构

在这里插入图片描述

特殊点:

1、在第三层卷积层,网络和前面所有信息相连接;

2、第一个和第二个卷积层后有LRN网络结构;

重点介绍:

1、ReLU非饱和激活函数:加速训练,公式为f(x) = max(0, x);优点还有防止梯度消失,使网络具有稀疏性;对比sigmoid激活函数:y = 1 / 1 + e-x,sigmoid梯度会消失,而ReLU不会;

2、LRN结构(已经弃用):

LRN叫做局部响应标准化,有助于AlexNet泛化能力的提升,受真实神经元侧抑制启发,使用后top-1和top-5都有提升;

侧抑制:细胞分化为不同时,会对周围细胞产生抑制信号,阻止他们向相同方向分化,最终表现为细胞命运的不同;

3、重叠池化:也就是说kernel的移动步长小于kernel大小,精度提升了一些;

  • 训练技巧

数据增强:

1、针对位置

训练阶段:

  • 图片统一缩放至256x256
  • 随机位置裁剪出224x224区域,总共得到1024张图片
  • 随机进行水平翻转,得到2048张图片

测试阶段:

  • 图片统一缩放至256x256
  • 裁剪出四个角和中心的224x224区域
  • 进行水平翻转,得到10张图片

2、色彩扰动

对RGB三通道进行PCA主成分分析,增加扰动因子,最终增加图像色彩的多样性;(淘汰)

Dropout(随机失活):

为了实现不同模型对数据集的训练;

注意事项:训练和测试阶段的数据尺度发生变化,测试时神经元输出需要乘以p失活概率

  • 实验结果分析

最终的分类模型是采用7CNNs两个预训练微调,与5CNNs(训练五个AlexNet取平均值)取平均值;

特征相似性:相似图片的第二个全连接层输出特征向量的欧式距离相近;

启发:可用网络提取高级特征进行图像检索、图像聚类、图像编码等;

论文总结

启发点:

1、深度与宽度可决定网络能力;

2、更强大GPU及更多的数据可进一步提高模型性能;

3、图片缩放细节,对短边先缩放;

4、ReLU不需要对输入进行标准化来防止饱和现象,而sigmoid、tanh激活函数有必要对输入做标准化;

5、卷积核学习到频率、方向和颜色特征;

6、相似图片具有相近的高级特征;

7、图像检索可基于高级特征,效果应高于基于原始图像;

8、网络结构具有相关性,不能轻易移除某一层;

9、采用视频数据,可能有新突破(视频理解);

代码讲解

关键函数:

1、torch.topk(input,k,dim=None,largest=True,sorted=True,out=None)

功能:找出前k大的数据,及其索引序号

返回值:Tensor(前k大的值)、LongTensor(前k大的值所在位置)

2、transforms.TenCrop(size,vertical_flip=False)

功能:在图像的四个角以及中心裁剪出5张图片,进行翻转得到10张图片;

vertical_flip:是否垂直翻转,False的话为水平翻转;

返回值:一个BCHW的四维矩阵,B为10;

3、torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, range=None, scale_each=False, pad_value=0)

功能:制作图像网格;

tensor:图像数据,BCHW形式;

nrow:行数,列数自动计算; padding:图像间距,单位为像素;

normalize:是否将像素值标准化; range:标准化范围;

padding_value:padding的像素值;

inference部分

代码结构:

加载图片——加载模型——模型推理——获取类别——分类结果可视化

代码说明:

1、加载图片

def process_img(path_img):
	
	# 在ImageNet数据集上的均值和标准差
	norm_mean = [0.485, 0.456, 0.406]
	norm_std = [0.229, 0.224, 0.225]
	# 图像变换方法
	inference_transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    # 读入图像,并且转成RGB
    img_rgb = Image.open(path_img).convert('RGB')
    
    # 将图像数据转换成tensor
    img_tensor = img_transform(img_rgb, inference_transform)
    # 加一个维度,chw --> bchw
    img_tensor.unsqueeze_(0)
    img_tensor = img_tensor.to(device)
    
    return img_tensor, img_rgb

2、加载模型

用官方给定的model.alexnet()即可得到模型网络结构;

在全连接层之前,为了确保传入的特征图为6x6,加了一个自适应的平均池化:

self.avgpool = nn.AdaptiveAvgPool2d((6, 6))

将模型结构打印出来:

from torchsummary import summary
summary(model, input_size=(3, 224, 224), device="cpu")

3、打印结果

将图片分类的top5输出在图片上:

plt.imshow(img_rgb)
plt.title("predict:{}".format(pred_str))
top5_num = top5_idx.cpu().numpy().squeeze()
text_str = [cls_n[t] for t in top5_num]
	for idx in range(len(top5_num)):
		plt.text(5, 15+idx*30, "top {}:{}".format(idx+1, text_str[idx]), bbox=dict(fc='yellow'))
plt.savefig('test1.jpg')
plt.show()

在这里插入图片描述

train部分

代码结构:

构建DataLoader——构建模型——构建损失函数——构建优化器——迭代训练

1、数据部分

  • transform部分
# 训练数据的数据处理函数
train_transform = transforms.Compose([
	transforms.Resize((256)),		# 与(256, 256)的区别在于先从短边缩放 
	transforms.CenterCrop(256),		# 裁剪得到(256, 256)
	transforms.RandomCrop(224),		# 随机裁剪,得到1024张图片
	transforms.RandomHorizontalFlip(p=0.5),		# 随机水平翻转,得到2048张图片
	transforms.ToTensor(),
	transforms.Normalize(norm_mean, norm_std),
    ])

# 测试数据的数据处理函数
valid_transform = transforms.Compose([
	transforms.Resize((256, 256)),
	transforms.TenCrop(224, vertical_flip=False),	#得到10张裁剪图片
	# 用一个列表生成式将10张图片合并成tensor[10,B,C,H,W]
	transforms.Lambda(lambda crops: torch.stack([normalizes(transforms.ToTensor()(crop)) for crop in crops])),
    ])
  • DataSet的编写
# 首先是__init__函数
def __init__(self, data_dir, mode="train", split_n=0.9, rng_seed=620, transform=None):
	self.mode = mode
	self.data_dir = data_dir
	self.rng_seed = rng_seed
	self.split_n = split_n
	self.data_info = self._get_img_info() #data_info存储所有图片路径和标签,在DataLoader中通过index读取样本
	self.transform = transform

# 其次是__getitem__函数
def __getitem__(self, index):
	path_img, label = self.data_info[index]
	img = Image.open(path_img).convert('RGB')     # 0~255

	if self.transform is not None:
	img = self.transform(img)   # 在这里做transform,转为tensor等等
	return img, label

# 计算数据长度的函数
def __len__(self):
	if len(self.data_info) == 0:
	raise Exception("\ndata_dir:{} is a empty dir! Please checkout your path to images!".format(self.data_dir))
	return len(self.data_info)

# 数据信息获取
def _get_img_info(self):
    img_names = os.listdir(self.data_dir)
	img_names = list(filter(lambda x: x.endswith('.jpg'), img_names))

	random.seed(self.rng_seed)
	random.shuffle(img_names)

	img_labels = [0 if n.startswith('cat') else 1 for n in img_names]

	split_idx = int(len(img_labels) * self.split_n)  # 25000* 0.9 = 22500
	if self.mode == "train":
		img_set = img_names[:split_idx]     # 数据集90%训练
		label_set = img_labels[:split_idx]
	elif self.mode == "valid":
		img_set = img_names[split_idx:]
		label_set = img_labels[split_idx:]
	else:
		raise Exception("self.mode 无法识别,仅支持(train, valid)")

	path_img_set = [os.path.join(self.data_dir, n) for n in img_set]
	data_info = [(n, l) for n, l in zip(path_img_set, label_set)]

	return data_info

2、损失函数

criterion = nn.CrossEntropyLoss()

3、验证集处理TenCrop输出

bs, ncrops, c, h, w = inputs.size()     # [4, 10, 3, 224, 224]
outputs = alexnet_model(inputs.view(-1, c, h, w))	# [40, 3, 224, 224]
outputs_avg = outputs.view(bs, ncrops, -1).mean(1)	# [4, 2]

总结:该网络是进入CV领域的第一个网络,有一定的参考价值,通过代码实现也清楚了数据的使用,以及训练和测试的步骤

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值