allenlu2007

A great WordPress.com site

Sparse Autoencoder in Keras

Reference: https://blog.keras.io/building-autoencoders-in-keras.html

 

在 reference 只有一段話。沒有源代碼。

 

Adding a sparsity constraint on the encoded representations

In the previous example, the representations were only constrained by the size of the hidden layer (32). In such a situation, what typically happens is that the hidden layer is learning an approximation of PCA (principal component analysis). But another way to constrain the representations to be compact is to add a sparsity contraint on the activity of the hidden representations, so fewer units would “fire” at a given time. In Keras, this can be done by adding an activity_regularizer to our Dense layer:

from keras import regularizers

encoding_dim = 32

input_img = Input(shape=(784,))
# add a Dense layer with a L1 activity regularizer
encoded = Dense(encoding_dim, activation='relu',
                activity_regularizer=regularizers.l1(10e-5))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)

autoencoder = Model(input_img, decoded)

Let’s train this model for 100 epochs (with the added regularization the model is less likely to overfit and can be trained longer). The models ends with a train loss of 0.11 and test loss of 0.10. The difference between the two is mostly due to the regularization term being added to the loss during training (worth about 0.01).

Here’s a visualization of our new results:

sparse autoencoder

They look pretty similar to the previous model, the only significant difference being the sparsity of the encoded representations. encoded_imgs.mean() yields a value 3.33 (over our 10,000 test images), whereas with the previous model the same quantity was 7.30. So our new model yields encoded representations that are twice sparser.

 

我把 simplest autoencoder Keras code template 改成 sparse autoencoder 如下:

Adding a Sparsity Constraint on the Encoder

from keras.layers import Input, Dense
from keras.models import Model

from keras import regularizers

encoding_dim = 32

input_img = Input(shape=(784,))
# add a Dense layer with a L1 activity regularizer
encoded = Dense(encoding_dim, activation='relu',
                activity_regularizer=regularizers.l1(10e-9))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)

autoencoder = Model(input_img, decoded)
Using TensorFlow backend.
# this model maps an input to its encoded representation
encoder = Model(input_img, encoded)
# create a placeholder for an encoded (32-dimensional) input
encoded_input = Input(shape=(encoding_dim,))
# retrieve the last layer of the autoencoder model
decoder_layer = autoencoder.layers[-1]
# create the decoder model
decoder = Model(encoded_input, decoder_layer(encoded_input))
#autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
from keras.datasets import mnist
import numpy as np
(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:])))
print x_train.shape
print x_test.shape
(60000, 784)
(10000, 784)
autoencoder.fit(x_train, x_train,
                epochs=50,
                batch_size=256,
                shuffle=True,
                validation_data=(x_test, x_test))
Train on 60000 samples, validate on 10000 samples
Epoch 1/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0922
Epoch 2/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0922
Epoch 3/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0921
Epoch 4/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0922
Epoch 5/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0922
Epoch 6/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0921
Epoch 7/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0922
Epoch 8/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 9/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 10/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 11/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0922
Epoch 12/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 13/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0920
Epoch 14/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 15/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0921
Epoch 16/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 17/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0921
Epoch 18/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 19/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 20/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 21/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 22/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 23/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 24/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 25/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 26/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 27/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 28/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 29/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 30/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 31/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 32/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 33/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 34/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 35/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 36/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 37/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 38/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 39/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 40/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 41/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 42/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 43/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 44/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 45/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0918
Epoch 46/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0918
Epoch 47/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 48/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0918
Epoch 49/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 50/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
<keras.callbacks.History at 0x7fcbd81c8450>
# encode and decode some digits
# note that we take them from the *test* set
encoded_imgs = encoder.predict(x_test)
decoded_imgs = decoder.predict(encoded_imgs)
# use Matplotlib (don't ask)
import matplotlib.pyplot as plt

n = 10  # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
    # display original
    ax = plt.subplot(2, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # display reconstruction
    ax = plt.subplot(2, n, i + 1 + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

如果修改這一個參數,結果就完全錯了。

encoded = Dense(encoding_dim, activation='relu',
                activity_regularizer=regularizers.l1(10e-5))(input_img)

   
10e-9 —> 10e-5  即使 epoches=100 仍然一樣。注意 optimizer 是用 adam 而非 adadelta

 10-9 —> 1e-6 OK

NewImage

 

Autoencoder in Keras

Reference: https://blog.keras.io/building-autoencoders-in-keras.html

前(數)文討論各類 autoencoder (fully connected autoencoder, sparse autoencoder, multi-layer fully connected autoencoder, convolutional autoencoder, denoise autoencoder, variational autoencoder).

剛好 reference 用 Keras 重新整理以上的 autoencoders.

  • a simple autoencoder based on a fully-connected layer
  • a sparse autoencoder
  • a deep fully-connected autoencoder
  • a deep convolutional autoencoder
  • an image denoising model
  • a sequence-to-sequence autoencoder
  • a variational autoencoder

What are autoencoders?

Autoencoder: schema

“Autoencoding” is a data compression algorithm where the compression and decompression functions are 1) data-specific, 2) lossy, and 3) learned automatically from examples rather than engineered by a human. Additionally, in almost all contexts where the term “autoencoder” is used, the compression and decompression functions are implemented with neural networks.

本文主要是從 compression 角度出發。先說結論:autoencoder 不適合做一般普適性 compression algorithm (like JPEG/MPEG etc.).  

1. Pretrain deep learning network

原先 autoencoder 似乎沒有太實際的用途。在 2012 (Hinton?) 發現 autoencoder 非常適合 deep convolutional neural network 的 greedy layer-wise pretraining!  

前文說到 deep neural network 如何避免陷入 local minimum.  Greedy layer-wise pretraining 似乎可以避免 local minimum. 

不過之後發現 random initial weights 似乎效果也不錯。因此後來也不用 autoencoder 做 pretraining.  2014 batch normalization; 2015 一般開始用 residual learning to train deep networks from scratch.

 

2. Dimension reduction for visualization

 t-SNE 算是目前最好的 2D visualization algorithm.  但是 t-SNE 一般需要低維的 input data.  一個好的策略是 autoencoder + t-SNE.

先用 autoencoder 把高維的 data 壓縮到低維 (e.g. 32 dimensional), 然後再用 t-SNE map 壓縮 data 到 2D plane.  

3. Denoise


 

 

 

Keras 介紹

 

Reference:  一份不負責任的Keras介紹

 

Keras:宏觀特性Keras是最近蒸蒸日上的深度學習框架, 非常的蒸蒸日上,5月的時候有這麼個圖:

NewImage

Caffe是老牌選手,Tensorflow有個神爹Google,不跟這倆比Keras的表現還是十分亮眼的,我想現在如果有排名也會一樣出色(注意mxnet是萬年老5雖然我一直覺得mxnet其實非常出色……)

那麼,Keras有啥特點呢,我想下面這些可能是屬於Keras的關鍵詞:

  • 符號主義
  • Python
  • 快速原型
  • 輕量級,高度模塊化
  • 易擴展 

Keras事實上是一個基於Theano和Tensorflow上的一個包裝,所謂站在巨人的肩膀上也不外如是了。 因為Theano和Tensorflow都是符號主義的東西(下面講再說這是啥),因此Keras自然也是符號主義的。

