theano學習指南1(翻譯)

原文鏈接:http://www.cnblogs.com/xueliangliu/archive/2013/04/03/2997437.html

theano學習指南,主要翻譯官方文檔:官方文檔鏈接:http://deeplearning.net/tutorial/

基礎知識

本學習指南不是一份機器學習的教程,但是首先我們會對其中的概念做一個簡單的回顧,以確保我們在相同的起跑線上。大家還需要下載幾個數據庫,以便於跑這個指南里面的程序。

theano下載安裝

在學習每一個算法的時候,大家都需要下載安裝相應的文件,如果你想要一次下載所有的文件,可以通過下面這種方式

git clone git://github.com/lisa-lab/DeepLearningTutorials.git

數據庫

MNIST數據集(mnist.pkl.gz

MNIST數據集由手寫的數字的圖像組成,它分爲了60,000訓練數據和10,000個測試數據。在很多文獻以及這個指南里面,官方的訓練數據又進一步的分成50,000的訓練數據和10,000的驗證數據,以便於模型參數的選擇。所有的圖像都做了規範化的處理,每個圖像的大小都是28*28.在原始數據中,圖像的像素存成常用的灰度圖(灰度區間0~255)。

爲了方便在python中調用改數據集,我們對其進行了序列化。序列化後的文件包括三個list,訓練數據,驗證數據和測試數據。list中的每一個元素都是由圖像和相應的標註組成的。其中圖像是一個784維(28*28)的numpy數組,標註則是一個0-9之間的數字。下面的代碼演示瞭如何使用這個數據集。

import cPickle, gzip, numpy

# Load the dataset
f = gzip.open('mnist.pkl.gz', 'rb')
train_set, valid_set, test_set = cPickle.load(f)
f.close()


在使用這個數據集的時候,我們一般把它分成若干minibatch。我們也鼓勵你吧數據集存成共享變量,並根據minibatch的索引來訪問它。這樣做是爲了在GPU上運行代碼的方便。當複製代碼到GPU上時,數據會有很大的重疊。如果你按照程序請求來複制數據,而不是通過共享變量的方式,GPU上面的程序就不會比運行在CPU上面的快。如果你運用theano的共享數據,就使得theano可以通過一個調用複製所有數據到GPU上。(有些說明沒翻譯,對GPU的原理不是很理解-譯者

到目前爲止,數據保存到了一個變量中,minibatch則是這個變量的一系列的切片,它最自然的定義方法是這個切片的位置和大小。在我們的設置彙總,每個塊的大小都是固定的,所以函數只要通過切片的位置就可以訪問每個minibatch。下面的代碼演示瞭如果存儲數據及minibatch。

複製代碼
def shared_dataset(data_xy):
    """ Function that loads the dataset into shared variables

    The reason we store our dataset in shared variables is to allow
    Theano to copy it into the GPU memory (when code is run on GPU).
    Since copying data into the GPU is slow, copying a minibatch everytime
    is needed (the default behaviour if the data is not in a shared
    variable) would lead to a large decrease in performance.
    """
    data_x, data_y = data_xy
    shared_x = theano.shared(numpy.asarray(data_x, dtype=theano.config.floatX))
    shared_y = theano.shared(numpy.asarray(data_y, dtype=theano.config.floatX))
    # When storing data on the GPU it has to be stored as floats
    # therefore we will store the labels as ``floatX`` as well
    # (``shared_y`` does exactly that). But during our computations
    # we need them as ints (we use labels as index, and if they are
    # floats it doesn't make sense) therefore instead of returning
    # ``shared_y`` we will have to cast it to int. This little hack
    # lets us get around this issue
    return shared_x, T.cast(shared_y, 'int32')

test_set_x, test_set_y = shared_dataset(test_set)
valid_set_x, valid_set_y = shared_dataset(valid_set)
train_set_x, train_set_y = shared_dataset(train_set)

batch_size = 500    # size of the minibatch
# accessing the third minibatch of the training set

data  = train_set_x[2 * 500: 3 * 500]
label = train_set_y[2 * 500: 3 * 500]
複製代碼

符號

數據集符號

首先,我們用 D

來表示數據集,爲了區分的方便,訓練,驗證和測試數據可以分別用DtrainDvalidDtest

來表示。

本指南着眼於分類問題,對於每一個數據集,都有一些數據對(x(i),y(i)

)組成。其中x(i)RD爲特徵向量,y(i)(0 L)  表示了數據x(i)

的類別。

對於其他符號,如無特殊說明,做如下約定,

  • W
大寫符號表示矩陣Wij矩陣第i行,第j列的元素Wi.行向量W.j列向量b 向量bi
  •  向量的元素

符號和函數的定義列表如下

  • D
 輸入向量的維度Dih 第i層隱變量的個數fθ(x),f(x)分類函數L  標註的個數L(θ,D)模型似然函數的對數形式l(θ,D) 預測函數的經驗損失NLL    負的以對數表示的似然函數θ
  •  模型的參數集合

 Python名字空間

本指南的程序一般引用如下名字空間

import theano
import theano.tensor as T
import numpy

監督優化問題入門

在深度學習中,深度網絡的無監督學習得到了廣泛的應用。但是監督學習仍然扮演着重要角色。本章節簡單的回顧一下分類問題的監督學習模型,並且介紹在theano下面隨機梯度下降算法的實現。

分類器的學習

0-1損失

在本指南中介紹的方法也常常用於一般的分類問題中。訓練一個分類器的目的是最小化預測函數在測試實例上面的錯誤。這種錯誤最簡單的表示方法是0-1損失。如果預測函數定義爲f:RD>0,...,L

,那麼損失函數可以表示爲:

l0,1=i=0|D|If(xiyi)

這裏,D

可以是訓練過程中的訓練數據,或者和訓練數據沒有任何交集,以避免驗證或測試過程中的偏差。 指標函數I

定義爲:

Ix={10 if x is True otherwise

在本指南中,預測函數定於爲:

f(x)=argmaxkP(Y=k|x,θ)

在python中,結合Theano,該函數的實現如下:

# zero_one_loss is a Theano variable representing a symbolic
# expression of the zero one loss ; to get the actual value this
# symbolic expression has to be compiled into a Theano function (see
# the Theano tutorial for more details)
zero_one_loss = T.sum(T.neq(T.argmax(p_y_given_x), y))

負對數似然損失

因爲0-1損失函數是不可微的,在一個含有幾千甚至幾萬個參數的複雜問題中,模型的求解變得非常困難。因此我們最大化分類器的對數似然函數:

L(θ,D)=i=0|D|logP(Y=yi|xi,θ)

 正確類別的似然,並不和正確預測的數目完全一致,但是,從隨機初始化的分類器的角度看,他們是非常類似的。但是請記住,似然函數和0-1損失函數是不同的,你應該看到他們的在驗證數據上面的正相關性,但是有時候又是負相關。(這段是不是很明白

既然我們可以最小化損失函數,那麼學習的過程,也就是最小化負的對數似然函數的過程:

NLL(θ,D)=i=0|D|logP(Y=yi|xi,θ)

NLL函數其實是0-1損失函數的一種可以微分的替代,這樣我們就可以用它在訓練集合的梯度來訓練分類器。相應的代碼如下:

複製代碼
# NLL is a symbolic variable ; to get the actual value of NLL, this symbolic
# expression has to be compiled into a Theano function (see the Theano
# tutorial for more details)
NLL = -T.sum(T.log(p_y_given_x)[T.arange(y.shape[0]), y])
# note on syntax: T.arange(y.shape[0]) is a vector of integers [0,1,2,...,len(y)].
# Indexing a matrix M by the two vectors [0,1,...,K], [a,b,...,k] returns the
# elements M[0,a], M[1,b], ..., M[K,k] as a vector.  Here, we use this
# syntax to retrieve the log-probability of the correct labels, y.
複製代碼

隨機梯度下降算法

什麼是一般的梯度下降呢?如果我們定義了損失函數,這種方法在錯誤平面上面,重複地小幅的向下移動參數,以達到最優化的目的。通過梯度下降,訓練數據在損失函數上面達到極值,相應的僞代碼如下:

複製代碼
# GRADIENT DESCENT

while True:
    loss = f(params)
    d_loss_wrt_params = ... # compute gradient
    params -= learning_rate * d_loss_wrt_params
    if <stopping condition is met>:
        return params
複製代碼

隨機梯度下降(SGD)也遵從類似的原理,但是它每次估計梯度的時候,只採用一小部分訓練數據,因而處理速度更快,相應的僞代碼如下:

複製代碼
# STOCHASTIC GRADIENT DESCENT
for (x_i,y_i) in training_set:
                            # imagine an infinite generator
                            # that may repeat examples (if there is only a finite training set)
    loss = f(params, x_i, y_i)
    d_loss_wrt_params = ... # compute gradient
    params -= learning_rate * d_loss_wrt_params
    if <stopping condition is met>:
        return params
複製代碼

當在深度學習中採用minibatch的時候,SGD稍微有一點變化。在minibatch SGD中,我們每次用多個訓練數據來估計梯度。這種技術減少了估計的梯度方差,也充分的利用了現在計算機體系結構中的內存的層次化組織技術。

複製代碼
for (x_batch,y_batch) in train_batches:
                            # imagine an infinite generator
                            # that may repeat examples
    loss = f(params, x_batch, y_batch)
    d_loss_wrt_params = ... # compute gradient using theano
    params -= learning_rate * d_loss_wrt_params
    if <stopping condition is met>:
        return params
複製代碼

以上的僞代碼描述了算法是如何工作的,在Theano平臺下的具體實現爲:

複製代碼
# Minibatch Stochastic Gradient Descent

# assume loss is a symbolic description of the loss function given
# the symbolic variables params (shared variable), x_batch, y_batch;

# compute gradient of loss with respect to params
d_loss_wrt_params = T.grad(loss, params)

# compile the MSGD step into a theano function
updates = [(params, params - learning_rate * d_loss_wrt_params)]
MSGD = theano.function([x_batch,y_batch], loss, updates=updates)

for (x_batch, y_batch) in train_batches:
    # here x_batch and y_batch are elements of train_batches and
    # therefore numpy arrays; function MSGD also updates the params
    print('Current loss is ', MSGD(x_batch, y_batch))
    if stopping_condition_is_met:
        return params
複製代碼

規則化

機器學習要優化複雜一些。我們從一些數據上面訓練模型的目的,是要把它應用到新的數據上面。但是前面的訓練算法並沒有考慮這一點,這有可能引起訓練過度的問題。一種解決訓練過度的辦法是規則化,有幾種技術可以實現,這裏我們主要介紹L1/L2規則化,以及提前結束訓練的技術。

L1/L2規則化

這種技術主要是在損失函數上面添加一項,從而達到對相關的參數的懲罰的目的。假設我們的損失函數爲:

NLL(θ,D)=i=0|D|logP(Y=yi|xi,θ)

那麼規則化的後的損失函數可以定義爲:

E(θ,D)=NLL(θ,D)+λR(θ)

在我們的問題,函數可以具體定義爲:

E(θ,D)=NLL(θ,D)+λ||θpp||

這裏,

||θ||p=(j=0|θ||θj|p)1p

爲參數θ

Lp

範數。通常p的取值爲1或者2。當p=2的是,規範化又稱權衰減。

應該注意的是,這種簡單的方法並不一定意味着模型的泛化。在實際應用過程中,人們發現在神經網絡中應用這種技術有助於泛化,特別是小數據集上面。下面的代碼演示瞭如何應用這種技術。

複製代碼
# symbolic Theano variable that represents the L1 regularization term
L1  = T.sum(abs(param))

# symbolic Theano variable that represents the squared L2 term
L2_sqr = T.sum(param ** 2)

# the loss
loss = NLL + lambda_1 * L1 + lambda_2 * L2
複製代碼

提前結束訓練

提前結束訓練是另一種處理訓練過度的辦法,它的解決思路是監測模型在驗證數據上的表現。驗證數據在訓練過程中,可以用來做測試數據。如果模型的性能在驗證數據中改進很小,真是變差,那麼就應該放棄進一步的優化。

停止優化的判別有很多方法,在這個指南中,我們用一種基於patience(???)幾何增長的策略。

複製代碼
# early-stopping parameters
patience = 5000  # look as this many examples regardless
patience_increase = 2     # wait this much longer when a new best is
                              # found
improvement_threshold = 0.995  # a relative improvement of this much is
                               # considered significant
validation_frequency = min(n_train_batches, patience/2)
                              # go through this many
                              # minibatches before checking the network
                              # on the validation set; in this case we
                              # check every epoch

best_params = None
best_validation_loss = numpy.inf
test_score = 0.
start_time = time.clock()

done_looping = False
epoch = 0
while (epoch < n_epochs) and (not done_looping):
    # Report "1" for first epoch, "n_epochs" for last epoch
    epoch = epoch + 1
    for minibatch_index in xrange(n_train_batches):

        d_loss_wrt_params = ... # compute gradient
        params -= learning_rate * d_loss_wrt_params # gradient descent

        # iteration number. We want it to start at 0.
        iter = (epoch - 1) * n_train_batches + minibatch_index
        # note that if we do `iter % validation_frequency` it will be
        # true for iter = 0 which we do not want. We want it true for
        # iter = validation_frequency - 1.
        if (iter + 1) % validation_frequency == 0:

            this_validation_loss = ... # compute zero-one loss on validation set

            if this_validation_loss < best_validation_loss:

                # improve patience if loss improvement is good enough
                if this_validation_loss < best_validation_loss * improvement_threshold:

                    patience = max(patience, iter * patience_increase)
                best_params = copy.deepcopy(params)
                best_validation_loss = this_validation_loss

        if patience <= iter:
            done_looping = True
            break

# POSTCONDITION:
# best_params refers to the best out-of-sample parameters observed during the optimization

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章