【TensorFlow-windows】扩展层之STN

前言

读TensorFlow相关代码看到了STN的应用,搜索以后发现可替代池化,增强网络对图像变换(旋转、缩放、偏移等)的抗干扰能力,简单说就是提高卷积神经网络的空间不变性。

国际惯例,参考博客:

理解Spatial Transformer Networks

github-STN

Deep Learning Paper Implementations: Spatial Transformer Networks - Part I

Deep Learning Paper Implementations: Spatial Transformer Networks - Part II
将STN加入网络训练的一个关于图像隐写术的案例:StegaStamp

理论

图像变换

因为图像的本质就是矩阵,那么图像变换就是矩阵变换,先复习一下与图像相关的矩阵变换。假设MMM为变换矩阵,NNN为图像,为了简化表达,设MMM的维度是(2,2)(2,2)(2,2)NNN代表像素点坐标,则维度是(2,1)(2,1)(2,1),以下操作均为对像素位置的调整操作,而非对像素值的操作。

  • 缩放
    M×N=[p00q]×[xy]=[pxqy]M\times N=\begin{bmatrix} p&0\\ 0&q \end{bmatrix}\times \begin{bmatrix} x\\y \end{bmatrix}=\begin{bmatrix} px\\qy \end{bmatrix} M×N=[p00q]×[xy]=[pxqy]

  • 旋转:绕原点顺时针旋转θ\thetaθ
    M×N=[cos⁡θ−sin⁡θsin⁡θcos⁡θ]×[xy]=[xcos⁡θ−ysin⁡θxsin⁡θ+ycos⁡θ]M\times N=\begin{bmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta \end{bmatrix}\times \begin{bmatrix} x\\y \end{bmatrix}=\begin{bmatrix} x\cos\theta-y\sin\theta\\x\sin\theta+y\cos\theta \end{bmatrix} M×N=[cosθsinθsinθcosθ]×[xy]=[xcosθysinθxsinθ+ycosθ]

  • 错切(shear):类似于将字的正体变成斜体
    M×N=[1mn1]×[xy]=[x+myy+nx]M\times N=\begin{bmatrix} 1&m\\ n&1 \end{bmatrix}\times \begin{bmatrix} x\\y \end{bmatrix}=\begin{bmatrix} x+my\\y+nx \end{bmatrix} M×N=[1nm1]×[xy]=[x+myy+nx]

  • 平移:要转换为齐次矩阵做平移
    M′×N′=[10a01b]×[xy1]=[x+ay+b]M'\times N'=\begin{bmatrix} 1&0&a\\ 0&1&b \end{bmatrix}\times \begin{bmatrix} x\\y\\1 \end{bmatrix}=\begin{bmatrix} x+a\\y+b \end{bmatrix} M×N=[1001ab]×xy1=[x+ay+b]

盗用参考博客的图解就是:
在这里插入图片描述

注意,我们进行多次变换的时候有多个变换矩阵,如果每次计算一个变换会比较耗时,参考矩阵的乘法特性,我们可以先将变换矩阵相乘,得到一个完整的矩阵代表所有变换,最后乘以图像,就可将图像按照组合变换顺序得到变换图像。这个代表一系列的变换的矩阵通常表示为:
M=[abcdef]M=\begin{bmatrix} a&b&c\\d&e&f \end{bmatrix} M=[adbecf]
因为直接计算位置的值,很可能得到小数,比如将(3,3)(3,3)(3,3)的图像放大到(9,9)(9,9)(9,9),也就是放大3倍,那么新图像(8,8)(8,8)(8,8)位置的像素就是原图(8/3,8/3)(8/3,8/3)(8/3,8/3)位置的像素,但是像素位置不可能是小数,因而出现了解决方案:双线性插值

双线性插值

先复习一下线性插值,直接去看之前写的这篇博客,知道(x1,y1)(x_1,y_1)(x1,y1)(x2,y2)(x_2,y_2)(x2,y2),求(x1,x2)区间内的点(x_1,x_2)区间内的点(x1,x2)xxx位置的y值,结果是:
y=x−x2x1−x2y1+x−x1x2−x1y2y=\frac{x-x_2}{x_1-x_2}y_1+\frac{x-x_1}{x_2-x_1}y_2 y=x1x2xx2y1+x2x1xx1y2
可以发现线性插值是针对一维坐标的,即给xxxyyy,但是双线性插值是针对二维坐标点的,即给(x,y)(x,y)(x,y)求值QQQ。方法是先在xxx轴方向做两次线性插值,再在yyy轴上做一次线性插值。

设需要求(x,y)(x,y)(x,y)处的值,我们需要预先知道其附近四个坐标点及其对应的值,如:

  • (x,y)(x,y)(x,y)左下角坐标为(x1,y1)(x_1,y_1)(x1,y1),值为Q1Q_1Q1
  • (x,y)(x,y)(x,y)右下角坐标为(x2,y1)(x_2,y_1)(x2,y1), 值为Q2Q_2Q2
  • (x,y)(x,y)(x,y)左上角坐标为(x1,y2)(x_1,y_2)(x1,y2), 值为Q3Q_3Q3
  • (x,y)(x,y)(x,y)右上角坐标为(x2,y2)(x_2,y_2)(x2,y2),值为Q4Q_4Q4

首先对下面的(x1,y1)(x_1,y_1)(x1,y1)(x2,y1)(x_2,y_1)(x2,y1)做线性插值,方法是把它两看做一维坐标(x1,Q1)(x_1,Q_1)(x1,Q1)(x2,Q2)(x_2,Q2)(x2,Q2),得到:
P1=x−x2x1−x2Q1+x−x1x2−x1Q2P_1=\frac{x-x_2}{x_1-x_2}Q_1+\frac{x-x_1}{x_2-x_1}Q_2 P1=x1x2xx2Q1+x2x1xx1Q2
同理得到上面的两个坐标(x1,y2)(x_1,y_2)(x1,y2)(x2,y2)(x_2,y_2)(x2,y2)的插值结果,也就是(x1,Q3)(x_1,Q_3)(x1,Q3)(x2,Q4)(x_2,Q_4)(x2,Q4)的线性插值结果:
P2=x−x2x1−x2Q3+x−x1x2−x1Q4P_2=\frac{x-x_2}{x_1-x_2}Q_3+\frac{x-x_1}{x_2-x_1}Q_4 P2=x1x2xx2Q3+x2x1xx1Q4
再对(y1,P1)(y_1,P_1)(y1,P1)(y2,P2)(y_2,P_2)(y2,P2)做线性插值:
P=x−y2y1−y2P1+y−y1y2−y1P2P=\frac{x-y_2}{y_1-y_2}P_1+\frac{y-y_1}{y_2-y_1}P_2 P=y1y2xy2P1+y2y1yy1P2
解决上面图像变换的问题,假设变换后的坐标不是整数,那么就选择这个坐标四个角的坐标的双线性插值的结果,比如(8/3,8/3)(8/3,8/3)(8/3,8/3)位置的像素就是(2,2),(3,2),(2,3),(3,3)(2,2),(3,2),(2,3),(3,3)(2,2),(3,2),(2,3),(3,3)位置像素的双线性插值结果。

总之就是先计算目标图像像素在源图像中的位置,然后得到源图像位置是小数,针对小数位置的四个顶点做双线性插值。

上面就是STN做的工作,也可以发现STN接受的参数就是6个,接下来看看为什么STN能提高卷积网络的旋转、平移、缩放不变性。

总结一下:

图像处理中的仿射变换通常包含三个步骤:

  • 创建由(x,y)(x,y)(x,y)组成的采样网格,比如(400,400)(400,400)(400,400)的灰度图对应创建一个同样大小的网格。
  • 将变换矩阵应用到采样网格上
  • 使用插值技术从原图中计算变换图的像素值

池化

强行翻译一波这篇文章关于池化的部分,建议看原文,这里摘取个人认为重要部分:

池化在某种程度上增加了模型的空间不变性,因为池化是一种下采样技术,减少了每层特征图的空间大小,极大减少了参数数量,提高了运算速度。

池化提供的不变性确切来说是什么?池化的思路是将一个图像切分成多个单元,这些复杂单元被池化以后得到了可以描述输出的简单的单元。比如有3张不同方向的数字7的图像,池化是通过图像上的小网格来检测7,不受7的位置影响,因为通过聚集的像素值,我们得到的信息大致一样。个人觉得,作者的本意是单看小网格,是有很多一样的块。

池化的缺点在于:

  • 丢失了75%的信息(应该是(2,2)(2,2)(2,2)的最大值池化方法),意味着我们一定丢了是精确的位置信息。有人会问,这样可以增加空间鲁棒性哇。然而,对于视觉识别人物,空间信息是非常重要的。比如分类猫的时候,知道猫的胡须的位置相对于鼻子的位置有可能很重要,但是如果使用最大池化,可能丢失了这个信息。
  • 池化是局部的且预定义好的。一个小的接受域,池化操作的影响仅仅是针对更深的网络层(越深感受野越大),也就是中间的特征图可能受到严重的输入失真的影响。我们不能任意增加接受域,这样会过度下采样。

主要结论就是卷积网络对于相对大的输入失真不具有不变性。

The pooling operation used in convolutional neural networks is a big mistake and the fact that it works so well is a disaster. (Geoffrey Hinton, Reddit AMA)

STN理论

STN的全称是Spatial Transformer Networks,空间变换网络。时空变换机制就是通过给CNN提供显式的空间变换能力,以解决上述池化出现的问题。有三种特性:

  • Modular:STN能够被插入到网络的任意地方,仅需很小的调整
  • differentiable:STN可以通过反向传播训练
  • dynamic:STN是对每个输入样本的一个特征图做空间变换,而池化是针对所有样本。

在这里插入图片描述

上图是STN网络的主要框架。所以到底什么是空间变换?通过结构图可发现模型包含三部分:localisation networkgrid generatorsampler

Localisation Network

主要是提取被应用到输入特征图上的仿射变换的参数θ\thetaθ,网络结构是:

  • 输入:大小为(H,W,C)(H,W,C)(H,W,C)的特征图UUU
  • 输出:大小为(6,1)(6,1)(6,1)的变换矩阵θ\thetaθ
  • 结构:全连接或者卷积

Parametrised Sampling Grid

输出参数化的采样网格,是一系列的点,每个输入特征图能够产生期望的变换输出。

具体就是:网格生成器首先产生于输入图像UUU大小相同的标准网格,然后将仿射变换应用到网格。公式表达即,假设输入图的索引是(xt,yt)(x^t,y^t)(xt,yt),将θ\thetaθ代表的变换应用到坐标上得到新的坐标:
[xsys]=[θ1θ2θ3θ4θ5θ6]×[xtyt1]\begin{bmatrix} x^s\\y^s \end{bmatrix}=\begin{bmatrix} \theta_1&\theta_2&\theta_3\\\theta_4&\theta_5&\theta_6 \end{bmatrix}\times\begin{bmatrix} x^t\\y^t\\1 \end{bmatrix} [xsys]=[θ1θ4θ2θ5θ3θ6]×xtyt1
Differentiable Image Sampling

依据输入特征图和参数化采样网格,我们可以利用双线性插值方法获得输出特征图。注意,这一步我们可以通过制定采样网格的大小执行上采样或者下采样,很像池化。

在这里插入图片描述

左图使用了单位变换,右图使用了旋转的仿射变换。

【注】因为双线性插值是可微的,所以STN可以作为训练网络的一部分。

代码

利用STN前向过程做图像变换

GitHub上有作者提供了源码,也可以用pip直接安装。

代码直接贴了,稍微改了一点点:

导入包

import tensorflow as tf
import cv2
import numpy as npfrom stn import spatial_transformer_network as transformer

读入图像,转换为四维矩阵:

img=cv2.imread('test_img.jpg')
img=np.array(img)
H,W,C=img.shape
img=img[np.newaxis,:]
print(img.shape)

旋转变换的角度

degree=np.deg2rad(45)
theta=np.array([[np.cos(degree),-np.sin(degree),0],[np.sin(degree),np.cos(degree),0]
])

构建网络结构

x=tf.placeholder(tf.float32,shape=[None,H,W,C])
with tf.variable_scope('spatial_transformer'):theta=theta.astype('float32')theta=theta.flatten()loc_in=H*W*C #输入维度loc_out=6 #输出维度W_loc=tf.Variable(tf.zeros([loc_in,loc_out]),name='W_loc')b_loc=tf.Variable(initial_value=theta,name='b_loc')#运算fc_loc=tf.matmul(tf.zeros([1,loc_in]),W_loc)+b_loch_trans=transformer(x,fc_loc)

把图像喂进去,并显示图像

init=tf.global_variables_initializer()
with tf.Session() as sess:sess.run(init)y=sess.run(h_trans,feed_dict={x:img})print(y.shape)y=np.squeeze(np.array(y,dtype=np.uint8))
print(y.shape)
cv2.imshow('trasformedimg',y)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

重点关注网络构建

权重w_loc是全零的大小为(HWC,6)(HWC,6)(HWC,6)的矩阵,偏置b_loc是大小为(1,6)(1,6)(1,6)的向量,这样经过运算

fc_loc=tf.matmul(tf.zeros([1,loc_in]),W_loc)+b_loc

得到的其实就是我们指定的旋转角度对应的6维变换参数,最后利用变换函数transformer执行此变换就行了。

将STN加入网络中训练

主要参考StegaStamp作者的写法,这里做STN部分加入网络的方法:
输入一张图片到如下网络结构(Keras网络结构搭建语法):

stn_params = Sequential([Conv2D(32, (3, 3), strides=2, activation='relu', padding='same'),Conv2D(64, (3, 3), strides=2, activation='relu', padding='same'),Conv2D(128, (3, 3), strides=2, activation='relu', padding='same'),Flatten(),Dense(128, activation='relu')])

得到(1,128)(1,128)(1,128)维的向量,其实用一个网络替换上面前向计算中的loc_in,目的是为了得到二维图像对应的一维信息
后面的过程就和前向计算一样了,定义权重和偏置:

W_fc1 = tf.Variable(tf.zeros([128, 6]), name='W_fc1')
b_fc1 = tf.Variable(initial_value=initial, name='b_fc1')

然后利用一维信息得到图像变换所需的6个值:

x = tf.matmul(stn_params, self.W_fc1) + self.b_fc1

最后利用STN库将变换应用到图像中,得到下一层网络结构的输入

transformed_image = stn_transformer(image, x, [self.height, self.width, 3])

可以看出,STN加入到网络后,训练参数有:

  • 二维图像到一维特征向量的卷积+全连接网络的权重和偏置
  • 一维向量到6维变换参数的权重和偏置

总结

通篇就是对池化方案的改变,使用STN能够增加网络的变换不变性,比池化的效果更好。

代码:

链接:https://pan.baidu.com/s/1kDs9T-Mf1F_mzQyvslcROA
提取码:crdu

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

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

相关文章

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

前言 轻量级神经网络中,比较重要的有MobileNet和ShuffleNet,其实还有其它的,比如SqueezeNet、Xception等。 本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。 国际惯例,参考博客: 纵…

【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姿态估计等等。但是配置和调用有点小恶心,这里以道路分割为例,展示如何…