Keras由純Python編寫,這意味著它的原始碼簡單易懂,你可以隨時進去看看它都做了什麼,怎麼做的。並且,當你需要修改原始碼的時候,大膽修改就可以了,它會立刻生效。儘管Python的運行效率要低於C++,但Keras只是Tensorflow和Theano的包裝而已,這層包裝的運行代價是很小的。

Keras是一款高度模塊化的框架,使用它搭建網絡和訓練網絡將會非常容易,如果你需要深入模型中控制細節,通常使用Keras提供的一些函數就可以了,很少需要深入到Tensorflow或Theano那一層。因此,Keras適合於快速原型生成,如果你經常需要很快的實現一下自己的idea,Keras會是一個不錯的選擇。

另外,Keras的預訓練模型庫也在逐步建設,目前有VGG-16,VGG-19,resnet50,Inceptionv3四種預訓練好的模型供大家使用。

計算圖,符號主義和張量

符號主義,Google一下會發現是一個機器學習的名詞,但我們這說的符號主義不是那個東西,這裡說的符號主義,指的是使用符號式編程的一種方法。 另一種相對的方法是命令式編程。或者是

要說Theano/Tensorflow/Keras,就不能不提它的符號主義特性

事實上,Theano也好,Tensorflow也好,其實是一款符號主義的計算框架,未必是專為深度學習設計的。假如你有一個與深度學習完全無關的計算任務想運行在GPU上,你完全可以通過Theano/Tensorflow編寫和運行。

假如我們要求兩個數a和b的和,通常只要把值賦值給a和b,然後計算a+b就可以了,正常人類都是這麼寫的:

a=3
b=5
z = a + b

運行到第一行,a真的是3.運行到第2行,b真的是5,然後運行第三行,電腦真的把a和b的值加起來賦給z了。

一點兒都不神奇。

但總有不正常的,不正常的會這麼想問題:a+b這個計算任務,可以分為三步。(1)聲明兩個變量a,b。建立輸出變量z(2)確立a,b和z的計算關係,z=a+b(3)將兩個數值a和b賦值到變量中,計算結果z

後面那種「先確定符號以及符號之間的計算關係,然後才放數據進去計算」的辦法,就是符號式編程 (latent instantiate)。當你聲明a和b時,它們裡面是空的。當你確立z=a+b的計算關係時,a,b和z仍然是空的,只有當你真的把數據放入a和b了,程序才開始做計算。

符號之間的運算關係,就稱為運算圖。

這樣做當然不是閒的無聊,符號式計算的一大優點是,當確立了輸入和輸出的計算關係後,在進行運算前我們可以對這種運算關係進行自動化簡,從而減少計算量,提高計算速度。另一個優勢是,運算圖一旦確定,整個計算過程就都清楚了,可以用內存復用的方式減少程序占用的內存。

在Keras,theano和Tensorflow中,參與符號運算的那些變量統一稱作張量。張量是矩陣的進一步推廣。

規模最小的張量是0階張量,即標量,也就是一個數。

當我們把一些數有序的排列起來,就形成了1階張量,也就是一個向量

如果我們繼續把一組向量有序的排列起來,就形成了2階張量,也就是一個矩陣

把矩陣摞起來,就是3階張量,我們可以稱為一個立方體,具有3個顏色通道的彩色圖片就是一個這樣的立方體

把矩陣摞起來,好吧這次我們真的沒有給它起別名了,就叫4階張量了,不要去試圖想像4階張量是什麼樣子,它就是個數學上的概念。

一言以蔽之,Keras的計算過程,就是建立一個從張量到張量的映射函數,然後再放入真實數據進行計算。對深度學習而言,這個「映射函數」就是一個神經網絡,而神經網絡中的每個層自然也都是從張量到張量的映射。

Keras框架結構

我想畫一個圖,可是想了半天畫不明白……我就羅列就好了,Keras的結構大致是這樣的:

  • backend:後端,對Tensorflow和Theano進行封裝,完成低層的張量運算、計算圖編譯等
  • models:模型,模型是層的有序組合,也是層的「容器」,是「神經網絡」的整體表示
  • layers:層,神經網絡的層本質上規定了一種從輸入張量到輸出張量的計算規則,顯然,整個神經網絡的模型也是這樣一種張量到張量的計算規則,因此keras的model是layer的子類

上面的三個模塊是Keras最為要緊和核心的三塊內容,搭建一個神經網絡,就只用上面的內容即可。注意的是,backend雖然很重要,但其內容多而雜,大部分內容都是被其他keras模塊調用,而不是被用戶直接使用。所以它不是新手馬上就應該學的,初學Keras不妨先將backend放一旁,從model和layers學起。

為了訓練神經網絡,必須定義一個神經網絡優化的目標和一套參數更新的方式,這部分就是目標函數和優化器:

  • objectives:目標函數,規定了神經網絡的優化方向
  • optimizers:優化器,規定了神經網絡的參數如何更新

上面的兩個模塊的內容,是在訓練一個網絡時必須提供的。此外,Keras提供了一組模塊用來對神經網絡進行配置:

  • initialization:初始化策略,規定了網絡參數的初始化方法
  • regularizers:正則項,提供了一些用於參數正則的方法,以對抗過擬合
  • constraints:約束項,提供了對網絡參數進行約束的方法

為了方便調試、分析和使用網絡,處理數據,Keras提供了下面的模塊:

  • callbacks:回調函數,在網絡訓練的過程中返回一些預定義/自定義的信息
  • visualization:可視化,用於將網絡結構繪製出來,以直觀觀察
  • preprocessing:提供了一組用於對文本、圖像、序列信號進行預處理的函數
  • utils:常用函數庫,比較重要的是utils.np_utils中的to_categorical,用於將1D標籤轉為one-hot的2D標籤和convert_kernel函數,用於將卷積核在theano模式和Tensorflow模式之間轉換。最新的代碼顯示utils的utils.layer_utils里提供了將模型中全部卷積核進行模式轉換的函數。大部分其他utils的函數你或許很難用到,但有空不妨一讀,或有進益。

最後,為了能讓用戶一上手就能跑一些模型,Keras提供了一個常用資料庫的模塊,用來載入常用的資料庫:

  • datasets:提供了一些常用資料庫的接口,用戶將通過這些接口下載和載入數據集

額外的一點是,如果用戶希望將Keras與scikit-learn聯動,Keras也提供了這種聯動機制,這個模塊是:

  • wrappers.scikit-learn

上回書我們簡單的把Keras的框架理了一下,下面我們深入(也不怎麼深)具體的模塊理一下Keras,主要聊一聊每個模塊的具體功能和核心函數

backend:百貨商店

backend這個模塊的主要作用,是對tensorflow和theano的底層張量運算進行了包裝。用戶不用關心具體執行張量運算的是theano還是tensorflow,就可以編寫出能在兩個框架下可以無縫對接的程序。backend中的函數要比文檔里給出的多得多,完全就是一家百貨商店。但一般情況下,文檔里給出的那些就已經足夠你完成大部分工作了,事實上就連文檔里給出的函數大部分情況也不會用,這裡提幾個比較有用的函數——當然是對我來說比較有用,畢竟這是一份不怎麼負責任的介紹,如果你想找對你有用的函數,就去backend淘一淘吧~:

  • function:毫無疑問這估計是最有用的一個函數了,function用於將一個計算圖(計算關係)編譯為具體的函數。典型的使用場景是輸出網絡的中間層結果。
  • image_ordering和set_image_ordering:這組函數用於返回/設置圖片的維度順序,由於Theano和Tensorflow的圖片維度順序不一樣,所以有時候需要獲取/指定。典型應用是當希望網絡自適應的根據使用的後端調整圖片維度順序時。
  • learning_phase:這個函數的主要作用是返回網絡的運行狀態,0代表測試,1代表訓練。當你需要便攜一個在訓練和測試是行為不同的層(如Dropout)時,它會很有用。
  • int_shape:這是我最常用的一個函數,用於以整數tuple的形式返回張量的shape。要知道從前網絡輸出張量的shape是看都看不到的,int_shape可以在debug時起到很大作用。
  • gradients: 求損失函數關於變量的導數,也就是網絡的反向計算過程。這個函數在不訓練網絡而只想用梯度做一點奇怪的事情的時候會很有用,如圖像風格轉移。

