1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【CNN】搭建AlexNet网络——并处理自定义的数据集(猫狗分类)

【CNN】搭建AlexNet网络——并处理自定义的数据集(猫狗分类)

时间:2022-02-17 20:09:56

相关推荐

【CNN】搭建AlexNet网络——并处理自定义的数据集(猫狗分类)

前言

年,AlexNet横空出世。它首次证明了学习到的特征可以超越手工设计的特征。它一举打破了计算机视觉研究的现状。AlexNet使用了8层卷积神经网络,并以很大的优势赢得了ImageNet图像识别挑战赛。

论文地址:/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf

这里我用的是猫狗分类的数据集,如下图所示:

本博文完整数据集:链接:/s/1ySqPErgpnUdk_mqrQU-GTg?pwd=6666

一,介绍

AlexNet和LeNet的架构非常相似,

AlexNetLeNet的设计理念非常相似,但也存在显著差异。 首先,AlexNet比相对较小的LeNet5要深得多。AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。

其次,AlexNet使用ReLU而不是sigmoid作为其激活函数。

AlexNet的第一层,卷积窗口的形状是11 x 11。 由于ImageNet中大多数图像的宽和高比MNIST图像的多10倍以上,因此,需要一个更大的卷积窗口来捕获目标。 第二层中的卷积窗口形状被缩减为5 x 5,然后是3 x 3。 此外,在第一层、第二层和第五层卷积层之后,加入窗口形状为、步幅为2的最大汇聚层。 而且,AlexNet的卷积通道数目是LeNet10倍。

在最后一个卷积层后有两个全连接层,分别有4096个输出。

但是,我们这里只有两类需要输出,所以,这里最后把全两层拉成2个输出。

二,代码实现

按照卷积的计算公式和上面的超参数,通过卷积的输出计算公式搭建网络:

项目中的目录结构:

2.1 数据处理

对网络中的数据进行处理,由于我们已经得到了猫狗数据,开始对模型中的数据进行2:8开,

训练集:8;验证集:2;

import osfrom shutil import copyimport randomdef mkfile(file):if not os.path.exists(file):os.makedirs(file)# 获取所有要分类的文件夹file_path = "./raw_data/"train_path = 'data/train/'validate_path = 'data/validate/'# 列出所有花的种类flow_cases = [clazz for clazz in os.listdir(file_path)]# 创建出验证集和训练集文件夹,并由类名在其目录下创建五个子目录mkfile(train_path)mkfile(validate_path)for clazz in flow_cases:mkfile(train_path + clazz)mkfile(validate_path + clazz)# 按照比例来进行划分, 训练集和测试集 = 9 : 1split_rate = 0.1# 遍历所有类别的全部图像,并按照比例分成训练集合验证集for clazz in flow_cases:clazz_path = file_path + '/' + clazz + '/' # 某一个类别的文件夹images = os.listdir(clazz_path) # images 列表存储来目录下的所有图片的名称num = len(images)sample_images = random.sample(images, k=int(num * split_rate)) # 从images列表随机sample出k个样本for index, image in enumerate(images):# sample_images保存的是所有取出来的图片if image in sample_images:image_path = clazz_path + imagenew_path = validate_path + clazzcopy(image_path, new_path)# 其他的所有图片都保留在训练集中else:image_path = clazz_path + imagenew_path = train_path + clazzcopy(image_path, new_path)print(f'\r[{clazz}] processing [{index + 1} / {num}]', end="") # process barprint()print("processing done!")

2.2 模型的搭建

按照文章中的内容,对模型进行搭建

