当前位置:   article > 正文

CNN经典网络(2)---AlexNet(模型搭建+训练+预测+可视化)_alexnet训练集

alexnet训练集

写在前面:根据b站博主霹雳吧啦Wz 学习CNN,作为个人的学习记录。

目录

论文:

模型架构:

模型参数:

模型搭建:

应用:

数据集:

1、公开数据集

2、自己的数据集

训练:

1、寻找可使用的GPU

2、图像预处理

3、找到训练集 

4、将类索引写入json文件

5、加载数据集

 6、定义网络、训练设备、损失函数、优化器

7、训练

训练结果:

预测: 

​编辑

预测结果:

数据可视化:


论文:

2012年才出现的AlexNet,在ImageNet LSVRC-2010比赛中实现了前1和前5的错误率分别为37.5%和17.0%,在ILSVRC-2012比赛中获得了15.3%的前5的获胜测试错误率。

论文:《ImageNet Classification with Deep Convolutional Neural Networks》

论文贡献:

  • 在ILSVRC-2010和ILSVRC-2012比赛中使用的ImageNet子集上训练了迄今为止最大的卷积神经网络之一,并在这些数据集上获得了迄今为止报道过的最好的结果
  • 为了防止过拟合,首次使用ReLUs作为激活函数,并指出快速学习对在大数据集上训练的大模型的性能有很大的影响。
  • 首次使用两个GPU进行训练,这将前1和前5的错误率分别降低了1.7%和1.2%,双gpu网络的训练时间比单gpu网络略短。
  • 使用局部响应归一化(LRN),有助于快速收敛,增强了模型的泛化能力。(一般是在激活、池化后进行的一种处理方法)。
  • 使用重叠池化层,降低了0.4%和0.3%的错误率。避免过拟合。
  • 使用数据增强方式:平移和翻转;PCA进行降维。避免过拟合。
  • 首次应用DropOut机制,大约使收敛所需的迭代次数翻了一倍。避免过拟合。

模型架构:

翻译:网络架构明确地显示了两个gpu之间的职责描述。一个GPU运行图形顶部的层部件,另一个运行图形底部的层部件。gpu只在特定的层进行通信。网络的输入是150,528维,网络剩余层中的神经元数量由253,440-186,624-64,896-64,896-43,264 - 4096-4096-1000给出。 

模型参数:

模型搭建:

由于这个模型太大,在这里,所有的卷积核个数和全连接层节点数都减半。

  1. import torch.nn as nn
  2. import torch
  3. class AlexNet(nn.Module):
  4. def __init__(self, num_classes=1000, init_weights=False):
  5. super(AlexNet, self).__init__()
  6. self.features = nn.Sequential(
  7. nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55]
  8. nn.ReLU(inplace=True),
  9. nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]
  10. nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]
  11. nn.ReLU(inplace=True),
  12. nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]
  13. nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]
  14. nn.ReLU(inplace=True),
  15. nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]
  16. nn.ReLU(inplace=True),
  17. nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]
  18. nn.ReLU(inplace=True),
  19. nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]
  20. )
  21. self.classifier = nn.Sequential(
  22. nn.Dropout(p=0.5), # 随机失活一半的神经元
  23. nn.Linear(128 * 6 * 6, 2048),
  24. nn.ReLU(inplace=True),
  25. nn.Dropout(p=0.5),
  26. nn.Linear(2048, 2048),
  27. nn.ReLU(inplace=True),
  28. nn.Linear(2048, num_classes), # 这里的num_classes是根据数据集内的种类决定
  29. )
  30. if init_weights: # 初始化权重
  31. self._initialize_weights()
  32. def forward(self, x):
  33. x = self.features(x)
  34. x = torch.flatten(x, start_dim=1)
  35. x = self.classifier(x)
  36. return x
  37. def _initialize_weights(self): # 初始化权重
  38. for m in self.modules():
  39. if isinstance(m, nn.Conv2d): # 如果在卷积层,那就使用kaiming_normal_方法初始化
  40. nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
  41. if m.bias is not None:
  42. nn.init.constant_(m.bias, 0)
  43. elif isinstance(m, nn.Linear): # 如果在全连接层,那就正态分布方法初始化
  44. nn.init.normal_(m.weight, 0, 0.01)
  45. nn.init.constant_(m.bias, 0)

