【theano-windows】学习笔记十四——堆叠去噪自编码器
来源:互联网 发布:ubuntu输入法设置中文 编辑:程序博客网 时间:2024/06/08 18:29
前言
前面已经学习了softmax,多层感知器,CNN,AE,dAE,接下来可以仿照多层感知器的方法去堆叠自编码器
国际惯例,参考文献:
Stacked Denoising Autoencoders (SdA)
Greedy Layer-Wise Training of Deep Networks
理论
通过将降噪自编码器的低层隐单元输出作为当前层的输入,可以把自编码器堆叠成深度网络。 这类的无监督预许连方法是逐层展开的。每一层都通过最小化输入(上一层的输出编码)的重构误差训练一个降噪自编码器. 一旦之前的k层被训练了,我们就可以训练第
如果所有层都预训练完毕,就进入第二个阶段称为微调(fine-tuning), 这里由于手头只有mnist数据集,所以我们就采用有监督微调。首先在预训练好的网络顶部添一个softmax层,然后将整个网络当做多层感知机训练。
训练过程包含两个阶段:自编码器的逐层训练,多层感知器MLP的整体训练,这两个阶段是链接着的,因为:
- 自编码与MLP的
sigmoid
层是共享参数的,这里的参数说的是权重和偏置 - MLP的中间层计算得到的特征表示作为自编码器的输入
【思考】其实这里感觉的难点在于如何将训练好的逐层AE参数导入到第二阶段的MLP中,剩下的比如怎么建立模型,可以参考MLP(由多隐层和softmax层组成)的写法.
我个人比较喜欢的代码风格是, 先定义一些必要的函数(比如读取数据, 数据预处理等), 然后定义网络结构(初始化每一层参数), 随后针对网络结构进行梯度更新和训练, 最后测试模型, 所以写堆叠自编码的流程大概也是:①定义读数据函数, ②定义网络结构③定义预训练④定义微调⑤测试
代码
老样子, 先数据集的读取函数
import theanoimport theano.tensor as Timport numpy as npimport osimport cPickle,gzipfrom 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_xy shared_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
然后我们搭建堆叠自编码器需要有隐层的建立和最后一层的softmax层, 直接copy前面学到的建立方法
对于隐层的代码
#预训练需要有隐层class HiddenLayer(object): def __init__(self,rng,input,n_in,n_out,W=None,b=None,activation=T.tanh): self.input=input if W is None: W_values=np.asarray(rng.uniform(low=- np.sqrt(6./(n_in+n_out)), high= np.sqrt(6./(n_in+n_out)), size=(n_in,n_out)),dtype=theano.config.floatX) if activation==T.nnet.sigmoid: W_values *= 4 W=theano.shared(value=W_values,name='W',borrow=True) if b is None: b_vaules=np.zeros((n_out,),dtype=theano.config.floatX) b=theano.shared(value=b_vaules,name='b',borrow=True) self.W=W self.b=b lin_output=T.dot(input,self.W)+self.b#未被激活的线性操作 self.output=(lin_output if activation is None else activation(lin_output)) self.params=[self.W,self.b]
然后是softmax层的建立方法
#微调需要softmaxclass LogisticRegression(object): def __init__(self,input,n_in,n_out): #共享权重 self.W=theano.shared(value=np.zeros((n_in,n_out),dtype=theano.config.floatX), name='W', borrow=True) #共享偏置 self.b=theano.shared(value=np.zeros((n_out,),dtype=theano.config.floatX), name='b', borrow=True) #softmax函数 self.p_y_given_x=T.nnet.softmax(T.dot(input,self.W)+self.b) #预测值 self.y_pred=T.argmax(self.p_y_given_x,axis=1) self.params=[self.W,self.b]#模型参数 self.input=input#模型输入 #定义负对数似然 def negative_log_likelihood(self,y): return -T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]),y]) #定义误差 def errors(self, y): # check if y has same dimension of y_pred if y.ndim != self.y_pred.ndim: raise TypeError( 'y should have the same shape as self.y_pred', ('y', y.type, 'y_pred', self.y_pred.type) ) # check if y is of the correct datatype if y.dtype.startswith('int'): # the T.neq operator returns a vector of 0s and 1s, where 1 # represents a mistake in prediction return T.mean(T.neq(self.y_pred, y)) else: raise NotImplementedError()
预训练过程是逐层按照自编码训练, 因为需要自编码的训练方法, 同样copy前面的自编码建立代码
#定义自编码部分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_visible self.n_hidden=n_hidden if 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=W self.vb=v_b self.hb=h_b self.W_prime=self.W.T if input is None: self.x=T.dmatrix(name='input') else: self.x=input self.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)
然后重头戏来了, 根据上面的隐层、softmax层、自编码层搭建一个堆叠式的多隐层自编码网络:
主要包含两个过程: 预训练网络搭建, 整体微调代码(在预训练模型顶层加入softmax, 重点过程是如何将预训练好的模型与微调模型的参数和输出对接好, 从代码中很容易分析, 不多说
#初始化整个SdA网络class SdA(object): def __init__(self,rng,n_ins=784,n_hiddens=[500,500],n_outs=10): self.sigmoid_layers=[] #存储每个隐层 self.params=[]#存储每层参数 self.dA_layers=[] self.n_hiddens=n_hiddens self.x=T.matrix('x')#存储输入 self.y=T.ivector('y')#存储对应标签 for i in range(len(n_hiddens)): if i==0: #当是输入层的时候 input_size=n_ins input=self.x else : #当是隐层的时候 input_size=n_hiddens[i-1] input=self.sigmoid_layers[-1].output #定义当前循环时候的隐层 sigmoid_layer=HiddenLayer(rng,input,input_size,n_hiddens[i],activation=T.nnet.sigmoid) #将隐层一层一层堆叠起来 self.sigmoid_layers.append(sigmoid_layer) #参数也一层一层存起来 self.params.extend(sigmoid_layer.params) #降噪自编码器的参数与隐单元参数共享 dA_layer=dA(rng,input,input_size,n_hiddens[i],W=sigmoid_layer.W,h_b=sigmoid_layer.b) #堆叠降噪自编码器 self.dA_layers.append(dA_layer) ######################微调################### #在最顶层加一个softmax self.loglayer=LogisticRegression(self.sigmoid_layers[-1].output,n_hiddens[-1],n_outs) #最终参数 self.params.extend(self.loglayer.params) #微调阶段的损失 self.finetune_cost=self.loglayer.negative_log_likelihood(self.y) #微调阶段的误差 self.finetune_err=self.loglayer.errors(self.y) #定义预训练函数 def pre_train(self,train_set,batch_size,corrupt_level=0.2,learning_rate=0.1): index=T.lscalar('index') pretrain_fns=[]#存储每层的预训练函数 for i,dA in enumerate(self.dA_layers): #对于每一个自编码层 cost,updates=dA.get_cost_updates(corrupt_level[i],learning_rate) #编译函数 fn=theano.function(inputs=[index], outputs=cost, updates=updates, givens={ self.x:train_set[index*batch_size:(index+1)*batch_size] }) pretrain_fns.append(fn) return pretrain_fns #定义微调部分 def fine_tune(self,datasets,batch_size,learning_rate=0.1): 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_size#训练集每批大小 n_valid_batches=valide_set_x.get_value(borrow=True).shape[0]//batch_size#验证集每批大小 n_test_batches=test_set_x.get_value(borrow=True).shape[0]//batch_size#测试集每批大小 index=T.lscalar('index') #对所有层参数的梯度 gparams=T.grad(self.finetune_cost,self.params) #更新参数 updates=[(param,param-gparam*learning_rate) for param,gparam in zip(self.params,gparams)] #训练过程 train_fn=theano.function(inputs=[index], outputs=self.finetune_cost, updates=updates, givens={ self.x:train_set_x[batch_size*index:batch_size*(index+1)], self.y:train_set_y[batch_size*index:batch_size*(index+1)] }) #验证过程 valid_fn=theano.function(inputs=[index], outputs=self.finetune_err, givens={ self.x:valide_set_x[batch_size*index:batch_size*(index+1)], self.y:valide_set_y[batch_size*index:batch_size*(index+1)] }) #测试过程 test_fn=theano.function(inputs=[index], outputs=self.finetune_err, givens={ self.x:test_set_x[batch_size*index:batch_size*(index+1)], self.y:test_set_y[batch_size*index:batch_size*(index+1)] }) return train_fn,valid_fn,test_fn
按照流程, 我们就可以定义整个网络的训练了,包含数据集的分批, 按照上面定义的网络结构, 初始化一个网络实例, 随后调用上面写出来的训练和验证函数去训练整个模型, 采用提前停止算法, 前面说过这个算法, 不赘述
#训练及测试def test_SdA(pretrain_epoch=1,pretrain_lr=0.001, train_epoch=1,finetune_lr=0.1, dataset='mnist.pkl.gz',batchsize=100): #读取数据集 datasets=load_data(dataset=dataset) train_set_x, train_set_y = datasets[0] valid_set_x, valid_set_y = datasets[1] test_set_x, test_set_y = datasets[2] #总共多少小批次需要训练 n_train_batches=train_set_x.get_value(borrow=True).shape[0]//batchsize n_test_batches=test_set_x.get_value(borrow=True).shape[0]//batchsize n_valid_batches=valid_set_x.get_value(borrow=True).shape[0]//batchsize #初始化SdA网络 rng=np.random.RandomState(1234) sda=SdA(rng,n_ins=28*28,n_hiddens=[1000,1000,1000],n_outs=10) #输入层和第一二隐层的加噪程度 corruption_levels=[0.1,0.2,0.3] ############预训练############# print('############预训练#############') pretrain_fns=sda.pre_train(train_set_x,batchsize,corrupt_level=corruption_levels,learning_rate=0.1) #对每一层每一小批都执行训练 for i in range(len(sda.n_hiddens)): #对第i层的所有小批执行epoch次训练 for epoch in range(pretrain_epoch): c=[]#记录总误差 #对每一小批都训练 for batch_index in range(n_train_batches): c.append(pretrain_fns[i](index=batch_index)) print('第%d层的第%d次预训练损失为%f' %(i,epoch,np.mean(c,dtype='float64'))) #############微调############### print('#############微调##############') train_fn,valid_fn,test_fn=sda.fine_tune(datasets,batchsize,learning_rate=finetune_lr) #提前停止方法 patiences=10000 patience_inc=2 improvement_threshold=0.995 validation_frequency=min(n_train_batches,patiences) best_validation_loss=np.inf test_score=0 done_loop=False while (epoch<train_epoch) and (not done_loop): epoch=epoch+1 print epoch for minibatch_index in range(n_train_batches): #对于每一批都训练 minibatch_cost=train_fn(minibatch_index) iter=(epoch-1)*n_train_batches+minibatch_index #累计训练了多少批,用于决定是否验证准确率 if(iter+1)%validation_frequency==0: validation_loss=np.mean([valid_fn(j) for j in range(n_valid_batches)],dtype='float64') print('训练第%d次,对于第%d个小批训练的参数得到误差为%f' %(epoch,minibatch_index,validation_loss)) if validation_loss<best_validation_loss: if validation_loss<best_validation_loss*improvement_threshold: patiences=patiences*patience_inc best_validation_loss=validation_loss best_iter=iter #验证集上的误差为 test_score=np.mean([test_fn(k) for k in range(n_test_batches)],dtype='float64') if patiences<=iter: done_loop=True break save_file=open('best_model_SdA.pkl','wb') model=[sda.params] for i,par in enumerate(model): l=[] for t in range(len(par)): l.append(par[t].get_value()) cPickle.dump( l,save_file) print ('最好模型参数的验证集误差为%f,测试集误差%f' %(best_validation_loss,test_score))
最后训练
test_SdA()
很神奇的是我只对每一层预训练一次, 最后整体微调也只训练一次就达到了非常非常非常非常好的效果, 可能内部原因是采用了小批训练, 结果每一批数据对网络的参数改变都足够使它逼近最优处, 所以无需训练太多, 训练结果如下
############预训练#############第0层的第0次预训练损失为75.621635第1层的第0次预训练损失为461.637759第2层的第0次预训练损失为170.258758#############微调##############1训练第1次,对于第499个小批训练的参数得到误差为0.080000最好模型参数的验证集误差为0.080000,测试集误差0.083700
单张图片分类
这里有个坑是我原本想使用MLP里面的方法直接调用logistics模型中的pred方法, 结果死活载入不进去图片, 主要思想如下:
#测试过程sda=cPickle.load(open('best_model_SdA.pkl'))#读取模型#初始化一个用于测试的网络sda_test=SdA(rng=np.random.RandomState(1234),n_ins=28*28,n_hiddens=[1000,1000,1000],n_outs=10)#初始化所有权重和偏置sda_test.sigmoid_layers[0].W.set_value(sda[0])sda_test.sigmoid_layers[0].b.set_value(sda[1])sda_test.sigmoid_layers[1].W.set_value(sda[2])sda_test.sigmoid_layers[1].b.set_value(sda[3])sda_test.sigmoid_layers[2].W.set_value(sda[4])sda_test.sigmoid_layers[2].b.set_value(sda[5])sda_test.loglayer.W.set_value(sda[6])sda_test.loglayer.b.set_value(sda[7])#取一张图片预测from PIL import Imageimport pylabdataset='mnist.pkl.gz'datasets=load_data(dataset)test_set_x,test_set_y=datasets[2]test_set_x=test_set_x.get_value()test_data=test_set_x[12:13]predict_model=theano.function(inputs=[x],outputs=classifier_test.logRegressitionLayer.y_pred)predicted_value=predict_model(test_data)print predicted_value
结果这个代码导数第三行一直提示这个x与需要的输入不匹配, 逼得我直接使用了最笨的方法, 手动执行前向计算, 需要注意的是我们一定要对自己的前向过程很熟悉, 包括权重每个维度的意义, 偏置和激活函数的使用等等
然后我就写出了如下代码:
#测试过程sda=cPickle.load(open('best_model_SdA.pkl'))#读取模型#取一张图片预测from PIL import Imageimport pylabdataset='mnist.pkl.gz'datasets=load_data(dataset)test_set_x,test_set_y=datasets[2]test_set_x=test_set_x.get_value()test_data=test_set_x[12:13]img=test_data.reshape(28,28)pylab.imshow(img)pylab.show()#预测for i in range(len(sda)/2-1): if i==0: outputs=T.nnet.sigmoid(T.dot(test_data,sda[i])+sda[i+1]) else: outputs=T.nnet.sigmoid(T.dot(outputs,sda[i*2])+sda[i*2+1])#softma层outputs=T.nnet.softmax(T.dot(outputs,sda[6])+sda[7])#结果a=outputs.eval()print np.argmax(a)
部分结果输出如下:
博客源码:链接: https://pan.baidu.com/s/1mh5AwXi 密码: gqa4
- 【theano-windows】学习笔记十四——堆叠去噪自编码器
- 【theano-windows】学习笔记十三——去噪自编码器
- Tensorflow——去噪自编码器
- TensorFlow学习--自编码器/稀疏自编码器/堆叠自编码器
- 自编码器与堆叠自编码器简述
- 自编码器与堆叠自编码器简述
- 【theano-windows】学习笔记一——theano中的变量
- 【theano-windows】学习笔记三——theano中的导数
- 深度学习笔记:稀疏自编码器(4)——稀疏自编码器代码练习
- 【TensorFlow-windows】(二) 实现一个去噪自编码器
- 深度学习,如何用去噪自编码器预测原始数据?
- 学习笔记TF025:自编码器
- 【theano-windows】学习笔记七——logistic回归
- 【theano-windows】学习笔记八——预备知识
- 【theano-windows】学习笔记十二——卷积神经网络
- 【theano-windows】学习笔记十五——受限玻尔兹曼机
- 【theano-windows】学习笔记十七——梯度中的consider_constant
- 深度学习笔记:稀疏自编码器(3)——稀疏自编码算法
- day02_数据存储
- 深入浅出的TensorFlow可视化工具TensorBoard用法教程(二)
- Android EditText在界面恢复时数据出现重复问题
- day03_数据存储
- 【LeetCode】10.Regular Expression Matching(hard)解题报告
- 【theano-windows】学习笔记十四——堆叠去噪自编码器
- 运算符之:5、位运算符(7个)
- 新博客
- LeetCode 287. Find the Duplicate Number (Medium)
- Storm之——组合多种流操作
- 关于引进制转换(凌乱)
- 深度学习主机环境配置: Ubuntu16.04+Nvidia GTX 1080/980ti+CUDA8.0
- PAT (Basic Level) Practise (中文)1017. A除以B (20)
- c++多个cpp要使用一个变量(vector、list之类的),该怎么办?