第十九節,去噪自編碼和棧式自編碼

上一節咱們講到自編碼能夠用於進行數據降維、數據壓縮、對文字或圖像提取主題並用於信息檢索等。 根據所解決的問題不一樣 ,自編碼能夠有許多種不一樣形式的變形,例如: 去噪自編碼器(DAE)、變分自編碼器 (VAE)、收縮自編碼器(CAE)和稀疏自編碼器等 。下面咱們先從去噪自編碼講起。python

一 去噪自編碼

要想取得好的特徵只靠重構輸入數據是不夠的,在實際應用中,還須要讓這些特徵具備靠干擾的能力,即當輸入數據發生必定程度的干擾時,生成的特徵仍然保持不變。這時須要添加噪聲來爲模型增長更大的困難。在這種狀況下訓練出來的模型纔會有更好的魯棒性,因而就有了本節所要介紹的去噪自編碼。git

去噪自編碼(Denoising Auto Encoder),是在自編碼的基礎上,訓練數據加入噪聲,輸出的標籤還是原來的樣本(沒有加過噪聲的),這樣自編碼必須學習去除噪聲而得到真正的沒有被噪聲污染過的輸入特徵。所以,這就迫使編碼器去學習輸入信號的更具備魯棒性的特徵表達,即具備更強的泛化能力。網絡

在實際訓練中,人爲加入的噪聲有兩種途徑:dom

  • 在選擇訓練數據集時,額外選擇一些樣本集之外的數據。
  • 改變已有的樣本數據集中的數據(使樣本個體不完整,或經過噪聲與樣本進行加減乘除之類的運算,使樣本數據發生變化)

二 使用去燥自編碼提取MNIST特徵

# -*- coding: utf-8 -*-
"""
Created on Sun May 27 17:49:18 2018

@author: zy
"""


import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST-data',one_hot=True)

print(type(mnist)) #<class 'tensorflow.contrib.learn.python.learn.datasets.base.Datasets'>

print('Training data shape:',mnist.train.images.shape)           #Training data shape: (55000, 784)
print('Test data shape:',mnist.test.images.shape)                #Test data shape: (10000, 784)
print('Validation data shape:',mnist.validation.images.shape)    #Validation data shape: (5000, 784)
print('Training label shape:',mnist.train.labels.shape)          #Training label shape: (55000, 10)

train_X = mnist.train.images
train_Y = mnist.train.labels
test_X = mnist.test.images
test_Y = mnist.test.labels

