pytorch搭建AlexNet并训练花分类数据集

一.学习资料

B站上的神仙Up主,适合机器视觉的初学者,可以避免少走很多的弯路。
1.AlexNet网络详解以及花分类数据集的下载
2.使用pytorch搭建AlexNet并训练花分类数据集

二.数据集处理

1.数据集下载

数据集下载链接
包含 5 中类型的花,每种类型有600~900张图像不等。
在这里插入图片描述

2.划分训练集和测试集

参考:添加链接描述
shift + 右键 打开 PowerShell ,执行 “split_data.py” 分类脚本自动将数据集划分成 训练集train 和 验证集val。

三.模型构建(model.py)

代码中需要注意的是:

1)pytorch 中 Tensor 参数的顺序为 (batch, channel, height, width) ,下面代码中没有写batch
2)卷积的参数为Conv2d(in_channels, out_channels, kernel_size, stride, padding, …),一般关心这5个参数即可
3)卷积池化层提取图像特征,全连接层进行图像分类,代码中写成两个模块,方便调用
4)为了加快训练,代码只使用了一半的网络参数,相当于只用了原论文中网络结构的下半部分,后来视频作者又用完整网络跑了遍,发现一半参数跟完整参数的训练结果acc相差无几。

import torch.nn as nn
import torch


class AlexNet(nn.Module):
    #类ALEXNET继承nn.module这个父类
    def __init__(self, num_classes=1000, init_weights=False):
        #通过初始化函数,定义网络在正向传播过程中需要使用的层结构
        #num_classes是指输出的图片种类个数,init_weights=False意味着不定义模型中的初始权重
        super(AlexNet, self).__init__()
        #nn.Sequential模块,可以将一系列的层结构进行打包,组合成一个新的结构,
        # 将专门用于提取图像特征的结构的名称取为features
        self.features = nn.Sequential(
            nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),
            # input[3, 224, 224]  output[48, 55, 55]
            #第一个卷积层,彩色图片深度为3,卷积核个数位48,卷积核大小11,步长4,padding2
            nn.ReLU(inplace=True),
            #使用Relu激活函数时要将设置inplace=True
            #使用relu激活函数,f(x)=max(0,x),
            #相比sigmod函数与tanh函数有以下几个优点
            # 1)克服梯度消失的问题
            # 2)加快训练速度
            # 注:正因为克服了梯度消失问题,训练才会快
            # 缺点:
            # 1)输入负数,则完全不激活,ReLU函数死掉。
            # 2)ReLU函数输出要么是0,要么是正数,也就是ReLU函数不是以0为中心的函数

            nn.MaxPool2d(kernel_size=3, stride=2),                  # output[48, 27, 27]
            nn.Conv2d(48, 128, kernel_size=5, padding=2),#步长默认为1,当步长为1时不用设置# 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),#随机将一半的节点失活,默认为0.5
            nn.Linear(128 * 6 * 6, 2048),#将特征矩阵展平,128*6*6最后输出的长*宽*高,2048为全连接层节点个数
            nn.ReLU(inplace=True),#Relu激活函数
            nn.Dropout(p=0.5),
            nn.Linear(2048, 2048),#全连接层2的输入为全连接层1的输出2048,全连接层2的节点个数2048
            nn.ReLU(inplace=True),
            nn.Linear(2048, num_classes),#全连接层3的输入为全连接层2的输出2048
            #全连接层最后的输出就是图片类别的个数
        )
        if init_weights:
            self._initialize_weights()
        #是否初始化权重,如果初始化函数中的init_weights=Ture,就会进入到初始化权重的函数


    def forward(self, x):
        #forward正向传播过程,x为输入的变量
        x = self.features(x)
        #将训练样本输入features
        x = torch.flatten(x, start_dim=1)
        #将输入的变量进行展平从深度高度宽度三个维度进行展开,索引从1开始,展成一个一维向量
        x = self.classifier(x)
        #将展平后的数据输入到分类器中(三个全连接层组成的)
        return x#最后的输出为图片类别

    #初始化权重的函数
    def _initialize_weights(self):
        for m in self.modules():#遍历self.modules这样一个模块,该模块继承自它的父类nn.module,该模块会迭代每一个层次
            if isinstance(m, nn.Conv2d):#如果该层次是一个卷积层,就会使用kaiming_normal_这样一个方法初始化权重
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)#如果偏值不为空的话,就用0对权重进行初始化
            elif isinstance(m, nn.Linear):#如果该层次是一个全连接层,就用normal进行初始化
                nn.init.normal_(m.weight, 0, 0.01)#正态分布对权重进行赋值,均值为0,方差为0.01
                nn.init.constant_(m.bias, 0)#设置全连接层的偏值为0
           

