TensorFlow實戰之實現自編碼器過程

        關於本文說明,已同步本人另一個博客地址位於http://blog.csdn.net/qq_37608890,詳見http://blog.csdn.net/qq_37608890/article/details/79352212。
python

      本文根據最近學習TensorFlow書籍網絡文章的狀況,特將一些學習心得作了總結,詳情以下.若有不當之處,請各位大拿多多指點,在此謝過。算法

1、相關概念

一、稀疏性(Sparsity)及稀疏編碼(Sparse Coding)
網絡

         Sparsity 是當今機器學習領域中的一個重要話題。
dom

          Sparsity 的最重要的「客戶」大概要屬 high dimensional data 了吧。如今的機器學習問題中,具備很是高維度的數據隨處可見。例如,在文檔或圖片分類中經常使用的 bag of words 模型裏,若是詞典的大小是一百萬,那麼每一個文檔將由一百萬維的向量來表示。高維度帶來的的一個問題就是計算量:在一百萬維的空間中,即便計算向量的內積這樣的基本操做也會是很是費力的。不過,若是向量是稀疏的的話(事實上在 bag of words 模型中文檔向量一般都是很是稀疏的),例如兩個向量分別只有L1 和 L2 個非零元素,那麼計算內積能夠只使用min(L1,L2) 次乘法完成。所以稀疏性對於解決高維度數據的計算量問題是很是有效的。機器學習

          稀疏編碼(Sparse Coding)算法是一種無監督學習方法,它用來尋找一組「超完備」基向量來更高效地表示樣本數據。稀疏編碼算法的目的就是找到一組基向量 ,使得咱們能將輸入向量 表示爲這些基向量的線性組合:

函數

       雖然形如主成分分析技術(PCA)能使咱們方便地找到一組「完備」基向量,可是這裏咱們想要作的是找到一組「超完備」基向量來表示輸入向量(也就是說,k > n)。超完備基的好處是它們能更有效地找出隱含在輸入數據內部的結構與模式。然而,對於超完備基來講,係數 ai 再也不由輸入向量 惟一肯定。所以,在稀疏編碼算法中,咱們另加了一個評判標準「稀疏性」來解決因超完備而致使的退化(degeneracy)問題。要求係數 ai 是稀疏的意思就是說:對於一組輸入向量,咱們只想有儘量少的幾個係數遠大於零。選擇使用具備稀疏性的份量來表示咱們的輸入數據是有緣由的,由於絕大多數的感官數據,好比天然圖像,能夠被表示成少許基本元素的疊加,在圖像中這些基本元素能夠是面或者線。oop

         在早期,學者們研究稀疏編碼(Sparse Coding)時,蒐集了大量黑白風景照片,且從中提取了16像素*16像素的圖片碎片。結果發現:幾乎全部的圖像碎片均可以用64種正交的邊組合獲得,且組合出一張圖像碎片須要的邊的數量是不多的,即稀疏的。學者們同時也發現,聲音其實也存在這種狀況,他們從大量的未標註的音頻中發現了20種基本結構,絕大多數聲音能夠由這些基本結構線性組合獲得。顯然,這就是特徵的稀疏表達,使用少許的基本特徵組合拼裝獲得更高層抽象的特徵。通常狀況想,咱們也須要多層的神經網絡,對每一層神經網絡而言,前一層的輸出都是未加工的像素,而這一層則是對像素進行加工組織成更高階的特徵。性能

二、自編碼(AutoEncoder)
學習

         顧名思義,便可以使用自身的高階特徵編碼本身。自編碼器其實也是一種神經網絡,它的輸入和輸出是一致的,它藉助稀疏編碼的思想,目標是使用稀疏的一些高階特徵從新組合來重構本身,即 :對全部的自編碼器來說,目標都是樣本重構。測試

         在機器學習中,自編碼器的使用十分普遍。自編碼器首先經過編碼層,將高維空間的向量,壓縮成低維的向量(潛在變量),而後經過解碼層將低維向量解壓重構出原始樣本

