【theano-windows】学习笔记十三——去噪自编码器

前言

上一章节学习了卷积的写法,主要注意的是其实现在theano.tensor.nnettheano.sandbox.cuda.dnn中都有对应函数实现, 这一节就进入到无监督或者称为半监督的网络构建中. 首先是自编码器(Autoencoders)和降噪自编码器(denoising Autoencoders)

国际惯例, 参考网址:

Denoising Autoencoders (dA)

Learning deep architectures for {AI}

Extracting and Composing Robust Features with Denoising Autoencoders

降噪自动编码器(Denoising Autoencoder)

理论

自编码器

其实就是先将dd维输入数据x[0,1]映射到dd′维的隐层y[0,1]y∈[0,1], 然后再重构回去得到dd维的z的过程. 这两步的数学表达式为:

y=s(Wx+bh)z=s(Wy+br)y=s(W∗x+bh)z=s(W′∗y+br)

依据第三个参考文献 ,这里面 xx代表输入数据,W代表输入层到隐层的 (d×d)(d′×d)维权重, bhbh是隐层偏置, ss代表激活函数,所以编码层参数就是θ=(W,b), 反向映射(隐层到重构层)的权重 WW′可以被约束为 W=WTW′=WT, 称为绑定权重( tied weights),所以解码层的参数就是 θ=(W,b)θ′=(W′,b′)

损失函数可以是平均重构误差

θ,θ=argminθ,θ1ni=1nL(X(i),z(i))=argminθ,θ1ni=1nL(x(i),gθ(fθ(x(i))))θ∗,θ′∗=arg⁡minθ,θ′1n∑i=1nL(X(i),z(i))=arg⁡minθ,θ′1n∑i=1nL(x(i),gθ′(fθ(x(i))))

其中 LL可以是传统的方差损失
L(x,z)=∥xz2

如果 xxz是位向量或者位(伯努利)概率向量,那么损失就可以是重构交叉熵
LH(x,z)=H(BxBz)=k=1d[xklogzk+(1xk)log(1zk)]LH(x,z)=H(Bx∥Bz)=−∑k=1d[xklog⁡zk+(1−xk)log⁡(1−zk)]

而根据第二篇参考博客所说, 如果隐层是线性的, 且以均方误差为损失去训练网络,那么 kk个隐单元学到的就是将输入投影到数据的前k个主分量中, 可以看成是一个PCA了.

自编码器所期望的是隐层的分布表示能够捕捉到数据变化的主要因素, 对于所有的输入xx, 它会是一个很好的有损压缩,学习使得它对训练数据的压缩更好,而且也希望对其它数据也更好, 当然并非是针对任意的输入.

有一个严重的问题是,如果没有其他任何的限制,具有n维输入的自编码和至少nn维的隐单元(编码层), 那么可能学习到的就是恒等函数(‘identity function ‘), 很多隐神经元就没用了(仅仅是将输入数据复制了一遍), 令人惊讶的是, 实际上如果我们使用随机梯度下降训练方法, 即使是具有比输入层神经元数更多的隐单元(过完备overcomplete)的非线性自编码器, 也产生了有用的特征表示, 一个简单的原因是提前停止的随机梯度下降算法与l2正则项很像. 为了达到对连续数据更好的重构效果,具有非线性隐单元层的自编码器在第一层需要很小的权重(将隐单元的非线性引入到它们线性状态中), 第二层需要很大的权重. 如果是二值输入, 需要非常大的权重去完全最小化重构误差.但是由于显式或者隐式的正则化很难得到较大的权重解, 优化算法找到的编码单元(隐层)仅仅对于训练集相似的样本效果好, 这就是我们希望的. 这也就以为这特征表示在探索训练集的统计规律而非去学习恒等函数.