def denoising_auto_encoder():
    '''
    去燥自編碼器 784-256-256-784
    
    對MNIST原始輸入圖片加入噪聲,在自編碼網絡中進行訓練,以獲得抗干擾更強的特徵提取模型
    '''
    n_input = 784                      #輸入節點數
    n_hidden = 256                     #隱藏層節點個數
    learning_rate = 0.01               #學習率
    training_epochs = 20               #迭代輪數
    batch_size = 256                   #小批量數量大小
    display_epoch = 2
    show_num = 10                      #顯示的圖片個數   
    
    #定義佔位符
    input_x = tf.placeholder(dtype=tf.float32,shape=[None,n_input])
    input_y = tf.placeholder(dtype=tf.float32,shape=[None,n_input])
    keep_prob = tf.placeholder(dtype=tf.float32)
    #定義參數
    weights = {
            'h1':tf.Variable(tf.random_normal(shape=[n_input,n_hidden],stddev=0.1)),
            'h2':tf.Variable(tf.random_normal(shape=[n_hidden,n_hidden],stddev=0.1)),
            'out':tf.Variable(tf.random_normal(shape=[n_hidden,n_input],stddev=0.1))
            }
    
    biases = {
            'b1':tf.Variable(tf.zeros(shape=[n_hidden])),
            'b2':tf.Variable(tf.zeros(shape=[n_hidden])),
            'out':tf.Variable(tf.zeros(shape=[n_input]))
            }
    
    #網絡模型 去燥自編碼
    h1 = tf.nn.sigmoid(tf.add(tf.matmul(input_x,weights['h1']),biases['b1']))
    h1 = tf.nn.dropout(h1,keep_prob)
    h2 = tf.nn.sigmoid(tf.add(tf.matmul(h1,weights['h2']),biases['b2']))
    h2 = tf.nn.dropout(h2,keep_prob)
    pred = tf.nn.sigmoid(tf.add(tf.matmul(h2,weights['out']),biases['out']))
    
    #計算代價
    cost = tf.reduce_mean((pred-input_y)**2)

    #定義優化器
    #train = tf.train.RMSPropOptimizer(learning_rate).minimize(cost)
    train = tf.train.AdamOptimizer(learning_rate).minimize(cost)
        
    num_batch = int(np.ceil(mnist.train.num_examples / batch_size))
    
    #開始訓練
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                #添加噪聲 每次取出來一批次的數據,將輸入數據的每個像素都加上0.3倍的高斯噪聲  
                batch_x_noise = batch_x + 0.3*np.random.randn(batch_size,784)  #標準正態分佈
                _,loss = sess.run([train,cost],feed_dict={input_x:batch_x_noise,input_y:batch_x,keep_prob:1.0})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
        print('訓練完成')
            
        #數據可視化                 
        test_noisy= mnist.test.images[:show_num]  + 0.3*np.random.randn(show_num,784)         
        reconstruction = sess.run(pred,feed_dict = {input_x:test_noisy,keep_prob:1.0})
        plt.figure(figsize=(1.0*show_num,1*2))        
        for i in range(show_num):
            #原始圖像
            plt.subplot(3,show_num,i+1)            
            plt.imshow(np.reshape(mnist.test.images[i],(28,28)),cmap='gray')   
            plt.axis('off')
            #加入噪聲後的圖像
            plt.subplot(3,show_num,i+show_num*1+1)
            plt.imshow(np.reshape(test_noisy[i],(28,28)),cmap='gray')       
            plt.axis('off')
            #去燥自編碼器輸出圖像
            plt.subplot(3,show_num,i+show_num*2+1)
            plt.imshow(np.reshape(reconstruction[i],(28,28)),cmap='gray')       
            plt.axis('off')
        plt.show()
        
        
        #測試魯棒性 爲了測試模型的魯棒性,咱們換一種噪聲方式,而後再生成一個樣本測試效果
        plt.figure(figsize=(2.4*3,1*2))
        #生成一個0~mnist.test.images.shape[0]的隨機整數
        randidx = np.random.randint(test_X.shape[0],size=1)
        orgvec = test_X[randidx]       #1x784
        
        #獲取標籤
        label = np.argmax(test_Y[randidx],1)
        
        print('Label is %d'%(label))
        #噪聲類型  對原始圖像加入噪聲
        print('Salt and Paper Noise')
        noisyvec = test_X[randidx]           # 1 x 784
        #噪聲點所佔的比重
        rate  = 0.15
        #生成噪聲點索引
        noiseidx = np.random.randint(test_X.shape[1],size=int(test_X.shape[1]*rate)).astype(np.int32)
        #對這些點像素進行反向
        for i in noiseidx:
            noisyvec[0,i] = 1.0 - noisyvec[0,i] 
        
        #噪聲圖像自編碼器輸出
        outvec = sess.run(pred,feed_dict={input_x:noisyvec,keep_prob:1.0})
        outimg = np.reshape(outvec,(28,28))
        
        #可視化       
        plt.subplot(1,3,1)
        plt.imshow(np.reshape(orgvec,(28,28)),cmap='gray')
        plt.title('Original Image')
        plt.axis('off')
        
        plt.subplot(1,3,2)        
        plt.imshow(np.reshape(noisyvec,(28,28)),cmap='gray')
        plt.title('Input Image')
        plt.axis('off')
                
        plt.subplot(1,3,3)
        plt.imshow(outimg,cmap='gray')
        plt.title('Reconstructed Image')
        plt.axis('off')
        
        
if  __name__ == '__main__':
    denoising_auto_encoder()

上面咱們在訓練的時候給keep_prob傳入的值爲1.0,運行結果以下:ide

第一張圖片總共有3行10列,第一行我原始圖像,第二行爲加入隨機高斯噪聲以後的圖像,第三行爲通過去噪編碼器的輸出。性能

第二幅圖像是給定一個輸入,通過加入其它噪聲以後,處理輸出的圖像,是用來測試改網絡的魯棒性。學習

 咱們經過訓練時的keep_prob改爲0.5,運行結果以下:測試

對比一下,咱們會發現加入棄權後效果更好些,還原後的數據幾乎將噪聲所有過濾了,加入棄權能夠提升網絡的泛化能力,有更好的擬合效果,加強網絡對噪聲的魯棒性。優化

 

三 棧式自編碼

一、簡單介紹

棧式自編碼神經網絡(Stacked Autoencoder,SA)是自編碼網絡的一種使用方法,是一個由多層訓練好的自編碼器組成的神經網絡。因爲網絡中的每一層都是單獨訓練而來,至關於都初始化了一個合理的數值。因此,這樣的網絡會更容易訓練,而且有更快的收斂性及更高的準確度。編碼

棧式自編碼經常被用於預訓練(初始化)深度神經網絡以前的權重預訓練步驟。例如,在一個分類問題上,能夠按照從前向後的順序執行每一層經過自編碼器來訓練,最終將網絡中最深層的輸出做爲softmax分類器的輸入特徵,經過softmax層將其分開。

爲了使這個過程很容易理解,下面以訓練一個包含兩個隱藏層的棧式自編碼網絡爲例,一步一步的詳細介紹:

二、棧式自編碼器在深度學習中的意義

看到這裏或許你會奇怪?爲何要這麼麻煩,直接使用多層神經網絡來訓練不是也能夠嗎?在這裏是爲你們介紹的這種訓練方法,更像是手動訓練,之因此咱們願意這麼麻煩,主要是由於有以下幾個有點:

  • 每一層都是單獨訓練,保證降維特徵的可控性。
  • 對於高維度的分類問題,一下拿出一套完整可用的模型相對來說並非容易的事,由於節點太多,參數太多,一味地增長深度只會使結果愈來愈不可控,成爲完全的黑盒,而使用棧式自編碼器逐層降維,能夠將複雜問題簡單化,更容易完成任務。
  • 任意深層,理論上是越深層的神經網絡對現實的擬合度越高,可是傳統的多層神經網絡,因爲使用的是偏差反向傳播方式,致使層越深,傳播的偏差越小,棧式自編碼巧妙的繞過這個問題,直接使用降維後的特徵值進行二次訓練,能夠任意層數的加深。
  • 棧式自編碼神經網絡具備強大的表達能力和深度神經網絡的全部優勢,可是它一般可以獲取到輸入的"層次型分組"或者"部分-總體分解"結構,自編碼器傾向於學習獲得與樣本相對應的低位向量,該向量能夠更好地表示高維樣本的數據特徵。

若是網絡輸入的是圖像,第一層會學習識別邊,第二層會學習組合邊,構成輪廓等,更高層會學習組合更形象的特徵。例如:人臉識別,學習如何識別眼睛、鼻子、嘴等。

三、代替和級聯

棧式自編碼會將網絡中的中間層做爲下一個網絡的輸入進行訓練。咱們能夠獲得網絡中每個中間層的原始值,爲了能有更好的效果,還可使用級聯的方式進一步優化網絡的參數。

在已有的模型上接着優化參數的步驟習慣上成爲"微調"。該方法不只在自編碼網絡,在整個深度學習裏都是常見的技術。

微調一般在有大量已標註的訓練數據的狀況下使用。在這樣的狀況下,微調能顯著提升分類器的性能。但若是有大量未標記數據集,卻只有相對較少的已標註數據集,則微調的做用有限。

四 自編碼器的應用場合

在以前咱們使用自編碼器和去噪自編碼器演示了MNIST的例子,主要是爲了獲得一個很好的可視化效果。可是在實際應用中,全鏈接網絡的自編碼器並不適合處理圖像類的問題(網絡參數太多)。

自編碼器更像是一種技巧,任何一種網絡及方法不可能不變化就能夠知足全部的問題,現實環境中,須要使用具體的模型配合各類技巧來解決問題。明白其原理,知道它的優缺點纔是核心。在任何一個多維數據的分類中也可使用自編碼,或者在大型圖片分類任務中,卷積池化後的特徵數據進行自編碼降維也是一個好辦法。

五 去噪自編碼器和棧式自編碼器的綜合實現

  • 咱們首先創建一個去噪自編碼器(包含輸入層在內共四層的網絡);
  • 而後再對第一層的輸出作一次簡單的自編碼壓縮;
  • 而後再將第二層的輸出作一個softmax分類;
  • 最後把這3個網絡裏的中間層拿出來,組成一個新的網絡進行微調;

 1.導入數據集

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
import os

mnist = input_data.read_data_sets('MNIST-data',one_hot=True)

print(type(mnist)) #<class 'tensorflow.contrib.learn.python.learn.datasets.base.Datasets'>

print('Training data shape:',mnist.train.images.shape)           #Training data shape: (55000, 784)
print('Test data shape:',mnist.test.images.shape)                #Test data shape: (10000, 784)
print('Validation data shape:',mnist.validation.images.shape)    #Validation data shape: (5000, 784)
print('Training label shape:',mnist.train.labels.shape)          #Training label shape: (55000, 10)

train_X = mnist.train.images
train_Y = mnist.train.labels
test_X = mnist.test.images
test_Y = mnist.test.labels

2.定義網絡參數

