theano深度学习实现:LR模型代码讲解

来源:互联网 发布:php 上传类 编辑:程序博客网 时间:2024/04/27 12:48

好几个月之前写的了,现在才搬出来,也是懒得不行了。

原文链接:http://deeplearning.net/tutorial/logreg.html#logreg

没有逐字逐句的翻译,只是将自己的一些感悟写了一下。

LR模型
    逻辑回归模型是一个概率线性分类模型。它的参数为权重矩阵W和偏置向量b。它通过将输入向量投射到一系列的超平面上实现分类,每一个超平面为一类(感觉这里说的好抽象啊)。输入向量到超平面的距离反映了输入是这个类别的概率。数学上,输入向量x的类别Y为第i个类别的概率可以写成下边的形式:

    那么输入x的类别被预测为概率最高的类别,即:

    其实在《概率统计方法》(李航老师著)中是这样来说明的。考虑二分类的问题,假设有两个分类:0和1,其中输入向量x为类别1的概率为p,则为0的概率为1-p。那么
    
考虑感知机模型,


此时:

类似的,可令

则化简可得

也就是我们的非常熟悉的sigmod函数啦!将二分类推广到多分类,就是softmax函数。

下边学习一下theano是如何实现LR的。
# 使用0初始化权重W,此处W是一个n_in*n_out的矩阵,将W设置成theano.shared类型,是共享变量,当调用theano.function()函数时,有两组输出:一组为output,通过函数调用可以返回,另一组就是updates,updates更新shared变量。borrow参数貌似与GPU的使用有关,具体还有待考证。
self.W=theano.shared(value=numpy.zeros((n_in,n_out),dtype=theano.config.floatX),name='W',borrow=True)
# 对输出单元设置偏置,每个输出单元的计算方式是sigmod(w*x+b),故每个输出单元的计算对应一个偏置单元。
self.b=theano.shared(value=numpy.zeros((n_out,),dtype=theano.config.floatX),name='b',borrow=True)
#此处,T.dot是矩阵运算,即多条输入数据并行处理,w*x+b作为softmax的输入,可以根据上文中softmax的公式来理解。得到每个类别的概率。
self.p_y_given_x=T.nnet.softmax(T.dot(input,self.W)+self.b)
# 选取概率最大的类别作为x的预测类别。
self.y_pred=T.argmax(self.p_y_given_x,axis=1)
上边的代码是对模型框架的构建。初始化了权值、偏置以及类别预测的函数。

损失函数
    前边所做的可以说是前向传播吧,现在就要考虑反向传播了。反向传播无非就是参数更新,涉及到了损失函数。theano的损失函数的定义是这样的:

    这里只给出了最终的损失函数,没有具体过程,也不是很清晰,下边还是引用李航老师的《统计学习方法》中LR损失函数的定义原理。
    还是以二分类为例。令p(xi)为第i个输入样本类别为1的概率,则类别为0的概率为1-p(xi)。于是,M个样本的似然函数为:

对数似然函数为:

一般是对logL求平均的,文档上说可以减小学习率对minibatch的依赖,不知道我这样理解对不对,这样是不是清晰明了啦。代码如下:
# y.shape[0]是y列的个数,比如说是一个minibatch的个数(假设为n)# T.arange(y.shape[0])=[0,1,2,... n-1]# T.log(self.p_y_given_x)是Log-Probabilities (假设为LP)的矩阵,每一行是一个样例分为每个类别的概率的对数# LP[T.arange(y.shape[0]),y]=[LP[0,y[0]], LP[1,y[1]], LP[2,y[2]], ...,LP[n-1,y[n-1]]]
# T.mean(LP[T.arange(y.shape[0]),y]) 是求平均值,为了降低cost对mini-batch size的依赖。
return-T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]),y])
LR类的定义
classLogisticRegression(object):
    """这是一个多分类的模型,具体的输出个数可以由n_out定义"""def__init__(self,input,n_in,n_out):#定义W*x+b中的参数W和b,并初始化为0.
    self.W=theano.shared(value=numpy.zeros((n_in,n_out),dtype=theano.config.floatX),name='W',borrow=True)
    self.b=theano.shared(value=numpy.zeros((n_out,),dtype=theano.config.floatX),name='b',borrow=True)
    # 此处输出为一个长度为n_out的vector,每个值表示输入x预测为label为i的概率
    self.p_y_given_x=T.nnet.softmax(T.dot(input,self.W)+self.b)
    # 选取概率最大的label为此样例的y类别
    self.y_pred=T.argmax(self.p_y_given_x,axis=1)
    self.params=[self.W,self.b]
self.input=input
defnegative_log_likelihood(self,y):#这个函数上边已经讲过了,这里就不说了
    return-T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]),y])
deferrors(self,y):#这里的error定义为预测错误的个数的平均值
    if y.ndim!=self.y_pred.ndim:
        raiseTypeError('y should have the same shape as self.y_pred',('y',y.type,'y_pred',self.y_pred.type))
    # check if y is of the correct datatype
    if y.dtype.startswith('int'):
        # T.neq会返回一个0或1的vector,1表示两个值不同
        return T.mean(T.neq(self.y_pred,y))else:raiseNotImplementedError()
模型学习   
    模型的学习使用的方法是mini-batch梯度下降法。theano中可以使用tensor.grad函数方便的求导,所以不需要我们自己再求导数了。
g_W=T.grad(cost=cost,wrt=classifier.W)g_b=T.grad(cost=cost,wrt=classifier.b)
    由于,theano是一种图模型(不知道理解的对不对),即我们先定义好整个模型所有的输入、输出及运算路径,后期的学习训练直接调用即可,下边定义的就是模型的训练过程。
    updates=[(classifier.W,classifier.W-learning_rate*g_W),(classifier.b,classifier.b-learning_rate*g_b)]
 此处的updates很好理解啦,即W*x+b中w和b的更新规则。   
 train_model=theano.function(inputs=[index],outputs=cost,updates=updates,givens={x:train_set_x[index*batch_size:(index+1)*batch_size],y:train_set_y[index*batch_size:(index+1)*batch_size]})
    train_model就是整个学习过程啦。inputs可以看做函数的输入,outputs为函数的输出,updates为函数的操作。注意train_set_x以及train_set_y是theano.shared的类型,即相当于全局变量。此处的x和y在是已经定义的,且作为cost和classifiy的输入,可以查看整体的代码。
    写到这里就算完了。上边主要是将我自己在学习过程中不理解或容易困惑的地方点了一下,若有不对的地方欢迎指出。最近刚看完mlp和lstm,并且自己也修改过,运行过,找时间再写一下。

0 0