有很多方法去阻止自编码器学习到恒等函数, 同时在隐层表示中获取到有用的知识. 除了给自编码器添加隐式或者显式的权重正则, 另一个方法是给编码层增加噪声.这实际上是RBM所做的东东. 另一个方法是基于编码的稀疏约束. 但是稀疏或者正则化为了避免学习到恒等函数而降低了表达能力, RBM相对来说表达能力很强, 也不会学习恒等函数, 因为它不仅编码输入, 也通过生成模型的极大似然估计逼近方法捕捉到了输入的统计结构.

降噪自编码器

有一种自编码器就共享了RBM的这个特性,称为降噪自编码器(denoising auto-encoder), 它是最小化对输入的随机损坏变换的重构误差, 最小化生成模型的对数似然的下界.

降噪自编码器做了两件事情:

  • 尝试编码输入
  • 尝试重做对输入的随机损坏处理操作

后者仅仅可以通过捕捉输入之间的非统计依赖.实际上, 随机损坏过程包含将输入的某些值置零.因而降噪自编码器尝试对丢失值进行预测. 训练标准就是重构对数似然

logP(x|c(x^))−log⁡P(x|c(x^))

其中 xx是无损的输入, x^是随机有损输入, c(x^)c(x^)是从 x^x^获得的编码.

降噪自编码两个有趣的属性就是:

  • 它与生成模型对应, 它的训练准则就是生成模型的对数似然的一个界限
  • 他能够被用于修复丢失数据或者多模数据, 因为它就是在有损数据上训练的

代码实现

模型构建与训练

先引入相关库,这里要注意引入一个theano中的随机数生成器,主要用于对数据的有损处理

import theano
import theano.tensor as T
import numpy as np
import os
import cPickle,gzip
from theano.tensor.shared_randomstreams import RandomStreams

然后读数据没啥好说的

#定义读数据的函数,把数据丢入到共享区域
def load_data(dataset):data_dir,data_file=os.path.split(dataset)if os.path.isfile(dataset):with gzip.open(dataset,'rb') as f:train_set,valid_set,test_set=cPickle.load(f)#共享数据集def shared_dataset(data_xy,borrow=True):data_x,data_y=data_xyshared_x=theano.shared(np.asarray(data_x,dtype=theano.config.floatX),borrow=borrow)shared_y=theano.shared(np.asarray(data_y,dtype=theano.config.floatX),borrow=borrow)return shared_x,T.cast(shared_y,'int32')#定义三个元组分别存储训练集,验证集,测试集train_set_x,train_set_y=shared_dataset(train_set)valid_set_x,valid_set_y=shared_dataset(valid_set)test_set_x,test_set_y=shared_dataset(test_set)rval=[(train_set_x,train_set_y),(valid_set_x,valid_set_y),(test_set_x,test_set_y)]return rval

初始化网络结构, 虽然代码长,但是按部就班地写就可以了, 和所有的神经网络定义方法一样, 先初始化参数权重偏置, , 随后定义梯度更新方法, 只不过自编码的提取更新涉及到编码和解码两个阶段, 所以再额外对这两个操作进行定义, 而在输入的时候, 为了切换是去噪自编码还是普通的自编码器, 加入一个有损函数去随机对输入置零

