图像分类:搭建AlexNet并定义加载训练特定数据集

2023-11-11

目录

 

前言

AlexNet搭建

模型架构

卷积神经网络输出

层设置

模型代码

数据集定义加载

自定义数据集

数据集定义代码

读取txt文件

处理加载数据集

数据集加载代码

训练测试

训练过程

测试过程


前言

AlexNet模型是2012由Alex等人在ImageNet竞赛中提出,很优秀的机器学习分类的算法。也由此深度学习开始迅速发展,更多更深的神经网络被提出。在对一个简单图像分类任务下手时,我们要考虑到数据集的定义加载,然后搭建模型并对数据集进行训练测试。本文将讲一下其过程和遇到的一些问题。

AlexNet搭建

模型架构

在模型最初被提出时,其结构如下图所示:

该模型主要有以下几个优点:

(1)首次利用GPU进行网络加速训练;

(2)使用了Relu激活函数(f(x)=max(0,x)),而不是传统的sigmoid和tanh函数,训练速度有了较大提升;

(3)提出了LRN(局部响应归一化)层,抑制了反馈较小的神经元,增强了模型的泛化能力;

(4)使用了Dropout和数据增强进而有效减少过拟合发生。

卷积神经网络输出

Output=\frac{W-F+2P}{S} +1

  • 输入图片大小 W×W(一般情况下Width=Height)
  • Filter大小F×F
  • 步长 S
  • padding的像素数 P

下面以AlexNet第一层卷积层、池化层为例:

#卷积层1
    nn.Conv2d(in_channels=3, out_channels=48, kernel_size=11, stride=4,padding=0),  # input[3, 224, 224]  output[48, 55, 55]
    nn.ReLU(inplace=True),  # 直接修改覆盖原值,节省运算内存
#池化层1
    nn.MaxPool2d(kernel_size=3, stride=2),  # output[48, 27, 27]

#全连接层1
    nn.Dropout(p=0.5),  # Dropout 随机失活神经元,默认比例为0.5
    nn.Linear(128 * 6 * 6, 2048),
    nn.ReLU(inplace=True),

卷积层1的几个参数:
       in_channels=3:表示的是输入的通道数,由于是RGB型的,所以通道数是3
       out_channels=96:表示的是输出的通道数,设定输出通道数的96(这个是可以根据自己的需要来设置的)
       kernel_size=11:表示卷积核的大小是11x11的,也就是上面的 “F”, F=11
       stride=4:表示的是步长为4,也就是上面的S, S=4
       padding=0:表示的是填充值的大小为2,也就是上面的P, P=2

假定图像输入大小为224*224,根据计算公式可以算得Output=55,那么输出的size即为55*55。通道数则为自己设定的数值。

通过全连接层我们看到里面使用了Dropout。该层包含权重向量和激活函数,将一个图片先拉伸为一向量作为输入,与权重向量点乘后再通过激活函数进一步输出。

层设置

cove1d:用于文本数据,只对宽度进行卷积,对高度不进行卷积
cove2d:用于图像数据,对宽度和高度都进行卷积

maxpool意为最大池化层,MaxPoold即为二维最大池化操作。池化层用于特征融合和降维。

