pytorch框架搭建AlextNet网络实例——实现图像的分类

pytorch框架搭建AlextNet网络实例——实现图像的分类

  • 1、网络的搭建
  • 2、训练模型
    • 2.1、数据的获取
    • 2.2、训练模型方法
    • 2.3、测试模型方法
    • 2.4、保存模型方法
    • 2.2、画图方法
  • 3、调取保存好的模型进行验证

1、网络的搭建

搭建ALextNet网络,这里我们采用和上一篇搭建DNN神经网络不同的方式,采用管道流的方式搭建。集体网络结构如下:
pytorch框架搭建AlextNet网络实例——实现图像的分类_第1张图片

卷积层5层:
第一层:
(0): Conv2d(3,48,kernel_size=(11,11),stride=(4,4),padding=(2,2),bias=False)
(1): BatchNorm2d(48,eps=1e-05,momentum=0.1,affine=True,track_running_stats=True)
(2): ReLu(inplace=True)
(3): MaxPool2d(kernel_size=3,stride=2,padding=0,dilation=1,ceil_mode=False)
第二层:
(4): Conv2d(48,128,kernel_size=(5,5), stride=(1,1),padding=(2,2))
(5): BatchNorm2d(128,eps=1e-05,momentum=0.1,affine=True,track_running_stats=True)
(6): ReLU( inplace=True)
(7):MaxPool2d(kernel_size=3,stride=2,padding=0, dilation=1, ceil_mode=False)
第三层:
(8): Conv2d(128,192,kernel_size=(3,3),stride=(1,1), padding=(1,1))
(9): BatchNorm2d(192,eps=1e-05,momentum=0.1,affine=True,track_running_stats=True)
(10): ReLU( inplace=True)
第四层:
(11): Conv2d(192,192,kernel_size=(3,3),stride=(1,1),padding=(1,1))
(12): BatchNorm2d(192,eps=1e-05,momentum=0.1, affine=True,track_running_stats=True)
(13): ReLu( inplace=True)
第五层:
(14): Conv2d(192,128,kernel_size=(3,3),stride=(1,1), padding=(1,1))
(15): BatchNorm2d(128,eps=1e-05,momentum=0.1, affine=True,track_running_stats=True)
(16): ReLU(inplace=True)
(17):MaxPool2d(kernel_size=3,stride=2,padding=0, dilation=1, ceil_mode=False)
全连接层3层
第一层:
nn.Dropout(0.2),
nn.Linear(2048,2048)
nn.Sigmoid()
第二层:
nn.Dropout(0.3),
nn.Linear(2048,2048)
nn.Sigmoid()
第三层:
nn.Dropout()默认值为0.5
nn.Linear(2048,1000)

导入必要的包和模块

'''先导入必要的包'''
import torch
import json
from torchsummary import summary#查看参数个数的模块
torch.manual_seed(4)#设定随机数种子
# import warnings#这两个是处理警告的机制
# warnings.filterwarnings('ignore')
import torch.nn as nn#优化器包#搭建网络的层要用到,(卷积,全连接都在这里)

网络搭建代码实现如下:

'''
基本结构:
1、搭建网络
2、获取,加载数据
3、训练模型
4、测试模型
5、保存模型
6、模型部署
'''
'''搭建网络
1、所有网络结构都要继承Module类,必须继承
2、可以用dir(nn.module)查看类中的方法
3、搭建网络层级结构,有两种方式:
    一种是在实例方法中定义好传递通道,在前馈传播的时候直接传递参数
    一种是实例化方法中定义网络种类,在前馈传播的时候一一列出网络层
4、这里我们用第一种方式
    定义两个通道,一个是特征提取器,一个是分类器
'''