class dA(object):#初始化所需参数,随机初始化,输入,输入单元数,隐单元数, 权重,偏置def __init__(self,rng,input=None,n_visible=784,n_hidden=500,W=None,h_b=None,v_b=None):self.n_visible=n_visibleself.n_hidden=n_hiddenif not W:initial_W=np.asarray(rng.uniform(low=-4*np.sqrt(6./(n_hidden+n_visible)),high=4*np.sqrt(6./(n_hidden+n_visible)),size=(n_visible,n_hidden)),dtype=theano.config.floatX)W=theano.shared(initial_W,name='W',borrow=True)if not h_b:h_b=theano.shared(np.zeros(n_hidden,dtype=theano.config.floatX),borrow=True)if not v_b:v_b=theano.shared(np.zeros(n_visible,dtype=theano.config.floatX),borrow=True)self.W=Wself.vb=v_bself.hb=h_bself.W_prime=self.W.Tif input is None:self.x=T.dmatrix(name='input')else:self.x=inputself.params=[self.W,self.vb,self.hb]#编码阶段def get_hidden_value(self,input):return T.nnet.sigmoid(T.dot(input,self.W)+self.hb)#解码阶段def get_reconstructed_input(self,hidden):return T.nnet.sigmoid(T.dot(hidden,self.W_prime)+self.vb)#是否有损输入,如果是有损输入就是降噪自编码器了def get_corrupted_input(self,input,corruption_level):srng=RandomStreams(np.random.randint(2**30))return srng.binomial(size=input.shape,n=1,p=1-corruption_level,dtype=theano.config.floatX)*input#更新参数def get_cost_updates(self,corruption_level,learning_rate):tilde_x=self.get_corrupted_input(self.x,corruption_level)#有损数据y=self.get_hidden_value(tilde_x)#编码z=self.get_reconstructed_input(y)#解码#损失函数L=-T.sum(self.x*T.log(z)+(1-self.x)*T.log(1-z),axis=1)cost=T.mean(L)#参数梯度gparams=T.grad(cost,self.params)#更新权重偏置updates=[(param,param-learning_rate*gparam) for param,gparam in zip(self.params,gparams)]return (cost,updates)

定义训练过程,这里就不用那个提前停止算法了,直接让他训练15次, 训练方法照旧, 重点注意的是模型结构的定义, 建议将前面的MLPCNN的博客对网络结构的定义部分与dA的网络结构的定义对比着看. 保存模型这里直接保存权重和偏置算了.

#定义训练过程
def test_dA(learning_rate=0.1,n_epoches=15,dataset='mnist.pkl.gz',n_visible=28*28,n_hidden=500,corruption_level=0.3,batch_size=20):#数据集datasets=load_data(dataset=dataset)train_set_x,train_set_y=datasets[0]valide_set_x,valide_set_y=datasets[1]test_set_x,test_set_y=datasets[2]#计算小批数据的批数n_train_batches=train_set_x.get_value(borrow=True).shape[0]//batch_sizen_valid_batches=valide_set_x.get_value(borrow=True).shape[0]//batch_sizen_test_batches=test_set_x.get_value(borrow=True).shape[0]//batch_sizeindex=T.iscalar()#批索引x=T.matrix('x')rng=np.random.RandomState(123)#初始化一个去噪自编码器da=dA(rng=rng,input=x,n_visible=n_visible,n_hidden=n_hidden)#参数更新cost,updates=da.get_cost_updates(corruption_level=0,learning_rate=learning_rate)#训练函数train_model=theano.function([index],cost,updates=updates,givens={x:train_set_x[index*batch_size:(index+1)*batch_size]})#训练for epoch in range(n_epoches):c=[]for batch_index in range(n_train_batches):c.append(train_model(batch_index))print ('Training epoch %d, cost' % epoch,np.mean(c,dtype='float32'))save_file=open('best_model_dA.pkl','wb')model=[da.params]cPickle.dump( model,save_file)

最后就可以进行模型训练了

test_dA()
#输出
'''
('Training epoch 0, cost', 63.23605)
('Training epoch 1, cost', 55.798237)
('Training epoch 2, cost', 54.78653)
('Training epoch 3, cost', 54.273125)
('Training epoch 4, cost', 53.922806)
('Training epoch 5, cost', 53.654221)
('Training epoch 6, cost', 53.436089)
('Training epoch 7, cost', 53.253082)
('Training epoch 8, cost', 53.096138)
('Training epoch 9, cost', 52.95927)
('Training epoch 10, cost', 52.838261)
('Training epoch 11, cost', 52.730087)
('Training epoch 12, cost', 52.632538)
('Training epoch 13, cost', 52.543911)
('Training epoch 14, cost', 52.462799)
'''