backend的其他大部分函數的函數名是望而知義的,什麼max,min,equal,eval,zeros,ones,conv2d等等。函數的命名方式跟numpy差不多,下次想用時不妨先『.』一下,說不定就有。

models/layers:Keras的核心主題

使用Keras最常見的目的,當然還是訓練一個網絡。之前說了網絡就是張量到張量的映射,所以Keras的網絡,其實是一個由多個子計算圖構成的大計算圖。當這些子計算圖是順序連接時,稱為Sequential,否則就是一般的model,我們稱為泛型模型。

模型不但是張量的計算方式,還是層對象的容器,模型用來將它所含有的層整合起來,大家手拉手一起走

模型有兩套訓練和測試的函數,一套是fit,evaluate等,另一套是fit_generator,evaluate_generator,前者適用於普通情況,後者適用於數據是以疊代器動態生成的情況。疊代器可以在內存/顯存不足,實時動態數據提升進行網絡訓練,所以使用Keras的話,Python的疊代器這一部分是一定要掌握的內容。對模型而言,最核心的函數有兩個:

  • compile:編譯,模型在訓練前必須編譯,這個函數用於完成添加正則項啊,確定目標函數啊,確定優化器啊等等一系列模型配置功能。這個函數必須指定的參數是優化器和目標函數,經常還需要指定一個metrics來評價模型。

  • fit/fit_generator:用來訓練模型,參數較多,是需要重點掌握的函數,對於keras使用者而言,這個函數的每一個參數都需要掌握。

其他的函數請自己學習。

另外,模型還有幾個常用的屬性和函數:

  • layers:該屬性是模型全部層對象的列表,是的就是一個普通的python list
  • get_layer:這個函數通過名字來返回模型中某個層對象
  • pop:這個函數文檔里沒有,但是可以用。作用是彈出模型的最後一層,從前進行finetune時沒有pop,大家一般用model.layers.pop來完成同樣的功能。

當然,因為Model是Layer的子類,Layer的所有屬性和方法也自動被Model所有,這些有用的屬性稍後介紹。

Keras的層對象是構築模型的基石,除了卷積層,遞歸神經網絡層,全連接層,激活層這種爛大街的Layer對象外,Keras還有一些不是那麼爛大街的東西:

  • Adacanced Activation:高級激活層,主要收錄了包括leakyReLU,pReLU,ELU,SReLU等一系列高級激活函數,這些激活函數不是簡單的element-wise計算,所以單獨拿出來實現一下
  • Merge層:這個層用於將多個層對象的輸出組合起來,支持級聯、乘法、餘弦等多種計算方式,它還有個小兄弟交merge,這個函數完成與Merge相同的作用,但輸入的對象是張量而不是層對象。
  • Lambda層:這是一個神奇的層,看名字就知道它用來把一個函數作用在輸入張量上。這個層可以大大減少你的工作量,當你需要定義的新層的計算不是那麼複雜的時候,可以通過lambda層來實現,而不用自己完全重寫。
  • Highway/Maxout/AtrousConvolution2D層:這個就不多說了,懂的人自然懂,keras還是在一直跟著潮流走的
  • Wrapper層:Wrapper層用於將一個普通的層對象進行包裝升級,賦予其更多功能。目前,Wrapper層里有一個TimeDistributed層,用於將普通的層包裝為對時間序列輸入處理的層,而BiDirectional(這個層還沒發布,只能在github中看到)可以將輸入的遞歸神經網絡層包裝為雙向的(如把LSTM做成BLSTM)
  • Input:補一個特殊的層,Input,這個東西實際上是一個Keras tensor的占位符,主要用於在搭建Model模型時作為輸入tensor使用,這個Input可以通過keras.layers來import。
  • stateful與unroll:Keras的遞歸神經網絡層,如SimpleRNN,LSTM等,支持兩種特殊的操作。一種是stateful,設置stateful為True意味著訓練時每個batch的狀態都會被重用於初始化下一個batch的初始狀態。另一種是unroll,unroll可以將遞歸神經網絡展開,以空間換取運行時間。

Keras的層對象還有一些有用的屬性和方法,比較有用的是:

  • name:別小看這個,從茫茫層海中搜索一個特定的層,如果你對數數沒什麼信息,最好是name配合get_layer來用。
  • trainable:這個參數確定了層是可訓練的還是不可訓練的,在遷移學習中我們經常需要把某些層凍結起來而finetune別的層,凍結這個動作就是通過設置trainable來實現的。
  • input/output:這兩個屬性是層的輸入和輸出張量,是Keras tensor的對象,這兩個屬性在你需要獲取中間層輸入輸出時非常有用
  • get_weights/set_weights:這是兩個方法用於手動取出和載入層的參數,set_weights傳入的權重必須與get_weights返回的權重具有同樣的shape,一般可以用get_weights來看權重shape,用set_weights來載入權重

既然是核心主題,我們就多嘮兩句,在Keras中經常有的一個需求是需要自己編寫一個新的層,如果你的計算比較簡單,那可以嘗試通過Lambda層來解決,如果你不得不編寫一個自己的層,那也不是什麼大不了的事兒。前兩天群里有朋友想編寫一個卷積核大小不一樣的卷積層(雖然不知道為啥他這麼想不開……活著不好嗎?),這個顯然就要自己編寫層了。

要在Keras中編寫一個自己的層,需要開一個從Layer(或其他層)繼承的類,除了init以為你需要覆蓋三個函數:

  • build,這個函數用來確立這個層都有哪些參數,哪些參數是可訓練的哪些參數是不可訓練的。

  • call,這個函數在調用層對象是自動使用,裡面就是該層的計算邏輯,或計算圖了。顯然,這個層的核心應該是一段符號式的輸入張量到輸出張量的計算過程。
    get_output_shape_for:如果你的層計算後,輸入張量和輸出張量的shape不一致,那麼你需要把這個函數也重新寫一下,返回輸出張量的shape,以保證Keras可以進行shape的自動推斷

其實也不難~是吧,不要忘記Keras是基於Python的框架,你可以隨時隨地查看那些已經寫好的層的代碼,模仿著看看你自己的層要怎麼寫~

優化器,目標函數,初始化策略,等等…

和model,layers這種核心功能相比,這些模塊的重要性就沒有那麼大,我們簡單介紹一下,裡面的具體技術,(下)篇可能會說,也可能不會……我還沒想好,但是基本上不說也沒什麼影響

objectives是優化目標, 它本質上是一個從張量到數值的函數,當然,是用符號式編程表達的。具體的優化目標有mse,mae,交叉熵等等等等,根據具體任務取用即可,當然,也支持自己編寫。需要特別說明的一點是,如果選用categorical_crossentropy作為目標函數,需要將標籤轉換為one-hot編碼的形式,這個動作通過utils.np_utils.to_categorical來完成(記得上篇我就提過了)

