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

news2024/12/23 5:02:30

前言

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

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

这里我用的是猫狗分类的数据集,如下图所示:
在这里插入图片描述
本博文完整数据集:链接:https://pan.baidu.com/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 os
from shutil import copy
import random

def 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 : 1
split_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 + image
            new_path = validate_path + clazz
            copy(image_path, new_path)
        # 其他的所有图片都保留在训练集中
        else:
            image_path = clazz_path + image
            new_path = train_path + clazz
            copy(image_path, new_path)
        print(f'\r[{clazz}] processing [{index + 1} / {num}]', end="") # process bar
    print()

print("processing done!")

2.2 模型的搭建

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

import torch.nn as nn
import torch


class 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.5
            nn.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)  # 初始化偏重为0
            elif 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": transforms.Compose([transforms.RandomResizedCrop(224),       # 随机裁剪,再缩放成 224×224
                                 transforms.RandomHorizontalFlip(p=0.5),  # 水平方向随机翻转,概率为 0.5, 即一半的概率翻转, 一半的概率不翻转
                                 transforms.ToTensor(),
                                 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),

    "validate": 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))])}
# 加载数据集
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.5
lr_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, 0
    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]

        # 反向传播
        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所需总step
        a = "*" * 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))
    # 返回平均的loss
    train_loss = loss / n
    train_acc = current / n
    print('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, 0
    with 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 + 1

    val_loss = loss / n
    val_acc = current / n
    print('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 = 20
min_acc = 0
for 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_acc
        print(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": transforms.Compose([transforms.RandomResizedCrop(224),       # 随机裁剪,再缩放成 224×224
                                 transforms.RandomHorizontalFlip(p=0.5),  # 水平方向随机翻转,概率为 0.5, 即一半的概率翻转, 一半的概率不翻转
                                 transforms.ToTensor(),
                                 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),

    "validate": 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))])}
# 加载数据集
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)的平均效应

完整代码: https://github.com/fckey/DeepLearning_cases/tree/master/AlexNet

四,参考


https://zhuanlan.zhihu.com/p/116197079
https://blog.csdn.net/frighting_ing/article/details/120774252

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows OpenGL ES 图像色彩替换

目录 一.OpenGL ES 图像色彩替换 1.原始图片2.效果演示 二.OpenGL ES 图像色彩替换源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 …

MongoDB的使用

MongoDB 是一个跨平台的,以 JSON 为数据模型的文档数据库,是当前 NoSQL 数据库产品中最热门的一种。它介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富,最像关系数据库的产品。 1、 MongoDB 与 MySQL 数据库 数据…

openGauss 高效数据压缩算法

openGauss 高效数据压缩算法 2.1 主要创新技术点 openGauss 的主要创新技术点总结如下: (一)改进压缩算法,压缩数据单位依然是选择页级别,进一步结合 Page 结构以及本身数据的特征,将 page 交给通用压缩算…

使用ClickHouse JDBC官方驱动,踩坑无数

前言 最近遇到一个ClickHouse的线上问题: Code: 242, e.displayText() DB::Exception: Table is in readonly mode(zookeeper path:/clickhouse/tables/02/xxx) (version 21.12.4.1) (official build) 这个问题我在网上查原因说是由于Zookeeper压力过大&#xf…

MySQL基础篇之多表查询(内连接、外连接、自连接、子查询、union)

05、多表查询 5.1、多表关系 1、概述 项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分…

maven学完总结!少走弯路一百遍

一、学习方式 学完maven大概花了两天的时间,其实之前学javaweb时用的是maven,因此有点接触,一到两天便能学完maven。 这次我也做了每个知识点的流程图。 但是我学完之后我发现可能没什么要做流程图的,因为更多的是自己动手操作的…

澳大利亚昆士兰大学博士后职位—生物活性肽

【国外博士后招聘-知识人网】澳大利亚昆士兰大学博士后职位—生物活性肽 昆士兰大学(The University of Queensland),简称“昆大”“UQ” ,世界高等科研学府。始建于1910年,是昆士兰州第一所综合型大学,同时…

网页JS自动化脚本(二)查找定位页面元素的方法

