1 概述
1.1 背景介绍
随着人工智能技术的不断发展,图像识别技术在众多领域得到了广泛应用。花卉识别作为图像识别的一个重要分支,具有很高的研究价值和实际应用前景。
1.2 适用对象
1.3 案例时间
本案例总时长预计 40 分钟。
1.4 案例流程
说明:
① 登录开发者空间;② 获取和划分数据集;③ 训练神经网络模型;④ 使用神经网络模型进行预测识别。
2 操作步骤
2.1 登录云主机
点击华为开发者空间进入开发者空间页面。点击“进入桌面”打开云主机。
2.2 获取数据集
获取数据集为进行图像识别和分类的机器学习任务做准备。数据集中包含了五种不同花卉的图片,分别是雏菊(daisy)、蒲公英(dandelion)、玫瑰(roses)、向日葵(sunflowers)和郁金香(tulips)。每个花卉类别都有相应的子文件夹,用其英文名字进行命名,其中存储了该类别的图片。
在云主机上,依次点击“CodeArts IDE for Python”>“新建工程”>“名称”>“位置”>“基础解释器”>“创建”。
参数设置如下:
创建完毕后首先创建一个文件夹,空白处单击鼠标右键,选择“新建”>“文件夹”(可以参考如下图片),文件夹命名为:“flower_data”。
在下方终端窗口输入命令进入“flower_data”文件。
然后输入指令获取数据集。
 sudo wget http://download.tensorflow.org/example_images/flower_photos.tgz
       复制代码
 
使用 tar 命令解压 flower_photos.tgz 文件,输入指令后会解压出如下图左侧所示文件,可以看到数据集中不同花卉的目录。
 tar -xzf flower_photos.tgz
       复制代码
 
2.3 划分训练集与验证集
我们要将原始的花卉图片数据集分割成两个部分:90%作为训练集,10%作为验证集。划分训练集和验证集的原因是为了更有效地训练和评估机器学习模型。
首先,返回上一级文件。
再创建一个 py 文件:空白处单击鼠标右键,选择“新建”>“文件”,文件命名为:“split_data.py”。将下列代码复制到文件中。
代码内容说明:
1)删除并重新创建训练集和验证集的根目录。对于每个花卉类别,程序会创建相应的子目录。
2)遍历每个类别的图片,随机选择 10%的图片作为验证集,剩余的 90%作为训练集。图片被复制到相应的训练集或验证集目录。处理过程中,程序会在控制台打印出进度条,显示当前正在处理的类别和图片数量。
 import osfrom shutil import copy, rmtreeimport random#导入需要用的包
def mk_file(file_path: str):if os.path.exists(file_path):# 如果文件存在,那么将先删除原文件夹在重新创建        rmtree(file_path)os.makedirs(file_path)  def main():
    random.seed(0)     split_rate = 0.1     cwd = os.getcwd()    data_root = os.path.join(cwd, "flower_data")    origin_flower_path = os.path.join(data_root, "flower_photos")    assert os.path.exists(origin_flower_path)    flower_class = [cla for cla in os.listdir(origin_flower_path)                    if os.path.isdir(os.path.join(origin_flower_path, cla))]       # 建立保存训练集的文件夹    train_root = os.path.join(data_root, "train")    mk_file(train_root)    for cla in flower_class:        mk_file(os.path.join(train_root, cla))    # 建立保存验证集的文件夹    val_root = os.path.join(data_root, "val")    mk_file(val_root)    for cla in flower_class:               mk_file(os.path.join(val_root, cla))     for cla in flower_class:        cla_path = os.path.join(origin_flower_path, cla)        images = os.listdir(cla_path)        num = len(images)            eval_index = random.sample(images, k=int(num*split_rate))        for index, image in enumerate(images):            if image in eval_index:                         image_path = os.path.join(cla_path, image)                new_path = os.path.join(val_root, cla)                copy(image_path, new_path)            else:                        image_path = os.path.join(cla_path, image)                new_path = os.path.join(train_root, cla)                copy(image_path, new_path)            print("\r[{}] processing [{}/{}]".format(cla, index+1, num), end="")  # processing bar        print()     print("processing done!")  if __name__ == '__main__':    main()
       复制代码
 