应用:

数据集:

1、公开数据集

使用的是花分类数据集,分别有雏菊、蒲公英、玫瑰、向日葵、郁金香五种类别。首先新建一个名为"flower_data"的新文件夹,然后在浏览器下载该数据集,解压进"flower_data",使用split_data.py程序对数据集进行划分,变成训练集和验证集。

这是所有图片下的文件,一种类别的图片是一个文件夹,文件夹名称为类别名称: 

 

2、自己的数据集

只需要将自己的数据集按类别分开(例如daisy、dandelion、roses、sunflowers、tulips),放在一个文件夹内(例如flower_data)。然后使用split_data.py程序划分训练集和验证集。最后在训练程序和测试程序中找到变量num_classes的值,修改成自己数据集的种类个数。即可

训练程序里: 

预测程序里:

 

 split_data.py:

  1. import os
  2. from shutil import copy, rmtree
  3. import random
  4. def mk_file(file_path: str):
  5. if os.path.exists(file_path):
  6. # 如果文件夹存在,则先删除原文件夹在重新创建
  7. rmtree(file_path)
  8. os.makedirs(file_path)
  9. def main():
  10. # 保证随机可复现
  11. random.seed(0)
  12. # 将数据集中10%的数据划分到验证集中
  13. split_rate = 0.1
  14. # 指向你解压后的flower_photos文件夹
  15. cwd = os.getcwd()
  16. data_root = os.path.join(cwd, "flower_data")
  17. origin_flower_path = os.path.join(data_root, "flower_photos")
  18. assert os.path.exists(origin_flower_path), "path '{}' does not exist.".format(origin_flower_path)
  19. flower_class = [cla for cla in os.listdir(origin_flower_path)
  20. if os.path.isdir(os.path.join(origin_flower_path, cla))]
  21. # 建立保存训练集的文件夹
  22. train_root = os.path.join(data_root, "train")
  23. mk_file(train_root)
  24. for cla in flower_class:
  25. # 建立每个类别对应的文件夹
  26. mk_file(os.path.join(train_root, cla))
  27. # 建立保存验证集的文件夹
  28. val_root = os.path.join(data_root, "val")
  29. mk_file(val_root)
  30. for cla in flower_class:
  31. # 建立每个类别对应的文件夹
  32. mk_file(os.path.join(val_root, cla))
  33. for cla in flower_class:
  34. cla_path = os.path.join(origin_flower_path, cla)
  35. images = os.listdir(cla_path)
  36. num = len(images)
  37. # 随机采样验证集的索引
  38. eval_index = random.sample(images, k=int(num*split_rate))
  39. for index, image in enumerate(images):
  40. if image in eval_index:
  41. # 将分配至验证集中的文件复制到相应目录
  42. image_path = os.path.join(cla_path, image)
  43. new_path = os.path.join(val_root, cla)
  44. copy(image_path, new_path)
  45. else:
  46. # 将分配至训练集中的文件复制到相应目录
  47. image_path = os.path.join(cla_path, image)
  48. new_path = os.path.join(train_root, cla)
  49. copy(image_path, new_path)
  50. print("\r[{}] processing [{}/{}]".format(cla, index+1, num), end="") # processing bar
  51. print()
  52. print("processing done!")
  53. if __name__ == '__main__':
  54. main()

训练:

1、寻找可使用的GPU

如果有,默认使用第一块GPU;如果没有,那就用CPU。

  1. ### 1. 电脑如果有GPU的话,就会选择使用GPU,否则就用CPU
  2. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
  3. print("using {} device.".format(device))

2、图像预处理

