机器学习——logistic回归

来源:互联网 发布:淘宝it代购是真的吗 编辑:程序博客网 时间:2024/06/10 20:11

何为回归?假设有一系列数据点,我们使用一条直线对这些点进行拟合(该线称为最佳拟合直线),这个拟合过程就称为回归。利用Logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,并以此进行分类。训练分类器时的做法就是寻找最佳拟合参数,使用的是最优化算法。

Logistic回归的一般过程如下。

(1)收集数据

(2)准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式最佳。

(3)分析数据

(4)训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。

(5)测试算法:一旦训练步骤完成,分类将会很快。

(6)使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其它分析工作。

在这里,我们需要一个函数,它能够接受所有的输入然后预测出类别。与单位阶跃函数相比,Sigmoid函数也具备类似的性质,且数学上更易处理。它的具体计算公式如下:


当z为0时,Sigmoid函数只为0.5;随着z的增大,对应的Sigmoid值将逼近于1;而随着z的减小,Sigmoid值将逼近于0。

如果横坐标的刻度足够大,Sigmoid函数看起来很像一个阶跃函数。

为了实现Logistic回归分类器,我们可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将这个总和代入Sigmoid函数中,进而得到一个范围在0~1之间的数值。任何大于0.5的数据被分入1类,小于0.5即被归入0类。所以,Logistic回归也可以被看成是一种概率估计。

现在分类器的函数已经确定了,接下来的问题就是:最佳的回归系数是多少?

Sigmoid函数的输入记为z,由以下公式得出:

如果采用向量的写法,上述公式可以写成,它表示将这两个数值向量对应元素相乘,然后全部加起来即得到z值。其中的向量X是分类器的输入数据,向量w是我们要找的最佳参数(系数),从而使得分类器尽可能地精确。为了寻找该最佳参数,需要用到最优化理论的一些知识。

首先介绍梯度上升算法,该算法基于的思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。如果梯度记为,则函数的梯度由下式表示:

这个梯度意味着要沿x的方向移动,沿y的方向移动。其中函数f(x,y)必须要在待计算的点上有定义并且可微。一个具体例子如下:

梯度上升算法到达每个点后都会重新估计移动的方向。从P0开始,计算完该点的梯度,函数就根据梯度移动到下一点P1。在P1点,梯度再次被重新计算,并沿新的梯度方向移动到P2。如此循环迭代,直到满足停止条件。迭代的过程中,梯度算子总是保证我们能选取到最佳的移动方向。

梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量称为步长,记做。用向量来表示的话,梯度上升算法的迭代公式如下:

该公式将一直被迭代执行,直到达到某个停止条件为止,比如迭代次数达到某个指定值或算法误差达到一定范围。

下面是一个Logistic回归分类器的应用例子,我们所采用的数据集如下:

下面是梯度上升算法的伪代码:

每个回归系数初始化为1

重复R次:

计算整个数据集的梯度

使用alpha´gradient更新回归系数的向量

返回回归系数

下面是具体代码实现:

def loadDataSet():    dataMat = []; labelMat = []    fr = open('testSet.txt')#打开存有数据集的文件    for line in fr.readlines():#将数据按行进行解析        lineArr = line.strip().split()        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])        labelMat.append(int(lineArr[2]))    return dataMat,labelMatdef sigmoid(inX):#Sigmoid函数实现    return 1.0/(1+exp(-inX))#两个输入分别是特征矩阵和类别标签列表def gradAscent(dataMatIn, classLabels):    dataMatrix = mat(dataMatIn)             #转换为NumPy 矩阵    labelMat = mat(classLabels).transpose()#转换为NumPy 矩阵    m,n = shape(dataMatrix)    alpha = 0.001    maxCycles = 500#设定迭代次数    weights = ones((n,1))#初始化权重为1    for k in range(maxCycles):                      h = sigmoid(dataMatrix*weights)     #h是一个(m1)的列向量        error = (labelMat - h)              #计算误差,error也是(m1)的列向量        weights = weights + alpha * dataMatrix.transpose()* error #更新权值    return weights

上述代码中将输入数据集转换为NumPy中的矩阵,是因为后面会用到对应的转置操作,这样执行起来更方便。

下面的代码将使用梯度上升算法绘制分类的决策边界:

# weights为使用gradAscent函数计算所得的权值向量def plotBestFit(weights):    import matplotlib.pyplot as plt    dataMat,labelMat=loadDataSet()    dataArr = array(dataMat)    n = shape(dataArr)[0]     xcord1 = []; ycord1 = []    xcord2 = []; ycord2 = []    for i in range(n):        if int(labelMat[i])== 1:            xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])        else:            xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])    fig = plt.figure()    ax = fig.add_subplot(111)    ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')    ax.scatter(xcord2, ycord2, s=30, c='green')    x = arange(-3.0, 3.0, 0.1)    y = (-weights[0]-weights[1]*x)/weights[2]    ax.plot(x, y)    plt.xlabel('X1'); plt.ylabel('X2');    plt.show()

    代码执行效果如下:

从分类结果来看还是比较理想的,绝大部分的数据点都被分配到了合适的分类之中。这个方法的缺点是计算量较大,下面将针对这种情况对算法做出适当的改进,从而使它可以用在真实数据集上。

梯度上升算法在每次更新回归系数时都需要遍历整个数据集,在数据量较小时尚可,但如果有大量的样本和特征,那么该方法的计算复杂度就太高了。一种改进的方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法。由于可以在新样本到来时对分类器进行增量式更新,因而它是一种在线学习算法。与之对应的算法称为“批处理”或“离线学习算法”。

随机梯度上升算法的伪代码如下:

所有回归系数初始化为1

对数据集中每个样本

计算该样本的梯度

使用alpha´gradient更新回归系数值

返回回归系数值

以下是具体代码实现:

def stocGradAscent0(dataMatrix, classLabels):    m,n = shape(dataMatrix)    alpha = 0.01    weights = ones(n)   #initialize to all ones    for i in range(m):        h = sigmoid(sum(dataMatrix[i]*weights))        error = classLabels[i] - h        weights = weights + alpha * error * dataMatrix[i]    return weights

与前面的梯度上升算法相比,这里的h和error都是数值,而不再是向量;而且也不再有矩阵转换操作。

使用这种方法进行类边界绘制的效果如下:

当然直接拿这个结果和前面的结果进行比较是不合理的,毕竟前面的结果是在整个数据集上迭代了500次才得到的。

我们可以对随机梯度上升代码做如下的改进:

def stocGradAscent1(dataMatrix, classLabels, numIter=150):    m,n = shape(dataMatrix)    weights = ones(n)       for j in range(numIter):        dataIndex = range(m)        for i in range(m):            alpha = 4/(1.0+j+i)+0.0001    #改进处1            randIndex = int(random.uniform(0,len(dataIndex)))#改进出2            h = sigmoid(sum(dataMatrix[randIndex]*weights))            error = classLabels[randIndex] - h            weights += alpha * dot(error,dataMatrix[randIndex])            del(dataIndex[randIndex])    return weights

上述代码的执行效果如下:

从图中的分类效果可以看出,该方法的结果与第一种方法相差不多,但是所用的计算量更少。

在第一个改进的地方,alpha在每次迭代的时候都会调整。虽然alpha会随着迭代次数不断减小,但永远不会减少到0,这样可以保证在多次迭代后新数据仍然具有一定的影响。如果要处理的问题是动态变化的,那么可以适当加大上述常数项,来确保新的值获得更大的回归系数。第二个改进处通过随机选取样本来更新回归系数,这样可以减少迭代期间的周期性波动。而且算法中还增加了一个确定迭代次数的参数,方便使用者进行调节。



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信中的表情符号不全怎么办 球球大作战找不到团战服务器怎么办 小孩的腰有点弯怎么办 小朋友不听话被老师罚站怎么办 幼儿园小朋友被老师罚站怎么办 生完小孩弯腰驼背怎么办 小孩爱捡垃圾是怎么办 腰扭了不敢弯腰怎么办 小人狗在背后骂我怎么办 微信表情缺失了怎么办 动图过大 微信 怎么办 我能怎么办图片带字 我该怎么办图片带字 学化妆找不到模特练妆怎么办? cf进房间闪退怎么办 手游cf账号封了怎么办 大门牙缺了一块怎么办 缺了一颗牙齿怎么办 CF购买医疗包竞猜币没到账怎么办 CF手游昵称不合法怎么办 微信gif尺寸过大怎么办 微信表情上限300怎么办 太受欢迎了怎么办快穿 兔宝宝沾上人气味怎么办 我该怎么办的文字图片 爱奇艺缓存视频显示下载失败怎么办 把老公惹生气了怎么办 苹果x用电量太快怎么办 小中考地生没过怎么办 如果遇到不负责的语文老师怎么办 孩子的语文老师教的不好怎么办 刚买的小猫很凶怎么办 2月幼犬不吃东西怎么办 小狗狗不吃狗粮怎么办 母猫不会照顾小猫怎么办 刚买的小狗拉稀怎么办 母兔子吃小兔子怎么办 照片照出来背亮景人是黑的怎么办 手机透明壳变黄了怎么办 ae视频渲染太慢怎么办 3ce口红太干了怎么办