咱們最終訓練的網絡包含一個輸入,兩個隱藏層,一個輸出層。除了輸入層,每一層都用一個網絡來訓練,因而咱們須要訓練3個網絡,最後再把訓練好的各層組合在一塊兒,造成第4個網絡。

    '''
    網絡參數定義
    '''
    n_input = 784
    n_hidden_1 = 256
    n_hidden_2 = 128
    n_classes = 10
    
    learning_rate = 0.01               #學習率
    training_epochs = 50               #迭代輪數
    batch_size = 256                   #小批量數量大小
    display_epoch = 10
    show_num = 10
    
    savedir = "./stacked_encoder/"     #檢查點文件保存路徑
    savefile = 'mnist_model.cpkt'      #檢查點文件名
    
    #第一層輸入
    x = tf.placeholder(dtype=tf.float32,shape=[None,n_input])
    y = tf.placeholder(dtype=tf.float32,shape=[None,n_input])
    keep_prob = tf.placeholder(dtype=tf.float32)
    
    #第二層輸入
    l2x = tf.placeholder(dtype=tf.float32,shape=[None,n_hidden_1])
    l2y = tf.placeholder(dtype=tf.float32,shape=[None,n_hidden_1])
    
    #第三層輸入
    l3x = tf.placeholder(dtype=tf.float32,shape=[None,n_hidden_2])
    l3y = tf.placeholder(dtype=tf.float32,shape=[None,n_classes])

3.定義學習參數

除了輸入層,後面的其它三層(256,128,10),每一層都須要單獨使用一個自編碼網絡來訓練,因此要爲這三個網絡建立3套學習參數。

    '''
    定義學習參數
    '''
    weights = {
            #網絡1 784-256-256-784
            'l1_h1':tf.Variable(tf.truncated_normal(shape=[n_input,n_hidden_1],stddev=0.1)),    #級聯使用
            'l1_h2':tf.Variable(tf.truncated_normal(shape=[n_hidden_1,n_hidden_1],stddev=0.1)),
            'l1_out':tf.Variable(tf.truncated_normal(shape=[n_hidden_1,n_input],stddev=0.1)),
            #網絡2 256-128-128-256
            'l2_h1':tf.Variable(tf.truncated_normal(shape=[n_hidden_1,n_hidden_2],stddev=0.1)), #級聯使用
            'l2_h2':tf.Variable(tf.truncated_normal(shape=[n_hidden_2,n_hidden_2],stddev=0.1)),
            'l2_out':tf.Variable(tf.truncated_normal(shape=[n_hidden_2,n_hidden_1],stddev=0.1)),
            #網絡3 128-10
            'out':tf.Variable(tf.truncated_normal(shape=[n_hidden_2,n_classes],stddev=0.1))     #級聯使用
            }
    
    biases = {
            #網絡1 784-256-256-784
            'l1_b1':tf.Variable(tf.zeros(shape=[n_hidden_1])),       #級聯使用
            'l1_b2':tf.Variable(tf.zeros(shape=[n_hidden_1])),
            'l1_out':tf.Variable(tf.zeros(shape=[n_input])),
            #網絡2 256-128-128-256
            'l2_b1':tf.Variable(tf.zeros(shape=[n_hidden_2])),       #級聯使用
            'l2_b2':tf.Variable(tf.zeros(shape=[n_hidden_2])),
            'l2_out':tf.Variable(tf.zeros(shape=[n_hidden_1])),
            #網絡3 128-10
            'out':tf.Variable(tf.zeros(shape=[n_classes]))           #級聯使用
            }

4.第一層網絡結構

    '''
    定義第一層網絡結構  
    注意:在第一層里加入噪聲,而且使用棄權層 784-256-256-784
    '''
    l1_h1 = tf.nn.sigmoid(tf.add(tf.matmul(x,weights['l1_h1']),biases['l1_b1']))     
    l1_h1_dropout = tf.nn.dropout(l1_h1,keep_prob)
    l1_h2 = tf.nn.sigmoid(tf.add(tf.matmul(l1_h1_dropout,weights['l1_h2']),biases['l1_b2']))
    l1_h2_dropout = tf.nn.dropout(l1_h2,keep_prob)
    l1_reconstruction = tf.nn.sigmoid(tf.add(tf.matmul(l1_h2_dropout,weights['l1_out']),biases['l1_out']))
    
        
    #計算代價
    l1_cost = tf.reduce_mean((l1_reconstruction-y)**2)

    #定義優化器    
    l1_optm = tf.train.AdamOptimizer(learning_rate).minimize(l1_cost)

5.第二層網絡結構

    '''
    定義第二層網絡結構256-128-128-256
    '''
    l2_h1 = tf.nn.sigmoid(tf.add(tf.matmul(l2x,weights['l2_h1']),biases['l2_b1']))    
    l2_h2 = tf.nn.sigmoid(tf.add(tf.matmul(l2_h1,weights['l2_h2']),biases['l2_b2']))    
    l2_reconstruction = tf.nn.sigmoid(tf.add(tf.matmul(l2_h2,weights['l2_out']),biases['l2_out']))
    
        
    #計算代價
    l2_cost = tf.reduce_mean((l2_reconstruction-l2y)**2)

    #定義優化器    
    l2_optm = tf.train.AdamOptimizer(learning_rate).minimize(l2_cost)