四.模型训练(train.py)

import os
import json
import torch
import torch.nn as nn
from torchvision import transforms, datasets, utils
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim
from tqdm import tqdm
from model import AlexNet


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    #r如果当前有可使用的gpu,默认使用第一块gpu设备。如果没有gpu就使用cpu设备
    print("using {} device.".format(device))

    data_transform = {
    	#该方法为数据预处理方法
        #当关键字为train时,返回训练集的数据与处理方法
        "train": transforms.Compose([transforms.RandomResizedCrop(224),#将图片用随机裁剪方法裁剪成224*224
                                     transforms.RandomHorizontalFlip(),#在水平方向随机翻转
                                     transforms.ToTensor(),#将它转化成tnesor
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
                                     #将数据进行标准化处理

        #当关键字为val时,返回训练集的数据与处理方法
        "val": transforms.Compose([transforms.Resize((224, 224)),#将图片转化成224*224大小
                                   transforms.ToTensor(),#将数据转化成tensor
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
                                   #将数据进行标准化处理
                                   }

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  
    #返回到上一级目录的上一级目录,获取数据的根目录
    image_path = os.path.join(data_root, "data_set", "flower_data")  
    #再进入到data_set下的flower_data文件夹下
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)#查看是否找到该文件
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
                                 


	#传入train,使用训练集的数据处理方法处理数据
    train_num = len(train_dataset)#将训练集中的图片个数赋值给train_num

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx#获取分类名称所对应的索引
    cla_dict= dict((val, key) for key, val in flower_list.items()
    #遍历所获取的分类以及索引的字典,并且将key,values交换位置
    
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4) #将字典编码成json格式
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32#定义batch_size=32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=0)
    #train_loader函数是为了随机在数据集中获取一批批数据,num_workers=0加载数据的线程个数,在windows系统下该数为                 0,意思为在windows系统下使用一个主线程加载数据

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=4, shuffle=True,
                                                  num_workers=0)

    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))
	 net = AlexNet(num_classes=5, init_weights=True)#num_classes=5花有5种类别,初始化权重

    net.to(device)#将该网络分配到制定的设备上(gpu或者cpu)
    loss_function = nn.CrossEntropyLoss()#定义损失函数,针对多类别的损失交叉熵函数
    # pata = list(net.parameters())
    optimizer = optim.Adam(net.parameters(), lr=0.0002)
    #定义一个Adam优化器,优化对象是所有可训练的参数,定义学习率为0.0002,通过调试获得的最佳学习率

    epochs = 10
    save_path = './AlexNet.pth'#保存准确率最高的那次模型的路径
    best_acc = 0.0#最佳准确率
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()#使用net.train()方法,该方法中有dropout
        running_loss = 0.0#使用running_loss方法统计训练过程中的平均损失
        train_bar = tqdm(train_loader)

        for step, data in enumerate(train_bar):#遍历数据集
            images, labels = data#将数据分为图像标签
            optimizer.zero_grad()#清空之前的梯度信息
            outputs = net(images.to(device))#通过正向传播的到输出
            loss = loss_function(outputs, labels.to(device))#指定设备gpu或者cpu,通过Loss_function函数计算预测值与真实值之间的差距
            loss.backward()#将损失反向传播到每一个节点
            optimizer.step()#通过optimizer更新每一个参数

            # print statistics
            running_loss += loss.item()#累加损失
            #print train process
            rate = (step+1)/len(train_loader)
            a = "*"* int(rate*50)
            b = "."* int((1-rate)*50)
            print("\rtrain loss:{:^3.0f}%[{}->{}]{:.3f}".format(int(rate*100),a,b,loss),end="")
        print()


        # validate
        net.eval()#预测过程中使用net.eval()函数,该函数会关闭掉dropout
        acc = 0.0  # accumulate accurate number / epoch
        with torch.no_grad():#使用该函数,禁止pytorch对参数进行跟踪,即训练过程中不会计算损失梯度
            val_bar = tqdm(validate_loader)
            for val_data in val_bar:#遍历验证集
                val_images, val_labels = val_data#将数据划分为图片和标签
                outputs = net(val_images.to(device))
                predict_y = torch.max(outputs, dim=1)[1]#求的预测过程中最有可能的标签
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()#准确的个数累加

        val_accurate = acc / val_num#测试集准确率
        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
              (epoch + 1, running_loss / train_steps, val_accurate))

        # 如果当前准确率大于历史最优准确率,就将当前的准确率赋给最优准确率,并将参数进行保存
        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)

    print('Finished Training')