代码输入完毕后运行“split_data.py”文件。
运行后显示“processing done!”,表示运行成功。
处理过程进度显示效果如下:
2.4 创建神经网络模型
基于 AlexNet 架构的卷积神经网络模型,通过其多层结构,能够自动从原始图像数据中提取出有用的特征。这些特征比手工设计的特征更加抽象和复杂,能够更好地表示图像内容,从而提高识别的准确性。
创建一个 py 文件:空白处单击鼠标右键,单击“新建”>“文件”,文件命名为:“model.py”。将下列代码复制到文件中。
 import torch.nn as nnimport torch#导入需要用的包 class AlexNet(nn.Module):    def __init__(self, num_classes=1000, init_weights=False):        super(AlexNet, self).__init__()              self.features = nn.Sequential(  # 卷积层提取图像特征            nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),              nn.ReLU(inplace=True),                               nn.MaxPool2d(kernel_size=3, stride=2),                             nn.Conv2d(48, 128, kernel_size=5, padding=2),                    nn.ReLU(inplace=True),            nn.MaxPool2d(kernel_size=3, stride=2),                             nn.Conv2d(128, 192, kernel_size=3, padding=1),                      nn.ReLU(inplace=True),            nn.Conv2d(192, 192, kernel_size=3, padding=1),                    nn.ReLU(inplace=True),            nn.Conv2d(192, 128, kernel_size=3, padding=1),                     nn.ReLU(inplace=True),            nn.MaxPool2d(kernel_size=3, stride=2),                         )        self.classifier = nn.Sequential(  # 全连接层对图像分类            nn.Dropout(p=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            def _initialize_weights(self):        for m in self.modules():            if isinstance(m, nn.Conv2d):                                        nn.init.kaiming_normal_(m.weight, mode='fan_out',                                          nonlinearity='relu')                if m.bias is not None:                    nn.init.constant_(m.bias, 0)                              elif isinstance(m, nn.Linear):                            nn.init.normal_(m.weight, 0, 0.01)                   nn.init.constant_(m.bias, 0)    
       复制代码
 
下载运行所需要的包。
 pip install torchpip install numpypip install torchvisionpip install matplotlib
       复制代码
 
torch :PyTorch 的核心库,提供了多维数组(称为张量)的操作。
numpy :Python 中用于科学计算的基础库,提供了强大的多维数组对象和一系列用于处理数组的函数。
torchvision :PyTorch 的一个视觉处理库,它提供了流行的数据集、模型架构和用于计算机视觉的常见图像变换。
matplotlib :是一个 Python 的 2D 绘图库,它可以在各种平台上以各种硬拷贝格式和交互环境生成具有出版品质的图形。
下载完毕后终端输入命令,运行“model.py”文件。
2.5 训练神经网络模型
训练 model 模型来对花卉图像进行分类,通过多次迭代(epoch)的训练和验证,模型学习如何从图像中提取特征并正确分类不同种类的花卉。最终,代码保存了在验证集上表现最好的模型参数,以便后续用于预测或进一步的分析。
创建一个 py 文件:空白处单击鼠标右键,选择“新建”>“文件”,文件命名为:“train.py”。将下列代码复制到文件中。
 import torchimport torch.nn as nnfrom torchvision import transforms, datasets, utilsimport matplotlib.pyplot as pltimport numpy as npimport torch.optim as optimfrom model import AlexNetimport osimport json#导入需要用的包import time 
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")with open(os.path.join("train.log"), "a") as log:    log.write(str(device)+"\n") #数据预处理data_transform = {    "train": transforms.Compose([transforms.RandomResizedCrop(224),                                        transforms.RandomHorizontalFlip(p=0.5),                                  transforms.ToTensor(),                                 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),#裁剪、翻转     "val": transforms.Compose([transforms.Resize((224, 224)),                                transforms.ToTensor(),                               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 = "/home/developer/Desktop/data_set/flower_data/"             
train_dataset = datasets.ImageFolder(root=image_path + "train",              transform=data_transform["train"])train_num = len(train_dataset) train_loader = torch.utils.data.DataLoader(train_dataset,                                             batch_size=32,                                              shuffle=True,                                             num_workers=0)   validate_dataset = datasets.ImageFolder(root=image_path + "/val",                                        transform=data_transform["val"])val_num = len(validate_dataset) validate_loader = torch.utils.data.DataLoader(validate_dataset,                                                batch_size=32,                                               shuffle=True,                                              num_workers=0) flower_list = train_dataset.class_to_idxcla_dict = dict((val, key) for key, val in flower_list.items()) json_str = json.dumps(cla_dict, indent=4)with open('class_indices.json', 'w') as json_file:    json_file.write(json_str) net = AlexNet(num_classes=5, init_weights=True)    net.to(device)                     loss_function = nn.CrossEntropyLoss()          optimizer = optim.Adam(net.parameters(), lr=0.0002)     save_path = './AlexNet.pth'best_acc = 0.0 for epoch in range(5):    net.train()                   running_loss = 0.0              time_start = time.perf_counter()      for step, data in enumerate(train_loader, start=0):          images, labels = data   #获取训练集的图像和标签        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)                  a = "*" * int(rate * 50)        b = "." * int((1 - rate) * 50)        with open(os.path.join("train.log"), "a") as log:              log.write(str("\rtrain loss: {:^3.0f}%[{}->{}]{:.3f}".format(int(rate * 100), a, b, loss))+"\n")        print("\rtrain loss: {:^3.0f}%[{}->{}]{:.3f}".format(int(rate * 100), a, b, loss), end="")    print()    with open(os.path.join("train.log"), "a") as log:              log.write(str('%f s' % (time.perf_counter()-time_start))+"\n")    print('%f s' % (time.perf_counter()-time_start))     net.eval()       acc = 0.0      with torch.no_grad():        for val_data in validate_loader:            val_images, val_labels = val_data            outputs = net(val_images.to(device))            predict_y = torch.max(outputs, dim=1)[1]              acc += (predict_y == val_labels.to(device)).sum().item()            val_accurate = acc / val_num        # 保存准确率最高的那次网络参数        if val_accurate > best_acc:            best_acc = val_accurate            torch.save(net.state_dict(), save_path)        with open(os.path.join("train.log"), "a") as log:              log.write(str('[epoch %d] train_loss: %.3f  test_accuracy: %.3f \n' %              (epoch + 1, running_loss / step, val_accurate))+"\n")        print('[epoch %d] train_loss: %.3f  test_accuracy: %.3f \n' %              (epoch + 1, running_loss / step, val_accurate))with open(os.path.join("train.log"), "a") as log:      log.write(str('Finished Training')+"\n")print('Finished Training')
       复制代码
 