optimizers是優化器,沒什麼可說了,如何選用合適的優化器不在本文討論範疇。注意模型是可以傳入優化器對象的,你可以自己配置一個SGD,然後將它傳入模型中。 另外,最新版本的Keras為所有優化器額外設置了兩個參數clipnorm和clipvalue,用來對梯度進行裁剪。

activation是激活函數,這部分的內容一般不直接使用,而是通過激活層Activation來調用,此處的激活函數是普通的element-wise激活函數,如果想使用高級激活函數,請翻到高級激活函數層。

callback是回調函數,這其實是一個比較重要的模塊,回調函數不是一個函數而是一個類,用於在訓練過程中收集信息或進行某種動作。比如我們經常想畫一下每個epoch的訓練誤差和測試誤差,那這些信息就需要在回調函數中收集。預定義的回調函數中CheckPoint,History和EarlyStopping都是比較重要和常用的。其中CheckPoint用於保存模型,History記錄了訓練和測試的信息,EarlyStopping用於在已經收斂時提前結束訓練。回調函數LearningRateScheduler支持按照用戶的策略調整學習率,做模型精調或研究優化器的同學可能對這個感興趣。

值得注意的是,History是模型訓練函數fit的返回值,也就是說即使你沒有使用任何回調函數,找一個變量接著model.fit,還是能得到不少訓練過程中的有用信息。

另外,回調函數還支持將信息發送到遠程伺服器,以及與Tensorflow的tensorboard聯動,在網頁上動態展示出訓練和測試的情況(需要使用tensorflow為後端)

回調函數支持用戶自定義,定義方法也非常簡單,請參考文檔說明編寫即可

初始化方法,正則項,約束項,可視化沒有什麼特別值得注意的,就略過了。Keras中所有的模塊都是可以用戶自己定義的,這就是開源和Python的魅力,講真你讓我拿C++寫這麼個東西……我光把結構摸清楚就要吐血了!

另一個文檔中沒有但實際上有的東西是metrices,這裡面定義了一系列用於評價模型的指標,例如「accuracy」。在訓練模型時,可以選擇一個或多個指標來衡量模型性能。

數據預處理和utils

數據預處理是Keras提供的用於預處理圖像、文本和序列數據的一套工具,這個地方就屬於各回各家各找各媽了,你處理什麼問題就從這裡面找什麼工具。

特別指出的是,數據預處理的圖像預處理部分,提供了一套用於實時圖像數據提升的工具,這個東西支持用各種各樣的方法對輸入圖像進行數據提升,然後以生成器的形式返回。另外,該工具還支持從文件夾中自動生成數據和標籤,簡直方便的不要不要的。

utils文檔中沒有,裡面包含的函數也不必怎麼了解,除了兩個。一個是說了很多遍的to_catgoraical,另一個是convert_kernel。後者的主要作用是把卷積濾波器的卷積核在th和tf之間互相轉換。theano和tensorflow相愛想殺,到處搞對抗。其中之一就是卷積核,卷積這個東西,按照信號與系統(哼,才不會告訴你們我是信號系統助教咧)的定義,是翻轉->平移->相乘->相加。但反正卷積網絡的卷積核都是訓練出來的,翻轉不翻轉有什麼關係?

所以有些人沒翻轉,有些人翻轉了。是的,說的就是你倆,theano和tensorflow。於是如果一個網絡預訓練權重是由其中一種後端訓練出來的,又要在另一種後端上跑,那麼你就需要用kernel_convert這個函數搞一搞了。

估計這事兒太不地道,作者也看不下去了。現在utils出了一個新的layer_utils,裡面有一個convert_all_kernels_in_model函數,用來把一個模型的所有卷積核全部進行轉換,以後就用這個吧~

與scikit-learn聯動

有人留言說希望多講點這塊的內容,很抱歉……我……我也不怎麼會,原諒我畢竟是一隻菜雞

雖然不怎麼會,但是不妨礙我知道這應該是一個非常重要和有潛力的功能。Keras與scikit-learn的協作通過keras.wrapper實現,在這個腳本里定義了兩個類,分別是KerasClassifier和KerasRegressor,搭建好Sequential模型(只能是Sequential)將被它們包裝為sklearn的分類器和疊代器加入的sklearn的工作流中。

這裡有一個使用sklearn對Keras進行超參數調整的例子,大家可以參考這篇文章學習Keras和sklearn的聯動:Keras/Python深度學習中的網格搜索超參數調優(附源碼)

下篇主要說一下Keras的有用特性,以及一些常見問題,如果還有精力的話,補一些使用Keras的陷阱,沒精力這部分就留到番外篇了。理解這些特性對深入了解Keras有比較重要的幫助。

callable,全部Layer都要callable!

Keras的一大性質是所有的layer對象都是callable的。所謂callable,就是能當作函數一樣來使用,層的這個性質不需要依賴任何模型就能成立。比方說你想算算一個向量x的sigmoid值是多少,如果用keras的話,你可以這樣寫:

importkeras.backendasKfromkeras.layersimportActivationimportnumpyasnp x = K.placeholder(shape=(3,)) y = Activation('sigmoid')(x) f = K.function([x],[y]) out = f([np.array([123])])

很顯然,我絕對不會寫這種代碼來求sigmoid,這個例子只是說明,可以這麼幹,而可以這麼幹的話很多工作就會很方便。比方說有一次我想給目標函數加一項全變差的正則項,而全變差可以用特定的卷積來實現, 那麼我的目標函數的全變差正則項完全就可以用一個Convolution2D層來實現。把層和模型當作張量的函數來使用,是需要認真貫徹落實的一個東西。

順便我們也複習一下上一篇文章說的符號式計算方法。正文第1行先定義了一個「占位符」,它的shape是一個長為3的向量。所謂占位符就是「先占個位置 「的符號,翻譯成中文就是」此處應有一個長為3的向量「。注意第2行,這行我們使用了一個激活層,激活層的激活函數形式是sigmoid,在激活層的後面 又有一個括號,括號內是我們的輸入張量x,可以看到,層對象『Activation(『sigmoid』)』是被當做一個函數來使用的。上篇文章說層就是 張量到張量的運算,那麼其輸出y自然也是一個張量。

第3行通過調用function函數對計算圖進行編譯,這個計算圖很簡單,就是輸入張量經過sigmoid作用變成輸出向量,計算圖的各種優化通過這一步得以完成,現在,f就是一個真正的函數了,就可以按照一般的方法使用了。

之前說了,模型也是張量到張量的映射,所以Layer是Model的父類,因此,一個模型本身也可以像上面一樣使用。總而言之,在Keras中,層對象是callable的。

Node:Keras的網絡層復用

Keras的網絡層復用是一個很常用的需求,例如當某一層與多個層相連時,實際上這層是將同一種計算方式復用多次。再比如你用一個網絡來抽取兩條微博的特徵,然後在後面用網絡來判斷二者是否是同一個主題,那麼抽取兩次微博的特徵這一工作就可以復用同一個網絡。

Keras的網絡復用由一個叫「Node」,或稱「計算節點」的東西來實現。籠統地說,每當在某個輸入上調用層時,就會為網絡層添加一個節點。這個節點將輸入張量映射為輸出的張量,當你多次調用該層,就會產生多個結點,結點的下標是0,1,2,3…

