前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用卷积深度神经网络和PyTorch库对花卉图像进行分类

使用卷积深度神经网络和PyTorch库对花卉图像进行分类

作者头像
代码医生工作室
发布2019-08-09 20:05:36
4.1K0
发布2019-08-09 20:05:36
举报
文章被收录于专栏:相约机器人相约机器人

作者 | Avishek Nag

来源 | Medium

编辑 | 代码医生团队

语言图像数据是深度学习技术的一种非常流行的用法。在本文中将讨论使用深度卷积神经网络识别花卉图像。

为此将使用Python的PyTorch,TorchVision和PIL库

数据探索

可以在Kaggle找到此问题所需的数据集。它包含文件夹结构和花卉图像。有5种不同类型的花。文件夹结构如下所示

https://www.kaggle.com/alxmamaev/flowers-recognition/

图1

现在将看到文件夹'rose'中的花卉图像样本

代码语言:javascript
复制
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
 
def show_image(path):
    img = Image.open(path)
    img_arr = np.array(rose_img)
    plt.figure(figsize=(5,5))
plt.imshow(np.transpose(img_arr, (0, 1, 2)))
代码语言:javascript
复制
show_image("../data/flowers/rose/537207677_f96a0507bb.jpg")

数据预处理

PyTorch总是期望以“张量”的形式提供数据。这些“张量”在神经网络的节点之间运行,包含原始和预处理或后处理的数据。基本上,简而言之,“张量”类似于“numpy”阵列。

对于图像数据,还必须将图像作为张量读取,并在进行任何分类之前应用几个预处理阶段。

可以将图像视为三维张量。每个图像可以有3种类型的像素颜色值 - 分别为红色,绿色和蓝色。我们称之为RGB颜色编码。另外两个维度是长度和宽度方向的像素值。

通常,图像数据需要两个非常常见的预处理阶段,如下所示:

1.调整大小为模板:将图像调整为方形。将每个图像的大小调整为64x64图像。

2.归一化:使用每个像素值的(x - mean)/ sd机制进行统计归一化。它有助于改善图像中的可视化,增强功能和拉伸对比度。

使用PyTorch,将进行这组预处理。

还可以定义一个函数来显示一组变换图像

代码语言:javascript
复制
def show_transformed_image(image):
    np_image = image.numpy()
    plt.figure(figsize=(20,20))
plt.imshow(np.transpose(np_image, (1, 2, 0)))

现在,可以看到第一批的转换图像

代码语言:javascript
复制
show_transformed_image(make_grid(image))

也可以让类索引数据字典

这将有助于识别类。

构建模型

要构建图像数据的机器学习模型,仅提供像素值是不够的。图像中有许多隐藏的功能仍未被发现。为此,应该使用卷积和最大池层的组合来提取重要特征。

卷积层

在数学上,两个函数f&g之间的卷积运算被定义为

实际上,如果将f视为图像张量,则g应该是另一个可以作为“卷积核”的张量。

它是两个张量的乘法值的逐像素求和。

下图显示了卷积运算对样本图像张量的影响

大小为3x3的卷积核在图像张量周围移动,作为从(0,0)位置开始的窗口,输出张量(0,0)处的样本结果如下所示

输出(0,0)=图像张量(0,0)x内核(0,0)+图像张量(0,1)x内核(0,1)+图像张量(0,2)x内核(0,2) )+图像张量(1,0)x内核(1,0)+图像张量(1,1)x内核(1,1)+图像张量(1,2)x内核(1,2)+图像张量( 2,0)x内核(2,0)+图像张量(2,1)x内核(2,1)+图像张量(2,2)x内核(2,2)= 1 x 1 + 1 x 0 + 1 x 1 + 0 x 0 + 1 x 1 + 1 x 0 + 0 x 1 + 0 x 0 + 1 x 1 = 4

内核将位置移位1位以计算输出张量的其他位置的值。这种“转变”被称为“跨步”。

