#Deep Learning 上一篇主要是講了全鏈接神經網絡,這裏主要講的就是深度學習網絡的一些設計以及一些權值的設置。神經網絡能夠根據模型的層數,模型的複雜度和神經元的多少大體能夠分紅兩類:Shallow Neural Network和Deep Neural Network。比較一下二者:git
Network Name | Time | complexity | theoretical |
---|---|---|---|
Shallow Neural Network | more efficient to train | simple | powerful enough |
Deep Nerual Network | need more time | sophisticated | more meaningful |
###Autoencoder 什麼是權重?在神經網絡的模型裏面權重表明的就是特徵轉換(feature transform)。從編碼方面來講,權重也能夠當作是一種編碼方式,把一個數據編碼成另外一種數據。神經網絡是一層一層的進行的,因此若是是一個好的權重的話,是應該能夠提取出數據的特徵的,也就是information-preserving encoding,一種能夠保留有用信息的編碼。第i層提取了特徵,傳到了i+1層,i+1層在i層的基礎上再提取一些更加複雜的特徵,傳到i+2層。最後每一次均可以提取出特徵來,這樣就能夠最大限度的把數據的特徵保留下來。上面講到的手寫數字識別是能夠把一個數字劃分紅不少個筆畫,反過來,也能夠由特徵轉換回數字。這種可逆的轉換叫作information-preserving,經過神經網絡轉換的特徵最後是能夠轉換回原來的輸入,這也正是pre-train要作到的。因此pre-train要知足的特徵就是要求information-preserving。由於拿到了特徵又能夠用獲得的特徵獲得原輸入,證實獲得的特徵是能夠表明整個數據的,沒有遺漏。github
想要獲得這樣的效果,只須要創建一個三層的神經網絡便可。 ![]()
這個神經網絡通過權重$$W_{ij}^{1}$$獲得的結果就是encode以後的數據,也就是經過feature transform提取特徵的過程,通過$$W_{ji}^2$$的就是解碼過程,要求輸出層輸出的數據要和原數據差很少接近。整個網絡是$$d - d' - d$$的結構,重點就在重構,輸入層到隱含層是特徵轉換,隱含層到輸出層是重構。這種結構咱們叫autoencode,對應編碼和解碼,整個過程就是在逼近indentity function(恆等函數) ![]()
可是這樣好像畫蛇添足,既然輸出都是差很少的爲何要畫蛇添足?對於監督式學習,隱藏層其實就是特徵轉換,乘上一些權值轉換成對應特徵的值。就好像手寫數字識別獲得每個數字的特徵,包含了一些提取出有用的特徵,能夠獲得一些數據具備表明性的信息。對於非監督的學習,也可使用autoencode來作density estimation,密度估計,若是$$g(x) ≈ x$$密度較大,說明能夠提取到的特徵不少,不然就是密度很小。也能夠作outlier detection,異常值的檢測,也就是孤立點,噪音。找出哪些是典型的資料,哪些不是。 ![]()
對於編碼器,咱們更加關心的實際上是中間的隱藏層,也就是特徵轉換權重$$W_{ij}^{(1)}$$天然對應的error function:$$\sum_{i=1}^d(g_i(x) - x_i)^2$$ ![]()
三層的autoencode也叫basic autoencode 由於它是最簡單的了。一般限定$$d' < d$$方便數字的編碼,數據集能夠表示爲$$(x_1, y_1 = x_1) (x_2, y_2 = x_2)(x_3, y_3 = x_3)......$$因此能夠當作是一個非監督式的學習,一個重要的限制條件是$$W_{ij}^{(1)} = W_{ji}^{(2)}$$ 解碼權重等於編碼權重,這起到了regularization的做用。 ![]()
深度學習中,basic autoencoder的過程也就對應着pre-training的過程,使用這種方法,對無label的原始數據進行編碼和解碼,獲得的編碼權重$$W_{ij}^{(1)}$$就能夠做爲pre-trained的比較不錯的初始化權重,也就是做爲深度學習中層與層之間的初始化權重。 ![]()
因此,pre-train的訓練過程:首先,autoencoder會對深度學習網絡第一層(即原始輸入)進行編碼和解碼,獲得編碼權重$$W_{ij}^{(1)}$$,做爲網絡第一層到第二層的的初始化權重;而後再對網絡第二層進行編碼和解碼,獲得編碼權重W(1)ij,做爲網絡第二層到第三層的初始化權重,以此類推,直到深度學習網絡中全部層與層之間都獲得初始化權重。值得注意的是,對於l-1層的網絡$${{x_n^{l-1}}}$$,autoencoder中的d˘應與下一層(即l層)的神經元個數相同。 ![]()
這裏介紹的只是一種最簡單的,還有不少的編碼器,稀疏編碼器,去燥編碼器等等。 ####②regularization 控制模型的複雜度就是使用正則化了。 ![]()
神經網絡每一層都有不少的權重和神經元,這就致使了模型的複雜度會很大,regularization是必要的。 咱們以前介紹過一些方法: 1.structural decisions。架構能夠設計的簡單點,可是深度學習神經網絡的結構是不可能簡單的,這輩子的不可能。只能是相對來講用dropout減輕一些壓力。2.weight decay or weight elimination regularizers。可使用正則化來減少權值。 3.early stop。不要訓練的這麼準確,差很少差很少就夠了。 ![]()
下面是一種新的regularization的方式: ![]()
###Denoising Autoencoder 回顧一下以前的overfit的緣由: 算法
和樣本數量,噪聲大小都有關係,若是數據量是不變的,那麼noise的影響就會很是大。那麼這個時候實現regularization的一個方法就是消除noise的影響。 有一種方法是對數據clean操做,可是這樣費時費力。有一種更加牛逼的作法,就是在數據中添加一些noise再訓練。 ![]()
這種作法的初衷是想創建一個比較健壯,容錯性高的autoencode。在一個已經訓練好的autoencode中,g(x) ≈ x的,對於已經容錯性很高,比較健壯的autoencode,獲得的輸出是和x很接近的。好比手寫數字識別,一個很規範很正的6輸進去能夠獲得一個6,可是若是原始的圖片是歪歪的6,而後仍是能夠獲得6的話那麼這個自編碼器就是一個健壯的自編碼器了。比較不是每個人寫6都是寫的正正,這就使得這個自編碼器的抗噪能力很強。 因此,最後咱們要訓練的樣本集$$(x_1',y_1 = x_1),(x_2',y_2 = x_2),(x_3',y_3 = x_3)......$$ 其中$$x_n' = x_n + noise$$ 因此使用這種autoencode的目的就是使得加入了有噪音的x均可以獲得純淨的x。不只能從純淨的樣本中編解碼獲得純淨的樣本,還能從混入noise的樣本中編解碼獲得純淨的樣本。這樣獲得的權重初始值更好,由於它具備更好的抗噪聲能力,即健壯性好。實際應用中,denoising autoencoder很是有用,在訓練過程當中,輸入混入人工noise,輸出純淨信號,讓模型自己具備抗噪聲的效果,讓模型健壯性更強,最關鍵的是起到了regularization的做用。這也是以前說的去燥自編碼器。 ![]()
#linear autoencode 剛剛介紹的自編碼器是非線性的,由於中間的tanh(x)函數就是非線性的。常見的autoencode經常使用於在深度學習中,這裏要介紹的是線性的自編碼器,linear autoencode。對於一個線性的自編碼器,只須要不包括非線性轉換就行了。 因此,就變成$$h_k(x) = \sum_{j = 0}^{d'}W_{jk}^{(2)}\sum_{i = 0}^{d}W_{ij}^{(1)}$$ 這裏有三個限制條件: ①移除bias項,保持維度一致。 ②編碼權重與解碼權重一致:$$W_{ij}^{(1)} = W_{jk}^{(2)} = W_{ij}$$ ③$$d' < d$$ 因爲兩個權重是同樣的,W的維度是dxd',x的維度是dx1,因此整個公式能夠變爲:$$h_k(x) = WW^Tx$$ 可是要求$$h(x) = x$$ 因此error function$$E_{in}(h) = E_{in}(W) = \frac{1}{N}\sum_{n=1}^{N}|x_n - WW^Tx_n|^2 with(dxd')W$$ 對於那個協方差矩陣首先能夠進行特徵值分解$$WW^T = VΓV^T$$其中Γ是一個對角矩陣,V矩陣知足$$VV^T = I_d$$ 而I矩陣有一個很重要的性質,因爲W是dxd'的,d < d’。有I的非零元素的數量是不大於d'的。 ![]()
###證實 對於一個矩陣W(dxd',d < d'),那麼有Rank(W) <= d'。這個結論直接給出,不用證實。假設有兩個矩陣A,B,C = AB,那麼AX = C的解就是B,也就是惟一解,也就是說Rank(A, C) = Rank(A )。證實:對於Rank(A,C)和Rank(A)無非就是兩種狀況,=和>。<是沒有的,Rank(A)是不可能大於Rank(A,C)的。若是是Rank(A) < Rank(A,C),那麼這個增廣矩陣(A,C)變換成初等矩陣以後,最後一行就會出現$$[0,0,0,0,a]$$的狀況,0 != a,天然就是無解了。因此證實了上訴狀況必定是有Rank(A) = Rank(A,C) 而因爲R(C) <= R(A,C),因此R(C) <= R(A),同理,也能夠證實得R(C) <= R(B),而在這裏$$A = W,B = W^T,C = WW^T$$因此,$$rank(WW^T)$$是不大於d'的,由於一個矩陣的秩是不能夠大於它最小的維度的。$$Rand(W) = Rank(W^T) <= d'$$綜上所訴,$$Rank(WW^T) <= d'$$更重要的是,秩就是這個矩陣特徵值的數量,而I矩陣的斜對角線就是WW^T矩陣的特徵值,因此纔有I的非零元素的數量是不大於d'的。bash
回到正文。$$VV^T = I => VIV^T = I $$那麼就能夠推出$$x_n = VIV^Tx_n$$ 網絡
因此,通常的,若是咱們有M個N維向量,想將其變換爲由R個N維向量表示的新空間中,那麼首先將R個基按行組成矩陣A,而後將向量按列組成矩陣B,那麼兩矩陣的乘積AB就是變換結果,其中AB的第m列爲A中第m列變換後的結果。 架構
問題來了,這5個數據都是2維度的,如今想降到一維,若是咱們必須使用一維來表示這些數據,又但願儘可能保留原始的信息,你要如何選擇?這個問題其實是要在二維平面中選擇一個方向,將全部數據都投影到這個方向所在直線上,用投影值表示原始記錄。這是一個實際的二維降到一維的問題。 一種直觀的見解是:但願投影后的投影值儘量分散。同一個維度數據之間不"擠"在一塊兒,由於擠在一塊兒表達不了數據自己了。而表徵數據之間離散程度,就考慮到了方差。具體作法就是 想辦法讓高維數據左乘一個矩陣,獲得降維後的數據,降維後的數據的方差最大化。在線性代數中學過,一個矩陣能夠當作是不少個列向量,每個向量表明着一個物體的數據。要表達這個矩陣真的須要這麼多數據嗎,顯然不必定,求解一個矩陣的秩R,看看R 的值,在絕大多數狀況下,矩陣都不是滿秩的,說明矩陣中的元素表達客觀世界中的物體有一些是多餘的。 既然是求離散程度,那麼就是方差了,對於每個字段的方差$$Var(a) = \frac{1}{m}\sum_{i=1}^{m}(a_i - u)^2$$若是已經去除了均值那就能夠直接轉換$$Var(a) = \frac{1}{m}\sum_{i=1}^{m}(a_i)^2$$ 對於上面二維降成一維的問題來講,找到那個使得方差最大的方向就能夠了。不過對於更高維,還有一個問題須要解決。考慮三維降到二維問題。與以前相同,首先咱們但願找到一個方向使得投影后方差最大,這樣就完成了第一個方向的選擇,繼而咱們選擇第二個投影方向。可是若是第二次仍是堅持選擇方差最大的方向,那麼確定仍是會和第一個方向相同的,這樣表達的信息頗有可能重合,因此應該要加上一些限制。從直觀上說,讓兩個字段儘量表示更多的原始信息,咱們是不但願它們之間存在(線性)相關性的,由於相關性意味着兩個字段不是徹底獨立,必然存在重複表示的信息。 事實上兩個字段的相關性是能夠用協方差來表示。$$Cov(a,b) = \frac{1}{m}\sum_{i=1}^{m}a_ib_i$$協方差爲0的時候,兩個字段是互相獨立的。協方差爲0因此只能在一個基的正交方向選擇第二個基。將一組N維向量降爲K維(K大於0,小於N),其目標是選擇K個單位(模爲1)正交基,使得原始數據變換到這組基上後,各字段兩兩間協方差爲0,而字段的方差則儘量大(在正交的約束下,取最大的K個方差)。 ####⑤協方差矩陣的優化 協方差矩陣的對角線表明了原來樣本在各個維度上的方差,其餘元素表明了各個維度之間的相關關係。因此咱們須要把協方差對角化,由於咱們須要處理的就是維度中的關係,而不是維度之間的。設Y=PX,則Y爲X對P作基變換後的數據。設Y的協方差矩陣爲D,則有: dom
總結:一開始對數據進行降維操做,咱們想到的就是要使得數據投影以後方差最大,那麼就須要尋找一組基使達成這個條件。可是發現若是隻是找最大的方差是會有重複,好比第一個發現的基是最大的,那麼第二個方向確定也差很少是這個方向,這樣信息就有重複,因此又至關數據維度之間是應該有非相關性的,因而就使用到了協方差來表明,可是咱們須要的是數據裏的非相關性最大化,數據間的最大化方差最大已經處理了,因此這個時候協方差只須要關注對角線,天然就是對角化了。又發現對角化其實就是特徵分解的一個過程,最後求出結果。 另外一個方面,W咱們能夠看作是基的組合,WX就是降維以後的數據$$|x - ww^Tx|^2$$就是要求了降維以後的數據要和原來的數據相差不遠,爲何不是X-WX呢?首先這兩個東西不是同緯度的,不可能計算,其次乘上一個轉置其實就是轉換回來的結果了。若是壓縮以後轉換回來的結果和原來差很少,那不就證實了這個降維是OK的嗎?因此這就和Autoencode的思想同樣了,因此上面linear autoencode的過程也能夠看作就是PCA的公式化推導。 ####⑤PCA算法過程: ①將原始數據按列組成n行m列矩陣X ②均值化操做。 ③求協方差 ④求特徵值特徵向量 ⑤排列取前k大的特徵值對應的特徵向量 ⑥降維操做 #Coding ####Autoencode 編碼器有幾個很重要的特徵: ①編碼器是數據相關的,這就意味着只能壓縮那些和訓練數據相關的數據,好比用數字的圖像訓練了而後有跑去壓縮人臉的圖片,這樣效果是不好的。 ②編碼器不管是訓練多少次,都是有損的。 ③自動編碼器是從數據樣本中自動學習的,這意味着很容易對指定類的輸入訓練出一種特定的編碼器,而不須要完成任何新工做 ####Tool 先介紹一下工具類:機器學習
import numpy as np
from keras.datasets import mnist
import matplotlib.pyplot as plt
def get_dataSets():
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32')/255.
x_test = x_test.astype('float32')/255.
x_train = x_train.reshape(len(x_train), np.prod(x_train.shape[1:]))
x_test = x_test.reshape(len(x_test), np.prod(x_test.shape[1:]))
return x_train, x_test
pass
複製代碼
首先是引入數據,mnist.load_data()這個數據導入有點問題,上網百度了一下發現百分之90的博客都是有問題本身下的,因此不例外我也是本身下載了4個壓縮包放在了當前目錄data下面。 獲得數據時候先進行歸一化操做,而後reshape,由於後面一開始用到的是單層的編碼器,用的全鏈接神經網絡,因此應該變成是二維的數據。函數
def show(orignal, x):
n = 10 # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
ax = plt.subplot(2, n, i + 1)
plt.imshow(orignal[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(x[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
複製代碼
這個函數用於顯示,傳入的參數是原始圖像和預測的圖像。好像都蠻直觀的。 雖然每一種編碼器都是用了一個類來封裝,可是每個只有應該create方法,返回一個已經訓練好的autoencode,因此下面全部的代碼都是在create裏面。 ####①單層自編碼器 單層自編碼器就是以前說的最簡單的base autoencode。使用Keras實現。 所有都封裝在一個類裏面,用類的create函數返回一個已經訓練好的autoencode。使用的數據集是mnist數據集。工具
def create(self):
x_train, x_test = get_dataSets()
encoding_dim = 32
input_img = Input(shape=(784,))
複製代碼
獲得數據,隱藏層神經元的數量,入口函數。
encoded = Dense(encoding_dim, activation='relu')(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)
複製代碼
編碼層,最後那個小括號裏面(input_img)就是輸入的內容了,解碼層就是從encoded輸入。Dense裏面定義的是當前層的神經元,decoded層輸出了,天然就是784 = 28 x 28個了。
autoencoder = Model(input=input_img, output=decoded)
encoder = Model(input=input_img, output=encoded)
encoded_input = Input(shape=(encoding_dim,))
decoder_layer = autoencoder.layers[-1]
decoder = Model(input=encoded_input, output=decoder_layer(encoded_input))
複製代碼
接下來的這幾個就是獲得完整的模型,使用Keras的Model API來構建。下面的幾個就是encoder獲得編碼層,獲得解碼層。都很直觀。
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train,
epochs=50,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test))
return autoencoder, encoder, decoder
複製代碼
以後就是模型的編譯,模型的訓練了。 最後的效果:
input_img = Input(shape=(784,))
encoded = Dense(128, activation='relu')(input_img)
encoded = Dense(64, activation='relu')(encoded)
encoded = Dense(32, activation='relu')(encoded)
decoded = Dense(64, activation='relu')(encoded)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='sigmoid')(decoded)
autoencoder = Model(input=input_img, output=decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train,
epochs=1,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test))
複製代碼
中間那幾層獲得編碼層的那些去掉了。最後看看效果。
encoded = Dense(encoding_dim, activation='relu',
activity_regularizer=regularizers.l1(10e-5))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)
複製代碼
####④卷積自編碼器 其實就是卷積神經網絡作編碼而已。
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test),28, 28, 1))
複製代碼
數據格式要正確,由於是卷積處理了,要按照(個數,長,寬,顏色)排列。
input_img = Input(shape=(28, 28, 1))
x = Convolution2D(16, (3, 3), activation='relu', border_mode='same')(input_img)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(x)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(x)
encoded = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(x)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(16, 3, 3, activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, (3, 3), activation='sigmoid', border_mode='same')(x)
複製代碼
encode和decode是兩個對應的東西,反着來而已,因爲是一個圖片一個圖片的進來,因此輸出就是一個。
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train, epochs=20, batch_size=256,
shuffle=True,
validation_data=(x_test, x_test))
return autoencoder
複製代碼
最後就是常規操做了。 這個訓練時間有點長,電腦不行沒辦法。效果:
def get_nosing(noise_factor):
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))
noise_factor = noise_factor
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)
x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape)
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)
return x_train, x_train_noisy, x_test, x_test_noisy
pass
複製代碼
時間就是使用高斯分佈來獲得了。
x_train, x_train_noisy, x_test, x_test_noisy = get_nosing(0.5)
input_img = Input(shape=(28, 28, 1))
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(input_img)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(x)
encoded = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, 3, 3, activation='sigmoid', border_mode='same')(x)
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train_noisy, x_train, shuffle=True,epochs=1, batch_size=128,
validation_data=(x_test_noisy, x_test))
return autoencoder
複製代碼
這裏的網絡有些許不一樣,是由於便於計算罷了。過程是同樣,扔的數據不一樣而已。效果:
####PCA PCA就不用mnist數據集了,畢竟784維降到2維不是很好看,使用iris數據集。 獲取數據那些就不寫了,畢竟蠻簡單的。
class pca(object):
def fit(self, data_features, y):
data_mean = np.mean(data_features, axis=0)
data_features -= data_mean
cov = np.dot(data_features.T, data_features)
eig_vals, eig_vecs = np.linalg.eig(cov)
eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:, i]) for i in range(len(eig_vals))]
a = np.matrix(eig_pairs[0][1]).T
b = np.matrix(eig_pairs[1][1]).T
u = np.hstack((a, b))
data_new = np.dot(data_features, u)
return data_new
def show(self, data_new):
plt.scatter(data_new[:, 0].tolist(), data_new[:, 1].tolist(), c='red')
plt.show()
pass
複製代碼
步驟其實很簡單,均值化求特徵值排序組合向量基對原數據作乘法。
####最後附上GitHub代碼:github.com/GreenArrow2…