如果僅僅是這樣,這部分的東西你依然不需要了解,問題在於,當一個層有多個計算節點時,它的input,output,input_shape,output_shape等屬性可能是ill-defined的,因為不清楚你想要的output或input是哪一個。

此時,需要使用get_output_at,get_input_at,get_output_shape_at等以at為後綴結尾的函數,at的對象就是層的節點編號。例如get_output_shape_at(2)就會返回第3個輸出張量的shape。

Shape與Shape自動推斷

使用過Keras的都知道,Keras的所有的層有一個「input_shape」的參數,用來指定輸入張量的shape。然而這個input_shape,或者有時候是input_dim,只需要在模型的首層加以指定。一旦模型的首層的input_shape指定了,後面的各層就不用再指定,而會根據計算圖自動推斷。這個功能稱為shape的自動推斷。

Keras的自動推斷依賴於Layer中的get_output_shape_for函數來實現,如果大家還記得上一篇文章的話,在提到如何編寫自己的Keras層時,我們提到如果你的網絡層改變了輸入張量的shape,就應該複寫get_output_shape_for這個函數,以使後面的層能知道本層輸出的shape。

在所有的Keras中都有這樣一個函數,因此後面的層可以通過查看這個函數的返回值獲取前層的輸入shape,並通過自己的get_output_shape_for將這個信息傳遞下去。

然而,有時候,這個自動推斷會出錯。這種情況發生在一個RNN層後面接Flatten然後又接Dense的時候,這個時候Dense的output_shape無法自動推斷出。這時需要指定RNN的輸入序列長度input_length,或者在網絡的第一層通過input_shape就指定。這種情況極少見,大致有個印象即可,遇到的話知道大概是哪裡出了問題就好。

一般而言,神經網絡的數據是以batch為單位的,但在指明input_shape時不需要說明一個batch的樣本數。假如你的輸入是一個224*224*3的彩色圖片,在內部運行時數據的shape是(None,224,224,3),這點在你自己編寫層是需要注意。

TH與TF的相愛相殺

相愛沒有,全是相殺

Keras提供了兩套後端,Theano和Tensorflow,這是一件幸福的事,手中拿著饅頭,想蘸紅糖蘸紅糖,想蘸白糖蘸白糖

th和tf的大部分功能都被backend統一包裝起來了,但二者還是存在不小的衝突,有時候你需要特別注意Keras是運行在哪種後端之上,它們的主要衝突有:

  • dim_ordering,也就是維度順序。比方說一張224*224的彩色圖片,theano的維度順序是(3,224,224),即通道維在前。而tf的維度順序是(224,224,3),即通道維在後。dim_ordering不是一個必須和後端搭配的指標,它只規定了輸入圖片的維度順序,只要輸入圖片按此維度順序設置即可正確運行。然而,如果dim_ordering與後端搭配的話——我指的是所有層的dim_ordering都與後端搭配,會提高程序的運行效率。否則,數據的shape會在計算過程中不斷轉來轉去,效率會低一些。
  • 卷積層權重的shape:從無到有訓練一個網絡,不會有任何問題。但是如果你想把一個th訓練出來的卷積層權重載入風格為tf的卷積層……說多了都是淚。我一直覺得這個是個bug,數據的dim_ordering有問題就罷了,為啥卷積層權重的shape還需要變換咧?我遲早要提個PR把這個bug修掉!
  • 然後是卷積層kernel的翻轉不翻轉問題,這個我們說過很多次了,就不再多提。

總而言之,相愛沒有,全部都是相殺。儘管Keras已經在統一theano和tensorflow上走了很多很多步,但還需要走更多的一些步。

FAQ與學習資料

FAQ模塊請參考這裡:FAQ – Keras中文文檔

另外,在這裡有一些使用示範,包括與TensorFlow的聯動,分布式訓練等:

CNN眼中的世界

花式自動編碼器

面向小數據集構建圖像分類模型

在Keras模型中使用預訓練的詞向量

將Keras作為tensorflow的精簡接口

Keras/Python深度學習中的網格搜索超參數調優(附源碼)

Keras已經開始組建自己的Keras Zoo,也就是預訓練的模型庫,這個github在:

GitHub – fchollet/fchollet/deep-learning-models

關於我們老提的TH和TF的卷積核轉換,這裡是使用示範:

TF kernel – TH kernel

學習Keras最快的方法莫過於直接閱讀示例代碼了,這裡是example:

keras examples

 

 

 

Keras on TensorFlow in GCP

先說結論:如何在 GCP 上 install keras 同時可以 visualize 結果。


Method I:  Anaconda + tensorflow + keras (on VNC):  tf 是 virtue machine

(a) 最簡單的方式是 install anaconda -> tensorflow (見前文)

(b) 在來只要 > conda install keras 就搞定。

另外如果需要 display, 建議用 VNC 搭配。同樣見前文。


VNC client display 結果如下。python code 見下文。

(Server:  run > vncserver)  if need restart vncserver kill :1 then rerun vncserver)

NewImage


Method II:

(a) Google cloud shell + 8081 web preview + tensorflow (default) + Jupyter notebook (default). (見前文)

(b) 唯一的問題是如何 install keras in Jupyter notebook  => 在 jupyter notebook 用

! pip install keras  (Bingo!)

=> 後台好像變成 theano.

=> 不過執行好像自動切回 tensorflow!

 

Keras_autoencoder.ipynb

!pip install keras
Collecting keras
  Downloading Keras-2.0.6.tar.gz (228kB)
    100% |################################| 235kB 2.9MB/s
Collecting theano (from keras)
  Downloading Theano-0.9.0.tar.gz (3.1MB)
    100% |################################| 3.1MB 309kB/s
Requirement already satisfied: pyyaml in /usr/local/lib/python2.7/dist-packages (from keras)
Requirement already satisfied: six in /usr/local/lib/python2.7/dist-packages (from keras)
Requirement already satisfied: numpy>=1.9.1 in /usr/local/lib/python2.7/dist-packages (from theano->keras)
Requirement already satisfied: scipy>=0.14 in /usr/local/lib/python2.7/dist-packages (from theano->keras)
Building wheels for collected packages: keras, theano
  Running setup.py bdist_wheel for keras ... -   \   done
  Stored in directory: /root/.cache/pip/wheels/c2/80/ba/2beab8c2131e2dcc391ee8a2f55e648af66348115c245e0839
  Running setup.py bdist_wheel for theano ... -   \   |   /   done
  Stored in directory: /root/.cache/pip/wheels/d5/5b/93/433299b86e3e9b25f0f600e4e4ebf18e38eb7534ea518eba13
Successfully built keras theano Installing collected packages: theano, keras Successfully installed keras-2.0.6 theano-0.9.0
from keras.layers import Input, Dense
from keras.models import Model

# this is the size of our encoded representations
encoding_dim = 32  # 32 floats -> compression of factor 24.5, assuming the input is 784 floats

# this is our input placeholder
input_img = Input(shape=(784,))
# "encoded" is the encoded representation of the input
encoded = Dense(encoding_dim, activation='relu')(input_img)
# "decoded" is the lossy reconstruction of the input
decoded = Dense(784, activation='sigmoid')(encoded)

