【TensorFlow-windows】MobileNet理论概览与实现

前言

轻量级神经网络中,比较重要的有MobileNet和ShuffleNet,其实还有其它的,比如SqueezeNet、Xception等。

本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。

国际惯例,参考博客:

纵览轻量化卷积神经网络:SqueezeNet、MobileNet、ShuffleNet、Xception

为什么MobileNet及其变体如此之快?

【Tensorflow】tf.nn.depthwise_conv2d如何实现深度卷积?

Keras-application中的实现

MobileNetV1论文

MobileNetV2论文

MobileNetV3论文

MobileNetV1

理论

利用深层分离式卷积(Depthwise Separable Convolution)替换传统的卷积操作,深层分离式卷积是由深层卷积(depthwise convolution)和大小为1的卷积核(pointwise convolution)组合,

正常的卷积过程为:设有M个大小为(P,P)(P,P)(P,P)的输入特征图,使用N个大小为(S,S)(S,S)(S,S)的卷积核进行卷积操作,可得到N个大小为(Q,Q)(Q,Q)(Q,Q)的输出特征图。计算量是:
M×N×S2×Q2M\times N \times S^2\times Q^2 M×N×S2×Q2
MobileNet的卷积过程:M个大小为(P,P)(P,P)(P,P)的输入特征图,先与MMM个大小为(3,3)(3,3)(3,3)的卷积核分别卷积,也就是M个特征图与M个卷积核一一对应,第M个卷积核对除第M个特征图外的特征图无其它操作,就得到了M个大小为(Q,Q)(Q,Q)(Q,Q)的特征图。随后使用N个大小为(1,1)(1,1)(1,1)的卷积核对这M个特征图卷积,得到N个大小为(Q,Q)(Q,Q)(Q,Q)的特征图。计算量是:
M×S2×Q2+M×N×Q2M\times S^2\times Q^2 + M\times N\times Q^2 M×S2×Q2+M×N×Q2
在这里插入图片描述

上图左边就是常规的卷积操作,右边就是MobileNet对应的卷积操作。

此外论文还提供两个控制模型参数或者计算量的操作:

  • Width Multiplier:将模型变得更瘦,意思就是最终通道数减少,在pointwise convolution层操作,比如原来N个(1,1)(1,1)(1,1)个卷积核,现在变成了N×WidthMultiplierN \times WidthMultiplierN×WidthMultiplier
  • Resolution Multiplier:将模型的每个特征图变得更小,比如某层是240×240240\times240240×240的特征图,经过Resolution Multiplier=0.5调整后,就变成了120×120120\times120120×120大小的特征图,论文中是通过这个来调整输入大小。

In practice we implicitly set ρ by setting the input resolution

经评论区提醒,关于Resolution Multiplier的描述有所变动。之前描述的是tensorflow里面的验证结果:它没有调整特征图大小,而是提升了depth wise后的卷积特征图通道数。具体看下面的问题描述与验证。

问题

关于Resolution Multiplier,如果按照第三个参考博客计算,那么这个值必须是正整数,不可能是小于1的小数,刚好与tensorflow的实现对应,但是论文描述是为了降低计算量,一般是小于1的小数,所以不可能扩大。这两个理论刚好相反。

我个人理解是这个Resolution Multiplier是降低depthwise卷积得到的每个特征图的大小,比如输入10个大小为(224,224)(224,224)(224,224)的特征图,经过第一步深度卷积后得到的是10个(112,112)(112,112)(112,112)大小的特征图,而非5个(224,224)(224,224)(224,224)大小的特征图。从文章中的公式就能看出来:
DK⋅DK⋅αM⋅ρDF⋅ρDF+αM⋅αN⋅ρDF⋅ρDFD_K\cdot D_K\cdot \alpha M \cdot \rho D_F\cdot \rho D_F+\alpha M\cdot \alpha N\cdot \rho D_F\cdot \rho D_F DKDKαMρDFρDF+αMαNρDFρDF
其中α\alphaα代表输出特征图减少了,除了最开始的输入层外的层输入和输出特征图都变成了 α\alphaα 倍,所以第二项MMMNNN都乘了α\alphaα ,分别表示输入特征图与输出特征图个数;ρ\rhoρ乘在了DFD_FDF,而DFD_FDF代表特征图的大小而非个数,所以应该是降低了每层depthwise conv后的特征图大小,类似于卷积核变大了,输出特征图就变小了。

