花朵图像分类

1.项目目录介绍

1.flower_photos:通过BML标注的5类花朵数据集。2.train:训练集。3.val:测试集。4.1_create_dataset:划分训练集的代码。5.2_split_dataset:划分测试集的代码。5.AlexNet.pth:花朵识别模型。6.class_indices.json:数据集各类花朵对应的标识集。7.k.jpg和tulip.jpg:指的是用来验证测试项目测试成果。8.model.py:构建网络使用AlexNet 竞赛的冠军网络,分类准确率由传统的70%+提升到80%+。9.predict.py:指的是使用模型识别照片。10.train.py:训练模型并保存模型。

2.项目实现具体操作

1.图像数据标注:数据收集:收集适合标注的花朵图像数据集。从公开数据集采集图像。

标注需求定义:确定需要标注的对象或信息类型,使用图像分类

标注工具选择:根据标注需求选择适合的标注工具,使用BML

标注标准制定:制定清晰的标注标准和规范,确保标注结果的一致性和可靠性。定义物体的类别。

标注对象:使用相应的标注工具,在图像上进行目标标注

添加标签:为标注的对象或区域添加相应的标签信息,如类别名称、类别ID等。

质量控制:对标注结果进行质量检查和验证,确保标注准确性和一致性。可以进行内部评审

数据导出:将标注结果导出为特定的数据格式,格式JSON,以便后续的机器学习训练或数据分析。

2.数据收集:将数据集划分为训练集和测试集,解析json文件并将jpg图片按不同类别划分到不同的文件夹中, 收集代表各个类别的图像数据。确保每个类别都有足够数量的图片,并且图像质量良好。

将图像数据分为训练集和测试集。通常,将大部分数据用于训练,一小部分用于测试模型的性能。

2.1代码是解析json文件获取nng图片的分类类别保存到label,根据label的不同类别创建不同的文件夹,划分jpg图片到不同类别的文件夹。

2.2按8:2的比例划分训练集train和验证集val,这些文件名将用于后续将对应的图片文件从训练集移动到验证集

3.构建网络:使用AlexNet 是2012年 ISLVRC ( ImageNet Large Scale Visual Recognition Challenge)竞赛的冠军网络,分类准确率由传统的70%+提升到80%+。

3.1用nn.Sequential()将网络打包成一个模块,精简代码,卷积层提取图像特征直接修改覆盖原值,节省运算内存全连接层对图像分类,Dropout 随机失活神经元,默认比例为0.5。

3.2前向传播过程, 展平后再传入全连接层, 网络权重初始化,实际上 pytorch 在构建网络时会自动初始化权重。

4.数据预处理:花分类数据集使用datasets.ImageFolder()来导入。ImageFolder()返回的对象是一个包含数据集所有图像及对应标签构成的二维元组容器,支持索引和迭代,可作为torch.utils.data.DataLoader的输入

4.1导入训练集并进行预处理,按batch_size分批次加载训练集

4.2导入验证集并进行预处理方便在 predict 时读取信息,将 索引:标签 存入到一个 json 文件中

5. 训练模型:使用训练集对模型进行训练。在训练过程中,将特征和对应的类别标签作为输入,使模型学习图像特征与类别之间的关联关系。根据模型的训练误差和验证集的性能,进行模型调参和优化。

5.1实例化网络(输出类型为5,初始化权重),分配网络到指定的设备(GPU/CPU)训练,优化器(训练参数,学习率)

5.2保存准确率最高的那次网络参数

6. 模型评估与测试:用测试集对已训练好的模型进行评估。计算模型在测试集上的准确率、精确率、召回率等指标,以衡量其性能和泛化能力。通过分析模型在不同类别上的分类结果和错误情况,了解模型的强项和弱项,并可能调整数据收集或模型设计的策略。

6.1导入测试图片、模型,将输出压缩,即压缩掉 batch 这个维度

3.项目实现具体代码

1.1_create_dataset.py

import os
import json
from PIL import Image


# 获取.\label_image文件夹下所有以.json结尾的文件的完整文件路径,并存储在file_paths列表中
# 在Windows下,使用原始字符串(raw string)前缀r可以确保路径中的反斜杠\被正确解析
folder_path = r'.\flower_data\flower_photos'
file_names = os.listdir(folder_path)
file_paths = [os.path.join(folder_path, file_name) for file_name in file_names if file_name.endswith('.json')]

# 解析json文件并将jpg图片按不同类别划分到不同的文件夹中
data_path = r'.\flower_data\train'

for i in range(len(file_paths)):
    with open(file_paths[i], 'r') as file:
        # 解析json文件获取nng图片的分类类别保存到label
        json_content = json.load(file)
        label = json_content.get('labels')[0].get('name')
        # 根据label的不同类别创建不同的文件夹
        train_path = os.path.join(data_path, label)
        if not os.path.exists(train_path):
            os.makedirs(train_path)
        # 划分jpg图片到不同类别的文件夹
        img_name = os.path.basename(file_paths[i])[:-5]+".jpg"
        img_path = os.path.join(folder_path, img_name)
        pickImage = Image.open(img_path)
        save_path = os.path.join(train_path, img_name)
        pickImage.save(save_path)

2.2_split_dataset.py

import os
import random
import shutil

# 划分训练集和验证集
def move_picture(train_path, val_path):
    train_path_list = os.listdir(train_path)
    num_pic = len(train_path_list)
    # 按8:2的比例划分训练集train和验证集val
    rate = 0.5
    move_number = int(num_pic * rate)
    # 在训练集文件名列表中随机选取了move_number个文件名
    # 这些文件名将用于后续将对应的图片文件从训练集移动到验证集
    samples = random.sample(train_path_list, move_number)
    for sample in samples:
        shutil.move(train_path + r'\\' + sample, val_path + '\\' + sample)


if __name__ == '__main__':
    
    output_train_dir = r".\flower_data\train"
    output_val_dir = r".\flower_data\val"

    # 遍历所有不同类别的文件夹划分训练集和验证集
    file_names = os.listdir(output_train_dir)
    for i in file_names:
        train_path = os.path.join(output_train_dir, i)
        val_path = os.path.join(output_val_dir, i)
        if not os.path.exists(val_path):
            os.makedirs(val_path)
        move_picture(train_path, val_path)

3.model.py

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

4.predict.py

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))])
    img_path = "tulip.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    img = data_transform(img)
    img = torch.unsqueeze(img, dim=0)
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)

    model = AlexNet(num_classes=5).to(device)

    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()
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        predict = torch.softmax(output, dim=0)
        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)
    for i in range(len(predict)):
        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],
                                                  predict[i].numpy()))
    plt.show()


if __name__ == '__main__':
    main()

5.train.py

import os
import sys
import json
import torch
import torch.nn as nn
from torchvision import transforms, datasets
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")
    print("using {} device.".format(device))
    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": 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))])}
    image_path="E:\\桌面\\总文件夹\\新建文件夹\\小学期代码\\xiao\\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_num = len(train_dataset)

    # {'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())
    # write dict into json file
    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
    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=nw)

    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=False,
                                                  num_workers=nw)

    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))

    net = AlexNet(num_classes=5, init_weights=True)

    net.to(device)
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(net.parameters(), lr=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()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        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))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)

        # validate
        net.eval()
        acc = 0.0  # accumulate accurate number / epoch
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            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()

  • 39
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值