# this model maps an input to its reconstruction
autoencoder = Model(input_img, decoded)
Using TensorFlow backend.
# this model maps an input to its encoded representation
encoder = Model(input_img, encoded)
# create a placeholder for an encoded (32-dimensional) input
encoded_input = Input(shape=(encoding_dim,))
# retrieve the last layer of the autoencoder model
decoder_layer = autoencoder.layers[-1]
# create the decoder model
decoder = Model(encoded_input, decoder_layer(encoded_input))
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
from keras.datasets import mnist
import numpy as np
(x_train, _), (x_test, _) = mnist.load_data()
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
11075584/11490434 [===========================>..] - ETA: 0s
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:])))
print(x_train.shape)
print(x_test.shape)
(60000, 784)
(10000, 784)
autoencoder.fit(x_train, x_train,
                epochs=50,
                batch_size=256,
                shuffle=True,
                validation_data=(x_test, x_test))
Train on 60000 samples, validate on 10000 samples
Epoch 1/50
60000/60000 [==============================] - 8s - loss: 0.3702 - val_loss: 0.2734
Epoch 2/50
60000/60000 [==============================] - 8s - loss: 0.2670 - val_loss: 0.2574
Epoch 3/50
60000/60000 [==============================] - 8s - loss: 0.2475 - val_loss: 0.2353
Epoch 4/50
60000/60000 [==============================] - 8s - loss: 0.2265 - val_loss: 0.2159
Epoch 5/50
60000/60000 [==============================] - 8s - loss: 0.2100 - val_loss: 0.2020
Epoch 6/50
60000/60000 [==============================] - 8s - loss: 0.1980 - val_loss: 0.1916
….
Epoch 48/50
60000/60000 [==============================] - 8s - loss: 0.1045 - val_loss: 0.1026
Epoch 49/50
60000/60000 [==============================] - 8s - loss: 0.1041 - val_loss: 0.1023
Epoch 50/50
60000/60000 [==============================] - 8s - loss: 0.1037 - val_loss: 0.1020
<keras.callbacks.History at 0x7fa430902dd0>
# encode and decode some digits
# note that we take them from the *test* set
encoded_imgs = encoder.predict(x_test)
decoded_imgs = decoder.predict(encoded_imgs)
# use Matplotlib (don't ask)
import matplotlib.pyplot as plt

n = 10  # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
    # display original
    ax = plt.subplot(2, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # display reconstruction
    ax = plt.subplot(2, n, i + 1 + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

 NewImage


Variational Autoencoder (VAE) vs. GAN

Both VAE and GAN are unsupervised learning

Another difference: while they both fall under the umbrella of unsupervised learning, they are different approaches to the problem. A GAN is a generative model – it’s supposed to learn to generate realistic *new* samples of a dataset. Variational autoencoders are generative models, but normal “vanilla” autoencoders just reconstruct their inputs and can’t generate realistic new samples.

VAE

pro: clear objective/cost function

con: injected noise and imperfect reconstruction, result is blurred compared with GAN

 

GAN

pro: result is better especially with noise?  Nicer image.

con: no clear object/cost function for comparison.  Hard to train and converge

 

Combine both : adversarial autoencodre (AAE)

https://arxiv.org/pdf/1511.05644.pdf

By Ian Goodfellow group

 

GAN: maximum likelihood -> implicit density -> direct  (最後希望 generative model 產生 sample 原來的 density)

VAE: maximum likelihood -> explicit density -> approximate density (Gaussian)

 

NewImage

Variational Autoencoder – VAE

Reference: http://ithelp.ithome.com.tw/articles/10188453

先回到原始的 multi-layer fully connected autoencoder, 不是 multilayer convolutional autoencoder.

Autoencoder 本身是 non-probablistic model.  不過加上 variational => be aware! 引入 Gaussian probability density model!

Back to fundmental: probability model pro and con. 

先說 con, 主要是複雜度,不論是了解或是實際計算。pro 則是有比較好的數學 background (based on probability and statistics) 和彈性,可以解釋,處理,模擬更複雜的問題。例如 prediction, missing data, small sample, etc.  一個(不合適的)比擬就是量子力學 vs. 古典力學。

1. —> find the distribution of the source —> can tolerant missing data (application in reconstruction) —> only need a small samples (application in reconstruction and semi-supervised learning)  —> think about how brain works!  Probability based, imagination to patch the missing data!!!

2. —> if know the prior —> bayesian model

 

 

Variational autoencoder 是 probability (generative) model, 而且是假設 Gaussian approximation.  對於 Gaussian pdf, 只需要 mean and covariance.  

NewImage

 

Probabilistic model 可以再細分如下:Variation XXX 多半是 explicit density -> approximate density (Gaussian density).

NewImage

 

在 encoder 一開始的地方和 Autoencoder 一樣,循序的降維.這裡定義了維度從 784 維到 500 維到 200 維(再到 2 維).

Encoder

    W_e_1 = weights([784, 500], "w_encoder_1")
    W.append(W_e_1)
    b_e_1 = bias([500], "b_encoder_1")
    h_e_1 = tf.nn.relu(tf.add(tf.matmul(X, W_e_1),b_e_1))
    
    W_e_2 = weights([500, 200], "w_encoder_2")
    W.append(W_e_2)
    b_e_2 = bias([200], "b_encoder_2")
    h_e_2 = tf.nn.relu(tf.add(tf.matmul(h_e_1, W_e_2),b_e_2))

接下來就是有趣的地方了(就是 variational 部分),再進入 2 維前它把前面經過低維權重輸出的向量複製兩份,來建立 Gaussian,而且一份做成 mean 一份做成 stddev …

再來把它轉成二維的 code layer, Z 是最後的 Gaussian variable

    W_latent = weights([200, n_z], "w_latent")
    W.append(W_latent)
    b_latent = bias([n_z], "b_latent")
    z_mean = tf.add(tf.matmul(h_e_2, W_latent), b_latent)
    z_log_sigma = tf.add(tf.matmul(h_e_2, W_latent), b_latent)
    
    eps = tf.random_normal((batch_size, n_z), 0, 1, dtype = tf.float32)
    Z = tf.add(z_mean, tf.multiply(tf.sqrt(tf.exp(z_log_sigma)), eps))
 

Decoder

    W_d_1 = weights([n_z, 200], "w_decoder_1")
    W.append(W_d_1)
    b_d_1 = bias([200], "b_decoder_1")
    h_d_1 = tf.nn.relu(tf.add(tf.matmul(Z, W_d_1), b_d_1))
    
    W_d_2 = weights([200, 500], "w_decoder_2")
    W.append(W_d_2)
    b_d_2 = bias([500], "b_decoder_2")
    h_d_2 = tf.nn.relu(tf.add(tf.matmul(h_d_1, W_d_2), b_d_2))
    
    W_d_3 = weights([500, 784], "w_decoder_3")
    W.append(W_d_3)
    b_d_3 = bias([784], "b_decoder_3")
    h_d_3 = tf.nn.sigmoid(tf.add(tf.matmul(h_d_2, W_d_3), b_d_3))
    
    X_reconstruct = h_d_3

Cost function

Cost function 包含兩項:reconstruct loss (as expected) 加上特殊的一項 latent_loss.  

reconstruct_loss = -tf.reduce_sum(X * tf.log(1e-10 + X_reconstruct) + (1-X) * tf.log(1e-10 + 1 – X_reconstruct), 1)   

latent_loss = -0.5 * tf.reduce_sum(1 + z_log_sigma – tf.square(z_mean) – tf.exp(z_log_sigma), 1)    

cost = tf.reduce_mean(reconstruct_loss + latent_loss)

結果

我們變動 code layer 的數值來看看對應的 decoder 輸出是什麼

https://i2.wp.com/ithelp.ithome.com.tw/upload/images/20170105/20103494HQm826jBNP.png

 

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot = True)
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
def weights(shape, name):
    initial = tf.truncated_normal(shape = shape, stddev = 0.1)
    return tf.Variable(initial, name)

def bias(shape, name):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial, name)
 