三、隱含層

        指輸入層和輸出層之外,中間的那些層。輸入層和輸出層是可見的,且層的結構是相對固定的,而隱含層結構不固定,至關於不可見。只要隱含的節點足夠多,便是隻有一個隱含層的神經網絡也能夠擬合任意函數。隱含層層數越多,越容易擬合複雜的函數。擬合複雜函數須要的隱含節點數目隨着層數的增多而呈指數降低。即層數越深,概念越抽象,這就是深度學習。

四、過擬合

      指模型預測準確率在訓練集上升高,但在測試集上反而降低。這是模型的泛化性很差,只記住了當前數據的特徵。

五、Dropout

       Dropout:防止過擬合的一種方法。將神經網絡某一層的輸出節點數據隨機丟棄一部分。能夠理解爲是對特徵的採樣。

六、優化函數

        優化調試網絡中的參數。通常狀況下,在調參時,學習率的設置會致使最後結果差別很大。神經網絡一般不是凸優化,充滿局部最優,可是神經網絡可能有不少個局部最優都能達到良好效果,反而全局最優容易出現過擬合。

       對於SGD,一般一開始學習率大一些,能夠快速收斂,可是訓練的後期,但願學習率能夠小一些,能夠比較穩定地落到一個局部最優解。

        除SGD以外,還有自適應調節學習率的Adagrad、Adam、Adadelta等優化函數

七、激活函數

         Sigmoid函數的輸出在(0,1),最符合機率輸出的定義,但侷限性較大。
ReLU,當x<=0時,y=0;當x>0時,y=x。這很是相似於人類的閾值響應機制。ReLU的3個顯著特色:單側抑制;相對寬闊的興奮邊界;稀疏激活性。它是目前最符合實際神經元的模型。

 2、自解碼器算法原理      

一、BacsicAuto-Encoder

         Auto-Encoder(AE)是20世紀80年代晚期提出的,簡單講,AE能夠被視爲一個三層神經網絡結構:一個輸入層、一個隱藏層和一個輸出層,從數據規模上講,輸入層與輸出層具備相同的規模。

            

                             圖1  Auto-Encoder網絡結構示意圖

     其中,n表示輸入層(同時也是做爲輸出層)的規模;m表示隱藏層的規模;分別表示輸出層、隱藏層和輸出層上的向量,也是各層上對應的數據個數,這裏隱藏層h的數據低於輸入層和輸出層的數據,即x>h<y且x=y。根據輸入層x到隱藏層h的映射矩陣求出h,再根據隱藏層h到輸出層的映射矩陣求出y。分別表示隱藏層和輸出層上的偏置向量。表示輸入層與隱藏層之間的權值矩陣,即x到h的映射矩陣,是n乘m階矩陣;表示隱藏層與輸出層之間的權值矩陣,即h到y的映射矩陣,是m乘n階矩陣,也是W的逆矩陣。   

      針對AE而言,重要的是解決好矩陣W問題,一個好的矩陣W能夠保證x徹底等於y,可實際上很難,尤爲當輸入層x的數據量大成千上百個時,x和y的差異就能夠說明矩陣W的優良程度。總之,咱們的工做就是要求出矩陣W,並使得x和y之間的差別儘量小。下面給出下圖,進一步作出解釋

     

 

                                          

                         圖2 Auto-Encoder抽象結構 

       如圖2所示,AE由編碼器(Enconder)和解碼器(Decoder)兩部分構成。顯然,從輸入層到隱藏層屬於編碼過程,從隱藏層到輸出層屬於解碼過程。咱們設定f和g分別表示編碼和解碼函數,結合上面的內容,咱們能夠給出f和g的方程,以下(1.1)和(1.2)式所示

 

                 

       其中 爲編碼器的激活函數,一般狀況下取Sigmoid函數,即 爲解碼器的激活函數,通常取Sigmoid函數或恆等函數,即。權值矩陣通常取一些文獻資料中說起稱,這種狀況下的AE具備tiedweights。本文指討論兩者相等的狀況。

       因此,截止目前,咱們可知,AE的參數爲

       

         假設目前咱們有一個訓練集S=那麼咱們要考慮的問題是如何利用S訓練,首先是創建一個訓練目標。

       結合前面的梳理,咱們可知,AE能夠被當作一個普通的三層神經網絡,y也能夠當作由x作的一個預測(prediction),且保證x和y儘量接近,這種接近程度能夠經過重構偏差(reconstruction error)函數L(x,y)來進行表達。

    根據解碼器激活函數的不一樣,這裏L(x,y)通常有兩種取值方法:
             第一種,當爲恆等函數時,取平方偏差(sequared error)函數
     第二種,當 爲sigmoid函數(此種狀況輸入)時,取交叉熵(cross-entropy)函數
    取得重構函數後,咱們就能夠對數據集S進行鍼對性的訓練,去獲得一個總體的損失函數(loss function)(1.5)
                                

        將上面這個函數(1.5)進行極小化處理,咱們就能夠獲得所需的參數

        注意,通常文獻中損失函數多數使用的是平均重構偏差(average reconstruction error),即下面函數(1.6)

                        

         對於一個給定的訓練集S而言,係數1/N並不對於上面這個損失函數最小化產生多大的影響,因此,爲了簡便,這裏忽略這個係數。

    最開始,AE只是做爲一種降維技巧來使用的,視隱藏層h爲x的一種降維表示,也要求m<n(h<x)。可是,目前來看,AE在m>=n的狀況(有些文獻中稱爲 over-complete setting)下應用更多,以便獲取更高維數更有意義的表達方式。固然,在次狀況下,也會有一個問題不可忽略:若直接對損失函數(1.5)進行極小化的而沒有加入任何其餘限制條件的話,AE在這裏學到的極可能是一個恆等函數(where the auto-encoder could perfectly reconstruction the input without needing to extract any useful feature),這種結果是不符合預期的。須要解決,例如  , 在損失函數中加入稀疏性限制(Sparse Auto-Encoder),或在網絡中引入隨機性(如RBN,Denoising Auto-Encoder)等。
    下面先介紹 Regularized Auto-Encoer 和Sparse Auto-Encoder,其他的AE變種算法,後續有機會繼續梳理。

 二、 Regularized Auto-Encoder

         咱們在損失函數(1.5)中加入正則項,即可以獲得所謂的Regularized Auto-Encoder。 常見的正則化有L1 正則和 L2正則。以L2正則爲例進行介紹,損失  函數則變爲以下(2.7):

                                
      其中      爲權值矩陣W的元素。