我们写脚本往往是要提取某些关键字,那么我们对于元素的查找和定位就很重要首先我们打开浏览器,鼠标右键点击我们想要定位的元素,然后点击审查元素 然后浏览器会弹出调试工具台,我们继续看下图右侧的调试工具台 可以看到我画了9个序号,可以看到从第1个到第7个全部是div容器元素,…

[附源码]Python计算机毕业设计Django+Vue的健身房会员系统的设计与实现

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

数字信号处理-11-FPGA FFT IP应用实例

前言 本文根据FFT相关原理进行设计构建工程,仿造前文的工程构建的混频功能的工程,设计工程显示该混频信号的功率谱,然后进行仿真分析。 FFT仿真与分析 本文不再针对FFT的原理进行过多赘述,提供一份简单的matlab仿真代码。根据仿…

在Soliworks中便捷添加自己的LOGO

在Soliworks中,一般采用草图图片样条曲线的方式绘制个人logo,但是对于一些复杂的logo,对每个模型都绘制一遍显然是不显示的,所以我摸索了一套只需绘制LOGO一遍,就能够将其应用在其他模型的方式。 1. 绘制LOGO 采用草…

黑马点评--好友关注

好友关注: 关注和取关 在探店图文的详情页面中,可以关注发布笔记的作者: 需求:基于该数据结构,实现两个接口 关注和取关接口 Overridepublic Result follow(Long id, Boolean isFollow) {// 1.获取登录用户Long us…

软件设计师教程(二)计算机系统知识-计算机体系结构

软件设计师教程 软件设计师教程(一)计算机系统知识-计算机系统基础知识 计算机体系结构软件设计师教程计算机体系结构的发展计算机系统结构概述计算机体系结构分类指令系统指令集体系结构的分类CISC和RISC指令的流水处理阵列处理机、并行处理机和多处理…

wy的leetcode刷题记录_Day51

wy的leetcode刷题记录_Day51 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间:2022-11-24 前言 目录wy的leetcode刷题记录_Day51声明前言795. 区间子数组个数题目介绍思路代码收获98. 验证二叉搜索树题目介绍思路代码收获795. 区间子数组…

Linux中修改环境变量的几种方法比较分析

修改环境变量的作用 使得命令可以在命令行直接运行 第一种方式,在终端直接使用export **特点:**即可生效,无需重启或刷新文件;终端关闭则失效 第二种方式,修改/etc/profile文件 特点:对所有用户永久有…

算法刷题打卡第29天:省份数量---并查集

省份数量 难度:中等 有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相连的城市,组内不…

SQL注入漏洞 | iwebsec

文章目录靶场搭建SQL注入漏洞靶场搭建 配置 云服务器:阿里云 系统:CentOS 7.6 靶场:iwebsec CentOS-7.6安装docker 安装iwebsec 启动靶场 docker run --restartalways --name iwebsec -it -dp 8001:80 iwebsec/iwebsec访问不成功 可能是…

希望流程挖掘成为撬动企服市场的突破口 | 专访凡得科技CEO海广跃、首席技术顾问刘聪

2022年,全球流程挖掘市场规模预计将达70多亿人民币,而目前中国流程挖掘行业尚处于市场启蒙期,仅少数大型企业与机构对流程挖掘进行了初步或尝试性的投入。从目前来看,原生流程挖掘厂商会直接面向客户输出流程挖掘能力,…

Spring集合注入

一、环境准备 创建一个Maven项目pom.xml添加Spring依赖resources下添加spring的配置文件applicationContext.xml项目中添加BookDao、BookDaoImpl类 public interface BookDao {public void save(); }public class BookDaoImpl implements BookDao {private int[] array;priva…

容器云平台初始化(harbor的安装部署)

1.虚拟机规划 设备说明主机名接口IP地址虚拟机1MasterEth010.0.0.10/24虚拟机2Node1Eth010.0.0.20/24虚拟机3HarborEth010.0.0.30/24 2.容器云平台初始化(harbor的安装部署) 1.根据规划的IP地址,创建虚拟机,确保网络正常通信。按照规划表修改主机名并关…