一、算法概述逻辑回归(Logistic)虽然有回归二字,但它是一种经典的二元分类算法。适用于处理一些二元分类任务,比如疾病检测、垃圾邮件检测、用户点击率,以及上面涉及到的正负情绪分析等。首先了解什么是回归?假设现在有一些数据点,我们用一条直线对这些点进行拟合(这条直线称为最佳拟合直线),这个拟合过程称为回归。使用逻辑回归进行分类的主要思想是根据现有数据建立分类边界线的回归公式,从而进行分类。将介绍线性回归算法背后的注意事项。这是两者之间的简要比较。逻辑回归和线性回归的本质是一样的。他们都打算拟合一条直线,但线性回归的目的是拟合输入变量的分布,使得所有样本到直线的距离最短;而逻辑回归的目的是为了拟合决策边界,尽可能将数据集中的不同样本分开,所以两种算法的目的不同,处理的问题也不同。2.Sigmoid函数及相关推导我们想要的函数应该能够接受所有的输入并预测类别,比如二分类中的0或1,正或负。这种性质的一个函数叫做HeavisideStep函数,形象如下:但是这种函数的问题是从0跳到1的过程非常难处理。比如我们经常接触到的多个函数,在一定条件下可能需要推导才能解决问题;和Sigmoid函数也有相似的性质,在数学上更容易处理。其公式如下:下图为Sigmoid函数在不同坐标尺度下的两条曲线。当x为0时,Sigmoid函数值为0.5。随着x的增大,对应的Sigmoid值会趋近于1;并且随着x的减小,Sigmoid值会趋近于0。如果横坐标尺度足够大,sigmoid函数看起来很像一个阶梯函数。如果我们将Sigmoid函数的输入记录为z,则可以推导出下面的公式:意思是将两个数值向量的对应元素相乘,然后将它们全部相加得到z值,其中向量x为输入数据的分类器,而向量w是我们想要找到最好的参数,使分类器尽可能准确。由上式可得:其中$h_w(x)$的函数是给定输入时输出分类为正类(1)的可能性。例如,对于给定的x,$h_w(x)$的值为0.8,这意味着80%的可能输出是positiveclass(1),20%的可能输出是negativeclass(0),两者形成互补集关系。对于一个二元分类问题,给定输入,函数最终的输出只能有两种类型,0或1,所以我们可以对其进行分类。为了计算方便,我们把它整合成一个公式,如下:由于乘法计算量太大,我们可以把乘法改成加法,对上面的公式求对数,如下:可以看出即当y=1时,公式中加号后的sub的值为0;当y=0时,加号前的公式的值为0,与上面的分类公式达到的效果是一样的。L(w)称为似然函数,l(w)称为对数似然函数,由最大似然函数导出。这时候的应用就是通过梯度上升的方式寻找最大值。如果梯度下降是求最小值,可以在公式前乘以$-\frac{1}{n}$。为了学习,这里有另一种方法,使用损失函数来推导应用于梯度下降的公式;损失函数是衡量真实值与预测值差距的函数,因此损失函数的值越小,对应的模型效果越好,损失函数公式如下:只看公式就可以理解相对抽象,通过对应的函数图来理解上面的公式就够了,如下:注意!!!公式后面的y*不代表纵坐标!!!当类标签y=1时,对应的-log(x)图像越接近1,越接近x轴,loss越小;否则,当类标签y=0时,对应的-log(1-x)图像越接近0,越接近x轴,也就是loss越小,也就是越接近x轴预测值与真实值之比。结合两个损失函数:对于m个样本,总的损失函数为:其中m是样本数,yi是标签,可以是0或1,i是第i个样本,$p(x_i)$是预测输出。3.梯度3.1梯度上升上面已经罗列了很多公式。这是要开始一系列的大公式吗?保持心情舒畅。上面的公式虽然很多,但目的都是推导最终的对数似然函数或者总损失函数。掌握这两个是关键。梯度上升和梯度下降也将使用上述公式导出。所以两者之间是有联系的。首先,对于梯度下降,你需要了解什么是梯度?若梯度记为▽,则函数f(x,y)的梯度可以用下式表示:通俗地说,就是计算多元函数参数的偏导数,偏导数将得到的每个参数以vector的形式写下来。或者你只需??要明白这个梯度需要沿着x的方向移动$\frac{\deltaf(x,y)}{\deltax}$,$\frac{\deltaf(x,y)沿着y}{\deltay}$的方向就足够了,但是f(x,y)必须定义并且在要计算的点处是可微的。下图是梯度下降的一个例子。梯度下降法在到达每个点后会重新评估下一步的方向。从x0开始,计算完这个点的梯度后,函数会移动到下一个点x1。在x1点,重新计算梯度,然后移动到x2点。这个过程在循环中迭代,直到满足停止条件。每次迭代都是寻找当前可以选择的最佳移动方向。之前已经讨论了运动的方向,但是没有提到运动的幅度。这个幅度称为步长,表示为$\alpha$。那么就可以得到梯度上升法的迭代公式:所以对于上面提到的对数似然函数$J(w)$,我们也可以用上面的迭代法一步步向目标值移动或者无限接近根据目标值计算$J(w)$的偏导数的公式如下:有些人可能被这个偏导数公式搞糊涂了。其实这里用到的三个函数公式上面都提到过。审查。求偏导的过程涉及高阶知识,即最外层函数求外层函数的偏导,外层函数求内层函数的偏导,内层函数求偏导它的元素。将三者相乘得到所需的导流板。推导过程有点繁琐,这里只给出推导结果,后面用到的时候只用最后的结果,如下:3.2梯度下降如果用log-likelihoodfunction代替lossfunction$J(\Theta)$,那么就得到计算梯度下降的公式,如下:两个公式中w和$\Theta$的含义是一样的,都代表我们要找的最好的回归系数。对比两个公式可以看出,梯度上升和梯度下降只有正负号的区别。下面这个动画可以很好的展示梯度下降法的过程:公式推导部分结束,有基础的小伙伴可能看一遍就懂了,但是基础薄弱的就很难看懂了。视频嚼了很久,多咬几口,总算是明白了。4.算法应用4.1数据概述有这样一个数据集,共有100个样本,两个特征(X1和X2)和一个分类标签。部分数据及绘制图像如下:X1X2类别0.1974459.74463800.0785570.0597361-1.0994581。68827411.3199442.1712281-0.78327711.0097250在这个数据集上,我们将使用梯度下降法来寻找最佳回归系数,这是拟合Logistic回归模型的最佳参数。算法伪代码如下:每个回归系数初始化为1,重复R次:计算整个数据集的梯度用alpha*gradient更新回归系数的向量返回回归系数4.2加载数据集defloadDataSet():dataMat=[]#创建数据列表labelMat=[]#创建标签列表fr=open('LRData.txt','r',encoding='utf-8')#逐行读取所有数据forlineinfr.readlines():#拆分数据并存入列表lineArr=line.strip().split()#将数据存入数据列表dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])#label存储在标签列表中labelMat.append(int(lineArr[2]))fr.close()returndataMat,labelMatloadDataSet的作用是打开文本文件中的数据存储在其中并逐行读取。每行的前两个值分别对应X1和X2,第三个值是数据对应的类别标签。为了计算方便,函数还在X1和X2前加了一个值为1.0的X1。x1可以理解为偏置,即下图中的x0。4.3训练算法#sigmoid函数defsigmoid(inX):return1.0/(1+np.exp(-inX))sigmoid函数是传入一个参数(这里是一个100*1的向量),计算并返回值通过公式。defgradAscent(dataMatIn,classLabels):#将列表转换为numpy的矩阵(matrix)dataMatrix=np.mat(dataMatIn)#将列表转换为numpy的mat,并进行转置labelMat=np.mat(classLabels).T#得到dataMatrix的行数和列数。m,n=np.shape(dataMatrix)#设置每次移动的步长alpha=0.001#设置最大迭代次数maxCycles=500#创建一个n行1列均为1的矩阵weights=np.ones((n,1))forkinrange(maxCycles):#公式中的hΘ(x)h=sigmoid(dataMatrix*weights)#error,即y-hΘ(x)error=labelMat-h中的公式weights=weights+alpha*dataMatrix.T*errorreturnweights最后weights返回一个3x1的矩阵,运行截图如下:gradAscent输入参数是loadDataSet的两个返回值,然后使用numpy的mat方法将dataMatrix和labelMat转换成100x3和1x100的矩阵,但是labelMat经过T转置后变成了100x1的矩阵。然后通过创建具有n行和1列的矩阵来初始化权重。整个算法的关键在于for循环。我们先回顾一下上面的两个公式。h的计算结果为$h_w(x)$,权重weight为W向量,输入矩阵dataMatrix为x向量。error错误表示$y^{(i)}-h_wx^{(i)}$,有人可能会发现代码中没有出现$\frac{1}{m}$,因为$\alpha$和$\frac{1}{m}$都是常数,两者相乘也是常数,所以只需要换成$\alpha$即可。求和部分如何体现在公式中?如果你学过线性代数或者了解过numpy运算,那么你应该了解矩阵的乘法。看不懂也没关系。请看下图中的示例。当两个矩阵相乘时,相应的元素将相加作为最终元素。4.4绘制决策边界defplotDataSet(weight):#获取权重数组weight=weight.getA()#加载数据和标签dataMat,labelMat=loadDataSet()#将列表转换为numpy数组arraydataArr=np.array(dataMat)#获取样本个数n=np.shape(dataMat)[0]#创建4个空列表,1代表正样本,0代表负样本xcord1=[];ycord1=[]xcord0=[];ycord0=[]#遍历标签列表,根据数据的标签进行分类foriinrange(n):ifint(labelMat[i])==1:#如果标签为1,则将数据填充到xcord1中和ycord1xcord1.append(dataArr[i,1]);ycord1.append(dataArr[i,2])else:#如果label为0,则将数据填充到xcord0和ycord0中xcord0.append(dataArr[i,1]);ycord0.append(dataArr[i,2])#绘制图像fig=plt.figure()ax=fig.add_subplot(111)ax.scatter(xcord1,ycord1,s=20,c='red',marker='*',label='Class1')ax.scatter(xcord0,ycord0,s=20,c='green',marker='s',label='Class2')#画一条直线,sigmoid设为0x=np.arange(-3.0,3.0,0.1)y=(-weight[0]-weight[1]*x)/weight[2]ax.plot(x,y)#title,xlabel,ylabelplt.title('LRData')plt.legend(loc='左上角')plt.xlabel('X1');plt.ylabel('X2')plt.savefig("E:\machine_learning\LR03.jpg")这部分plt.show()代码中唯一需要注意的是sigmoid的值设置为0.大家可以回忆一下文章开头的Sigmoid函数图。0是两个类别的分解点。因此,我们设$0=w_0x_0+w_1x_1+w_2x_2$,$x_0$的值为1,这样回归系数已知,就可以得到$x_1和x_2$之间的关系,从而画出决策边界。从上图可以看出分类效果不错。根据函数画出的直线已经在很大程度上区分了两类样本。100个样本中,只有5个样本被错误分类,其中3个还在回归线上。.5.总结本文提到的梯度上升公式属于批量梯度上升。此外,还有随机梯度上升和小批量梯度上升。批量梯度上升每次计算都要计算所有样本,所以程序计算过程非常复杂,并且很容易收敛到局部最优,而随机梯度上升将优化算法。下一篇文章将介绍随机梯度上升,分析两者的区别。欢迎关注公众号【奶糖猫】,后台回复“逻辑回归”可获取数据和源码供参考,感谢阅读。