公式(2.7)中的即爲L2正則項,也叫作權重衰減(weight-decay)項(下標中的 就是weight-decay的意思),lambda爲權重衰減參數,用來控制公式中兩項的相對重要性。

三、Sparse Auto-Encoder
         由公式(2.7)能夠看出,權重衰減項其實是對權重提出一些要求,也就是要求它們不能過大,不然會被懲罰。這裏所講的Sparse Auto-Encoder 是對隱藏層上神經元的激活度提出要求,使其知足必定的稀疏性。
        關於隱藏層上神經元激活度如何刻畫的問題,下面展開來看:
            假設表示輸入爲x時,隱藏層上第j個神經元的激活度(  是(1.1)中向量h的第j個份量),則(3.8)

         表示隱藏層上第j個神經元在訓練集 上的平均激活度。爲了保證隱藏層上的神經元大部分時間被抑制爲零或者接近於零(即稀疏性限制),這裏要求(3.9)
其中,爲稀疏性參數,通常狀況下取一個很小的數(如   )。對於那些與有顯著不一樣的,會進行懲罰。這個限制方法的實現有多種,這裏以一種基於KL divergence的方法,即引入(3.10)

其中,(3.11)

函數 具備以下性質:
      隨着和   之間的差別增大而單調遞增,尤爲當 時,存在達到最小值。因此,若是將(3.10)加入到損失函數裏,則最小化損失函數便可達到使得儘量靠近 的效果。這樣來看,Sparse Auto-Encoder的損失函數就能夠表達爲(3.12)
                          
         這裏beta爲控制稀疏性懲罰項的權重係數。

        咱們也能夠將L2正則和稀疏性限制結合起來使用,這時的損失函數也就變爲(3.13)

                       

3、實現去噪自編碼器過程

一、代碼實現過程以下

# 這裏以最具表明性的去噪自編碼器爲例。

#導入MNIST數據集

import numpy as np

