admin管理员组

文章数量:1630203

作者|Anders Ohrn 编译|VK 来源|Towards Data Science

利用深度卷积神经网络(DCNN)进行监督图像分类是一个成熟的过程。通过预训练模板模型加上微调优化,可以在许多有意义的应用中获得非常高的准确率——比如最近在医学图像上的这项研究,在日常物体图像上预训练的模板Inception v3模型对前列腺癌诊断的准确率达到了99.7%。

对于无监督的图像机器学习,目前的研究现状远没有定论。

聚类是无监督机器学习的一种形式,其中数据(本例中的图像)根据数据收集本身的某种结构进行分簇。在同一个簇中结束的图像应该比不同簇中的图像更相似。

图像数据可能是复杂的-变化的背景,视图中的多个对象-因此一对图像比另一对图像更相似意味着什么并不明显。如果没有基本的真实性标签,通常不清楚是什么使一种聚类方法优于另一种聚类方法。

一方面,无监督的问题因此比有监督的问题更加模糊。没有现成的正确答案可供优化。另一方面,从模糊的问题、假设的产生、问题的发现和修补中,最有趣的东西出现了。

我将描述一种最新的图像聚类方法的实现(https://arxiv/abs/1903.12355)。这是近年来发表的许多先进的DCNN聚类技术之一。

我使用PyTorch库来演示如何实现这个方法,并在整个文本中提供了几个详细的代码片段。仓库中提供完整的代码:https://github/anderzzz/monkey_caput

在标准库中没有无监督版本的聚类方法,这点不像有监督版本,它可以很容易获得图像聚类方法,但PyTorch仍然能够平稳地实现实际上非常复杂的方法。因此,我能够探索、测试和轻微地探究DCNNs应用于聚类任务时可以做什么。

我的目标是展示如何从一些概念和方程开始,你可以使用PyTorch来得到一些可以在计算机上运行的非常具体的东西,并指导进一步的创新和修改你所拥有的任何任务

我将把这个应用到真菌的图像上。为什么是真菌?你待会儿再看。

但首先…实现VGG自编码器

在讨论聚类方法之前,我将实现一个自动编码器(AE)。AEs有各种各样的应用,包括降维,并且本身很有趣。它们在图像聚类中的作用将在以后变得更加清楚。

用PyTorch库实现基本的ae并不是那么困难(请看这两个例子)。我将实现特定的AE架构,它是SegNet方法的一部分,它建立在VGG模板卷积网络上。VGG定义了一种体系结构,最初是为监督图像分类而开发的。

AE的架构如下图所示。

图像自编码的步骤如下:

  1. 准备输入图像(左上角)

  2. 将图像输入编码器,由具有标准CNN和ReLU激活的卷积层(绿色)和最大池层(紫色)组成

  3. 得到一个低维的编码

  4. 将编码输入译码器,它由转置的卷积层(带归一化和ReLU激活)(浅绿色)和解池化层(浅紫色)加上一个没有归一化或激活的最终卷积层(黄色)

  5. 获得与输入尺寸相同的输出图像。

是时候把这个设计变成代码了。

我从创建一个编码器模块开始。第一行,包括初始化方法,如下所示:

import torch
from torch import nn
from torchvision import models

class EncoderVGG(nn.Module):
    '''
    基于vgg16体系结构的图像编码器,具有batch normalization。
    Args:
        预训练的params (bool,可选):是否应该用预训练的vGG参数填充网络,默认值为True
    '''
    channels_in = 3
    channels_code = 512

    def __init__(self, pretrained_params=True):
        super(EncoderVGG, self).__init__()

        vgg = models.vgg16_bn(pretrained=pretrained_params)
        del vgg.classifier
        del vgg.avgpool

        self.encoder = self._encodify_(vgg)

编码器的结构与VGG-16卷积网络的特征提取层结构相同。因此,PyTorch库中很容易找到该部分—PyTorch models.vgg16_bn,请参阅代码片段中的第19行。

与VGG的规范应用程序不同,编码不会被输入到分类层中。最后两层vgg.classifier以及vgg.avgpool被丢弃。

编码器的层需要一次调整。在解码器的解池层中,编码器的最大池层中的池索引必须可用,在前面的图像中虚线箭头表示。VGG -16的模板版本不生成这些索引。然而,池化层可以重新初始化。这就是EncoderVGG模块的_encodify方法完成的工作。

    def _encodify_(self, encoder):
        '''
        基于VGG模板的架构创建编码器模块列表。在编码器-解码器体系结构中,解码器中的解池操作需要来自编码器中相应池操作的池索引。在VGG模板中,这些索引不返回。因此需要使用此方法扩展池操作。
        参数:
            编码器:模板VGG模型
        返回:
            模块:定义与VGG模型对应的编码器的模块列表
        '''
        modules = nn.ModuleList()
        for module in encoder.features:
            if isinstance(module, nn.MaxPool2d):
                module_add = nn.MaxPool2d(kernel_size=module.kernel_size,
                                          stride=module.stride,
                                          padding=module.padding,
                                          return_indices=True)
                modules.append(module_add)
            else:
                modules.append(module)

        return modules

因为这是一个PyTorch模块(nn.Module),通过EncoderVGG实例实现小批量图像数据的前向传播需要一个forward方法:

    def forward(self, x):
        '''将图像输入encoder
        Args:
            x (Tensor): 图片tensor
        Returns:
            x_code (Tensor): 编码 tensor
            pool_indices (list): 池索引张量
        '''
        pool_indices = []
        x_current = x
        for module_encode in self.encoder:
            output = module_encode(x_current)

            # 如果模块是池,有两个输出,第二个是池索引
            if isinstance(output, tuple) and len(output) == 2:
                x_current = output[0]
                pool_indices.append(output[1])
            else:
                x_current = output

        return x_current, pool_indices

该方法按顺序执行编码器中的每个层,并在创建池索引时收集它们。在执行编码器模块之后,代码与池索引的有序集合一起返回。

接下来是解码器。

它是VGG-16网络的“转置”版本。我使用引号是因为解码器层看起来很像反向的编码器,但严格地说,它不是反转或转置。

译码器模块的初始化:

class DecoderVGG(nn.Module):
    '''译码器的代码基于vgg16体系结构与batch normalization。
    Args:
        encoder: ' EncoderVGG '的编码器实例,它将被转换成一个解码器
    '''
    channels_in = EncoderVGG.channels_code
    channels_out = 3

    def __init__(self, encoder):
        super(DecoderVGG, self).__init__()

        self.decoder = self._invert_(encoder)

    def _invert_(self, encoder):
        '''将编码器反转,以将译码器创建为编码器的镜像
        译码器由两种主要类型组成:二维转置卷积和二维解池,2D卷积之后是批处理归一化和激活。
        译码器是反向的,编码器中的卷积变成了转置卷积加上归一化和激活,编码器中的maxpooling变成了unpooling。
        Args:
          

本文标签: 图像Pytorch