但是Tensorflow代码中验证的结果又和自己的想法不同。验证如下:

代码

Keras中,实现此过程的代码如下:

def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha,depth_multiplier=1, strides=(1, 1), block_id=1):channel_axis = 1 if backend.image_data_format() == 'channels_first' else -1pointwise_conv_filters = int(pointwise_conv_filters * alpha)if strides == (1, 1):x = inputselse:x = layers.ZeroPadding2D(((0, 1), (0, 1)),name='conv_pad_%d' % block_id)(inputs)x = layers.DepthwiseConv2D((3, 3),padding='same' if strides == (1, 1) else 'valid',depth_multiplier=depth_multiplier,strides=strides,use_bias=False,name='conv_dw_%d' % block_id)(x)x = layers.BatchNormalization(axis=channel_axis, name='conv_dw_%d_bn' % block_id)(x)x = layers.ReLU(6., name='conv_dw_%d_relu' % block_id)(x)x = layers.Conv2D(pointwise_conv_filters, (1, 1),padding='same',use_bias=False,strides=(1, 1),name='conv_pw_%d' % block_id)(x)x = layers.BatchNormalization(axis=channel_axis,name='conv_pw_%d_bn' % block_id)(x)return layers.ReLU(6., name='conv_pw_%d_relu' % block_id)(x)

很清晰的发现,是经过DepthWise卷积、BN、Relu、pointwise卷积、BN、Relu。
而且宽度因子alpha起作用的是pointwise卷积的时候,而分辨率因子depth_multiplier是对DepthwiseConv起作用的。下面能验证。

然后按照论文Table1复现结构的时候,要注意卷积步长,刚开始的正常卷积,步长(2,2)(2,2)(2,2),后面依次为不使用步长和使用步长的深层分离网络结构。

	x = _conv_block(img_input, 32, alpha, strides=(2, 2))x = _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id=1)x = _depthwise_conv_block(x, 128, alpha, depth_multiplier,strides=(2, 2), block_id=2)x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id=3)x = _depthwise_conv_block(x, 256, alpha, depth_multiplier,strides=(2, 2), block_id=4)x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id=5)x = _depthwise_conv_block(x, 512, alpha, depth_multiplier,strides=(2, 2), block_id=6)x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=7)x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=8)x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=9)x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=10)x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=11)x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier,strides=(2, 2), block_id=12)x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id=13)

验证问题

上面说了关于resolution multiplier的理解论文和tf官方实现,不清楚原因所在。

先来验证一下论文3.3节的width Multiplier,直接用Keras自带的模型:

import tensorflow as tf
mobilenet=tf.keras.applications.MobileNet(alpha=0.25,depth_multiplier=1,weights=None)
#mobilenet=tf.keras.applications.MobileNet(alpha=1,depth_multiplier=1,weights=None)
mobilenet.summary()

在这里插入图片描述

可以发现在第一次卷积减少了1/41/41/4的特征图,后面每次pwpwpw的时候也减少了1/41/41/4的特征图,这里的pw就是point wise卷积,所以width multiplier是在(1,1)(1,1)(1,1)卷积的时候减少卷积核数量

再来看看resolution multiplier

  • 输入小数的时候

    mobilenet=tf.keras.applications.MobileNet(alpha=1,depth_multiplier=0.5,weights=None)
    

    直接报错:

    TypeError: Value passed to parameter 'shape' has DataType float32 not in list of allowed values: int32, int64
    
  • 对比1和2的时候:

    mobilenet=tf.keras.applications.MobileNet(alpha=1,depth_multiplier=1,weights=None)
    #mobilenet=tf.keras.applications.MobileNet(alpha=1,depth_multiplier=2,weights=None)
    model.summary()
    

在这里插入图片描述

可以发现在dw的时候通道数增大了一倍,原理就是在(3,3)(3,3)(3,3)的深度卷积的时候,增加了通道数。这让我很郁闷啊,论文和tf的代码不一样哎,原文是减少,是小数。
这个在Keras中也有对应描述:

depth_multiplier: depth multiplier for depthwise convolution. This is called the resolution multiplier in the MobileNet paper

貌似Resolution Multiplier就是针对depthwise卷积的
如果有大佬能帮忙解惑,将不甚感激昂。
------------------更新日志2019-8-7---------------------------------
与评论区讨论了一下,这个Resolution Multiplier姑且就认为是改变输入大小吧,Keras官方的MobileNet不是默认支持四种输入大小么:

------------------------------------------------------------------------Resolution      | ImageNet Acc | Multiply-Adds (M) | Params (M)
------------------------------------------------------------------------
|  1.0 MobileNet-224  |    70.6 %    |        529        |     4.2     |
|  1.0 MobileNet-192  |    69.1 %    |        529        |     4.2     |
|  1.0 MobileNet-160  |    67.2 %    |        529        |     4.2     |
|  1.0 MobileNet-128  |    64.4 %    |        529        |     4.2     |
------------------------------------------------------------------------

MobileNetV2

理论

文章首先提出了一个概念Linear Bottlenecks,这个BottolenecksResNet中出现过,戳这篇博客看,讲道理就是shortcut连接,说明在MobileNetV2引入了ResNet的思想。

作者做了一个假设:神经网络中的兴趣流行(manifold of interest)可以被嵌入到低维子空间中,个人觉得像是PCA的理论,提取主分量就是降维到低维子空间了。比如某卷积层的第ddd个通道的像素所编码的信息,实际就存在某个特定的流行空间中,而这个流行空间可以嵌入到低维子空间。

基于这个假设,降低每一层的维度就能降低操作空间的维度,这在MobileNetV1中使用过,利用width multiplier减少每个block输出的特征图数目。但是,神经网络中的非线性变换打破了这个想法,比如Relu经常导致某些神经元坏死,将会损失信息。

随后又做了个假设,既然Relu在某个通道会丢失信息,搞不好在其它通道仍然保存着信息。

总结起来就是,作者认为,感兴趣的流行,一定存在于高维激活空间的一个低维子空间中:

  • 如果在Relu激活后仍然保持着非零,那就是线性变换。
  • Relu可以保存输入流行的完整信息,但是仅仅是在输入流行存在于输入空间的一个低维子空间的前提下。

这样就可以优化网络结构:假设兴趣流行在低维空间中,我们就可以通过插入linear bottleneck到卷积模块中,进而捕捉到这种低维信息。具体插入到哪里,看下面右图中带斜线的块,就是线性激活块。

在这里插入图片描述

左图是标准的Resnet模块,先(1,1)(1,1)(1,1)的卷积核卷积,然后Relu,再用(3,3)(3,3)(3,3)的卷积核卷积,然后Relu,再用(1,1)(1,1)(1,1)的卷积核卷积,再Relu,将最终的结果与输入相加。

右图是文章的Inverted Residual,带虚线的是不适用非线性激活。先(1,1)(1,1)(1,1)的卷积核卷积,注意这里要用大量的卷积核增加特征图数目,再用Relu激活,再用(3,3)(3,3)(3,3)的深层卷积depthwise conv卷积,使用Relu激活,再使用(1,1)(1,1)(1,1)的卷积核卷积,这里使用较少的卷积核,降低输出特征图的数目,最后来一次shortcut连接输入和输出特征图。

从上图可以发现,传统的Resnet输入和输出通道数都比较大,而Invert residual的通道数都比较小,只有中间极大增加了通道数,是想用大量的通道来确保流行信息被保存下来,后面用了深层卷积,确保了参数与计算量的降低。

Resnet通常称沙漏形状,先减少通道数,再增加通道数。Invert Residual刚好相反,先增加通道数,再降维;这就是为啥称为逆Res

初步总结,MobilenetV2的特点:

  • 依旧使用MobileNetV1的特点,使用depthwisepointwise的卷积,但是增加了残差结构连接
  • 与残差网的漏斗形状(两边粗,中间细)相反,MobileNetv2在中间增加了通道数,呈现两边细中间粗的形状,因为作者相信大量的低维空间能够捕捉到高维特征中有用的信息,所以要扩充通道数
  • Block最后一层,为了不破坏提取的信息,去掉了Relu激活,而直接shortcut连接

结构总结:

  • (1,1)(1,1)(1,1)pointwise卷积,提高通道数,结果Relu
  • (3,3)(3,3)(3,3)depthwise卷积,提特征,结果Relu
  • (1,1)(1,1)(1,1)pointwise卷积,单纯降维,不Relu
  • 输入与输出的shortcut

代码

Keras中做了一下简单的处理:

先做常规卷积,卷积核大小(3,3)(3,3)(3,3),步长(2,2)(2,2)(2,2),使用valid的卷积方式,降低特征图大小:

x = layers.ZeroPadding2D(padding=correct_pad(backend, img_input, 3),name='Conv1_pad')(img_input)
x = layers.Conv2D(first_block_filters,kernel_size=3,strides=(2, 2),padding='valid',use_bias=False,name='Conv1')(x)
x = layers.BatchNormalization(axis=channel_axis,epsilon=1e-3,momentum=0.999,name='bn_Conv1')(x)
x = layers.ReLU(6., name='Conv1_relu')(x)

其中correct_pad是为了让卷积能够正常进行,也就是下式能整除,避免特征图变成小数宽度:
n−m+paddingstride+1\frac{n-m+padding}{stride}+1 stridenm+padding+1
接下来便是使用Invert Residual模型结构:

    x = _inverted_res_block(x, filters=16, alpha=alpha, stride=1,expansion=1, block_id=0)x = _inverted_res_block(x, filters=24, alpha=alpha, stride=2,expansion=6, block_id=1)x = _inverted_res_block(x, filters=24, alpha=alpha, stride=1,expansion=6, block_id=2)x = _inverted_res_block(x, filters=32, alpha=alpha, stride=2,expansion=6, block_id=3)x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1,expansion=6, block_id=4)x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1,expansion=6, block_id=5)x = _inverted_res_block(x, filters=64, alpha=alpha, stride=2,expansion=6, block_id=6)x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1,expansion=6, block_id=7)x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1,expansion=6, block_id=8)x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1,expansion=6, block_id=9)x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1,expansion=6, block_id=10)x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1,expansion=6, block_id=11)x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1,expansion=6, block_id=12)x = _inverted_res_block(x, filters=160, alpha=alpha, stride=2,expansion=6, block_id=13)x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1,expansion=6, block_id=14)x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1,expansion=6, block_id=15)x = _inverted_res_block(x, filters=320, alpha=alpha, stride=1,expansion=6, block_id=16)

其中alpha是最后一层pointwise(1,1)(1,1)(1,1)卷积的输出特征图数目,expansioninvert residual的第一层pointwise卷积对原始通道数扩充的维度。

注意这里的卷积步长有变化,有(1,1)(1,1)(1,1)(2,2)(2,2)(2,2),所以在步长位(2,2)(2,2)(2,2)的时候,要注意使用correct_pad填充特征图,避免卷积出小数宽高特征图的情况。

还有一点是,Keras实现的时候,第1个block并没有做通道数的扩充:

    if block_id:# Expandx = layers.Conv2D(expansion * in_channels,kernel_size=1,padding='same',use_bias=False,activation=None,name=prefix + 'expand')(x)x = layers.BatchNormalization(axis=channel_axis,epsilon=1e-3,momentum=0.999,name=prefix + 'expand_BN')(x)x = layers.ReLU(6., name=prefix + 'expand_relu')(x)else:prefix = 'expanded_conv_'

随后进入(3,3)(3,3)(3,3)depthwise卷积提取特征,注意如果步长是(2,2)(2,2)(2,2),要核对是否能够满足步长卷积:

    if stride == 2:x = layers.ZeroPadding2D(padding=correct_pad(backend, x, 3),name=prefix + 'pad')(x)x = layers.DepthwiseConv2D(kernel_size=3,strides=stride,activation=None,use_bias=False,padding='same' if stride == 1 else 'valid',name=prefix + 'depthwise')(x)x = layers.BatchNormalization(axis=channel_axis,epsilon=1e-3,momentum=0.999,name=prefix + 'depthwise_BN')(x)x = layers.ReLU(6., name=prefix + 'depthwise_relu')(x)

随后直接做投影,也就是使用pointwise卷积降低通道数,不用Relu:

    # Projectx = layers.Conv2D(pointwise_filters,kernel_size=1,padding='same',use_bias=False,activation=None,name=prefix + 'project')(x)x = layers.BatchNormalization(axis=channel_axis,epsilon=1e-3,momentum=0.999,name=prefix + 'project_BN')(x)

最后如果输入输出的通道数相同,就shortcut:

if in_channels == pointwise_filters and stride == 1:return layers.Add(name=prefix + 'add')([inputs, x])
return x

为了更清晰理解每层的作用,我这里对部分层的特征图大小做了一下输出截图:

from keras.applications.mobilenetv2 import MobileNetV2
model = MobileNetV2(weights='imagenet')
model.summary()