小插曲

期间发生了一件有趣的事情,初始化权重的时候我使用了

initial_W=np.asarray(rng.uniform(low=-4*np.sqrt(6./(n_hidden+n_visible)),high=4*np.sqrt(6./n_hidden+n_visible),size=(n_visible,n_hidden)),dtype=theano.config.floatX)

而不是

initial_W=np.asarray(rng.uniform(low=-4*np.sqrt(6./(n_hidden+n_visible)),high=4*np.sqrt(6./(n_hidden+n_visible)),size=(n_visible,n_hidden)),dtype=theano.config.floatX)

也就是说我的权重应该是蛮大的, 最小的权重也比28*28的值大, 结果训练的时候出现了以下状况

('Training epoch 0, cost', 2455178.8)
('Training epoch 1, cost', 8450.71)
('Training epoch 2, cost', 7065.3008)
('Training epoch 3, cost', 6342.0913)
('Training epoch 4, cost', 5833.1704)
('Training epoch 5, cost', 5438.9961)
...
('Training epoch 292, cost', 146.0446)
('Training epoch 293, cost', 145.98045)
('Training epoch 294, cost', 145.89928)
('Training epoch 295, cost', 145.86646)
('Training epoch 296, cost', 145.71715)
('Training epoch 297, cost', 145.62868)
('Training epoch 298, cost', 145.54076)
('Training epoch 299, cost', 145.44417)
('Training epoch 300, cost', 145.35019)
('Training epoch 301, cost', 145.3033)
('Training epoch 302, cost', 145.21727)
('Training epoch 303, cost', 145.02127)
('Training epoch 304, cost', 144.89236)
('Training epoch 305, cost', 144.8385)
('Training epoch 306, cost', 144.68234)
('Training epoch 307, cost', 144.59572)
...

所以, 说实话, 权重的初始化对模型的训练的收敛情况有很大影响啊,以后权重初始化最好还是用fan_in,fan_out准则, 前面博客有讲这两个公式

使用模型

首先想一下这个best_model_dA.pkl里面存的是什么?依据我对python的菜鸟级想法, da.params应该就是self.params里面的一个权重和两个偏置, 然后我们去pychar中调试一波看看

这里写图片描述

果然是的, 那么我们就有谱怎么调用了.

先初始化一个测试网络结构, 用于对输入图像进行编码和解码

#使用模型
model_params=cPickle.load(open('best_model_dA.pkl'))
rng=np.random.RandomState(123)
x=T.matrix('x')
coder=T.matrix('coder')
single_input=x.reshape((1,28*28))
da_test=dA(rng=rng,input=single_input,n_visible=28*28,n_hidden=model_params[0][0].get_value().shape[1])
#权重赋值
da_test.W=model_params[0][0].get_value()
da_test.vb=model_params[0][1].get_value()
da_test.hb=model_params[0][2].get_value()
#重构计算
forward_compute=theano.function([single_input],da_test.get_reconstructed_input(coder),givens={coder:da_test.get_hidden_value(single_input)})

然后我们使用一张图片做做测试

#输入一张图片试试
from PIL import Image
import pylab
img=Image.open('E:\\code_test\\theano\\binarybmp\\9.bmp')
img_w,img_h=img.size#图像的宽和高
img=np.asarray(img,dtype='float32')
pylab.imshow(img)
pylab.show()
#原始图片是28*28,要增加两个维度
img=img.reshape((1,28*28))

这里写图片描述

重构一波试试

data_recon=forward_compute(img)
data_recon=data_recon.reshape(28,28)
data_recon=np.asarray(data_recon,dtype='float32')
pylab.imshow(data_recon)
pylab.show()

这里写图片描述

我只想说”这。。。。。诞生了一个什么鬼哦”,再测试一个数字2.bmp试试

这里写图片描述

吾有一橘麻麦皮,不知当桨不当桨