模型代码

    def __init__(self, num_classes=63, init_weights=False):
        super(AlexNet, self).__init__()
        # 用nn.Sequential()将网络打包成一个模块,精简代码
        self.features = nn.Sequential(  # 卷积层提取图像特征
            #卷积层1
            nn.Conv2d(3, 48, kernel_size=11, stride=4),  # input[3, 224, 224]  output[48, 55, 55]
            nn.ReLU(inplace=True),  # 直接修改覆盖原值,节省运算内存
            #池化层1
            nn.MaxPool2d(kernel_size=3, stride=2),  # output[48, 27, 27]
            #卷积层2
            nn.Conv2d(48, 128, kernel_size=5, padding=2),  # output[128, 27, 27]
            nn.ReLU(inplace=True),
            #池化层2
            nn.MaxPool2d(kernel_size=3, stride=2),  # output[128, 13, 13]
            #卷积层3
            nn.Conv2d(128, 192, kernel_size=3, padding=1),  # output[192, 13, 13]
            nn.ReLU(inplace=True),
            #卷积层4
            nn.Conv2d(192, 192, kernel_size=3, padding=1),  # output[192, 13, 13]
            nn.ReLU(inplace=True),
            #卷积层5
            nn.Conv2d(192, 128, kernel_size=3, padding=1),  # output[128, 13, 13]
            nn.ReLU(inplace=True),
            #池化层3
            nn.MaxPool2d(kernel_size=3, stride=2),  # output[128, 6, 6]
        )
        self.classifier = nn.Sequential(  # 全连接层对图像分类
            #全连接层1
            nn.Dropout(p=0.5),  # Dropout 随机失活神经元,默认比例为0.5
            nn.Linear(128 * 6 * 6, 2048),
            nn.ReLU(inplace=True),
            #全连接层2
            nn.Dropout(p=0.5),
            nn.Linear(2048, 2048),
            nn.ReLU(inplace=True),
            #全连接层3
            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

数据集定义加载

自定义数据集

untils.data包括Dataset和Dataloader,前者为一抽象类。自己定义的数据集需要继承抽象类class torch.utils.data.Dataset,并且需要重载两个重要的函数:__len__ __getitem__

def getitem(self, index):
def len(self):

其中__len__应该返回数据集的大小,而__getitem__应该编写支持数据集索引的函数
这里重点看 getitem函数,getitem接收一个index,然后返回图片数据和标签,这个index通常指的是一个list的index,这个list的每个元素就包含了图片数据的路径和标签信息。

数据集中包括两个文件夹,一个是图片(image),一个是标签(label)。我们需要将其一一对应,那这时就有两种方法。一种是读取标签信息所在的txt,对于图片名称加上路径再和label拼接组成一个txt;另一种方法则是读取图片所在的文件夹,读取图片路径后然后根据图片名称在标签信息txt中筛选找到对应的label然后组成一个新的txt。新的txt就可以作为数据集被读取加载。

 

数据集定义代码

# *************************************数据集的设置****************************************************************************
root = os.getcwd() + '/data/'  # 数据集的地址

# 定义读取文件的格式
def default_loader(path):
    return Image.open(path).convert('RGB')        #对于彩色图像返回RGB,对于灰度图像模式为L


class MyDataset(Dataset):
    # 创建自己的类: MyDataset,这个类是继承的torch.utils.data.Dataset
    # **********************************  #使用__init__()初始化一些需要传入的参数及数据集的调用**********************
    def __init__(self, txt,mulu, transform=None, target_transform=None, loader=default_loader):
        super(MyDataset, self).__init__()
        # 对继承自父类的属性进行初始化
        fh = open(txt,'r')
        imgs = []
        # 按照传入的路径和txt文本参数,以只读的方式打开这个文本
        for line in fh:  # 迭代该列表#按行循环txt文本中的内
           line = line.strip('\n')
           line = line.rstrip('\n')
           # 删除 本行string 字符串末尾的指定字符,这个方法的详细介绍自己查询python
           words = line.split()
           words[0] = './data/'+str(mulu)+'/'+str(words[0]) + '.JPG'
           # 用split将该行分割成列表  split的默认参数是空格,所以不传递任何参数时分割空格
           imgs.append((words[0], int(words[4])))
           #根据原本txt的内容,words[0]是图片信息,words[4]是lable

        self.imgs = imgs
        self.transform = transform
        self.target_transform = target_transform
        self.loader = loader

    def __getitem__(self, index):  # 这个方法是必须要有的,用于按照索引读取每个元素的具体内容
        fn, label = self.imgs[index]  # fn是图片path #fn和label分别获得imgs[index]也即是刚才每行中word[0]和word[4]的信息
        # print(fn)
        # img = Image.open(fn)

        img = self.loader(fn)  # 按照路径读取图片

        if self.transform is not None:
            img = self.transform(img)  # 数据标签转换为Tensor
        return img, label  # return回哪些内容,那么我们在训练时循环读取每个batch时,就能获得哪些内容

    def __len__(self):  # 这个函数它返回的是数据集的长度,也就是多少张图片,要和loader的长度作区分
        return len(self.imgs)

读取txt文件

open函数使用:open(name[, mode[, buffering]])
  • name : 一个包含了你要访问的文件名称的字符串值。

  • mode : mode 决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。

  • buffering : 如果 buffering 的值被设为 0,就不会有寄存。如果 buffering 的值取 1,访问文件时会寄存行。如果将 buffering 的值设为大于 1 的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。

fh=oprn(txt,'r')其中r代表以只读的方式打开文件。读取的时候逐行读取,删除首尾的换行符,由于原txt第一列是图片名称,第五列是label,所以在给第一列加上路径信息后即可和第五列进行拼接。输出的imgs即为我们想要得到的信息。

处理加载数据集

在导入数据集时我们要对其进行预处理,然后进一步加载。 处理过程有很多种方法,包括裁剪、翻转、加入噪声等方法。通过调用untils.data.Dataloader可以加载数据集。关于Dataloader的使用还涉及到下面几个参数:

batch_size:可以分批次读取

shuffle=True可以对数据进行随机读取,可以对数据进行洗牌操作(shuffling),打乱数据集内数据分布的顺序

num_workers=0  可以并行加载数据(利用多核处理器加快载入数据的效率

 

数据集加载代码

#数据预处理
data_transform = {
    "train": transforms.Compose([transforms.RandomResizedCrop(227),       # 随机裁剪,再缩放成 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))]),

    "test": transforms.Compose([transforms.Resize((227, 227)),  # cannot 224, must (224, 224)
                               transforms.ToTensor(),
                               transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}



#导入训练集并进行预处理
train_data = MyDataset(txt=root+'IR1_05-17.txt',mulu='img_05-17', transform=data_transform["train"])
train_num = len(train_data)
print(train_num)

#导入测试集并进行预处理
test_data = MyDataset(txt=root+'IR1_18.txt',mulu='img_2018', transform=data_transform["test"])
test_num = len(test_data)

print(test_num)
#print(test_data[1])

#加载训练集
train_loader = DataLoader(train_data,   	# 导入的训练集
                          batch_size=64, 	# 每批训练的样本数
                          shuffle=True,	    # 是否打乱训练集
                          num_workers=0)	# 使用线程数,在windows下设置为0

#加载测试集
test_loader = DataLoader(test_data,   	    # 导入的测试集
                          batch_size=64, 	# 每批测试的样本数
                          shuffle=True,	    # 是否打乱测试集
                          num_workers=0)    # 使用线程数,在windows下设置为0

训练测试

在这个过程中,要注意以下两点:

  • net.train():训练过程中开启 Dropout
  • net.eval(): 测试过程关闭 Dropout

训练过程

神经网络模型训练是经过前向传播计算Loss,根据其值进行反向推导,然后调整优化参数。下面损失函数选用了交叉熵误差。同时还保存了模型,以便在测试过程中加载模型,这样就可以将训练和测试独立,方便测试。

net = AlexNet(num_classes)  # 实例化网络(输出类型)

net.to(device)                                       # 分配网络到指定的设备(GPU/CPU)训练
loss_function = nn.CrossEntropyLoss()                # 交叉熵损失
optimizer = optim.Adam(net.parameters(), lr)  # 优化器(训练参数,学习率)

s_path = './lunwen.pkl'
best_acc = 0.0

for epoch in range(63):
    ########################################## train ###############################################
    net.train()                         # 训练过程中开启 Dropout
    running_loss = 0.0                  # 每个 epoch 都会对 running_loss  清零
    time_start = time.perf_counter()    # 对训练一个 epoch 计时

    for step, data in enumerate(train_loader, start=0):  # 遍历训练集,step从0开始计算
        images, labels = data  # 获取训练集的图像和标签
        #print(images.size())
        optimizer.zero_grad()  # 清除历史梯度

        outputs = net(images.to(device))                  # 正向传播
        loss = loss_function(outputs, labels.to(device))  # 计算损失
        loss.backward()   # 反向传播
        optimizer.step()  # 优化器更新参数
        running_loss += loss.item()

        # 打印训练进度(使训练过程可视化)
        rate = (step + 1) / len(train_loader)  # 当前进度 = 当前step / 训练一轮epoch所需总step
        a = "*" * int(rate * 50)
        b = "." * int((1 - rate) * 50)
        print("\rtrain loss: {:^3.0f}%[{}->{}]{:.3f}".format(int(rate * 100), a, b, loss), end="")
    print()
    print('%f s' % (time.perf_counter() - time_start))

    torch.save(net, s_path)

测试过程

当图片经过模型输出outputs是一个不同类别对应的概率矩阵,假定类别为63类,那么outputs第一行就代表第一张图的被预测成0-62类的概率 ,可经过softmax进一步确定。torch.max()输出值最大的位置,假使它和原标签一致,那么便预测准确,再比上总数即可得到准确率。

其性能可看MAE和RMSE,在sklearn中均有相应函数,当然注释中也根据其定义写了一段代码。

#加载模型
net=torch.load('./lunwen.pth')

net.to(device)                                       # 分配网络到指定的设备(GPU/CPU)训练


net.eval()    # 验证过程中关闭 Dropout

best_acc = 0.0
pre=[]
lab=[]
acc = 0.0
with torch.no_grad():
    for test_data in test_loader:
        test_images, test_labels = test_data
        #print(test_labels)
        num_test = test_labels.cpu().numpy()
        #print(num_test)
        lab.extend(num_test)
        #print(test_labels.item())
        outputs = net(test_images.to(device))
        #print(outputs.size())
        #print(outputs)
        predict_y = torch.max(outputs, dim=1)[1]                 # 以output中值最大位置对应的索引(标签)作为预测输出
        #print(predict_y.size())
        #print(predict_y)
        num_y = predict_y.cpu().numpy()
        #print(num_y)
        pre.extend(num_y)
        #y = torch.max(outputs, dim=1)[0]
            #mse = mean_squared_error(test_labels, predict_y)
            #print("MSE: %.4f" % mse)
            #print(y,predict_y,test_labels)
        acc += (predict_y == test_labels.to(device)).sum().item()
        print(acc)

    test_accurate = acc / test_num

        # 保存准确率最高的那次网络参数
    if test_accurate > best_acc:
        best_acc = test_accurate

    print(lab)
    print(pre)
    # error = []
    # for i in range(len(lab)):
    #     error.append(lab[i] - pre[i])
    # squaredError = []
    # absError = []
    # for val in error:
    #     squaredError.append(val * val)  # target-prediction之差平方
    #     absError.append(abs(val))  # 误差绝对值
    # print("1MSE = ", sum(squaredError) / len(squaredError))  # 均方误差MSE
    # print("1RMSE = ", sqrt(sum(squaredError) / len(squaredError)))  # 均方根误差RMSE
    # print("1MAE = ", sum(absError) / len(absError))  # 平均绝对误差MAE

    mae = mean_absolute_error(lab,pre)
    mse = mean_squared_error(lab, pre)
    rmse = math.sqrt(mse)
    print("MAE: %.2f   MSE: %.2f      RMSE:%.2f" %(mae,mse,rmse) )
    print('  test_accuracy: %.3f \n' %test_accurate)

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图像分类:搭建AlexNet并定义加载训练特定数据集 的相关文章

随机推荐

  • 如何用人工智能预测股票(完整答案)

    前言 十分钟实现人工智能股价预测 是一个深度学习的练习项目 其通过机器学习算法 根据过去几年与某只股票相关的K线走势 公司相关报道的情感分析作为数据集 通过训练来得到可以预测股价的机器学习模型 并用该模型对股价进行预测 本项目使用几种不同的
  • ajax请求必须打断点才能成功,请问ajax请求过程中都经历了哪些状态?

    紫衣仙女 AJAX运行过程中5种状态 0 未初始化 还没有调用send 方法 1 载入 已调用send 方法 正在发送请求 2 载入完成 send 方法执行完成 3 交互 正在解析响应内容 4 完成 响应内容解析完成 可以在客户端调用了 p
  • RT-DETR论文解读与代码

    RTdetr ecoder和decoder部分pytorch复现代码链接见文末 1 初始化策略与源码有所差异 使用过程中可以根据自己的需求进行更换 2 代码经过一条一条的debug 本身没有bug 并且是依据作者源码用pytorch实现 但
  • 给定数组长度2n,分成n对,求n对最小元素之和最大

    给定长度为 2n 的数组 你的任务是将这些数分成 n 对 例如 a1 b1 a2 b2 an bn 使得从1 到 n 的 min ai bi 总和最大 示例 1 输入 1 4 3 2 输出 4 解释 n 等于 2 最大总和为 4 min 1
  • 2021-09-23 opencv学习笔记(图像变换,二值化,滤波器介绍及python实现)

    opencv学习笔记 颜色空间 改变颜色空间 cv2 cvtColor 目标追踪 如何查找某个颜色的HSV值 图形变换 缩放 cv2 resize 平移 旋转 仿射变换 透视变换 二值化 简单阈值法 自适应阈值 Otsu二值化 俗称大津法
  • 解决 Mac Python安装 mysqlclient 库报错

    文章目录 安装 brew 安装 mysql 安装 MySQL Connector c 安装 XCode 安装 OpenSSL 取消链接MySQL和链接的MySQL连接器mysql connector c 安装 mysqlclient 再次连
  • `subprocess`模块之Popen

    subprocess模块之Popen https www liuzhongwei com page 72887 html https www runoob com w3cnote python3 subprocess html https
  • >_<NameError: name ‘history‘ is not defined 问题解决

    在使用 jupyter notebook运行程序时出现了错误 NameError name history is not defined 将出问题的程序截取了一段如下图所示 后来浏览了网上的资源尝试了别人给出的解决办法 并没有解决我运行的程
  • 使用FORCE训练的脉冲神经网络中的监督学习(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 1 1第一代神经网络 1 2 第二代神经网络 BP 神经网络 1 3 第三代神经网络 脉冲神经网络 2 运
  • html楼层效果图,如何使用纯html+css+javascript实现楼层跳跃式的页面布局

    如何使用纯html css javascript实现楼层跳跃式的页面布局 发布时间 2021 04 13 12 20 50 来源 亿速云 阅读 104 作者 小新 这篇文章给大家分享的是有关如何使用纯html css javascript实
  • AI大模型私有化部署流程

    私有化部署AI大模型需要一定的GPU支持 适合有一定经济实力的公司 今天和大家分享一下如何进行私有化部署 这涉及很专业的知识 包括硬件和软件环境 模型部署 API集成 测试验证等 这里只是初步讨论一下 希望对大家的工作有所帮助 北京木奇移动
  • 数据可视化:掌握数据领域的万金油技能

    欢迎来到我的博客 作者 秋无之地 简介 CSDN爬虫 后端 大数据领域创作者 目前从事python爬虫 后端和大数据等相关工作 主要擅长领域有 爬虫 后端 大数据开发 数据分析等 欢迎小伙伴们点赞 收藏 留言 关注 关注必回关 上一篇文章已
  • python 中 sm.graphics.plot_regress_exog 绘制图像的解释

    导入相关包 import numpy as np import pandas as pd import statsmodels api as sm import matplotlib pyplot as plt 创建数据 X np aran
  • 【华为OD统一考试B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • <van-empty description=““ /> 滚动条bug

    使用
  • 用 echarts画图时tooltip.formatter参数params不会更新

    使用echarts画地图时 遇到一个很奇怪的问题 首先说明我的目的 为了让地图漂亮些 不同的地图区域显示不同的颜色 由于待绘制的地图二级地市数量不确定 需要通过解析获取到的数据来确定 因此我在 series的itemStyle中采用了函数来
  • mysql 查询两个时间段是否有交集的情况

    只要查询自己选择的时间段 和 数据库里面的时间段 是否有交集 就可以了 数据库的字段 start time end time 输入的字段 a b 第一种 SELECT FROM test table WHERE start time gt
  • FPGA学习日记(二)使用quartusII创建ip核

    使用quartusII创建各类ip核 操作大体上都相似 区别在于根据实际需求对ip核进行设置 下面以pll的ip核创建为例 讲述ip核的一般创建过程 step1 找到tools下的魔棒选项 step2 选择创建一个新的ip核还是导入已有的i
  • 华为OD岗位机试指南

    首先 自己要去熟悉编程语言的方法 找一些题目来做 比如力扣的中等题目 过往的OD机试题目 主要学习解题思想 看代码里高级写法 而不是仅仅收藏 照抄 同时一定要优先熟悉牛客网的编程环境 尽量在网页编辑 本地编译会导致粘贴时错行 会很坑的 其次
  • 图像分类:搭建AlexNet并定义加载训练特定数据集

    目录 前言 AlexNet搭建 模型架构 卷积神经网络输出 层设置 模型代码 数据集定义加载 自定义数据集 数据集定义代码 读取txt文件 处理加载数据集 数据集加载代码 训练测试 训练过程 测试过程 前言 AlexNet模型是2012由A