class Axlenet(nn.Module):
    #定义构造方法
    def __init__(self,numclass):#numclass:分类的个数
        #一定要继承上边的父类的构造方法
        super(Axlenet, self).__init__()
        #定义特征提取器(卷积层)
        '''
        在特征提取器内,需要定义卷积的具体结构了,包括:
            1、卷积层(1输入通道数,2输出通道数,3卷积核大小,4步长 5.填充6、是否加入偏置项)
        除了卷积层,还要有:
            2、归一化层
            3、激活
            4、池化层
        '''
        # 图片的输入:H,W,C = 224*224*3
        self.featrues = nn.Sequential(
            # 第一层卷积:输入通道数=3,输出通道数 = 48 = 卷积核个数,卷积核大小 = (11,11),步长 = 4,padding=2,偏置项=True
            nn.Conv2d(3, 48, kernel_size=(11, 11), stride=(4, 4), padding=2, bias=True),  # 卷积
            nn.BatchNorm2d(48),  # 批归一化
            nn.ReLU(inplace=True),  # 激活,这里的True(覆盖)或者False作用是决定激活之前的值是被保留还是被激活之后的值覆盖掉
            nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False),  # 池化dilation=1——>隔空取

            nn.Conv2d(48, 128, kernel_size=(5, 5), stride=(1, 1), padding=2,bias=True),  # 卷积
            nn.BatchNorm2d(128),  # 批归一化
            nn.ReLU(inplace=True),  # 激活
            nn.MaxPool2d(kernel_size=3, stride=(2, 2), padding=0, dilation=1, ceil_mode=False),  # 池化

            nn.Conv2d(128, 192, kernel_size=(3, 3), stride=(1, 1), padding=1,bias=True),  # 卷积
            nn.BatchNorm2d(192),  # 批归一化
            nn.ReLU(inplace=True),  # 激活

            nn.Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=1,bias=True),  # 卷积
            nn.BatchNorm2d(192),  # 批归一化
            nn.ReLU(inplace=True),  # 激活

            nn.Conv2d(192, 128, kernel_size=(3, 3), stride=(1, 1), padding=1,bias=True),  # 卷积
            nn.BatchNorm2d(128),  # 批归一化
            nn.ReLU(inplace=True),  # 激活
            nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)  # 池化

        )
        #定义分类器(全连接层)
        self.fc = nn.Sequential(
            nn.Dropout(0.2),  # Dropout以一定比例抑制神经元的活跃度
            nn.Linear(4608,2048),#输入,输出
            nn.Sigmoid(),

            nn.Dropout(0.2),  # Dropout以一定比例抑制神经元的活跃度
            nn.Linear(2048,2048),
            nn.Sigmoid(),

            nn.Linear(2048,numclass)

        )
        #调用参数初始化方法
        self.init_weight()

        """参数初始化
        卷积层参数,全连接层参数,
        """
    def init_weight(self):
        for m in self.modules():
            if isinstance(m,nn.Conv2d):#卷积层初始化
                nn.init.kaiming_normal_(m.weight)#kaiming初始化权重
                if m.bias is not False:
                    nn.init.constant_(m.bias,1)#常数初始化偏置项
            elif isinstance(m,nn.Linear):#全连接层初始化
                nn.init.xavier_normal_(m.weight)#正态分布初始化
                nn.init.constant_(m.bias, 1)
    """前向传播"""
    def forward(self,x):
        x = self.featrues(x)
        #卷积完之后,进入全连接层之前要进行拉平操作
        x = torch.flatten(x,1)#第一个参数是拉平对象,第二个参数是开始拉平的维度
        out = self.fc(x)
        return out
        #输出的就是一个1*numclass的
"""
到这步我们先测试一下上边的模型能不能跑通,有任何一层的参数定义错误都不能跑通
"""
# if __name__ == "__main__":
#     #制造一个数据(B,C,H,W)思维:批次,图像通道数,高,宽
#     image = torch.ones((3,3,224,224))
#     #实例化一个网络,返回:批次*numcalss维度
#     net = Axlenet(numclass=10)
#     #传入数据
#     net(image)
#     print(net(image))
#     summary(net,(3,224,224),3,device='cpu')#参数(网络,输入,批次,cpu还是GPU)传入网络,查看参数

2、训练模型

2.1、数据的获取

训练数据使用同一个开源的数据集cifar10数据集
使用datasize下载数据源

CIFAR-10是由Hinton的学生 Alex Krizhevsky和llya Sutskever整理的一个用于识别普适物体的小型数据集。一共包含10个类别的RGB彩色图片:飞机 ( alane ) 、汽车 ( automobile ) 、鸟类( bird ) 、猫( cat )、鹿( deer ) 、狗 ( dog )、蛙类(frog)、马( horse )、船( ship )和卡车(truck )。图片的尺寸为32×32,数据集中一共有50000张训练圄片和10000张测试图片。CIFAR-10的图片样例如图所示。

pytorch框架搭建AlextNet网络实例——实现图像的分类_第2张图片
这个数据集中的图片数据是3232的,所以要做数据的预处理,把图片处理成224224的结构,才能把数据放到网络中去。