import sklearn.preprocessing as prep

import tensorflow as tf

from tensorflow.examples.tutorials.mnist import input_data


#這裏使用一種參數初始化方法xavier initialization,須要對此作好定義工做。

#Xaiver初始化器的做用就是讓權重大小正好合適。

#這裏實現的是標準均勻分佈的Xaiver初始化器。

def xavier_init(fan_in, fan_out, constant = 1):

"""

目的是合理初始化權重。

參數:

fan_in --行數;

fan_out -- 列數;

constant --常數權重,條件初始化範圍的倍數。

return 初始化後的權重tensor.

"""

low = -constant * np.sqrt(6.0 / (fan_in + fan_out))

high = constant * np.sqrt(6.0 / (fan_in + fan_out))

return tf.random_uniform((fan_in, fan_out),

minval = low, maxval = high,

dtype = tf.float32)


#定義一個去噪的自編碼類

class AdditiveGaussianNoiseAutoencoder(object):

"""

__init__() :構建函數;

n_input : 輸入變量數;

n_hidden : 隱含層節點數;

transfer_function: 隱含層激活函數,默認是softplus;

optimizer : 優化器,默認是Adam;

scale : 高斯噪聲係數,默認是0.1;

"""

def __init__(self, n_input, n_hidden, transfer_function =tf.nn.softplus, optimizer = tf.train.AdamOptimizer(),

scale = 0.1):

self.n_input = n_input

self.n_hidden = n_hidden

self.transfer = transfer_function

self.scale = tf.placeholder(tf.float32)

self.training_scale = scale

network_weights = self._initialize_weights()

self.weights = network_weights


# 定義網絡結構,爲輸入x建立一個維度爲n_input的placeholder,而後

#創建一個能提取特徵的隱含層。

self.x = tf.placeholder(tf.float32, [None, self.n_input])

self.hidden = self.transfer(tf.add(tf.matmul(self.x +scale * tf.random_normal((n_input,)),

self.weights['w1']),

self.weights['b1']))

self.reconstruction = tf.add(tf.matmul(self.hidden,self.weights['w2']), self.weights['b2'])


#首先,定義自編碼器的損失函數,在此直接使用平方偏差(SquaredError)做爲cost。

#而後,定義訓練操做做爲優化器self.optimizer對損失self.cost進行優化。

#最後,建立Session,並初始化自編碼器所有模型參數。

self.cost = 0.5 *tf.reduce_sum(tf.pow(tf.subtract(self.reconstruction, self.x), 2.0))

self.optimizer = optimizer.minimize(self.cost)


init = tf.global_variables_initializer()

self.sess = tf.Session()

self.sess.run(init)


def _initialize_weights(self):

all_weights = dict()

all_weights['w1'] = tf.Variable(xavier_init(self.n_input,self.n_hidden))

all_weights['b1'] = tf.Variable(tf.zeros([self.n_hidden],dtype = tf.float32))

all_weights['w2'] = tf.Variable(tf.zeros([self.n_hidden,self.n_input], dtype = tf.float32))

all_weights['b2'] = tf.Variable(tf.zeros([self.n_input],dtype = tf.float32))

return all_weights


def partial_fit(self, X):

cost, opt = self.sess.run((self.cost, self.optimizer),feed_dict = {self.x: X,

self.scale: self.training_scale})

return cost


def calc_total_cost(self, X):

return self.sess.run(self.cost, feed_dict = {self.x: X,

self.scale:self.training_scale})

#定義一個transform函數,以便返回自編碼器隱含層的輸出結果,目的是提供一個接口來獲取抽象後的特徵。

def transform(self, X):

return self.sess.run(self.hidden, feed_dict = {self.x: X,

self.scale:self.training_scale})


def generate(self, hidden = None):

if hidden is None:

hidden = np.random.normal(size = self.weights["b1"])

return self.sess.run(self.reconstruction, feed_dict ={self.hidden: hidden})

def reconstruct(self, X):

return self.sess.run(self.reconstruction, feed_dict ={self.x: X,

self.scale: self.training_scale})


def getWeights(self): #獲取隱含層的權重w1.

return self.sess.run(self.weights['w1'])