6.第三次網絡結構

    '''
    定義第三層網絡結構 128-10
    '''    
    l3_logits = tf.add(tf.matmul(l3x,weights['out']),biases['out'])
    
        
    #計算代價
    l3_cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=l3_logits,labels=l3y))

    #定義優化器    
    l3_optm = tf.train.AdamOptimizer(learning_rate).minimize(l3_cost)

7.定義級聯級網絡結構

    '''
    定義級聯級網絡結構
    
    將前三個網絡級聯在一塊兒,創建第四個網絡,並定義網絡結構
    '''
    #1 聯 2
    l1_l2_out = tf.nn.sigmoid(tf.add(tf.matmul(l1_h1,weights['l2_h1']),biases['l2_b1']))
    
    #2 聯 3
    logits = tf.add(tf.matmul(l1_l2_out,weights['out']),biases['out'])
    
    #計算代價
    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=l3y))

    #定義優化器    
    optm = tf.train.AdamOptimizer(learning_rate).minimize(cost)
    
    num_batch = int(np.ceil(mnist.train.num_examples / batch_size))
    
    #生成Saver對象,max_to_keep = 1,表名最多保存一個檢查點文件,這樣在迭代過程當中,新生成的模型就會覆蓋之前的模型。
    saver = tf.train.Saver(max_to_keep=1) 
    
    #直接載入最近保存的檢查點文件
    kpt = tf.train.latest_checkpoint(savedir)
    print("kpt:",kpt)    

8.訓練網絡並驗證