刚开始的卷积->BN->Relu:

在这里插入图片描述

随后是最开始的invert residual 块,没有维度扩充,只有depthwise卷积和pointwise映射:

在这里插入图片描述

可以发现输出维度由32降低成16。

再看第二块invert residual块:

在这里插入图片描述

上来就把维度由输入的16个通道提升为96个通道,然后使用(3,3)(3,3)(3,3)的卷积,步长为(2,2)(2,2)(2,2),得到特征图大小是
113−32+1=56\frac{113-3}{2}+1=56 21133+1=56
最后投影的时候,又将通道数降低了4倍,此时没有shorcut,因为输入输出的特征图通道和大小都不同。

再看第三个invert block

在这里插入图片描述

同样,一上来就把通道数由24扩充6倍到144,然后步长为(1,1)(1,1)(1,1)(3,3)(3,3)(3,3)卷积,最后降低通道数,此时输入和输出的特征图数目与大小都相同,为(56,56,24)(56,56,24)(56,56,24),直接shortcut加起来。

第四个invert block:
在这里插入图片描述

老样子:24扩充6倍到144,用(3,3)(3,3)(3,3)的卷积核步长位(2,2)(2,2)(2,2)卷积,最后降低通道数。

总结

主要稍微总结了一下MobileNet的两个版本的设计思路

MobileNetV1:使用depthwise卷积和(1,1)(1,1)(1,1)的卷积核代替传统的卷积算法

MobileNetV2:在版本1的基础上加入了残差网的思想,传统残差网是先将特征图数目变小,然后再变大,最后shortcut,而MobileNetV2之所以称为invert residual就是因为反过来,先将特征图数目变大,再变小,这是因为作者感觉特征图太少会受到Relu的影响,表达能力不强,那么就使用一堆的特征图去提取特征,总有一个能提取到有用信息,最后为了避免信息被破话,就不用Relu,直接shortcut了。

戳这里是V1的实现,戳这里是V2的实现,都是官方的,具体使用方法,可以戳Keras官方文档的这里仿写。

后续会继续学习其它的轻量级网络设计方法,包括InceptionXceptionShuflleNet

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246592.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【TensorFlow-windows】keras接口——ImageDataGenerator裁剪

前言 Keras中有一个图像数据处理器ImageDataGenerator,能够很方便地进行数据增强,并且从文件中批量加载图片,避免数据集过大时,一下子加载进内存会崩掉。但是从官方文档发现,并没有一个比较重要的图像增强方式&#x…

【TensorFlow-windows】TensorBoard可视化

前言 紧接上一篇博客,学习tensorboard可视化训练过程。 国际惯例,参考博客: MNIST机器学习入门 Tensorboard 详解(上篇) Tensorboard 可视化好帮手 2 tf-dev-summit-tensorboard-tutorial tensorflow官方mnist_…

深度学习特征归一化方法——BN、LN、IN、GN

前言 最近看到Group Normalization的论文,主要提到了四个特征归一化方法:Batch Norm、Layer Norm、Instance Norm、Group Norm。此外,论文还提到了Local Response Normalization(LRN)、Weight Normalization(WN)、Batch Renormalization(BR)…

【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据

前言 之前使用tensorflow和keras的时候,都各自有一套数据读取方法,但是遇到一个问题就是,在训练的时候,GPU的利用率忽高忽低,极大可能是由于训练过程中读取每个batch数据造成的,所以又看了tensorflow官方的…

骨骼动画——论文与代码精读《Phase-Functioned Neural Networks for Character Control》

前言 最近一直玩CV,对之前学的动捕知识都忘得差不多了,最近要好好总结一下一直以来学习的内容,不能学了忘。对2017年的SIGGRAPH论文《Phase-Functioned Neural Networks for Character Control》进行一波深入剖析吧,结合源码。 额…

颜色协调模型Color Harmoniztion

前言 最近做换脸,在肤色调整的那一块,看到一个有意思的文章,复现一波玩玩。不过最后一步掉链子了,有兴趣的可以一起讨论把链子补上。 主要是github上大佬的那个复现代码和原文有点差异,而且代码复杂度过高&#xff0…

Openpose推断阶段原理

前言 之前出过一个关于openpose配置的博客,不过那个代码虽然写的很好,而且是官方的,但是分析起来很困难,然后再opencv相关博客中找到了比较清晰的实现,这里分析一波openpose的推断过程。 国际惯例,参考博…

