AlexNet网络结构特点:
1.首次利用GPU进行网络加速训练 (GPU处理速度是CPU的20-50倍)
2.使用了ReLU激活函数,而不是传统的Sigmoid激活函数(缺点:求导麻烦、容易梯度消失)以及tanh激活函数
3.使用了LRN(Local Response Normalization)局部相应归一化
4.在全连接层的前两层中使用了Dropout随机失活神经元操作,以减少过拟合
解释一下什么叫过拟合
拟合的函数完美预测训练集,但对新数据的测试集预测结果差。
过度的拟合了训练数据,而没有考虑到泛化能力
根本原因
特征维度过多,模型假设过于复杂,参数过多,训练数据过少,噪声过多
解决方法
使用Dropout的方式在网络正向传播过程中随机失活一部分神经元
Dropout会在每一层中随机失活一部分神经元,可以理解为变相的减少了网络训练的参数,从而达到减少过拟合的作用
在将AlexNet网络结构之前。首先回顾一下卷积之后的矩阵尺寸大小计算公式
N = (W - F + 2P) / S + 1
N:卷积之后的矩阵尺寸大小
W:输入图片的大小W * W
F:卷积核的大小F * F
P:padding(补0)的像素p
S:滑动的步长
第一个卷积层
之所以有上下两部分,是因为有两块GPU进行并行运算
先看一下Conv1 就是框选出来的这一部分
输入图片是一个高为224 宽为224 深度为3的彩色图像
卷积核大小是11*11
所采用的步距是4
padding:[1,2] 在特征矩阵的左边加上1列0 左边加上2列0 上面加上一列0 下面加上2列0
采用卷积核的个数是2*48=96 一层有两个,一共48层
(224-11+2+1)/4 +1 =55
那么第一个卷积层输入:224*224*3
第一个卷积层输出:55*55*96(输出的深度是卷积核的个数)
第一个池化层
第一个池化层的输入:55*55*96
池化层池化核的尺寸:3
padding:0
步长:2
(55-3+2*0)/2+1=27
第一个池化层的输出:27*27*96 (池化操作只会改变特征矩阵的高度和宽度,不会改变深度)
第二个卷积层:
卷积核的个数:2*128=256(也是输出矩阵的深度)
卷积核的尺寸:5*5
padding:[2,2]
步长:1
第二个卷积层输出矩阵的尺寸:
(27-5+2+2)/1+1= 27
第二个卷积层的输入:[27,27,96]
第二个卷积层的输出:[27,27,256]
第二个池化层
卷积核的尺寸:3
padding:0
stride:2
池化后的矩阵尺寸:
(27-3+2*0)/2+1=13
第二个池化层输入:[27,27,256]
第二个池化层输出:[13,13,256]
第三个卷积层:
卷积核的个数:2*192=384
卷积核的尺寸:3
padding:[1,1]
stride:1
(13-3+1+1)/1+1=13
第三个卷积层输入:[13,13,256]
第三个卷积层输出:[13,13,384]
第四个卷积层
卷积核的个数:2*192=384
卷积核的大小:3
padding:[1,1]
步长:1
(13-3+1+1)/1+1=13
第四个卷积层的输入:[13,13,384]
第四个卷积层的输出:[13,13,384]
第五个卷积层
卷积核的个数:2*128=256
卷积核的大小:3
padding:[1,1]
步长:1
(13-3+1+1)/1+1=13
第五个卷积层的输入:[13,13,384]
第五个卷积层的输出:[13,13,256]
第三个池化层
池化核的大小:3
padding:0
stride:2
(13-3+2*0)/2+1=6
第三个池化层的输入:[13,13,256]
第三个池化层的输出:[6,6,256]
最后面是三个全连接层,将Maxpool输出的进行展平,然后和三个层进行连接
总结一下这个AlexNet网络结构:
一共是五个卷积层三个全连接层
分别是:
Conv1,Maxpool1,Conv2,Maxpool2,Conv3,Conv4,Conv5,Maxpool3,Full connection1,Full connection2,Full connection3
接下来是本次训练用到的数据集,是一个花分类的数据集
数据集下载地址:
https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
将下载的数据集放在这个文件夹下面,并将压缩包解压
提前写好一个将数据集分为训练集和测试集的脚本,比例是9:1
下面是脚本的代码
import os
from shutil import copy, rmtree
import random
def mk_file(file_path: str):
if os.path.exists(file_path):
# 如果文件夹存在,则先删除原文件夹在重新创建
rmtree(file_path)
os.makedirs(file_path)
def main():
# 保证随机可复现
random.seed(0)
# 将数据集中10%的数据划分到验证集中
split_rate = 0.1
# 指向你解压后的flower_photos文件夹
cwd = os.getcwd()
data_root = os.path.join(cwd, "flower_data")
origin_flower_path = os.path.join(data_root, "flower_photos")
assert os.path.exists(origin_flower_path), "path '{}' does not exist.".format(origin_flower_path)
flower_class = [cla for cla in os.listdir(origin_flower_path)
if os.path.isdir(os.path.join(origin_flower_path, cla))]
# 建立保存训练集的文件夹
train_root = os.path.join(data_root, "train")
mk_file(train_root)
for cla in flower_class:
# 建立每个类别对应的文件夹
mk_file(os.path.join(train_root, cla))
# 建立保存验证集的文件夹
val_root = os.path.join(data_root, "val")
mk_file(val_root)
for cla in flower_class:
# 建立每个类别对应的文件夹
mk_file(os.path.join(val_root, cla))
for cla in flower_class:
cla_path = os.path.join(origin_flower_path, cla)
images = os.listdir(cla_path)
num = len(images)
# 随机采样验证集的索引
eval_index = random.sample(images, k=int(num*split_rate))
for index, image in enumerate(images):
if image in eval_index:
# 将分配至验证集中的文件复制到相应目录
image_path = os.path.join(cla_path, image)
new_path = os.path.join(val_root, cla)
copy(image_path, new_path)
else:
# 将分配至训练集中的文件复制到相应目录
image_path = os.path.join(cla_path, image)
new_path = os.path.join(train_root, cla)
copy(image_path, new_path)
print("\r[{}] processing [{}/{}]".format(cla, index+1, num), end="") # processing bar
print()
print("processing done!")
if __name__ == '__main__':
main()
把脚本文件放在这个文件夹里面
就在这个页面,按住shit+鼠标右键 ,在终端中打开
输入这行代码,然后敲回车
完成
生成了训练集文件夹和测试集文件夹
接下来是代码详解部分
AlexNet网络结构的搭建代码详解
import torch.nn as nn
import torch
class AlexNet(nn.Module):
def __init__(self, num_classes=1000, init_weights=False):
super(AlexNet, self).__init__()
self.features = nn.Sequential(#nn.Sequential能够将一系列层结构组合成一个新的结构 features用于提取图像特征的结构
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55] 48是卷积核个数 batch channel high weight
#padding参数解释:如果是int型,比如说1 就是上下左右都补1列0 如果传入一个tuple(元组)的话 比如传入(1,2),1代表上下方各补一行0,2代表左右两侧各补两列0
nn.ReLU(inplace=True),#inplace这个参数可以理解为pytorch通过一种方法增加计算量,来降低内存使用容量的一种方法,可以通过这种方法在内存中载入更大的模型
nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]
nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27] 没有设置stride是因为这个卷积层的步长是1,而默认的步长就是1
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]
nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]
)
self.classifier = nn.Sequential(#包含了三层全连接层 是一个分类器
nn.Dropout(p=0.5),#p是每一层随机失活的比例 默认是0.5
nn.Linear(128 * 6 * 6, 2048),#第一个全连接层,全连接层的节点个数是2048个
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(2048, 2048),#第二个全连接层
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes),#第三个全连接层 输入的是数据集 类别的个数,默认是1000
)
if init_weights:#初始化权重
self._initialize_weights()
def forward(self, x):#正向传播过程
x = self.features(x)
x = torch.flatten(x, start_dim=1)#进行一个展平处理 将传进来的变量x进行展平,从index=1 这个维度开始 也就是channel
x = self.classifier(x)
return x#得到网络的预测输出
def _initialize_weights(self):
for m in self.modules():#遍历每一层结构
if isinstance(m, nn.Conv2d):#判断是哪一个类别
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')#kaiming_normal初始化权重方法
if m.bias is not None:#如果偏置不为空的话,那么就用0去初始化
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):#如果这个实例是全连接层
nn.init.normal_(m.weight, 0, 0.01)#采用normal,也就是正态分布来给我们的权重进行赋值,正态分布的均值等于0,方差是0.01
nn.init.constant_(m.bias, 0)#将偏置初始化为0
训练模块的代码详解
import os
import sys
import json
import torch
import torch.nn as nn
from torchvision import transforms, datasets, utils
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim
from tqdm import tqdm
from model import AlexNet
def main():
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")#指定训练过程中所指定的设备 如果有GPU设备就使用第一块GPU设备,如果没有的话,就使用CPU设备
print("using {} device.".format(device))
data_transform = {#数据预处理函数
"train": transforms.Compose([transforms.RandomResizedCrop(224),#随机裁剪成224*224像素大小
transforms.RandomHorizontalFlip(),#在水平方向随机反转
transforms.ToTensor(),#转换成一个张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),#进行一个标准化处理 output=(input-0.5)/0.5
"val": transforms.Compose([transforms.Resize((224, 224)), # cannot 224, must (224, 224)
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}
data_root = os.path.abspath(os.path.join(os.getcwd(), "../..")) # get data root path getcwd获取当前文件所在的目录 ..是返回上级目录 ../..是返回上上级目录 path.join是将两个目录连接在一起
image_path = os.path.join(data_root, "data_set", "flower_data") # flower data set path
assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
transform=data_transform["train"])
train_num = len(train_dataset)#记录训练集有多少张图片
# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4} 雏菊 蒲公英 玫瑰 向日葵 郁金香
flower_list = train_dataset.class_to_idx#获取分类名称所对应的索引
cla_dict = dict((val, key) for key, val in flower_list.items())#遍历分类索引的字典,然后将key value反过来 变成 0 daisy
# write dict into json file
json_str = json.dumps(cla_dict, indent=4) #通过json.dumps将cla这个字典转化为json模式
with open('class_indices.json', 'w') as json_file:
json_file.write(json_str)
batch_size = 32
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size, shuffle=True,
num_workers=0)
validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
transform=data_transform["val"])
val_num = len(validate_dataset)
validate_loader = torch.utils.data.DataLoader(validate_dataset,
batch_size=batch_size, shuffle=False,
num_workers=0)
print("using {} images for training, {} images for validation.".format(train_num,
val_num))
# test_data_iter = iter(validate_loader)
# test_image, test_label = test_data_iter.next()
# def imshow(img):
# img = img / 2 + 0.5 # unnormalize
# npimg = img.numpy()
# plt.imshow(np.transpose(npimg, (1, 2, 0)))
# plt.show()
# print(' '.join('%5s' % cla_dict[test_label[j].item()] for j in range(4)))
# imshow(utils.make_grid(test_image))
net = AlexNet(num_classes=5, init_weights=True)
net.to(device)
loss_function = nn.CrossEntropyLoss()
# pata = list(net.parameters())
optimizer = optim.Adam(net.parameters(), lr=0.0002)
epochs = 10
save_path = './AlexNet.pth'#保存权重的路径
best_acc = 0.0
train_steps = len(train_loader)
for epoch in range(epochs):
# train
net.train()#dropout只在训练集中使用 而不在验证集中使用
running_loss = 0.0
train_bar = tqdm(train_loader, file=sys.stdout)
for step, data in enumerate(train_bar):
images, labels = data
optimizer.zero_grad()
outputs = net(images.to(device))
loss = loss_function(outputs, labels.to(device))
loss.backward()#将损失反向传播
optimizer.step()#更新节点参数
# print statistics
running_loss += loss.item()
train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
epochs,
loss)
# validate
net.eval()
acc = 0.0 # accumulate accurate number / epoch
with torch.no_grad():
val_bar = tqdm(validate_loader, file=sys.stdout)
for val_data in val_bar:
val_images, val_labels = val_data
outputs = net(val_images.to(device))
predict_y = torch.max(outputs, dim=1)[1]
acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
val_accurate = acc / val_num
print('[epoch %d] train_loss: %.3f val_accuracy: %.3f' %
(epoch + 1, running_loss / train_steps, val_accurate))
if val_accurate > best_acc:
best_acc = val_accurate
torch.save(net.state_dict(), save_path)
print('Finished Training')
if __name__ == '__main__':
main()
训练结果
预测模块的代码详解
import os
import json
import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
from model import AlexNet
def main():
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
data_transform = transforms.Compose(
[transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# load image
img_path = "../tulip.jpg"
assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
img = Image.open(img_path)
plt.imshow(img)
# [N, C, H, W]
img = data_transform(img)
# expand batch dimension
img = torch.unsqueeze(img, dim=0)
# read class_indict
json_path = './class_indices.json'
assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)
with open(json_path, "r") as f:
class_indict = json.load(f)
# create model
model = AlexNet(num_classes=5).to(device)
# load model weights
weights_path = "./AlexNet.pth"
assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
model.load_state_dict(torch.load(weights_path))
model.eval()
with torch.no_grad():
# predict class
output = torch.squeeze(model(img.to(device))).cpu()
predict = torch.softmax(output, dim=0)
predict_cla = torch.argmax(predict).numpy()
print_res = "class: {} prob: {:.3}".format(class_indict[str(predict_cla)],
predict[predict_cla].numpy())
plt.title(print_res)
for i in range(len(predict)):
print("class: {:10} prob: {:.3}".format(class_indict[str(i)],
predict[i].numpy()))
plt.show()
if __name__ == '__main__':
main()
预测结果文章来源:https://www.toymoban.com/news/detail-753364.html
文章来源地址https://www.toymoban.com/news/detail-753364.html
到了这里,关于AlexNet网络结构的实现和详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!