'''
    訓練 網絡第一層
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        #若是存在檢查點文件 則恢復模型
        if kpt!=None:
            saver.restore(sess, kpt) 
        print('網絡第一層 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                #添加噪聲 每次取出來一批次的數據,將輸入數據的每個像素都加上0.3倍的高斯噪聲  
                batch_x_noise = batch_x + 0.3*np.random.randn(batch_size,784)  #標準正態分佈
                _,loss = sess.run([l1_optm,l1_cost],feed_dict={x:batch_x_noise,y:batch_x,keep_prob:0.5})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
                
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
            
        #數據可視化                 
        test_noisy= mnist.test.images[:show_num]  + 0.3*np.random.randn(show_num,784)         
        reconstruction = sess.run(l1_reconstruction,feed_dict = {x:test_noisy,keep_prob:1.0})
        plt.figure(figsize=(1.0*show_num,1*2))        
        for i in range(show_num):
            #原始圖像
            plt.subplot(3,show_num,i+1)            
            plt.imshow(np.reshape(mnist.test.images[i],(28,28)),cmap='gray')   
            plt.axis('off')
            #加入噪聲後的圖像
            plt.subplot(3,show_num,i+show_num*1+1)
            plt.imshow(np.reshape(test_noisy[i],(28,28)),cmap='gray')       
            plt.axis('off')
            #去燥自編碼器輸出圖像
            plt.subplot(3,show_num,i+show_num*2+1)
            plt.imshow(np.reshape(reconstruction[i],(28,28)),cmap='gray')       
            plt.axis('off')
        plt.show()
    
    
    '''
    訓練 網絡第二層
    注意:這個網絡模型的輸入已經再也不是MNIST圖片了,而是上一層網絡中的一層的輸出
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('網絡第二層 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                l1_out = sess.run(l1_h1,feed_dict={x:batch_x,keep_prob:1.0})
                                
                _,loss = sess.run([l2_optm,l2_cost],feed_dict={l2x:l1_out,l2y:l1_out})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
                
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
            
        #數據可視化                 
        testvec = mnist.test.images[:show_num]
        l1_out = sess.run(l1_h1,feed_dict={x:testvec,keep_prob:1.0})        
        reconstruction = sess.run(l2_reconstruction,feed_dict = {l2x:l1_out})
        plt.figure(figsize=(1.0*show_num,1*2))        
        for i in range(show_num):
            #原始圖像
            plt.subplot(3,show_num,i+1)            
            plt.imshow(np.reshape(testvec[i],(28,28)),cmap='gray')   
            plt.axis('off')
            #加入噪聲後的圖像
            plt.subplot(3,show_num,i+show_num*1+1)
            plt.imshow(np.reshape(l1_out[i],(16,16)),cmap='gray')       
            plt.axis('off')
            #去燥自編碼器輸出圖像
            plt.subplot(3,show_num,i+show_num*2+1)
            plt.imshow(np.reshape(reconstruction[i],(16,16)),cmap='gray')       
            plt.axis('off')
        plt.show()
    
    
    
    '''
    訓練 網絡第三層
    注意:同理這個網絡模型的輸入是要通過前面兩次網絡運算才能夠生成
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('網絡第三層 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                l1_out = sess.run(l1_h1,feed_dict={x:batch_x,keep_prob:1.0})
                l2_out = sess.run(l2_h1,feed_dict={l2x:l1_out})
                _,loss = sess.run([l3_optm,l3_cost],feed_dict={l3x:l2_out,l3y:batch_y})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
                
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
            

        '''
        棧式自編碼網絡驗證 咱們略過對第3層網絡模型的單獨驗證,直接去驗證整個分類模型,看看棧式自編碼器的分類效果如何
        '''
        correct_prediction =tf.equal(tf.argmax(logits,1),tf.argmax(l3y,1))
        #計算準確率
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,dtype=tf.float32))
        print('Accuracy:',accuracy.eval({x:mnist.test.images,l3y:mnist.test.labels}))
    
    
    '''
    級聯微調
    將網絡模型聯起來進行分類訓練
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('級聯微調 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)            
                _,loss = sess.run([optm,cost],feed_dict={x:batch_x,l3y:batch_y})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
        print('Accuracy:',accuracy.eval({x:mnist.test.images,l3y:mnist.test.labels}))

運行結果以下:

能夠看到,因爲網絡模型中各層的初始值已經訓練好了,咱們略過對第三層網絡模型的單獨驗證,直接去驗證整個分類模型,看看棧式自編碼器的分類效果如何。能夠看出直接將每層的訓練參數堆起來,網絡會有不錯的表現,準確率達到86.97%。爲了進一步優化,咱們進行了級聯微調,最終的準確率達到了97.74%。能夠看到這個準確率和前饋神經網絡準確度近似,可是咱們能夠增長網絡的層數進一步提升準確率。

完整代碼:

# -*- coding: utf-8 -*-
"""
Created on Wed May 30 20:21:43 2018

@author: zy
"""

'''
去燥自編碼器和棧式自編碼器的綜合實現
1.咱們首先創建一個去噪自編碼器(包含輸入層四層的網絡)
2.而後再對第一層的輸出作一次簡單的自編碼壓縮(包含輸入層三層的網絡)
3.而後再將第二層的輸出作一個softmax分類
4.最後把這3個網絡裏的中間層拿出來,組成一個新的網絡進行微調1.構建一個包含輸入層的簡單去噪自編碼其
'''

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
import os

mnist = input_data.read_data_sets('../MNIST-data',one_hot=True)

print(type(mnist)) #<class 'tensorflow.contrib.learn.python.learn.datasets.base.Datasets'>

print('Training data shape:',mnist.train.images.shape)           #Training data shape: (55000, 784)
print('Test data shape:',mnist.test.images.shape)                #Test data shape: (10000, 784)
print('Validation data shape:',mnist.validation.images.shape)    #Validation data shape: (5000, 784)
print('Training label shape:',mnist.train.labels.shape)          #Training label shape: (55000, 10)

train_X = mnist.train.images
train_Y = mnist.train.labels
test_X = mnist.test.images
test_Y = mnist.test.labels


def stacked_auto_encoder():
    tf.reset_default_graph()
    '''
    棧式自編碼器
    
    最終訓練的一個網絡爲一個輸入、一個輸出和兩個隱藏層
    MNIST輸入(784) - > 編碼層1(256)- > 編碼層3(128) - > softmax分類
    
    除了輸入層,每一層都用一個網絡來訓練,因而咱們須要訓練3個網絡,最後再把訓練好的各層組合在一塊兒,造成第4個網絡。
    '''
    
    
    '''
    網絡參數定義
    '''
    n_input = 784
    n_hidden_1 = 256
    n_hidden_2 = 128
    n_classes = 10
    
    learning_rate = 0.01               #學習率
    training_epochs = 20               #迭代輪數
    batch_size = 256                   #小批量數量大小
    display_epoch = 10
    show_num = 10
    
    savedir = "./stacked_encoder/"     #檢查點文件保存路徑
    savefile = 'mnist_model.cpkt'      #檢查點文件名
    
    #第一層輸入
    x = tf.placeholder(dtype=tf.float32,shape=[None,n_input])
    y = tf.placeholder(dtype=tf.float32,shape=[None,n_input])
    keep_prob = tf.placeholder(dtype=tf.float32)
    
    #第二層輸入
    l2x = tf.placeholder(dtype=tf.float32,shape=[None,n_hidden_1])
    l2y = tf.placeholder(dtype=tf.float32,shape=[None,n_hidden_1])
    
    #第三層輸入
    l3x = tf.placeholder(dtype=tf.float32,shape=[None,n_hidden_2])
    l3y = tf.placeholder(dtype=tf.float32,shape=[None,n_classes])
    
    '''
    定義學習參數
    '''
    weights = {
            #網絡1 784-256-256-784
            'l1_h1':tf.Variable(tf.truncated_normal(shape=[n_input,n_hidden_1],stddev=0.1)),    #級聯使用
            'l1_h2':tf.Variable(tf.truncated_normal(shape=[n_hidden_1,n_hidden_1],stddev=0.1)),
            'l1_out':tf.Variable(tf.truncated_normal(shape=[n_hidden_1,n_input],stddev=0.1)),
            #網絡2 256-128-128-256
            'l2_h1':tf.Variable(tf.truncated_normal(shape=[n_hidden_1,n_hidden_2],stddev=0.1)), #級聯使用
            'l2_h2':tf.Variable(tf.truncated_normal(shape=[n_hidden_2,n_hidden_2],stddev=0.1)),
            'l2_out':tf.Variable(tf.truncated_normal(shape=[n_hidden_2,n_hidden_1],stddev=0.1)),
            #網絡3 128-10
            'out':tf.Variable(tf.truncated_normal(shape=[n_hidden_2,n_classes],stddev=0.1))     #級聯使用
            }
    
    biases = {
            #網絡1 784-256-256-784
            'l1_b1':tf.Variable(tf.zeros(shape=[n_hidden_1])),       #級聯使用
            'l1_b2':tf.Variable(tf.zeros(shape=[n_hidden_1])),
            'l1_out':tf.Variable(tf.zeros(shape=[n_input])),
            #網絡2 256-128-128-256
            'l2_b1':tf.Variable(tf.zeros(shape=[n_hidden_2])),       #級聯使用
            'l2_b2':tf.Variable(tf.zeros(shape=[n_hidden_2])),
            'l2_out':tf.Variable(tf.zeros(shape=[n_hidden_1])),
            #網絡3 128-10
            'out':tf.Variable(tf.zeros(shape=[n_classes]))           #級聯使用
            }
    
    '''
    定義第一層網絡結構  
    注意:在第一層里加入噪聲,而且使用棄權層 784-256-256-784
    '''
    l1_h1 = tf.nn.sigmoid(tf.add(tf.matmul(x,weights['l1_h1']),biases['l1_b1']))     
    l1_h1_dropout = tf.nn.dropout(l1_h1,keep_prob)
    l1_h2 = tf.nn.sigmoid(tf.add(tf.matmul(l1_h1_dropout,weights['l1_h2']),biases['l1_b2']))
    l1_h2_dropout = tf.nn.dropout(l1_h2,keep_prob)
    l1_reconstruction = tf.nn.sigmoid(tf.add(tf.matmul(l1_h2_dropout,weights['l1_out']),biases['l1_out']))
    
        
    #計算代價
    l1_cost = tf.reduce_mean((l1_reconstruction-y)**2)

    #定義優化器    
    l1_optm = tf.train.AdamOptimizer(learning_rate).minimize(l1_cost)
    
    
    '''
    定義第二層網絡結構256-128-128-256
    '''
    l2_h1 = tf.nn.sigmoid(tf.add(tf.matmul(l2x,weights['l2_h1']),biases['l2_b1']))    
    l2_h2 = tf.nn.sigmoid(tf.add(tf.matmul(l2_h1,weights['l2_h2']),biases['l2_b2']))    
    l2_reconstruction = tf.nn.sigmoid(tf.add(tf.matmul(l2_h2,weights['l2_out']),biases['l2_out']))
    
        
    #計算代價
    l2_cost = tf.reduce_mean((l2_reconstruction-l2y)**2)

    #定義優化器    
    l2_optm = tf.train.AdamOptimizer(learning_rate).minimize(l2_cost)
    
    
    '''
    定義第三層網絡結構 128-10
    '''    
    l3_logits = tf.add(tf.matmul(l3x,weights['out']),biases['out'])
    
        
    #計算代價
    l3_cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=l3_logits,labels=l3y))

    #定義優化器    
    l3_optm = tf.train.AdamOptimizer(learning_rate).minimize(l3_cost)
    
    
    '''
    定義級聯級網絡結構
    
    將前三個網絡級聯在一塊兒,創建第四個網絡,並定義網絡結構
    '''
    #1 聯 2
    l1_l2_out = tf.nn.sigmoid(tf.add(tf.matmul(l1_h1,weights['l2_h1']),biases['l2_b1']))
    
    #2 聯 3
    logits = tf.add(tf.matmul(l1_l2_out,weights['out']),biases['out'])
    
    #計算代價
    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=l3y))

    #定義優化器    
    optm = tf.train.AdamOptimizer(learning_rate).minimize(cost)
    
    num_batch = int(np.ceil(mnist.train.num_examples / batch_size))
    
    #生成Saver對象,max_to_keep = 1,表名最多保存一個檢查點文件,這樣在迭代過程當中,新生成的模型就會覆蓋之前的模型。
    saver = tf.train.Saver(max_to_keep=1) 
    
    #直接載入最近保存的檢查點文件
    kpt = tf.train.latest_checkpoint(savedir)
    print("kpt:",kpt)    
    
    '''
    訓練 網絡第一層
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        #若是存在檢查點文件 則恢復模型
        if kpt!=None:
            saver.restore(sess, kpt) 
        print('網絡第一層 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                #添加噪聲 每次取出來一批次的數據,將輸入數據的每個像素都加上0.3倍的高斯噪聲  
                batch_x_noise = batch_x + 0.3*np.random.randn(batch_size,784)  #標準正態分佈
                _,loss = sess.run([l1_optm,l1_cost],feed_dict={x:batch_x_noise,y:batch_x,keep_prob:0.5})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
                
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
            
        #數據可視化                 
        test_noisy= mnist.test.images[:show_num]  + 0.3*np.random.randn(show_num,784)         
        reconstruction = sess.run(l1_reconstruction,feed_dict = {x:test_noisy,keep_prob:1.0})
        plt.figure(figsize=(1.0*show_num,1*2))        
        for i in range(show_num):
            #原始圖像
            plt.subplot(3,show_num,i+1)            
            plt.imshow(np.reshape(mnist.test.images[i],(28,28)),cmap='gray')   
            plt.axis('off')
            #加入噪聲後的圖像
            plt.subplot(3,show_num,i+show_num*1+1)
            plt.imshow(np.reshape(test_noisy[i],(28,28)),cmap='gray')       
            plt.axis('off')
            #去燥自編碼器輸出圖像
            plt.subplot(3,show_num,i+show_num*2+1)
            plt.imshow(np.reshape(reconstruction[i],(28,28)),cmap='gray')       
            plt.axis('off')
        plt.show()
    
    
    '''
    訓練 網絡第二層
    注意:這個網絡模型的輸入已經再也不是MNIST圖片了,而是上一層網絡中的一層的輸出
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('網絡第二層 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                l1_out = sess.run(l1_h1,feed_dict={x:batch_x,keep_prob:1.0})
                                
                _,loss = sess.run([l2_optm,l2_cost],feed_dict={l2x:l1_out,l2y:l1_out})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
                
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
            
        #數據可視化                 
        testvec = mnist.test.images[:show_num]
        l1_out = sess.run(l1_h1,feed_dict={x:testvec,keep_prob:1.0})        
        reconstruction = sess.run(l2_reconstruction,feed_dict = {l2x:l1_out})
        plt.figure(figsize=(1.0*show_num,1*2))        
        for i in range(show_num):
            #原始圖像
            plt.subplot(3,show_num,i+1)            
            plt.imshow(np.reshape(testvec[i],(28,28)),cmap='gray')   
            plt.axis('off')
            #加入噪聲後的圖像
            plt.subplot(3,show_num,i+show_num*1+1)
            plt.imshow(np.reshape(l1_out[i],(16,16)),cmap='gray')       
            plt.axis('off')
            #去燥自編碼器輸出圖像
            plt.subplot(3,show_num,i+show_num*2+1)
            plt.imshow(np.reshape(reconstruction[i],(16,16)),cmap='gray')       
            plt.axis('off')
        plt.show()
    
    
    
    '''
    訓練 網絡第三層
    注意:同理這個網絡模型的輸入是要通過前面兩次網絡運算才能夠生成
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('網絡第三層 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)
                l1_out = sess.run(l1_h1,feed_dict={x:batch_x,keep_prob:1.0})
                l2_out = sess.run(l2_h1,feed_dict={l2x:l1_out})
                _,loss = sess.run([l3_optm,l3_cost],feed_dict={l3x:l2_out,l3y:batch_y})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
                
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
            

        '''
        棧式自編碼網絡驗證
        '''
        correct_prediction =tf.equal(tf.argmax(logits,1),tf.argmax(l3y,1))
        #計算準確率
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,dtype=tf.float32))
        print('Accuracy:',accuracy.eval({x:mnist.test.images,l3y:mnist.test.labels}))
    
    
    '''
    級聯微調
    將網絡模型聯起來進行分類訓練
    '''
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('級聯微調 開始訓練')
        for epoch in range(training_epochs):
            total_cost = 0.0
            for i in range(num_batch):
                batch_x,batch_y = mnist.train.next_batch(batch_size)            
                _,loss = sess.run([optm,cost],feed_dict={x:batch_x,l3y:batch_y})
                total_cost += loss
                
            #打印信息
            if epoch % display_epoch == 0:
                print('Epoch {0}/{1}  average cost {2}'.format(epoch,training_epochs,total_cost/num_batch))
            #每隔1輪後保存一次檢查點            
            saver.save(sess,os.path.join(savedir,savefile),global_step = epoch)
        print('訓練完成')
        print('Accuracy:',accuracy.eval({x:mnist.test.images,l3y:mnist.test.labels}))

        
if  __name__ == '__main__':
    stacked_auto_encoder()
View Code
相關文章
相關標籤/搜索