换脸系列——眼鼻口替换

前言 想着整理一下换脸相关的技术方法,免得以后忘记了,最近脑袋越来越不好使了。应该会包含三个系列: 仅换眼口鼻;换整个面部;3D换脸 先看看2D换脸吧,网上已经有现成的教程了,这里拿过来整理一…

换脸系列——整脸替换

前言 前面介绍了仅替换五官的方法,这里介绍整张脸的方法。 国际惯例,参考博客: [图形算法]Delaunay三角剖分算法 维诺图(Voronoi Diagram)分析与实现 Delaunay Triangulation and Voronoi Diagram using OpenCV (…

3D人脸重建——PRNet网络输出的理解

前言 之前有款换脸软件不是叫ZAO么,分析了一下,它的实现原理绝对是3D人脸重建,而非deepfake方法,找了一篇3D重建的论文和源码看看。这里对源码中的部分函数做了自己的理解和改写。 国际惯例,参考博客: 什…

tensorflow官方posenet模型解析

前言 tensorflow官方有个姿态估计项目,这个输入和openpose还有点不一样,这里写个单人情况下的模型输出解析方案。 国际惯例,参考博客: 博客: 使用 TensorFlow.js 在浏览器端上实现实时人体姿势检测 tensorflow中posnet的IOS代…

tensorflow2安装时候的一个dll找不到的错误

电脑环境: vs2015python3.7.6,使用anaconda安装的CUDA 10.1cuDnn 7.6.5tensorflow2.1.0 错误内容 File "C:\Users\zb116\anaconda3\lib\imp.py", line 242, in load_modulereturn load_dynamic(name, filename, file)File "C:\Users\z…

PCA、SVD、ZCA白化理论与实现

简介 在UFLDL中介绍了主成分分析这一块的知识,而且当时学机器学习的时候,老师是将PCA和SVD联系起来将的,同时UFLDL也讲到了使用PCA做数据白化whitening处理,这个词经常在论文里面看到。 国际惯例,参考博客&#xff1…

OpenCV使用Tensorflow2-Keras模型

前言 最近工作上需要在C上快速集成Tensorflow/Keras训练好的模型,做算法验证。首先想到的就是opencv里面的dnn模块了,但是它需要的格式文件比较郁闷,是pb格式的模型,但是keras通常保存的是h5文件,查阅了很多资料&…

3D人脸表情驱动——基于eos库

前言 之前出过三篇换脸的博文,遇到一个问题是表情那一块不好处理,可行方法是直接基于2D人脸关键点做网格变形,强行将表情矫正到目标人脸,还有就是使用PRNet的思想,使用目标人脸的顶点模型配合源人脸的纹理&#xff0c…

3D姿态估计——ThreeDPose项目简单易用的模型解析

前言 之前写过tensorflow官方的posenet模型解析,用起来比较简单,但是缺点是只有2D关键点,本着易用性的原则,当然要再来个简单易用的3D姿态估计。偶然看见了ThreeDPose的项目,感觉很强大的,所以把模型扒下来…

简易的素描图片转换流程与实现

前言 之前经常在网上看到用PS实现真实图片到素描图片的转换,但是流程都大同小异,身为一只程序猿,必须来个一键转化额。 国际惯例,参考博客: Photoshop基础教程:混合模式原理篇 颜色减淡的原理讲解以及应…

一个简单好用的磨皮祛斑算法理论和python实现

前言 最近看了一个磨皮算法祛斑感觉效果不错,效果图看文末就行,个人觉得效果非常不错滴。 国际惯例,参考博客: 磨皮算法的源码:YUCIHighPassSkinSmoothing How To Smooth And Soften Skin With Photoshop 图像算法…

OpenVINO——配置与道路分割案例

前言 最近看到了一个深度学习库OpenVINO,专门用于Intel硬件上部署深度学习模型,其内置了非常非常多使用的预训练模型,比如道路分割、人脸提取、3D姿态估计等等。但是配置和调用有点小恶心,这里以道路分割为例,展示如何…

图像颜色迁移《color transfer between images》

前言 前段时间,在深度学习领域不是有个比较火的方向叫风格迁移的嘛,对于我这种不喜欢深度学习那种不稳定结果的人来说,还是想看看传统图像处理领域有什么类似的技术,发现了一个颜色迁移的算法,很久前的论文了。 国际…