"""
1、我们先造一批假数据,用来训练模型,把模型跑通保证数据可以进入模型
    用快速的方法验证程序
2、下载数据
3、调整成模型适合的数据格式
4、加入模型训练模型
    1、训练
    2、测试
"""
"""
在训练的时候我们更新梯度的时候会用到优化器,这里的优化器我们用SGD+动量的形式
普通的SGD方式;
theat = theta - lr*grad
SGD+动量:在grad上加上一个记忆参数,用来传递上一次grad的信息
初始化:
v0
monentum = 0.9 一般都设置成0.9
theta0
第一次迭代:
v1 = v0+monentum*grade
theta1 = theta0 - lr*v1
第二次迭代:
v2 = v2+monentum*grade
theta2 = theta1 - lr*v2
是这样迭代更新的
"""
"""
设定随机数种子,用于初始化操作,如果不设定随机数种子那么每次初始化的结果都不一样,训练模型就导致每次训练的结果都不同
"""
#导包
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
import torch
import torch.nn as nn
from Alexnet import Axlenet#导入我们之前构造好的模型
torch.manual_seed(4)#设定随机数种子
#torch.cuda.manual_seed_all(4)#为GPU设置随机数种子
from datetime import datetime
from torchvision import datasets,transforms#下载、转换数据
from torch.utils.data import DataLoader#加载数据到DataLoader中
from matplotlib import pyplot as plt#画图
import torch.optim as opitm#优化器包
import pickle#保存pickle文件

if __name__ == "__main__":

    # #构造一批假的训练数据(这个数据的格式要和模型要求的格式一样),用torch.rand(),4维(B,C,H,W)
    # train_image = torch.rand((4,3,224,224))#训练数据
    # train_y = torch.LongTensor([1,0,2,1])#训练数据的标签值,4个批次,则有4个标签值
    # test_image = torch.rand((4,3,224,224))#测试数据
    # test_y = torch.LongTensor([1,0,2,1])#测试数据的标签值

    """1、下载+转换+加载 数据"""
    """1.1定义一个通道,下载的时候用这个模块对图像进行变形转换,转换成适合模型的Tensor类型"""
    transform = transforms.Compose([
        #transforms.RandomResizedCrop(244),#裁剪
        #transforms.RandomHorizontalFlip(),#翻转,这两个都是在做数据增强
        transforms.Resize(224),#图像大小转换
        transforms.ToTensor()#转换成Tensor类型
        #transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))#对数据做归一化
    ])
    """1.2下载数据"""
    #datasets.ImageFolder()#如果用我们自己的文件夹的数据就用这个,这里我们用Cifar10的数据
    train_data = datasets.CIFAR10('E:\深度学习\AlexNet\imgdata', train=True, transform=transform, target_transform=None,
                                  download=True)  # 添加下载的路径,只要训练数据
    test_data = datasets.CIFAR10('E:\深度学习\AlexNet\imgdata', train=False, transform=transform, target_transform=None,
                                 download=True)
    """1.3、加载下载好的数据到DataLoader中,
        可以打乱数据,可以分批次加载,多线程加载
        参数:批次:batch_size,是否打乱shuffle,是否丢弃最后不够一个批次的数据drop_last
    """
    train_dataloader = DataLoader(train_data,batch_size=128,shuffle=True,drop_last=True)
    test_dataloader = DataLoader(test_data,batch_size=100,shuffle=True,drop_last=False)
    """1.4保存索引和类别对应的字典映射关系"""
    idx_to_class = {value:key for key,value in train_data.class_to_idx.items()}
    with open(r'image_label.pkl','wb') as f:
        pickle.dump(idx_to_class,f)
    """2、模型实例化"""
    net = Axlenet(numclass=10)
    """
    3、有了数据,首先要判断是在CPU还是在GPU上进行训练
    如果有GPU的话),数据就会加载到GPU中,
    """
    device = 'cuda' if torch.cuda.is_available() else 'cpu'#判断是否有GPU
    net = net.to(device)#加载模型到GPU或CPU中
    print(f"模型在{device}中训练......")

    """4、定义损失函数"""
    loss_function = nn.CrossEntropyLoss()#用交叉熵
    """5、定义优化器
        必要参数:模型参数,学习率lr,动量
    """
    lr = 0.001#定义学习率
    momentum = 0.9#定义动量
    optimizer = opitm.SGD(net.parameters(),lr=lr, momentum=momentum)

    """6、定义训练的轮次
        一轮就是把训练数据(一个批次)都训练一遍
    """
    max_epoch = 10
    """8、定义两个个记录损失的列表,用于记录每轮次训练、测试的损失、测试准确率"""
    train_loss = []
    test_loss = []
    test_acc = []
    # 定义变量,用于选择最好模型
    best_acc = 0.000
    """7、训练数据
        这里我们采用训练一轮测试一次的策略
    """
    for epoch in range(max_epoch):
        train(train_dataloader,device,net,loss_function,optimizer)
        test(test_dataloader,device,net)
    """
    8、画损失折线图
    """
    show_loss(train_loss)
    show_loss(test_loss)

2.2、训练模型方法

获取到数据以后,将数据放到GPU中进行训练,分批次迭代更新参数
同时训练的时候要评估模型效果,这里我们用准确率进行评估。

注意:训练的时候要加:net.train()目的是使dropout生效。

