Python中的卷积超柱

如果你是以下一些机器学习的消息,你肯定看到了瑞恩·达尔所做的工作自动彩色化(黑客新闻评论,Reddit的评论)。这个惊人的工作是采用像素超柱状体从VGG-16网络中提取的用于给图像着色的信息。Samim还利用网络处理黑白视频帧,制作了以下精彩视频:

https://www.youtube.com/watch?v=_MJU8VK2PI4

着色黑白电影与神经网络(由SAMIM视频,网络由Ryan)

但是这个超级列是如何工作的呢?如何提取它们来用于各种像素分类问题?这篇文章的主要思想是使用VGG-16预先训练的网络以及Keras和Scikit-Learn来提取像素超列,并对其上的信息进行浅显的查看。我写这篇文章是因为我在Python中还没有找到这样做的东西,这可能对其他研究像素分类、分割等的人非常有用。

超柱状体

许多使用CNNs (Convolutional Neural Networks,卷积神经网络)特征的算法通常使用最后一个FC (fully-connected)层特征来提取特定输入的信息。然而,最后一个FC层中的信息在空间上可能太粗糙,无法进行精确定位(由于maxpooling等的序列),另一方面,第一个层可能在空间上是精确的,但将缺乏语义信息。为了两全其美,本书的作者超柱纸将一个像素的超立方体定义为“高于”该像素的所有CNN单元的激活向量。

超柱萃取
超柱萃取(通过超列进行对象分割和细粒度定位)

在hypercolumns的提取的第一步是将图像馈送到CNN(卷积神经网络),并为图像的每个位置提取特征映射激活。比较棘手的是,当特征图小于输入图像时,例如,在池操作之后,本文作者对特征图进行双线性上采样,以保持特征图与输入图像的大小相同。财务委员会()层,因为你不能将单元在语义上仅与图像的一个像素相关联,所以FC激活被视为1×1的特征图,这意味着所有位置共享关于超列的FC部分的相同信息。然后将所有这些激活连接起来以创建超列。例如,如果我们使用VGG-16架构,在最大池操作之后只使用前2个卷积层,我们将得到一个大小为的超列:

64个过滤器(池之前的第一个conv层)

+

128过滤器(池前的第二个conv层)=192层的功能

这意味着图像的每个像素都有一个192维的超列向量。这个超列非常有趣,因为它将包含关于第一层的信息(其中我们有很多空间信息,但是很少有语义)和关于最后一层的信息(只有很少的空间信息和很多语义)。因此,这个超列肯定有助于许多像素分类任务,比如前面提到的自动着色,因为每个位置超列都携带关于这个像素在语义和空间上表示的内容的信息。这对于分割任务也非常有帮助亚洲金博宝(您可以在介绍超列概念的原始文章中了解更多相关信息)。

一亚洲金博宝切听起来很酷,但我们如何在实践中提取hypercolumns?

VGG-16

在能够提取超列之前,我们将设置VGG-16预训练网络,因为你知道,一个好的GPU在巴西的价格是非常昂贵的(我甚至无法想象有多少),我不想卖掉我的肾去买一个GPU。亚洲金博宝

VGG16网络体系结构(作者Yan Zhicheng等人)
VGG16网络体系结构(作者Yan Zhicheng等人)

要设置上Keras预训练VGG-16网络,你需要下载文件的权重从这里(具有大约500MB vgg16_weights.h5文件),然后设置的体系结构和加载使用Keras下载的权重(有关权值文件和体系结构的更多信息在这里):