运行前将“train.py”中的“image_path”路径改为“flower_data”的路径,右键“flower_data”文件,点击复制路径/引用,点击复制路径。
最后再终端输入命令运行“train.py”。
2.6 使用神经网络模型进行预测
使用一个预训练的模型来预测单个图像的类别,首先对图像进行预处理,然后使用加载的模型进行预测,并输出最可能的类别及其概率。这个脚本可以用来测试模型在未知数据上的表现,或者作为一个简单的图像分类工具。
创建一个 py 文件:空白处单击鼠标右键,选择“新建”>“文件”,文件命名为:“predict.py”。将下列代码复制到文件中。
 from model import AlexNetfrom PIL import Imagefrom torchvision import transformsimport matplotlib.pyplot as pltimport json#预处理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中可以修改图片路径img = Image.open("/home/developer/Desktop/data_set/flower_data/train/roses/24781114_bc83aa811e_n.jpg")img = data_transform(img)img = torch.unsqueeze(img, dim=0)#读取class_indices.jsontry:    json_file = open('./class_indices.json', 'r')    class_indict = json.load(json_file)except Exception as e:    print(e)    exit(-1) model = AlexNet(num_classes=5)model_weight_path = "./AlexNet.pth"model.load_state_dict(torch.load(model_weight_path, map_location='cpu'))#关闭Dropoutmodel.eval()with torch.no_grad():    output = torch.squeeze(model(img))        predict = torch.softmax(output, dim=0)    predict_cla = torch.argmax(predict).numpy()print(class_indict[str(predict_cla)], predict[predict_cla].item())plt.show()
       复制代码
 
运行前将“predict.py”中的“img”路径改为“图片”的路径。即 img = Image.open("/home/developer/Desktop/data_set/flower_data/train/roses/24781114_bc83aa811e_n.jpg"),本案例中选择玫瑰花图片的路径:“data_set”>“flower_data”>“train”>“roses”选择路径,通过鼠标右击图片名,复制路径。
最后运行“predict.py”文件。
运行后,识别图片成功就会出现“roses”字样。
至此,案例内容全部完成。
评论