if __name__ == '__main__':
    main()

    

1.训练流程

  1. 选择设备(GPU或者CPU)
  2. 定义处理数据的方法,训练集和测试集的数据处理方式不同,对训练集的预处理,多了随机裁剪和水平翻转这两个步骤。可以起到扩充数据集的作用,增强模型泛化能力。
  3. 读取图片数据
  4. 处理数据
  5. 将图片的索引字典存储成json文件,并且将索引和种类调换位置,以便输出时清晰明了
  6. 实例化类Alexnet
  7. 分配设备
  8. 数据处理
  9. 模型训练
  10. 将准确度最高的模型参数保存下来

2.训练结果

在这里插入图片描述

五.图片分类(predict.py)

在网上下载了一副向日葵的图片,使用Alexnet网络查看是否可以将图片种类正确识别。

import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import AlexNet


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(#图片预处理
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # load image
    img_path = "../db4d65709c08a7f5.jpg"#在python库中载入图片
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)#判断图片是否存在
    img = Image.open(img_path)

    plt.imshow(img)#展示图片
    # [N, C, H, W]
    img = data_transform(img)#对图片进行预处理操作
    # expand batch dimension  #batch是指一次处理图片的数量,批
    img = torch.unsqueeze(img, dim=0)#处理后变成[batch, C, H, W]

    # read class_indict
    json_path = './class_indices.json'#读取索引对应的类别名称
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)#将json文件解码成字典模式

    json_file = open(json_path, "r")
    class_indict = json.load(json_file)

    # create model
    model = AlexNet(num_classes=5).to(device)#初始化网络,类别为5

    # load model weights
    weights_path = "./AlexNet.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path))#载入网络模型

    model.eval()#进入eval模式,即关闭dropout方法
    with torch.no_grad():#让变量不去跟踪模型的损失梯度
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()#通过正向传播得到输出,并将输出进行压缩,将batch维度压缩
        predict = torch.softmax(output, dim=0)#通过softmax处理之后变成概率分布
        predict_cla = torch.argmax(predict).numpy()#获取概率最大处对应的索引值

    print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
                                                 predict[predict_cla].numpy())#打印类别名称以及预测正确的概率
    plt.title(print_res)
    print(print_res)
    plt.show()


if __name__ == '__main__':
    main()

分类结果
在这里插入图片描述

https://blog.csdn.net/m0_37867091/article/details/107150142

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用PyTorch搭建AlexNet实现图像分类的示例代码,其中使用了CIFAR-10数据集。 ``` import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F import torchvision import torchvision.transforms as transforms # 定义AlexNet模型 class AlexNet(nn.Module): def __init__(self): super(AlexNet, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2) self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2) self.conv2 = nn.Conv2d(64, 192, kernel_size=5, padding=2) self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2) self.conv3 = nn.Conv2d(192, 384, kernel_size=3, padding=1) self.conv4 = nn.Conv2d(384, 256, kernel_size=3, padding=1) self.conv5 = nn.Conv2d(256, 256, kernel_size=3, padding=1) self.pool5 = nn.MaxPool2d(kernel_size=3, stride=2) self.fc1 = nn.Linear(256 * 6 * 6, 4096) self.dropout1 = nn.Dropout() self.fc2 = nn.Linear(4096, 4096) self.dropout2 = nn.Dropout() self.fc3 = nn.Linear(4096, 10) def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool1(x) x = F.relu(self.conv2(x)) x = self.pool2(x) x = F.relu(self.conv3(x)) x = F.relu(self.conv4(x)) x = F.relu(self.conv5(x)) x = self.pool5(x) x = x.view(-1, 256 * 6 * 6) x = F.relu(self.fc1(x)) x = self.dropout1(x) x = F.relu(self.fc2(x)) x = self.dropout2(x) x = self.fc3(x) return x # 加载CIFAR-10数据集 transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2) # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4) # 训练模型 net = AlexNet() device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") net.to(device) for epoch in range(10): running_loss = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 100 == 99: print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 100)) running_loss = 0.0 print('Finished Training') # 测试模型 correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data images, labels = images.to(device), labels.to(device) outputs = net(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total)) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值