需要卷积层来增强和提取图像的重要和隐藏特征。在我们的例子中,可能会发生'花'位于图像的中心位置,因此应用卷积有助于检索花的核心特征,忽略其他背景对象和颜色。

由于每个图像都遵循RGB颜色编码,将对每种颜色应用卷积运算,因此将得到三个输出张量。最终输出将是所有三个的张量总和。这些“颜色代码”中的每一个在PyTorch API术语中称为“通道”。

在数学上,如果在大小为WxH的图像上应用大小为kxk的滤波器,则它会产生大小为(W-k + 1)x(H-k + 1)的输出图像/张量

在例子中,卷积是这样创建的

代码语言:javascript
复制
self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=3,stride=1, padding=1)

'out_channels'指定要应用的过滤器数量。在这里应用了12个滤镜,这些滤镜将产生12个尺寸为62x62的中间图像张量。这些图像中的每一个都包含原始图像的一个独特特征。

ReLU层

'ReLU'是一种激活函数,可捕获另一函数输出中的非线性。在数学上,它被定义为

f(x)=max(0,x)

所以它总是返回正值。可以说,它是一个'正面过滤器'。将在卷积后应用'ReLU'层。

在例子中,'ReLU'创建如下

代码语言:javascript
复制
self.relu1 = nn.ReLU()

最大池层

“最大汇集层”通常位于“ReLU”之后。大小为2的“最大池”是2x2窗口,它遍历“ReLU”操作的输出张量并选择窗口内的最大像素值。该操作可以通过下图解释

“最大池”图层的目标是仅选择那些具有高影响力且具有较大价值的特征。它有助于减少功能的尺寸。

在例子中,'Max Pool'的创建如下

代码语言:javascript
复制
self.maxpool1 = nn.MaxPool2d(kernel_size=2)

它会将图像尺寸减小50%(32 = 64/2)。

线性功能层

顾名思义,它是一个线性函数,它将“Max Pool”的输出作为一个展平数组,并将输出作为类索引。预测类索引的“线性函数”的输出值将是最大值。

在例子中,'线性函数'的创建方式如下

代码语言:javascript
复制
self.lf = nn.Linear(in_features=32 * 32 * 24, out_features=num_classes)

整体架构的模型

将应用不同的图层,如下图所示

有两套'卷积'和'ReLU'层。'View'使输出张量从最后一个'ReLU'层变平。将大小为64x64的图像张量作为输入,由于应用了内核大小为2x2(32 = 64/2)的“MaxPool2D”,它将减少到32x32。

首先,将数据集按80:20的比例划分为训练和测试

代码语言:javascript
复制
from torch.utils.data import random_split
 
train_size = int(0.8 * len(total_dataset))
test_size = len(total_dataset) - train_size
train_dataset, test_dataset = random_split(total_dataset, [train_size, test_size])
 
train_dataset_loader = DataLoader(dataset = train_dataset, batch_size = 100)
test_dataset_loader = DataLoader(dataset = test_dataset, batch_size = 100)

然后,将通过扩展PyTorch库给出的“Module”来编写一个自定义类来堆叠这些层

代码语言:javascript
复制
import torch.nn as nn
 
class FlowerClassifierCNNModel(nn.Module):
    
    def __init__(self, num_classes=5):
        super(FlowerClassifierCNNModel,self).__init__()
        
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=3,stride=1, padding=1)
        self.relu1 = nn.ReLU()
        
        self.maxpool1 = nn.MaxPool2d(kernel_size=2)
        
        self.conv2 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=3, stride=1, padding=1)
        self.relu2 = nn.ReLU()
        
        self.lf = nn.Linear(in_features=32 * 32 * 24, out_features=num_classes)
    
    def forward(self, input):
        output = self.conv1(input)
        output = self.relu1(output)
        
        output = self.maxpool1(output)
        
        output = self.conv2(output)
        output = self.relu2(output)
        
        output = output.view(-1, 32 * 32 * 24)
 
        output = self.lf(output)
 
        return output