def train(train_dataloader,device,net,loss_function,optimizer):
    net.train()#训练的时候要加上这个,目的是使得dropout,BN等生效,同时测试的时候要加net.eval()作用是使其不生效
    train_epoch_loss = 0.000#定义变量,存储每轮次次的损失和
    iter_cont = len(train_dataloader)#求一轮有多少批次(一轮迭代多少次)
    #1、把数据放到GPU或者CPU中
    #1.1先把数据for循环分成输入和类别目标
    for index,(train_x,train_y) in enumerate(train_dataloader):
        if index == 100:
            break
        train_x = train_x.to(device)
        train_y = train_y.to(device)
        #2、数据放到模型中进行前向传播,得到预测结果
        y_predict = net(train_x)
        #3、计算损失
        loss = loss_function(y_predict,train_y)
        #4、优化器梯度清零
        optimizer.zero_grad()
        #5、拿到损失进行反向传播,得到梯度
        loss.backward()
        #6、更新梯度
        optimizer.step()
        #7、记录损失
        print(f'训练轮次:{epoch+1}\t批次:{index+1}\t训练误差:{loss.item()}')
        train_epoch_loss += loss.item()
    train_loss.append(train_epoch_loss/iter_cont)#一轮的平均误差=误差和/一轮迭代次数
   

2.3、测试模型方法

测试的时候,只有正向评估,用训练好的模型对册数数据进行正向传播即可。

注意:训练的时候要加net.eval(),目的是使dropout失效。

"""
定义测试模型的方法
"""
def test(test_dataloader,device,net):
    global best_acc
    net.eval()
    num = 0#定义预测正确的数量和
    test_epoch_loss = 0.000
    # 1、把数据放到GPU或者CPU中
    for index,(test_x,test_y) in enumerate(test_dataloader):
        # if index == 100:
        #     break
        test_x = test_x.to(device)
        test_y = test_y.to(device)
        # 2、数据放到模型中进行前向传播,得到预测结果
        y_predict = net(test_x)
        # 3、返回概率再大的值的索引
        y_predict_index = torch.argmax(y_predict,1)#torch.argmax(目标,维度)
        # 4、记录测试误差
        test_loss_ = loss_function(y_predict,test_y)
        test_epoch_loss += test_loss_.item()
        # 5计算准确率
        # 利用列表推导式求相同位置元素相同的个数再除以总个数
        # 一个批次预测正确的数量
        predict_correct_num = sum([1 for index01 in range(len(test_y)) if test_y[index01] == y_predict_index[index01]])
        num += predict_correct_num
    acc = num/10000#可以再函数外用len(test_data)求出
    test_acc.append(acc)
    test_loss.append(test_epoch_loss/len(test_dataloader))#测试误差
    print("准确率:",acc)
    """保存模型"""
    if test_acc[-1] > best_acc:
        best_acc = test_acc[-1]
        torch.save(net.state_dict(),f'best_model.pth')

2.4、保存模型方法

保存模型的方法加到验证模型的方法里,因为我们肯定要验证数据集最好的模型进行保存

 if test_acc[-1] > best_acc:
        best_acc = test_acc[-1]
        torch.save(net.state_dict(),f'best_model.pth')
'''

2.2、画图方法

def show_loss(train_loss):
    plt.plot(range(len(train_loss)),train_loss)
    plt.show()

3、调取保存好的模型进行验证


#导包
import torch
import torch.nn as nn
from Alexnet import Axlenet#导入我们之前构造好的模型
from torchvision import datasets,transforms#下载、转换数据
from torch.utils.data import DataLoader#加载数据到DataLoader中
from PIL import Image
import pickle


"""1、读取-类别文件"""
with open('image_label.pkl','rb') as f:
    idx_to_class = pickle.load(f)

test_image = r'1.jpg'#图片路径
"""2、读取图片"""
image = Image.open(test_image)

"""3、图片格式处理成符合模型的格式"""
transform = transforms.Compose([
        transforms.Resize((224,224)),#图像大小转换
        transforms.ToTensor()#转换成Tensor类型
    ])
image = transform(image)
"""4、一张图片要进行扩围,因为没有批次这一维度"""
image = torch.unsqueeze(image,0)#扩充维度
"""5、加载模型+参数"""
net = Axlenet(10)#实例化模型
#torch.load('best_model.pth')#
net.load_state_dict(torch.load('best_model.pth'))#加载模型参数
device = 'cuda' if torch.cuda.is_available() else 'cpu'
"""6、图片放入模型"""
result = net(image)#图像加入模型
"""7、根据预测值对应输出类别"""
result_index = torch.argmax(result,1)
predict = idx_to_class[result_index.item()]#变标量
print(predict)

验证结果:
pytorch框架搭建AlextNet网络实例——实现图像的分类_第3张图片

你可能感兴趣的:(深度学习,pytorch,分类,卷积神经网络,神经网络)