不过嘛毕竟是最最最基本的自编码器,可能效果就是这样,毕竟上面我们也能看出来重构的分别是9和2. 虽然有点勉强。。。。。。如果是代码错误,希望各位指正
更新日志2018-8-15
后面用TensorFlow写了一下自编码,发现数据归一化对结果影响很大,这篇博客效果差的原因极有可能是数据未归一化问题,有兴趣的可以试试,不过我转型TensorFlow了暂时,所以这个代码就不折腾了o(╯□╰)o

博客code打包:链接: https://pan.baidu.com/s/1nvGDkTj 密码: ykid

官方code打包:链接: https://pan.baidu.com/s/1eRQK4nS 密码: yemn

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

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

相关文章

【theano-windows】学习笔记十四——堆叠去噪自编码器

前言 前面已经学习了softmax,多层感知器,CNN,AE,dAE,接下来可以仿照多层感知器的方法去堆叠自编码器 国际惯例,参考文献: Stacked Denoising Autoencoders (SdA) Greedy Layer-Wise Training of Deep Networks 理…

【theano-windows】学习笔记十五——受限玻尔兹曼机

前言 终于到了最喜欢的模型: 受限玻尔兹曼机(RBM)了, 发现关于RBM是如何从能量模型发展过来的介绍非常不错, 而关于详细理论证明, 可以去看我前面的受限玻尔兹曼机的一系列博客. 国际惯例, 参考博客,超级强推第二个博客, 证明过程很给力: Restricted Boltzmann Machines (R…

【Ogre-windows】环境配置

前言 由于工程原因, 学习一下Ogre面向对象图形渲染开源引擎, 慢慢爬坑吧。首先还是环境的配置问题哎. 其实最重要的是要预先编译三方库, 虽然官方说可以自动编译, 但是在自己电脑上还是出现了无法解析外部符号之类的问题, 正常情况下我就认为是三方库的lib出现了问题, 最后额外…

【theano-windows】学习笔记十六——深度信念网络DBN

前言 前面学习了受限玻尔兹曼机(RBM)的理论和搭建方法, 如果稍微了解过的人, 肯定知道利用RBM可以堆叠构成深度信念网络(deep belief network, DBN)和深度玻尔兹曼机(deep Boltzmann machine), 这里就先学习一下DBN. 国际惯例, 参考博文: Deep Belief Networks A fast lear…

【Ogre-windows】实例配置

前言 折腾了好久才搞定教程实例, 主要是因为上一篇博客安装的具体版本是Ogre1.10.9, 而官方的Ogre Wiki Tutorial Framework没有指定具体版本, 如果单纯下载Ogre Wiki Tutorial Framework 1.10 - (Windows line endings, updated 2015-10-15) 运行, 基本会血崩. 所以, 在经过仔…

【Ogre-windows】旋转矩阵及位置解析

前言 这篇博客主要针对三种问题 如何创建动画帧如何获取全局位置如何计算全局旋转矩阵 仿真环境为VS2013Ogre1.10.9与matlab验证 创建动画帧 这里只做一个简单的实验: 将自带的人物模型Jaiqua的run运动给新创建的运动myrun中并播放,直接贴代码了 void JaiQua:…

BP推导——续

前言 之前有证明过一次人工神经网络——【BP】反向传播算法证明 ,但是回头看的时候,有很多地方非常不严谨,特此拿出来再单独证明一次BP,并严格保证其严谨性。如果想看看粗略的证明,可以去看我之前的博客,毕…

matlab学习——强连通分量

前言 最近motion graph相关实验,发现实现运动过渡需要构建运动图,而为了避免运动过渡陷入死胡同,需要对图结构进行裁剪,方法就是计算图模型的极大强联通分量,但是自己懒得去实现,所以就去搜了一下matlab中…

【音频处理】离散傅里叶变换

前言 最近复现音乐驱动舞蹈的文章《Dancing-to-Music Character Animation》,用到了与傅里叶变换很相似的称为常Q变换的方法去分割音乐,所以对傅里叶变换做了一个小了解,本文不深入各种乱糟糟的理论,比如什么蝶形算法啥的&#x…

【音频处理】短时傅里叶变换

前言 上一篇博客讲了离散傅里叶变换,里面的实例是对整个信号进行计算,虽然理论上有N点傅里叶变换(本博客就不区分FFT和DFT了,因为它俩就是一个东东,只不过复杂度不同),但是我个人理解是这个N点是信号前面连续的N个数值…

【theano-windows】学习笔记十九——循环神经网络

前言 前面已经介绍了RBM和CNN了,就剩最后一个RNN了,抽了一天时间简单看了一下原理,但是没细推RNN的参数更新算法BPTT,全名是Backpropagation Through Time。 【注】严谨来说RNN有两个称呼:①结构上递归的recursive n…

【theano-windows】学习笔记二十——LSTM理论及实现

前言 上一篇学习了RNN,也知道了在沿着时间线对上下文权重求梯度的时候,可能会导致梯度消失或者梯度爆炸,然后我们就得学习一波比较常见的优化方法之LSTM 国际惯例,参考网址: LSTM Networks for Sentiment Analysis …

刚体运动学——欧拉角、四元数、旋转矩阵

前言 刚体运动旋转一般用:欧拉角、四元数、轴角对等表示,在对某个坐标旋转的时候,只需将欧拉角或四元数转换为旋转矩阵,并与原始坐标相乘,便可得到旋转以后的坐标。这里主要看看欧拉角、四元数和旋转矩阵。 国际惯例…

刚体运动学-四元数插值

前言 之前对写了一篇关于刚体运动学相关知识博客:刚体运动学——欧拉角、四元数、旋转矩阵,本篇博客就举例来说明,如何在运动捕捉数据中进行四元数插值。 国际惯例,参考博客: 探讨:向量(方向…

【TensorFlow-windows】学习笔记一——基础理解

前言 因为Theano已经停止更新了,所以在前面学完Theano搭建RBM,CNN,RNN相关结构以后,还是得选择一个主流框架的,由于我自身的学习最终是向强化学习靠近,可能用到的仿真环境是openai gym,所以选择了继续学习TensorFlow&…

【TensorFlow-windows】学习笔记二——低级API

前言 上一篇博客初步了解了tensorflow中建立机器学习模型的方法:可以使用eager execution和graph execution两种模式,可以使用高级API estimator中已经封装好的模型,也可以自己创建estimator,更重要的是我们也可以使用低级API自行…

【TensorFlow-windows】学习笔记五——自编码器

前言 上一篇博客介绍的是构建简单的CNN去识别手写数字,这一篇博客折腾一下自编码,理论很简单,就是实现对输入数据的重构,具体理论可以看我前面的【theano-windows】学习笔记十三——去噪自编码器 国际惯例,参考博客&…

【TensorFlow-windows】学习笔记六——变分自编码器

#前言 对理论没兴趣的直接看代码吧,理论一堆,而且还有点复杂,我自己的描述也不一定准确,但是代码就两三句话搞定了。 国际惯例,参考博文 论文:Tutorial on Variational Autoencoders 【干货】一文读懂…

【TensorFlow-windows】学习笔记七——生成对抗网络

前言 既然学习了变分自编码(VAE),那也必须来一波生成对抗网络(GAN)。 国际惯例,参考网址: 论文: Generative Adversarial Nets PPT:Generative Adversarial Networks (GANs) Generative Adversarial Nets in TensorFlow GAN原理学习笔记…

Openpose——windows编译(炒鸡简单)

前言 最近准备看看rtpose的代码,发现已经由openpose这个项目维护着了,由于经常在windows下调试代码,所以尝试了一下如何在windows下编译openpose源码,整体来说非常简单的。 国际惯例,参考博客: [OpenPos…