AlexNet实现图像分类-基于UCM数据集的遥感数据图像分类

数据集下载:UC Merced Land Use Dataset

准备好数据集后,我们开始学习吧~

1.model.py——定义AlexNet网络模型。
2.train.py——加载数据集并训练,训练集计算损失值loss,测试集计算accuracy,保存训练好的网络参数。
3.predict.py——利用训练好的网络参数后,用自己找的图像进行分类测试。

还需要创建一个文件名为AlexNet.pth用来保存模型。

第一步:划分训练集与测试集 ---------------------------------------------------split.py

划分比例,训练集 : 验证集 = 8 : 2

创建train文件夹和val文件夹

#-*- coding : utf-8-*-
# coding:unicode_escape
import os
from shutil import copy
import random
 
def mkfile(file):
    if not os.path.exists(file):
        os.makedirs(file)
        
# 获取 UCMerced_LandUse 文件夹下除 .txt 文件以外所有文件夹名(即21种类别的类名)
file_path = '/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/Images'
classes = [cla for cla in os.listdir(file_path) if ".txt" not in cla] 
 
# 创建 训练集train 文件夹,并由21种类名在其目录下创建21个子目录
mkfile('/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/train2')
for cla in classes:
    mkfile('/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/train2/'+cla)
    
# 创建 验证集val 文件夹,并由21种类名在其目录下创建21个子目录
mkfile('/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/val2')
for cla in classes:
    mkfile('/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/val2/'+cla)
 
# 划分比例,训练集 : 验证集 = 8 : 2
split_rate = 0.2
 
# 遍历21种类别的全部图像并按比例分成训练集和验证集
for cla in classes:
    cla_path = file_path + '/' + cla + '/'  # 某一类别的子目录
    images = os.listdir(cla_path)		    # iamges 列表存储了该目录下所有图像的名称
    num = len(images)
    eval_index = random.sample(images, k=int(num*split_rate)) # 从images列表中随机抽取 k 个图像名称
    for index, image in enumerate(images):
    	# eval_index 中保存验证集val的图像名称
        if image in eval_index:					
            image_path = cla_path + image
            new_path = '/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/val2/' + cla
            copy(image_path, new_path)  # 将选中的图像复制到新路径
           
        # 其余的图像保存在训练集train中
        else:
            image_path = cla_path + image
            new_path = '/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification/UCMerced_LandUse/train2/' + cla
            copy(image_path, new_path)
        print("r[{}] processing [{}/{}]".format(cla, index+1, num), end="")  # processing bar
    print()
 
print("processing done!")

第二步:定义AlexNet网络模型-------------------------------------------------model.py

#-*- coding : utf-8-*-
# coding:unicode_escape
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


2.train.py——加载数据集并训练,训练集计算损失值loss,测试集计算accuracy,保存训练好的网络参数。

在76行修改为21,因为有21类;

 net = AlexNet(num_classes=21, init_weights=True)   #num_classes=21有21种类别,初始化权重
bach_size=32;
学习率 0.0001
epoch 10 迭代次数,可以修改大些

注意修改你自己的训练集测试集路径

#-*- coding : utf-8-*-
# coding:unicode_escape
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(), "/home/data1/ffeng_data/3.ycx/RS-SC/Image-classification"))  
    #返回到上一级目录的上一级目录,获取数据的根目录
    image_path = os.path.join(data_root, "UCMerced_LandUse")  
    #再进入到data_set下的UCMerced_LandUse文件夹下
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)#查看是否找到该文件
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train2"),
                                         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格式
    json_str = json.dumps(cla_dict, indent=4)
    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, "val2"),
                                            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=21, init_weights=True)#num_classes=21有21种类别,初始化权重

    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("\train 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() 

时间很长,耐心等待~~~


3.predict.py——利用训练好的网络参数后,用自己找的图像进行分类测试。

注意修改你的路径 

#-*- coding : utf-8-*-
# coding:unicode_escape
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 = "1.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=21).to(device)#初始化网络,类别为21

    # 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()

预测结果:

有问题大家评论区留言哦~

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
原始的ucm-0822-10.bag数据集是一个采集自传感器的ROS bag文件。ROS(机器人操作系统)是一个灵活的、开源的机器人开发平台,通过使用ROS bag文件,可以记录和回放传感器数据。 要解析这个数据集,首先需要安装ROS,并使用ROS提供的工具包进行处理。下面是一个解析raw-ucm-0822-10.bag数据集的步骤: 1. 安装ROS:根据自己的操作系统版本,下载并安装ROS。可以选择ROS Kinetic、ROS Melodic等稳定版本。 2. 创建ROS工作空间:在终端中运行以下命令创建和设置ROS工作空间: ``` mkdir -p ~/catkin_ws/src cd ~/catkin_ws/ catkin_make ``` 3. 将raw-ucm-0822-10.bag文件复制到ROS的数据目录下: ``` cp <path_to_raw-ucm-0822-10.bag> ~/catkin_ws/src ``` 4. 运行ROS节点:在终端中运行以下命令,启动ROS节点,并解析raw-ucm-0822-10.bag数据集: ``` roscore ``` 打开新的终端窗口,运行以下命令,解析数据集: ``` rosbag play ~/catkin_ws/src/raw-ucm-0822-10.bag ``` ROS节点将开始解析数据集,并将传感器数据发布到相应的主题(topics)上。 5. 监听话题(topics):可以使用`rostopic list`命令查看当前发布的主题,然后使用`rostopic echo <topic_name>`命令监听特定主题的数据。 ``` rostopic list ``` ``` rostopic echo <topic_name> ``` 通过监听主题,可以获取到传感器数据的详细信息,如激光雷达数据、相机图像等。 通过以上步骤,我们可以解析raw-ucm-0822-10.bag数据集,并获取传感器数据的相关信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值