从matplotlib导入pyplot as plt导入no import cv2 import numpy as np import scipy as sp from路缘石模型从顺序导入路缘石.layers.core导入扁平、密集、从路缘石.层.卷积导入卷积2d,MaxPooling2D来自路缘石.层.卷积从导入ZeroPadding2Dkeras.优化器从中导入新加坡元sklearn.manifold公司从sklearn导入TSNE从sklearn导入流形从sklearn导入集群从sklearn.预处理导入标准缩放器def VGG_16(weights_path=None):model=Sequential()模型.add(零填充2d((1,1),输入_shape=(3224,224页)模型.add(卷积2d(64,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(64,3,3,激活='relu'))模型.add(最大池2d((2,2),跨距=(2,2)))模型.add(零填充2d((1,1)))模型.add(卷积2d(128,3,3,激活='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(128,3,3,激活='relu'))模型.add(最大池2d((2,2),跨距=(2,2)))模型.add(零填充2d((1,1)))模型.add(卷积2d(256,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(256,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(256,3,3,激活='relu'))模型.add(最大池2d((2,2),跨距=(2,2)))模型.add(零填充2d((1,1)))模型.add(卷积2d(512,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(512,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(512,3,3,激活='relu'))模型.add(最大池2d((2,2),跨距=(2,2)))模型.add(零填充2d((1,1)))模型.add(卷积2d(512,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(512,3,3,activation='relu'))模型.add(零填充2d((1,1)))模型.add(卷积2d(512,3,3,激活='relu'))模型.add(最大池2d((2,2),跨距=(2,2)))模型.add(展平())模型.add(密度(4096,活化率='relu'))模型.add(辍学(0.5))模型.add(密度(4096,活化率='relu'))模型.add(辍学(0.5))模型.add(密度(1000,激活='softmax'))如果权重路径:模型载荷(权重路径)返回模型

如您所见,这是一个非常简单的代码,用于声明VGG16亚洲金博宝体系结构并加载预先训练的权重(以及所需包的Python导入)。之后,我们将编译Keras模型:

model = VGG_16('vgg16_weight .h5') sgd =sgd (lr=0.1,衰减=1e-6,动量=0.9,nesterov=True) model.compile(optimizer=sgd, loss='categorical_crossentropy')

现在让我们使用一个图像来测试网络:

im_original=cv2.resize(cv2.imread('马德鲁加.jpg'),(224224)im=im_原稿转置(2,0,1)im=np.扩展尺寸(im,轴=0)im_converted=cv2.cvtColor(im_original,cv2.COLOR_BGR2RGB)节目单(im U转换)

使用的图像

使用的图像

正如我们所看到的,我们加载了图像,固定了轴,然后我们现在可以将图像输入到VGG-16中,以获得预测:

OUT = model.predict(IM)plt.plot(out.ravel())

预测
预测

如你所见,这些是softmax层的最后激活,与类的“泽西,t恤,t恤”类别。

提取任意特征映射

现在,要提取特征映射激活,我们必须能够从网络的任意卷积层提取特征映射。我们可以通过编译一个Theano函数来实现get_output ()Keras的方法,如下例所示:

get_feature = theano.function([model.layers [0]。输入],model.layers [3] .get_output(列车= FALSE),allow_input_downcast =假)feat = get_feature(im) plt.imshow(feat[0][2])

特征映射

特征映射

在上面的例子中,我编译Theano函数来获取3层(卷积层)的特征图,然后仅示出第三特征地图。在这里我们可以看到激活的强度。如果我们从功能,最终层激活的地图上,我们可以看到,提取的特征是比较抽象的,一样的眼睛,等看从15卷积层下面这个例子:

get_feature = theano.function([model.layers [0]。输入],model.layers [15] .get_output(列车= FALSE),allow_input_downcast =假)技艺= get_feature(IM)plt.imshow(技艺[0] [13])

更多语义特征图

更多语义特征图。

如您所见,第二个特征图提取的是更抽象的特征。你也可以注意形象似乎更紧张与我们之前看到的功能相比,这是因为第一个特征图有224×224的大小和这个有56个×56由于降尺度层的操作在卷积层之前,这就是为什么我们失去大量的空间信息。

提取超柱状体

现在,我们最后提取任意层集的超立方体。为此,我们将定义一个函数来提取这些超柱:

def extract_hypercolumn(model, layer_indexes, instance): layers = [model.layers[li].get_output(train=False) for li in layer_indexes] get_feature = theano.function([model.layers[0]])。输入],图层,allow_input_downcast=False) feature_maps = get_feature(instance)超列= []for convmap in feature_maps: for fmap in convmap[0]: upscale = sp.misc。imresize(fmap, size=(224, 224), mode="F", interp='双线性')

我们可以看到,这个功能会想到三个参数:模型本身,将被用于提取hypercolumn功能和将要用于提取hypercolumns图像实例层指标的清单。现在让我们来测试第2卷积层hypercolumn提取:

layers_extract = [3,8] HC = extract_hypercolumn(模型,layers_extract,IM)

就是这样,我们提取了每个像素的超列向量。这个“hc”变量的形状是:(192L, 224L, 224L),这意味着对于224×224像素中的每一个像素,我们都有一个192维的超列(共50176个像素,每个像素都有192个超列特征)。

让我们绘制每个像素的超列激活的平均值:

平均= np.average(hc.transpose(1,2,0),轴= 2)plt.imshow(AVE)
第3层和第8层的超柱平均值。
第3层和第8层的超柱平均值。

你可以看到,第一个超列激活看起来都像边缘探测器,让我们看看这些超列在第22层和第29层是什么样子的:

layers_extract = [22,29] hc = extract_hypercolumn(model, layers_extract, im) ave = np.average(hc。转置(1,2,0),轴=2)
第22层和第29层的超列平均值。
第22层和第29层的超列平均值。

正如我们现在看到的,这些特征更抽象,语义更有趣,但是空间信息有点模糊。

请记住,可以使用所有初始层和最终层提取超列,包括FC层。在这里,我分别提取它们,以显示它们在可视化图中的不同之处。

简单hypercolumn像素集群

现在,你可以做很多事情,你可以使用这些超列来为一些任务对像素进行分类,进行自动像素着色,分割,等等。作为一个实验,我在这里要做的是使用超列(来自VGG-16层的3、8、15、22、29),然后使用KMeans和2个集群对它进行集群:

m = hc.transpose (1 2 0)。重塑(50176 1)kmeans = cluster.KMeans(n_clusters=2, max_iter=300, n_jobs=5, precompute_distances=True) cluster_labels = kmeans .fit_predict(m) imcluster = np.zeros((224,224)) imcluster = imcluster.reshape((224*224,)) imcluster = cluster_labels plt.imshow(imcluster.reshape(224, 224), cmap="hot")
k均值聚类使用hypercolumns。
k均值聚类使用hypercolumns。

现在您可以想象超级列对于关键字提取、分段等任务是多么有用。这是一个非常优亚洲金博宝雅、简单和有用的概念。

希望你喜欢!

-克里斯蒂安·佩隆

本文引用如下:Christian S. Perone,“Python中的卷积超列”,in亚洲金博宝未发现的地域2016年1月11日,//www.cpetem.com/2016/01/convolutional-hypercolumns-in-python/

34个思考“在Python卷积hypercolumns”

  1. 感谢您对这篇文章!一个小错误 - 缺少线“进口”〜11:

    从keras.layers。convolutional import ZeroPadding2D

  2. 好贴!。如果您想更新的代码,以使其可以运行在您需要更改“步幅”,在网络定义“进步” keras的最后一个版本。

  3. 你好,

    瑞安·达尔在谈到自动着色时提到,hyercolumns并没有起到很好的作用。所以他尝试用剩余网络的思想来使用以前的图层特征。

    你能给我们提供密码吗?怎么做?

    最好

  4. 我爱它。伟大的文章。我不知道什么是超列。我还会继续学习吗

  5. 首先,好的文章!

    但得到的权重文件,复制/粘贴你的代码,一些微小的PLT显示调整写入图像到磁盘VS弹出式窗口,...。一亚洲金博宝切看起来都99%的匹配您的图像。

    然而,在最终图像中头部的中心显示只是一个单一的圆形斑点。

    不解通过这个一会尝试不同的东西,但没有运气,小单博客仍然存在,然后注意到您的评论“使用hypercolumns(从VGG-16层3,8,15,22,29)”。

    所以最后的猜测,我改变了最后的层代码如下:

    #layers_extract = [22,29]
    layers_extract = [3, 8, 15, 22, 29]

    对了,我的最终形象和你的一模一样!

    它可能是某个地方的一些愚蠢的事情,我在复制代码时偶然做的,但以防有人有类似的问题,我想我会张贴我的小解决方案的具体问题。

    1. 嗨,AHayden
      我刚遇到了你描述的类似问题。无论我将layers_extract设置为[22,29]或[3,8,15,22,29],最终的图像都只显示相同的“单个blob”。我的代码是

      layers_extract = [3, 8, 15, 22, 29]
      HC = extract_hypercolumn(模型,layers_extract,IM)
      m = hc.transpose (1 2 0)。重塑(50176 1)
      ....

      我只是想知道你知道原因吗?谢谢!

  6. 亚洲金博宝非常好的文章和想法。

    请更正以下内容:
    当定义ConvNet而不是“stride”时,使用“stride”,否则它不会编译。

    根据原稿,对图像进行预处理,从每个通道中减去训练图像的平均值。当使用预先训练好的VGG16权重识别新图像时,需要执行此操作。在这种情况下不需要标准缩放。亚洲金博宝

  7. 有趣的是,预测是t恤,而更深层次的人对脸有更多的语义理解!!

    1. 我认为ImageNet没有一组face,或者1000个类中的man ..我可能错了。Good post though ..

  8. 你好,

    谢谢你的教程。我目前有以下版本的代码从教程:https://www.dropbox.com/s/nlg78oyvkrj86v6/hypercolumn.py?dl=1

    当我尝试运行代码时,我得到以下信息:

    使用Theano后端。
    回溯(最近通话最后一个):
    文件“hypercolumn.py”,行85,在
    get_feature = theano.function ([model.layers [0]。输入),model.layers [3]。output(train=False), allow_input_downcast=False)
    类型错误:' TensorVariable '对象不可调用

    你知道我怎样才能解决这个错误吗?

    谢谢。

  9. 嗨,我用的是图形模型。
    像这样,

    模型=图()
    model.add_输入(名称='input0',输入形状=)
    model.add_node (Convolution2D()、名称= ' c1′,输入= ' input0′)
    …….

    然后我想看到c1的输出

    getFeatureMap = theano.function (model.inputs‘input0 .input, model.nodes [c1的].get_output(火车= False),
    允许输入(downcast=True)

    但它告诉了我
    ' TypeError:列表索引必须是整数,而不是str '

    你能给我一些建议吗?谢谢。

  10. 嘿,
    我真的很喜欢你的工作。
    我正在努力制定一个方案来解决searchwing.org网站团队目前遇到的一些问题。其中一个概念就是使用openCV。这里有一个讲座,涵盖了这里的工作:https://www.youtube.com/watch?v=SCUCRwFs_Lg&t=29s。不幸的是,我没有任何使用opencv的经验。这就是为什么我想问你是否愿意在这方面帮助我。请与我联系,以便我向你解释我的想法。
    问候forom德国
    阿克塞尔只

  11. 好的文章!Might help me a lot for my thesis I am working on a PixelNet which also uses hypercolumns! This helped me understand the concept very well! But I have write the code in MatConvNet!
    你是否理解2D-LSTM(长-短期记忆)的概念?

    谢谢和问候
    Savan

  12. 嗨,好帖子!
    问:为什么“layers_extract =[3,8]”从两个conv层获得第一个映射?

    从上面定义的VGG,它看起来像[3,6],得到它们。

    def VGG_16 (weights_path = None):
    模型=顺序()
    model.add (ZeroPadding2D ((1, 1), input_shape = (3224224)))
    模型。add(Convolution2D(64, 3, 3, activation= ' relu '))
    model.add (ZeroPadding2D ((1,1)))
    模型。add(Convolution2D(64, 3, 3, activation= ' relu '))
    model.add(MaxPooling2D((2,2),跨距=(2,2)))
    model.add (ZeroPadding2D ((1,1)))
    model.add(Convolution2D(128,3,3,活化=” RELU”))
    model.add (ZeroPadding2D ((1,1)))
    model.add(Convolution2D(128,3,3,活化=” RELU”))
    model.add(MaxPooling2D((2,2),跨距=(2,2)))

    1. def VGG_16 (weights_path = None):

      模型。add(Convolution2D(64, 3, 3, activation= ' relu ')) - 1
      模型。添加(Convolution2D(64, 3, 3, activation= ' relu ')) - 2
      模型.add(最大池2d((2,2),跨距=(2,2)))-3
      model.add(Convolution2D(128,3,3,活化=” RELU”)) - 4-
      model.add(Convolution2D(128,3,3,活化=” RELU”)) - 5
      添加(MaxPooling2D((2,2), stride=(2,2)) - 6

  13. 我复制你的代码来重新实现它。
    但是平台显示了一些错误,你能帮我吗?
    文件“v16.py” 75行,在VGG_16
    model.load_weights (weights_path)
    文件“C: \ Anaconda3 \ lib \网站\ keras \模型。py”,第706行,在load_weight中
    TS
    拓扑。load_weights_from_hdf5_group (f层)
    文件“C: \ Anaconda3 \ lib \网站\ keras \ \拓扑引擎。py”,第2869行,in
    load_weights_from_hdf5_group
    layer_names = [n.decode(' utf8 ') for n in f.attrs[' layer_names ']]
    文件”h5py \ _objects。“pyx”,第54行,用h5py._objects.with_phil表示。包装器(C: \ aroo
    t \ \ h5py \ _objects.c工作:2579)
    文件“h5py\_对象.pyx,第55行,h5py_对象与.wrapper(C:\ aroo
    t \ \ h5py \ _objects.c工作:2538)
    文件“C:\ Anaconda3\lib\site packages\h5py\uHL\属性py“,第58行,在getitem中”
    __
    attr = h5a.open(自我。_id self._e(名字)
    文件”h5py \ _objects。“pyx”,第54行,用h5py._objects.with_phil表示。包装器(C: \ aroo
    t \ \ h5py \ _objects.c工作:2579)
    文件“h5py\_对象.pyx,第55行,h5py_对象与.wrapper(C:\ aroo
    t \ \ h5py \ _objects.c工作:2538)
    文件“h5py \ h5a.pyx”,行77,在h5py.h5a.open(C:\ aroot \工作\ h5py \ h5a.c:2083)

    KeyError: "无法打开属性(无法定位属性:' layer_names ') "

  14. get_feature = theano.function([model.layers [0]。输入],model.layers [3] .get_output(列车= FALSE),allow_input_downcast =假)
    AttributeError: ' MaxPooling2D '对象没有属性' get_output '

    不管我用的是theano还是张量流的后端,我都会得到同样的错误。类MaxPoolin2D的源代码确认没有在代码中三次使用的名为get_output的方法或属性。

    我使用,我使用PIP刚刚升级的最新版本。

    有人能帮忙吗?

    1. 如果使用keras 2.x,则应更改get_输出(train=False)
      对于输出,
      您的代码应该是这样的
      get_feature = theano.function ([model.layers [0]。输入),model.layers [3]。输出,allow_input_downcast = False)

  15. 你好,
    我如何使用你的算法来训练新datasete有两个班?我需要新的权重。

    谢谢,
    格莱德松

留下一个回复

您的电子邮件地址将不会被公布。

这个站点使用Akismet来减少垃圾邮件。了解您的意见如何处理数据