用transforms方式进行预处理,用compose将各种方法合在一起。

  1. ### 2. 图像预处理
  2. data_transform = {
  3. # 训练集图片预处理方式
  4. "train": transforms.Compose([transforms.RandomResizedCrop(224), # 将图像随机裁剪成224*224
  5. transforms.RandomHorizontalFlip(), # 水平翻转
  6. transforms.ToTensor(), # 转换成(C*H*W),并将像素值大小归一化[0,1]
  7. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]), # 将三个通道的像素值标准化[-1,1]
  8. # 验证集图片预处理方式
  9. "val": transforms.Compose([transforms.Resize((224, 224)), # cannot 224, must (224, 224)
  10. transforms.ToTensor(),
  11. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

3、找到训练集 

  1. ### 3. 找到训练集
  2. # 返回上一级目录
  3. data_root = os.path.abspath(os.path.join(os.getcwd(), "../..")) # get data root path
  4. # 在上一级目录下寻找data_set文件夹,然后进入其下一级目录flower_data
  5. image_path = os.path.join(data_root, "data_set", "flower_data") # flower data set path
  6. assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
  7. # image_path是指向flower_data文件夹,接着将这个文件夹下的train定义成训练集,然后对训练集的图像根据前面写的data_transform预处理方法处理图像,最后放进train_dataset中。
  8. train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
  9. transform=data_transform["train"])
  10. train_num = len(train_dataset) # 获取训练集数量

4、将类索引写入json文件

  1. ### 4. 将类索引写入json文件
  2. # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
  3. flower_list = train_dataset.class_to_idx # 这里返回一个字典,key是文件夹名,value是顺序值。如上一行所示
  4. cla_dict = dict((val, key) for key, val in flower_list.items()) # 交换键值,主要因为后面的预测值是数字,想输出的时候是花的名称
  5. # write dict into json file 将类索引的字典写入json文件
  6. json_str = json.dumps(cla_dict, indent=4)
  7. with open('class_indices.json', 'w') as json_file:
  8. json_file.write(json_str)

生成的json文件如下图所示: 

5、加载数据集

加载训练集

根据找训练集的方式去找到验证集,而且加载进来。

  1. ### 5. 加载进训练集和验证集
  2. batch_size = 32 # 设置批次大小
  3. nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # 设置加载数据时的进程数
  4. print('Using {} dataloader workers every process'.format(nw))
  5. # 加载训练集
  6. train_loader = torch.utils.data.DataLoader(train_dataset,
  7. batch_size=batch_size, shuffle=True,
  8. num_workers=nw)
  9. # 找到且加载验证集
  10. validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
  11. transform=data_transform["val"])
  12. val_num = len(validate_dataset)
  13. validate_loader = torch.utils.data.DataLoader(validate_dataset,
  14. batch_size=4, shuffle=False,
  15. num_workers=nw)
  16. print("using {} images for training, {} images for validation.".format(train_num,
  17. val_num))

 6、定义网络、训练设备、损失函数、优化器

  1. net = AlexNet(num_classes=5, init_weights=True) # 定义网络
  2. net.to(device) # 定义训练设备
  3. loss_function = nn.CrossEntropyLoss() # 定义损失函数
  4. # pata = list(net.parameters())
  5. optimizer = optim.Adam(net.parameters(), lr=0.0002) # 定义优化器

7、训练

  1. ### 7. 训练
  2. epochs = 10 # 设置训练轮数
  3. save_path = './AlexNet.pth' # 模型权重保存路径
  4. best_acc = 0.0 # 用于记录最高的准确率
  5. train_steps = len(train_loader) # 训练步数
  6. for epoch in range(epochs):
  7. # train
  8. net.train()
  9. running_loss = 0.0 # 计算平均损失
  10. train_bar = tqdm(train_loader, file=sys.stdout) # 设置训练的进度条
  11. for step, data in enumerate(train_bar):
  12. images, labels = data # 将data分成图像和标签
  13. optimizer.zero_grad() # 梯度清零
  14. outputs = net(images.to(device)) # 正向传播获得输出
  15. loss = loss_function(outputs, labels.to(device)) # 计算损失值
  16. loss.backward() # 损失进行反向传播
  17. optimizer.step() # 更新权重w
  18. # print statistics
  19. running_loss += loss.item() # 累加损失值
  20. # 打印训练进度
  21. train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
  22. epochs,
  23. loss)
  24. # validate
  25. net.eval()
  26. acc = 0.0 # accumulate accurate number / epoch
  27. with torch.no_grad(): # 不计算每个节点的误差梯度
  28. val_bar = tqdm(validate_loader, file=sys.stdout) # 设置进度条
  29. for val_data in val_bar:
  30. val_images, val_labels = val_data # 将val_data分成图像和标签
  31. outputs = net(val_images.to(device)) # 验证集进入网络进行预测,获得所有的预测结果和预测值
  32. predict_y = torch.max(outputs, dim=1)[1] # 找到最大概率的所在索引在哪
  33. acc += torch.eq(predict_y, val_labels.to(device)).sum().item() # 将预测值与真实值进行对比,如果相等,eq(predict_y, val_label)返回为1,否则为0;然后用sum求和求出预测对了多少
  34. val_accurate = acc / val_num # 获得求和数值后再除以总的验证集大小,就是准确率,得到准确率
  35. print('[epoch %d] train_loss: %.3f val_accuracy: %.3f' %
  36. (epoch + 1, running_loss / train_steps, val_accurate)) # 打印这一轮的损失值和准确率
  37. if val_accurate > best_acc: # 为了最终得到的是在验证集上准确率最高的模型参数,然后进行保存
  38. best_acc = val_accurate
  39. torch.save(net.state_dict(), save_path)
  40. print('Finished Training')

