上一節我們講到自編碼可以用於進行數據降維、數據壓縮、對文字或圖像提取主題並用於信息檢索等。 根據所解決的問題不同 ,自編碼可以有許多種不同形式的變形,例如: 去噪自編碼器(DAE)、變分自編碼器 (VAE)、收縮自編碼器(CAE)和稀疏自編碼器等 。下面我們先從去噪自編碼講起。
一 去噪自編碼
要想取得好的特征只靠重構輸入數據是不夠的,在實際應用中,還需要讓這些特征具有靠干擾的能力,即當輸入數據發生一定程度的干擾時,生成的特征仍然保持不變。這時需要添加噪聲來為模型增加更大的困難。在這種情況下訓練出來的模型才會有更好的魯棒性,於是就有了本節所要介紹的去噪自編碼。
去噪自編碼(Denoising Auto Encoder),是在自編碼的基礎上,訓練數據加入噪聲,輸出的標簽仍是原來的樣本(沒有加過噪聲的),這樣自編碼必須學習去除噪聲而獲得真正的沒有被噪聲污染過的輸入特征。因此,這就迫使編碼器去學習輸入信號的更具有魯棒性的特征表達,即具有更強的泛化能力。
在實際訓練中,人為加入的噪聲有兩種途徑:
- 在選擇訓練數據集時,額外選擇一些樣本集以外的數據。
- 改變已有的樣本數據集中的數據(使樣本個體不完整,或通過噪聲與樣本進行加減乘除之類的運算,使樣本數據發生變化)
二 使用去燥自編碼提取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,運行結果如下:
第一張圖片總共有3行10列,第一行我原始圖像,第二行為加入隨機高斯噪聲之后的圖像,第三行為經過去噪編碼器的輸出。
第二幅圖像是給定一個輸入,經過加入其它噪聲之后,處理輸出的圖像,是用來測試改網絡的魯棒性。
我們通過訓練時的keep_prob改成0.5,運行結果如下:
對比一下,我們會發現加入棄權后效果更好些,還原后的數據幾乎將噪聲全部過濾了,加入棄權可以提高網絡的泛化能力,有更好的擬合效果,增強網絡對噪聲的魯棒性。
三 棧式自編碼
1、簡單介紹
棧式自編碼神經網絡(Stacked Autoencoder,SA)是自編碼網絡的一種使用方法,是一個由多層訓練好的自編碼器組成的神經網絡。由於網絡中的每一層都是單獨訓練而來,相當於都初始化了一個合理的數值。所以,這樣的網絡會更容易訓練,並且有更快的收斂性及更高的准確度。
棧式自編碼常常被用於預訓練(初始化)深度神經網絡之前的權重預訓練步驟。例如,在一個分類問題上,可以按照從前向后的順序執行每一層通過自編碼器來訓練,最終將網絡中最深層的輸出作為softmax分類器的輸入特征,通過softmax層將其分開。
為了使這個過程很容易理解,下面以訓練一個包含兩個隱藏層的棧式自編碼網絡為例,一步一步的詳細介紹:
2、棧式自編碼器在深度學習中的意義
看到這里或許你會奇怪?為什么要這么麻煩,直接使用多層神經網絡來訓練不是也可以嗎?在這里是為大家介紹的這種訓練方法,更像是手動訓練,之所以我們願意這么麻煩,主要是因為有如下幾個有點:
- 每一層都是單獨訓練,保證降維特征的可控性。
- 對於高維度的分類問題,一下拿出一套完整可用的模型相對來講並不是容易的事,因為節點太多,參數太多,一味地增加深度只會使結果越來越不可控,成為徹底的黑盒,而使用棧式自編碼器逐層降維,可以將復雜問題簡單化,更容易完成任務。
- 任意深層,理論上是越深層的神經網絡對現實的擬合度越高,但是傳統的多層神經網絡,由於使用的是誤差反向傳播方式,導致層越深,傳播的誤差越小,棧式自編碼巧妙的繞過這個問題,直接使用降維后的特征值進行二次訓練,可以任意層數的加深。
- 棧式自編碼神經網絡具有強大的表達能力和深度神經網絡的所有優點,但是它通常能夠獲取到輸入的"層次型分組"或者"部分-整體分解"結構,自編碼器傾向於學習得到與樣本相對應的低位向量,該向量可以更好地表示高維樣本的數據特征。
如果網絡輸入的是圖像,第一層會學習識別邊,第二層會學習組合邊,構成輪廓等,更高層會學習組合更形象的特征。例如:人臉識別,學習如何識別眼睛、鼻子、嘴等。
3、代替和級聯
棧式自編碼會將網絡中的中間層作為下一個網絡的輸入進行訓練。我們可以得到網絡中每一個中間層的原始值,為了能有更好的效果,還可以使用級聯的方式進一步優化網絡的參數。
在已有的模型上接着優化參數的步驟習慣上成為"微調"。該方法不僅在自編碼網絡,在整個深度學習里都是常見的技術。
微調通常在有大量已標注的訓練數據的情況下使用。在這樣的情況下,微調能顯著提高分類器的性能。但如果有大量未標記數據集,卻只有相對較少的已標注數據集,則微調的作用有限。
四 自編碼器的應用場合
在之前我們使用自編碼器和去噪自編碼器演示了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()