import torch.nn as nnimport torchclass AlexNet(nn.Module):def __init__(self, num_classes=1000, init_weights=False):super(AlexNet, self).__init__()# 用nn.Sequential()将网络打包成一个模块,精简代码self.features = nn.Sequential( # 卷积层提取图像特征nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55]nn.ReLU(inplace=True), # 直接修改覆盖原值,节省运算内存nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]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), # Dropout 随机失活神经元,默认比例为0.5nn.Linear(128 * 6 * 6, 2048),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(2048, 2048),nn.ReLU(inplace=True),nn.Linear(2048, num_classes),)if init_weights:self._initialize_weights()# 前向传播过程def forward(self, x):x = self.features(x)x = torch.flatten(x, start_dim=1) # 展平后再传入全连接层x = self.classifier(x)return x# 网络权重初始化,实际上 pytorch 在构建网络时会自动初始化权重def _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d): # 若是卷积层nn.init.kaiming_normal_(m.weight, mode='fan_out', # 用(何)kaiming_normal_法初始化权重nonlinearity='relu')if m.bias is not None:nn.init.constant_(m.bias, 0) # 初始化偏重为0elif isinstance(m, nn.Linear): # 若是全连接层nn.init.normal_(m.weight, 0, 0.01) # 正态分布初始化nn.init.constant_(m.bias, 0) # 初始化偏重为0

2.3 开始训练

自定义数据的训练逻辑

TRAIN_ROOT = r'data/train'VALIDATE_ROOT = 'data/validate'# 进行数据的处理,定义数据转换data_transform = {"train": pose([transforms.RandomResizedCrop(224), # 随机裁剪,再缩放成 224×224transforms.RandomHorizontalFlip(p=0.5), # 水平方向随机翻转,概率为 0.5, 即一半的概率翻转, 一半的概率不翻转transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),"validate": pose([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))])}# 加载数据集train_dataset = ImageFolder(TRAIN_ROOT, transform=data_transform['train'])validate_dataset = ImageFolder(VALIDATE_ROOT, transform=data_transform['validate'])# 讲数据进行小批量处理train_dataloader = DataLoader(train_dataset,batch_size=32,shuffle=True,num_workers=0)val_dataloader = DataLoader(validate_dataset,batch_size=32,shuffle=True,num_workers=0)device = 'cuda' if torch.cuda.is_available() else 'cpu'model = AlexNet(num_classes=2).to(device)# 定义一个损失函数loss_fn = nn.CrossEntropyLoss()# 定义一个优化器optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)# 学习率每隔10轮变为原来的0.5lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)# 定义训练函数def train(dataloader, model, loss_fn, optimizer):model.train()time_start = time.perf_counter() # 对训练一个 epoch 计时loss, current, n = 0.0, 0.0, 0for batch, (x, y) in enumerate(dataloader):image, y = x.to(device), y.to(device)output = model(image)cur_loss = loss_fn(output, y)# 取最大的哪个坐标_, pred = torch.max(output, axis=1)cur_acc = torch.sum(y==pred) / output.shape[0]# 反向传播optimizer.zero_grad()cur_loss.backward()optimizer.step()loss += cur_loss.item()current += cur_acc.item()n = n+1# 打印训练进度(使训练过程可视化)rate = (batch + 1) / len(dataloader) # 当前进度 = 当前step / 训练一轮epoch所需总stepa = "*" * int(rate * 50)b = "." * int((1 - rate) * 50)print("\r{:^3.0f}%[{}->{}]".format(int(rate * 100), a, b), end="")print('%f s' % (time.perf_counter() - time_start))# 返回平均的losstrain_loss = loss / ntrain_acc = current / nprint('train_loss' + str(train_loss))print('train_acc' + str(train_acc))return train_loss, train_acc# 定义一个验证函数def val(dataloader, model, loss_fn):# 将模型转化为验证模型model.eval()loss, current, n = 0.0, 0.0, 0with torch.no_grad():for batch, (x, y) in enumerate(dataloader):image, y = x.to(device), y.to(device)output = model(image)cur_loss = loss_fn(output, y)_, pred = torch.max(output, axis=1)cur_acc = torch.sum(y == pred) / output.shape[0]loss += cur_loss.item()current += cur_acc.item()n = n + 1val_loss = loss / nval_acc = current / nprint('val_loss' + str(val_loss))print('val_acc' + str(val_acc))return val_loss, val_acc# 解决中文显示问题plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False# 定义画图函数def matplot_loss(train_loss, val_loss):plt.plot(train_loss, label='train_loss')plt.plot(val_loss, label='val_loss')plt.legend(loc='best')plt.ylabel('loss')plt.xlabel('epoch')plt.title("训练集和验证集loss值对比图")plt.show()def matplot_acc(train_acc, val_acc):plt.plot(train_acc, label='train_acc')plt.plot(val_acc, label='val_acc')plt.legend(loc='best')plt.ylabel('acc')plt.xlabel('epoch')plt.title("训练集和验证集acc值对比图")plt.show()# 开始训练loss_train = []acc_train = []loss_val = []acc_val = []epoch = 20min_acc = 0for t in range(epoch):lr_scheduler.step()print(f"epoch{t+1}\n-----------")train_loss, train_acc = train(train_dataloader, model, loss_fn, optimizer)val_loss, val_acc = val(val_dataloader, model, loss_fn)loss_train.append(train_loss)acc_train.append(train_acc)loss_val.append(val_loss)acc_val.append(val_acc)# 保存最好的模型权重if val_acc > min_acc:folder = 'save_model'if not os.path.exists(folder):os.mkdir('save_model')min_acc = val_accprint(f"save best model, 第{t+1}轮")torch.save(model.state_dict(), 'save_model/best_model.pth')# 保存最后一轮的权重文件if t == epoch-1:torch.save(model.state_dict(), 'save_model/last_model.pth')matplot_loss(loss_train, loss_val)matplot_acc(acc_train, acc_val)print('Done!')