训练结果:

预测: 

  1. import os
  2. import json
  3. import torch
  4. from PIL import Image
  5. from torchvision import transforms
  6. import matplotlib.pyplot as plt
  7. from model import AlexNet
  8. def main():
  9. ### 1、寻找可使用的GPU,如果没有就用cpu
  10. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
  11. ### 2、 定义图像预处理方式
  12. data_transform = transforms.Compose(
  13. [transforms.Resize((224, 224)), # 将图像resize成224*224
  14. transforms.ToTensor(), # 变成C*H*W格式,并且归一化
  15. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 标准化
  16. ### 3、加载进需要的预测的图像
  17. img_path = "rose.jpeg"
  18. assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
  19. img = Image.open(img_path)
  20. ### 4、图像预处理
  21. plt.imshow(img) # 显示图像
  22. # [N, C, H, W]
  23. img = data_transform(img) # 预处理
  24. # expand batch dimension
  25. img = torch.unsqueeze(img, dim=0) # 拓展成四维,增加一个批次纬度
  26. ### 5、获取类别
  27. # 加载json文件获取类别
  28. json_path = './class_indices.json'
  29. assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)
  30. with open(json_path, "r") as f:
  31. class_indict = json.load(f)
  32. ### 6、定义模型,加载权重,然后预测
  33. # 定义模型
  34. model = AlexNet(num_classes=5).to(device)
  35. # 加载模型权重
  36. weights_path = "./AlexNet.pth"
  37. assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
  38. model.load_state_dict(torch.load(weights_path))
  39. # 和训练那里一样,不计算误差梯度
  40. model.eval()
  41. with torch.no_grad():
  42. # predict class
  43. output = torch.squeeze(model(img.to(device))).cpu() # 通过模型进行正向传输
  44. predict = torch.softmax(output, dim=0) # 将预测结果的概率值变成一个正太分布
  45. predict_cla = torch.argmax(predict).numpy() # 找到最大的预测结果的索引值
  46. print_res = "class: {} prob: {:.3}".format(class_indict[str(predict_cla)],
  47. predict[predict_cla].numpy()) # 在json文件里找到索引对应的种类名称打印出来,而且加上概率值
  48. plt.title(print_res) # 显示的图片名称就是预测的种类名称
  49. for i in range(len(predict)): # 打印出来每种类别的概率
  50. print("class: {:10} prob: {:.3}".format(class_indict[str(i)],
  51. predict[i].numpy()))
  52. plt.show()
  53. if __name__ == '__main__':
  54. main()

预测图片:

预测结果:

数据可视化:

准确率和轮数的关系:

损失值和轮数的关系:

 

网络架构:

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/123968
推荐阅读
相关标签
  

闽ICP备14008679号