'__init__'定义每个图层及其参数,而'forward'函数执行实际调用和图层堆叠。最后一层的输出从'forward'函数返回。

模型训练

需要有一个优化器和损失函数用于模型训练。将使用' Adam optimizer '和' Cross-Entropy Loss '。

代码语言:javascript
复制
from torch.optim import Adam
cnn_model = FlowerClassifierCNNModel()
optimizer = Adam(cnn_model.parameters())
loss_fn = nn.CrossEntropyLoss()

使用PyTorch,需要在训练模式下设置模型,然后通过迭代训练数据集,计算优化器的丢失和递增步骤来运行训练。

可以为此编写一个函数

代码语言:javascript
复制
def train_and_build(n_epoches):
    for epoch in range(n_epoches):
        cnn_model.train()
        for i, (images, labels) in enumerate(train_dataset_loader):
            optimizer.zero_grad()
            outputs = cnn_model(images)
            loss = loss_fn(outputs, labels)
            loss.backward()
            optimizer.step()

函数调用'loss.backward'返回到层并计算过程中发生的损失。

将使用200的'epoche'来训练模型

代码语言:javascript
复制
train_and_build(200)

模型测试和准确性

应该将模型设置为'eval'模式,以便在测试数据集上测试其准确性

代码语言:javascript
复制
import torch
 
cnn_model.eval()
test_acc_count = 0
for k, (test_images, test_labels) in enumerate(test_dataset_loader):
    test_outputs = cnn_model(test_images)
    _, prediction = torch.max(test_outputs.data, 1)
    test_acc_count += torch.sum(prediction == test_labels.data).item()
 
test_accuracy = test_acc_count / len(test_dataset)

'torch.max'函数返回'线性函数'输出张量的最大值。最大值推断出预测的类别标签。

'torch.sum'函数总结了张量中的'1',它是'预测'和'实际测试输出'张量之间'AND'运算的输出。因此,这个总和给出了正确预测图像的数量。

在这里得到准确性

代码语言:javascript
复制
test_accuracy

几乎是70.52%。用简单的模型获得了很好的准确性。这个模型可以进一步调整。

使用模型进行样本图像预测

现在将看到如何将此模型与数据集中的示例图像一起使用。

代码语言:javascript
复制
show_image("../data/flowers/dandelion/13920113_f03e867ea7_m.jpg")

这是'蒲公英'的形象。

现在将使用PIL图像API读取图像并将其输入到转换管道中以进行必要的预处理,然后使用该模型进行预测

代码语言:javascript
复制
test_image = Image.open("../data/flowers/dandelion/13920113_f03e867ea7_m.jpg")
test_image_tensor = transformations(test_image).float()
test_image_tensor = test_image_tensor.unsqueeze_(0)
output = cnn_model(test_image_tensor)
class_index = output.data.numpy().argmax()
代码语言:javascript
复制
class_index

因此如上所述,从类到索引字典,可以确认它是一个'蒲公英'。所以图像分类器模型运行良好!

结论

学习了如何使用PyTorch库进行图像分类。在此过程中,介绍了图像的预处理,构建卷积层以及测试输入图像的模型。

通过'Hyperparameter'调整可以进一步提高模型的准确性,例如尝试使用'Adam optimizer'参数,添加额外的卷积层,调整内核大小和最大池窗口大小等。本文的读者可以自己尝试这些技术。

Jupyter笔记本可以从下面的链接找到

https://github.com/avisheknag17/public_ml_models/blob/master/image_classification_cnn_pytorch/notebook/cnn_image_classification_pytorch.ipynb?source=post_page---------------------------

推荐阅读

重磅!字节跳动开源高性能分布式训练框架BytePS:兼容TensorFlow、PyTorch等

点击“阅读原文”图书配套资源

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档