[DL] 基於theano.tensor.dot的邏輯迴歸代碼中的SGD部分的疑問探幽

在Hinton的教程中, 使用Python的theano庫搭建的CNN是其中重要一環, 而其中的所謂的SGD - stochastic gradient descend算法又是如何實現的呢? 看下面源碼

(篇幅考慮只取測試模型函數, 訓練函數只是多了一個updates參數, 並且部分參數有改動):

 3     classifier = LogisticRegression(input=x, n_in=24 * 48, n_out=32)

 7     cost = classifier.negative_log_likelihood(y)

11     test_model = theano.function(inputs=[index],
12             outputs=classifier.errors(y),
13             givens={
14                 x: test_set_x[index * batch_size: (index + 1) * batch_size],
15                 y: test_set_y[index * batch_size: (index + 1) * batch_size]})

行3聲明瞭一個對象classifer, 它的輸入是符號x, 大小爲24*48, 輸出長度爲32.

行11定義了一個theano的函數對象, 接收的是下標index, 使用輸入數據的第index*batch_size~第(index+1)*batch_size個數據作爲函數的輸入, 輸出爲誤差.

我們再來看看行12中的errors函數的定義:

    def errors(self, y):
        # check if y has same dimension of y_pred
        if y.ndim != self.y_pred.ndim:
            raise TypeError('y should have the same shape as self.y_pred',
                ('y', target.type, 'y_pred', self.y_pred.type))
        # check if y is of the correct datatype
        if y.dtype.startswith('int'):
            # the T.neq operator returns a vector of 0s and 1s, where 1
            # represents a mistake in prediction
            return T.mean(T.neq(self.y_pred, y))
        else:
            raise NotImplementedError()

 self.y_pred 是一個大小爲batch_size的向量, 每個元素代表batch_size中對應輸入的網絡判斷結果, errors函數接受1個同等大小的期望輸出y, 將兩者進行比較求差後作均值返回, 這正是誤差的定義.

那麼問題來了, 這個 self.y_pred 是如何計算的? 這裏我們看LogisticRegression的構造函數:

 1     def __init__(self, input, n_in, n_out):
 2 
 3         # initialize with 0 the weights W as a matrix of shape (n_in, n_out)
 4         self.W = theano.shared(value=numpy.zeros((n_in, n_out),
 5                                                  dtype=theano.config.floatX),
 6                                 name='W', borrow=True)
 7         # initialize the baises b as a vector of n_out 0s
 8         self.b = theano.shared(value=numpy.zeros((n_out,),
 9                                                  dtype=theano.config.floatX),
10                                name='b', borrow=True)
11 
12         # compute vector of class-membership probabilities in symbolic form
13         self.p_y_given_x = T.nnet.softmax(T.dot(input, self.W) + self.b)
14 
15         # compute prediction as class whose probability is maximal in
16         # symbolic form
17         self.y_pred = T.argmax(self.p_y_given_x, axis=1)
18 
19         # parameters of the model
20         self.params = [self.W, self.b]

 在行13可以看到, 使用input和self.W, self.b進行了一個softmax的映射. softmax本身是一個一一映射所以此處不再細說, 詳情可以搜索引擎. 這裏我們來說一說T.dot(input, self.W).

這裏的dot是theano的tensor變量的點乘操作, T.dot接受兩個矩陣(向量)輸入, 計算它們的點積並返回一個保存了點乘信息的節點對象, 使用返回的對象調用eval()方法即可獲得實際數值結果, 如下:

1 >>> a = numpy.asarray([1,2,3])
2 >>> b = numpy.asarray([[3],[2],[1]])
3 >>> T.dot(a,b).eval()
4 array([10])

上述代碼是在ab大小匹配的情況, 即a的第2維等於b的第1維. 所以本例中正常情況(非SGD)下, input是1行1152列的, W是1152行32列的, b的長度爲1行32列.

這裏的T.dot計算的結果是(input*W), 見如下代碼:

1 >>> a = numpy.ones((1, 1152))
2 >>> W.shape
3 (1152, 32)
4 >>> dotaW = T.dot(a, W)
5 >>> dotaW = dotaW.eval()
1 >>> dotaW.shape
2 (1, 32)

注意, 如果顛倒了a和W的位置, 會報尺寸不匹配的錯誤.

那麼在SGD算法中, 行13代碼中的input的大小實際上是batch_size行1152(24*48)列的, 所以我不禁疑惑, 如果a和b大小不匹配呢? 一個合理的推斷就是, T.dot會自動截斷input. 看如下代碼:

1 >>> a = numpy.ones((10, 1152))
2 >>> dotaW = T.dot(a, W)
3 >>> dotaW = dotaW.eval()
4 >>> dotaW.shape
5 (10, 32)

果然, T.dot會自動截斷input, 將其按照匹配W的尺寸(1行1152列)依次和W進行點乘後返回, 所以大小爲(10, 32), 又偏置b的大小爲(1, 32), 所以b會按列進行相加.


 

解決了T.dot的問題, 一開篇的疑問就比較容易解答了, SGD基於T.dot的這種特性, 一次性輸入batch_size行1152列樣本, T.dot配合softmax生成batch_size行32列輸出, 再基於 T.argmax(self.p_y_given_x, axis=1) , 計算每行(樣本)的最大值作爲期望輸出y_pred(尺寸爲batch_size行1列).

本人預測, 卷積神經網絡中的conv2d也是同樣的機理, 對於一定數量的輸入, 如20個48行24列大小的圖像, 它會針對每一個輸入樣本產生對應的N張特徵圖->降採樣->第二層卷積層->MLP, 到了MLP中, 配合上述的T.dot的特性, 就完成了SGD中的批訓練的目的.

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