def getBiases(self): #獲取隱含層的偏執係數b1.

return self.sess.run(self.weights['b1'])

#利用TensorFlow提供的讀取示例數據的函數載入MNIST數據集。

mnist = input_data.read_data_sets('MNIST_data', one_hot = True)

#定義一個對訓練、測試數據進行標準化處理的函數。

def standard_scale(X_train, X_test):

preprocessor = prep.StandardScaler().fit(X_train)

X_train = preprocessor.transform(X_train)

X_test = preprocessor.transform(X_test)

return X_train, X_test


def get_random_block_from_data(data, batch_size):

start_index = np.random.randint(0, len(data) - batch_size)

return data[start_index:(start_index + batch_size)]


X_train, X_test = standard_scale(mnist.train.images,mnist.test.images)


n_samples = int(mnist.train.num_examples)

training_epochs = 20

batch_size = 128

display_step = 1

autoencoder = AdditiveGaussianNoiseAutoencoder(n_input = 784,

n_hidden = 200,

transfer_function =tf.nn.softplus,

optimizer =tf.train.AdamOptimizer(learning_rate = 0.001),

scale = 0.01)

for epoch in range(training_epochs):

avg_cost = 0.

total_batch = int(n_samples / batch_size)

# Loop over all batches

for i in range(total_batch):

batch_xs = get_random_block_from_data(X_train, batch_size)


# Fit training using batch data

cost = autoencoder.partial_fit(batch_xs)

# Compute average loss

avg_cost += cost / n_samples * batch_size

# Display logs per epoch step

if epoch % display_step == 0:

print("Epoch:", '%04d' % (epoch + 1), "cost=","{:.9f}".format(avg_cost))

#最後對訓練完的模型進行性能測試。

print("Total cost: " +str(autoencoder.calc_total_cost(X_test)))

 

二、執行結果以下

Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
Epoch: 0001 cost= 18871.253996591
Epoch: 0002 cost= 12308.673515909
Epoch: 0003 cost= 10227.495348864
Epoch: 0004 cost= 11243.596613636
Epoch: 0005 cost= 10782.029647727
Epoch: 0006 cost= 9165.328120455
Epoch: 0007 cost= 8487.490198295
Epoch: 0008 cost= 9195.667004545
Epoch: 0009 cost= 9026.087407955
Epoch: 0010 cost= 8301.502948295
Epoch: 0011 cost= 9921.268600568
Epoch: 0012 cost= 8789.966229545
Epoch: 0013 cost= 9115.636243182
Epoch: 0014 cost= 8993.681156818
Epoch: 0015 cost= 7670.030270455
Epoch: 0016 cost= 8108.834190341
Epoch: 0017 cost= 7897.135417045
Epoch: 0018 cost= 8332.914957955
Epoch: 0019 cost= 8091.132888068
Epoch: 0020 cost= 7822.976949432
Total cost: 725054.5

 

 

4、小結

    
           至此,咱們能夠發現,從實現的角度而言,自編碼器其實和一個單隱含層的神經網絡差很少,只是自編碼器在數據輸入時作了標準化處理,且加上了一個高斯噪聲,同時咱們的輸出結果不是數字分類結果,而是復原的數據,所以不須要用標註過的數據進行監督訓練。自編碼器做爲一種無監督學習方法,它與其餘無監督學習的區別主要在於:它不是對數據進行聚類,而是把數據中最有用、最頻繁的高階特徵提取出來,而後根據這些高階特徵進行重構數據。在深度學習發展早期很是流行的DBN,也是依靠這種思想,先對數據進行無監督學習,提取到一些有用的特徵,將神經網絡權重初始化到一個較好的分佈,而後再使用有標註的數據進行監督訓練,即對權重進行fine-tune。

         現實生活中,大部分數據是沒有標準信息的,但人腦比較擅長處理這些數據,會提取出其中的高階抽象特徵,並使用在別的地方。自編碼器做爲深度學習在無監督領域的應用的確很是成功,同時無監督學習也將成爲深度學習一個重要發展方向。

 

參考資料   主要參考資料《TensorFlow實戰》(黃文堅  唐源 著)(電子工業出版社)

相關文章
相關標籤/搜索