建立模型

Encoder

tf.reset_default_graph()
sess = tf.InteractiveSession()

n_z = 2

X = tf.placeholder(tf.float32, shape = [None, 784])
batch_size = 50

def build_vae():
    W = []
    W_e_1 = weights([784, 500], "w_encoder_1")
    W.append(W_e_1)
    b_e_1 = bias([500], "b_encoder_1")
    h_e_1 = tf.nn.relu(tf.add(tf.matmul(X, W_e_1),b_e_1))
    
    W_e_2 = weights([500, 200], "w_encoder_2")
    W.append(W_e_2)
    b_e_2 = bias([200], "b_encoder_2")
    h_e_2 = tf.nn.relu(tf.add(tf.matmul(h_e_1, W_e_2),b_e_2))
    
    W_latent = weights([200, n_z], "w_latent")
    W.append(W_latent)
    b_latent = bias([n_z], "b_latent")
    z_mean = tf.add(tf.matmul(h_e_2, W_latent), b_latent)
    z_log_sigma = tf.add(tf.matmul(h_e_2, W_latent), b_latent)
    
    eps = tf.random_normal((batch_size, n_z), 0, 1, dtype = tf.float32)
    Z = tf.add(z_mean, tf.multiply(tf.sqrt(tf.exp(z_log_sigma)), eps))
    
    W_d_1 = weights([n_z, 200], "w_decoder_1")
    W.append(W_d_1)
    b_d_1 = bias([200], "b_decoder_1")
    h_d_1 = tf.nn.relu(tf.add(tf.matmul(Z, W_d_1), b_d_1))
    
    W_d_2 = weights([200, 500], "w_decoder_2")
    W.append(W_d_2)
    b_d_2 = bias([500], "b_decoder_2")
    h_d_2 = tf.nn.relu(tf.add(tf.matmul(h_d_1, W_d_2), b_d_2))
    
    W_d_3 = weights([500, 784], "w_decoder_3")
    W.append(W_d_3)
    b_d_3 = bias([784], "b_decoder_3")
    h_d_3 = tf.nn.sigmoid(tf.add(tf.matmul(h_d_2, W_d_3), b_d_3))
    
    X_reconstruct = h_d_3
    
    reconstruct_loss = -tf.reduce_sum(X * tf.log(1e-10 + X_reconstruct) + (1-X) * tf.log(1e-10 + 1 - X_reconstruct), 1)
    latent_loss = -0.5 * tf.reduce_sum(1 + z_log_sigma - tf.square(z_mean) - tf.exp(z_log_sigma), 1)
    l2_loss = reduce(lambda x, y: x + y, map(lambda x: tf.nn.l2_loss(x), W))
    cost = tf.reduce_mean(reconstruct_loss + latent_loss)
    
    return Z, X_reconstruct, cost
Z, X_reconstruct, loss = build_vae()
optimizer = tf.train.AdamOptimizer(0.001).minimize(loss)

Loss

init_op = tf.global_variables_initializer()
sess.run(init_op)
for i in range(20000):
    batch = mnist.train.next_batch(batch_size)
    if i%100 == 0:
        print("step %d, loss %g"%(i, loss.eval(feed_dict={X:batch[0]})))
    optimizer.run(feed_dict={X: batch[0]})
step 0, loss 605.198
step 100, loss 207.343
step 200, loss 179.588
step 300, loss 203.732
step 400, loss 177.644
step 500, loss 183.692
step 600, loss 178.643
step 700, loss 180.738
step 800, loss 177.984
step 900, loss 170.849
step 1000, loss 190.2
...
step 18900, loss 163.14
step 19000, loss 166.592
step 19100, loss 168.936
step 19200, loss 159.927
step 19300, loss 162.26
step 19400, loss 166.656
step 19500, loss 164.643
step 19600, loss 167.563
step 19700, loss 154.026
step 19800, loss 155.236
step 19900, loss 163.766
d = np.zeros([batch_size,2],dtype='float32')
nx = ny = 20
x_values = np.linspace(-8, 2, nx)
y_values = np.linspace(-8, 2, ny)
canvas = np.empty((28*ny, 28*nx))
for i, yi in enumerate(x_values):
    for j, xi in enumerate(y_values):
        z_mu = np.array([[xi, yi]])
        d[0] = z_mu
        x_mean = sess.run(X_reconstruct, feed_dict={Z: d})
        canvas[(nx-i-1)*28😦nx-i)*28, j*28😦j+1)*28] = x_mean[0].reshape(28, 28)

plt.figure(figsize=(8, 10))
Xi, Yi = np.meshgrid(x_values, y_values)
plt.imshow(canvas, origin="upper", vmin=0, vmax=1,interpolation='none',cmap=plt.get_cmap('gray'))
plt.tight_layout()
 
  
 

TensorFlow Note

Excellent tutorial and sharing

Two path after antoencoder -> example as below;  more fundmental to RBM, multilayer RBM

http://ithelp.ithome.com.tw/users/20103494/ironman/1231

 

 

Data —>  feature extraction?  no (because no label, unsupervised)

—>  representation!! Yes (dimension), autoencoder etc.   

—>  factor analysis or factor graph  == RBM  —> probablistic model?? yes

Kind of PGM  – probablistic graph model’s factor analysis 

Variational Autoencoder – VAE

Convolutional Autoencoder

Reference: http://ithelp.ithome.com.tw/articles/10188326

前文討論 convolutional autoencoder 的 denoise 效果很好。具體是因為 multi-layer or convolution 架構的關係 (translation invariance)?如果用 multi-layer fully connected (RBM) autoencoder 是否也有很好的 denoise 效果?

本文仔細討論 multi-layer convolutional autoencoder.  以下摘自 reference.

Introduction

讓我們仔細來看一下之前所實作的 Autoencoder 的網路結構,不管它的 encoder 還是 decoder 都是 fully connected 的結構.那就會有一個問題是如果網路的結構換成 convolutional 的樣子,是不是同樣可以 work 呢?答案是可以的,也就是今天要來看的 convolutional autoencoder

在 CNN 中,主要有兩個部分一個是 convolutional layer,另一個是 max pooling layer.在 autoencoder 的 encoder 以及 decoder,fully connected 的結構都是相對應的,例如 encoder 中第一層是 784 維降到 300 維,則相對的在 decoder 中的最後一層就要是 300 維升到 784 維.因此如果在 encoder 的部分有 convolutional layer,則在 decoder 的部分就要有一個 deconvolutional layer;在 encoder 的部分有 max pooling layer,則在 decoder 的部分就要有一個 max unpooling layer

Deconvolution layer 是用來對應 convolution layer.

max unpooling layer 或是 upsampling layer 是用來對應 max pooling/down sampling layer.

 

Deconvolution

那在 encoder 中的 deconvolution 要怎麼做呢,以下有一個簡單的 gif 例子,而在 tensorflow 的實現上已經有了一個 tf.nn.conv2d_transpose 來讓我們直接使用.

