• [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中的批训练的目的.

  • 相关阅读:
    [leetCode]945.使数组唯一的最小增量
    [leetCode]974. 和可被 K 整除的子数组
    [leetCode]1035.不相交的线
    [leetCode]面试题 10.01. 合并排序的数组
    [leetCode]面试题04.二维数组
    [面试题03]数组中的重复数字
    [leetCode]169.多数元素
    [leetCode]160.相交链表
    [leetCode]155.最小栈
    POJ 2127 Greatest Common Increasing Subsequence
  • 原文地址:https://www.cnblogs.com/lancelod/p/4134276.html
Copyright © 2020-2023  润新知