2.4 最后对模型进行验证

TRAIN_ROOT = 'data/train'VALIDATE_ROOT = 'data/validate'# 进行数据的处理,定义数据转换data_transform = {"train": pose([transforms.RandomResizedCrop(224), # 随机裁剪,再缩放成 224×224transforms.RandomHorizontalFlip(p=0.5), # 水平方向随机翻转,概率为 0.5, 即一半的概率翻转, 一半的概率不翻转transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),"validate": pose([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))])}# 加载数据集train_dataset = ImageFolder(TRAIN_ROOT, transform=data_transform['train'])validate_dataset = ImageFolder(VALIDATE_ROOT, transform=data_transform['validate'])# 讲数据进行小批量处理train_dataloader = DataLoader(train_dataset,batch_size=32,shuffle=True,num_workers=0)val_dataloader = DataLoader(validate_dataset,batch_size=32,shuffle=True,num_workers=0)device = 'cuda' if torch.cuda.is_available() else 'cpu'model = AlexNet(num_classes=2).to(device)# 加载模型model.load_state_dict(torch.load("save_model/best_model.pth", map_location='cpu'))# 获取预测结果classes = ["cat","dog",]# 把张量转化为照片格式show = ToPILImage()# 进入到验证阶段model.eval()for i in range(10):x, y = validate_dataset[i][0], validate_dataset[i][1]show(x).show()x = Variable(torch.unsqueeze(x, dim=0).float(), requires_grad=True).to(device)x = torch.tensor(x).to(device)with torch.no_grad():pred = model(x)# 用argmax获取概率最大的一个物体predicted, actual = classes[torch.argmax(pred[0])], classes[y]print(f'predicted:"{predicted}", Actual:"{actual}"')

三,总结

在每个卷机后面添加了Relu激活函数,解决了Sigmoid的梯度消失问题,使收敛更快。使用随机丢弃技术(dropout)选择性地忽略训练中的单个神经元,避免模型的过拟合(也使用数据增强防止过拟合)添加了归一化LRNLocal Response Normalization,局部响应归一化)层,使准确率更高。重叠最大池化(overlapping max pooling),即池化范围z与步长s存在关系z>s避免平均池化(average pooling)的平均效应

完整代码: /fckey/DeepLearning_cases/tree/master/AlexNet

四,参考

/p/116197079

/frighting_ing/article/details/120774252

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。