這裡會建立一個包含兩層 encoder 以及兩層 decoder 的 convolutional autoencoder 來試試看它重建輸入的能力如何,而這裡的 strides 我們會設定成 2,也就是說對一個 mnist 輸入影像 28 * 28 維,經過 convolutional layer 之後會變成 14 * 14 維.達到維度降低的效果.以下是各層輸出的維度比較.

  • x 維度: 28 * 28,channel: 1
  • encoder layer1 維度: 14 * 14,channel: 16
  • encoder_layer2 維度: 7 * 7,channel: 32
  • decoder_layer1 維度: 14 * 14,channel: 16
  • decoder_layer2 維度: 28 * 28,channel: 1
  • x recontruct = decoder_layer2

(tf.nn.conv2d_transpose 的參數跟 tf.nn.conv2d 很像,只是要多一個 output_shape)

Build convolution and deconvolution function

def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 2, 2, 1], padding = 'SAME') def deconv2d(x, W, output_shape): return tf.nn.conv2d_transpose(x, W, output_shape, strides = [1, 2, 2, 1], padding = 'SAME') 
 

Max Unpooling

那在 Max Unpooling 要如何實現呢?最簡單的想法是怎麼來就怎麼回去,encoder 在做 max pooling 的時候記下取 max 的索引值,而在 unpooling 的時候依據索引回填數值,其他沒有記錄到的地方則為零.

使用 tf.nn.max_pool_with_argmax 這個函數,它除了會回傳 pooling 的結果外也會回傳對應原本的索引值 (argmax),如下.

The indices in argmax are flattened, so that a maximum value at position [b, y, x, c] becomes flattened index ((b * height + y) * width + x) * channels + c.

理論上在做 unpooling 的時就會用到這裡產生的對應表.不過目前 tensorflow 中沒有 unpooling 這個 op (可以參考 issue).因此以下展示了兩種方法作 unpooling 也都不會用到 argmax.

  1. 使用 Github Issue 討論中的方法,也就是放大兩倍後在固定的地方填值 (ex. 左上角)

  2. 借用影像的 upsample 函數 tf.image.resize_nearest_neighbor 來做等比例放大,也就不會補 0.

註:

  • 在 encoder 中都是一個 convolutional layer 接一個 max pooling,因此 convolutional layer 的 strides 就調回為 1,只讓 max pooling 做降維.
  • 經過測試以後 encoder 使用 relu;decoder 使用 sigmoid,才有比較好的還原效果,都使用 relu 會訓練失敗.

Build helper functions

def conv2d(x, W):return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding = 'SAME') def deconv2d(x, W, output_shape):return tf.nn.conv2d_transpose(x, W, output_shape, strides = [1, 1, 1, 1], padding = 'SAME') def max_unpool_2x2(x, output_shape): out = tf.concat_v2([x, tf.zeros_like(x)], 3) out = tf.concat_v2([out, tf.zeros_like(out)], 2) out_size = output_shape return tf.reshape(out, out_size) def max_pool_2x2(x): _, argmax = tf.nn.max_pool_with_argmax(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding = 'SAME') pool = tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME') return pool, argmax 
 

Plot recontructed images

可以看到重建的影像有成功,但是有點狀的稀疏情形,因為在經過 unpooling 的時候使採取補 0 的動作.

改用 resize_nearest_neighbor 好很多。但仍然比不上  https://zhuanlan.zhihu.com/p/27902193

的結果。後續有空再研究。

 

今日心得

這裡實現了 convolutional autoencoder,包含使用了 deconvolution 以及 max unpooling 兩個方法來組成 decoder.其中 deconvolution 使用了官方的 op,而 max unpooling 則使用了兩種非官方的方法,其中用 tf.image.resize_nearest_neighbor 的方法所做的 unpooling 效果較好.

遇到最困難的點會是一開始在 decoder 都使用 relu 作 activation function,但是完全得不出好的重建影像,而後來改用 sigmoid 後才成功.我想是因為 relu 會讓小於 0 的部分都等於 0,失去了影響後面網路的能力.或許在更大且複雜的網路,或是較長的訓練時間,才有可能成功.

=> 參考 https://zhuanlan.zhihu.com/p/27902193  有更詳細的說明。

Convolution -> Max pooling  in encoder  (activation = ReLU)

: resize_nearest_neighbor -> Convolution  (activation = ReLu)

 

Encoder 基本上是一樣的。

Difference in decoder:

Convolution <> convolution_transpose   in decoder

Unpooling 如果用 zero insertion (0-th order interpolation) 會很不好。改用 resize_nearest_neighbor.

ReLu <> sigmoid

 



Autoencoder

 

 

https://www.grb.gov.tw/search/planDetail?id=8320176&docId=441202

wavelet

cnn then clustering on feature!

apply to audio! emotion is a attribute

本計畫為“智慧型影音內容分析、創作及推薦”群體計畫之子計畫六。 近年來,具有多媒體功能之行動裝置快速普及於日常生活中,這些多媒體 應用衍生了許多重要相關研究議題,如拍攝影像之編修與影像內容分析 等。針對這些需求,本計畫希望能夠透過非監督式學習及半監督式學習的 技術來輔助發展視訊資料的內容分析技術,其中將分別從波譜式分群技術 (Spectral Clusteirng)、機率密度估測技術(Probability Density Estimation)以 及深度學習技術(Deep Learning)等三個不同的面向來討論這項議題。 在本計畫中,我們預計以三年的時間、針對三項主要議題來逐步進 行研究討論。第一年度將著重於從波譜式分群技術的角度來討論如何進 一步改善目前我們所提出的階層式影像晰分技術 (Hierarchical Image Matting),此外也將進一步討論視訊資料的晰分技術(Video Matting);第二 年度將著重於從機率密度估測技術的角度來討論視訊資料的拆解及分 析,其中我們計畫採用最新的貝氏循序切割演算法(Bayesian Sequential Partitioning);第三年則是將基於深度學習技術來自動地從視訊資料中學 習出有助於視訊內容分析的重要特徵,並據此進一步改善前兩年之研究成 果。此外,我們也將進一步討論機率密度估測技術與深度學習技術之間的 關聯性,希望能據此開發出有助於建構深度類神經網路之新式學習機制。 

This project is a sub-project of the joint-project “Intelligent Audio-Visual Content Analysis, Authoring, and Recommendation”. In recent years, mobile devices with various kinds of multi-media functionalities are prevalent in our daily life. Those new multi-media applications have been inspiring plentiful research topics, like the retouching/editing of image/video contents. In this project, we aim to discuss the development of video analysis techniques based on unsupervised learning tools or semi-supervised learning tools. In this project, we focus on three major aspects in unsupervised learning and semi-supervised learning: spectral clustering, probability density estimation, and deep learning. This is a three-year project. In the first year, we will focus on the improvement of the previously proposed hierarchical image matting technique from the aspect of spectral clustering. We will also discuss the extension of image matting to the matting of video data. In the second year, we will focus on the decomposition and analysis of visual data from the aspect of density estimation. Here, the newly proposed Bayesian Sequential Partitioning Algorithm will be adopted for density estimation. In the third year, we plan to automatically extract valuable features from visual data based on the state-of-the-art deep learning techniques. We will also apply these extracted features for the improvement of the techniques developed in the first two years. Besides, we will discuss the connections between density estimation and deep learning, hoping to develop a density-estimation-based mechanism for efficient learning of deep neural networks.