【机器学习笔记2】线性模型之逻辑回归

本博客仅为作者记录笔记之用,不免有很多细节不对之处。

还望各位看官能够见谅,欢迎批评指正。


从线性回归到逻辑回归[1]

       逻辑回归(Logistic Regression)不是回归问题,而是一个分类问题。这里先从二元分类开始讨论。

笔记1我们说到了线性回归,然后线性回归只能预测连续的值。然而对于分类问题,我们需要输出0或1。

如果我们继续用线性回归来解决分类问题,如下图所示我们将获得一条粉红色直线。我们可以设定阈值0.5,对于输入x,当hθ(x) ≥ 0.5时,我们认为x是1类,当hθ(x) < 0.5时,我们认为x是0类。这好像也解决了我们的分类问题,然而事实并非如此。



       假设我们又观测到一个很大的1类,我们将其加入到我们的训练集中来,我们将获得一条如下图所示蓝色的直线。这时候再使用0.5作为阈值便不合适了。可以看出,线性回归模型,因其预测的值可以超越[0,1]的范围,并不适合解决这样的分类问题。





判定边界




代价函数


这里为什么是非凸函数?



为什么这里的代价函数可以这样定义?


求导后得到:


怎么求导得到的?


代码示例[2]

批量梯度上升算法

批量梯度上升算法伪代码如下:

{

    每个回归系数初始化为1

    重复R次:

        计算整个数据集的梯度

        w := w + alpha * gradient更新回归系数向量

    返回回归系数

}

logRegres.py

梯度上升拟合直线:w0*x0 + w1*x1 + w2*x2 = 0,这里x0 = 1

#!/usr/bin/python
# -*- coding: UTF-8 -*-
'''
Created on Oct 27, 2010
Logistic Regression Working Module
@author: Peter
'''
from numpy import *

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,labelMat

def sigmoid(inX):
    return 1.0/(1+exp(-inX))

def gradAscent(dataMatIn, classLabels):
    weights_arr = []
    dataMatrix = mat(dataMatIn)             #convert to NumPy matrix
    labelMat = mat(classLabels).transpose() #convert to NumPy matrix
    m,n = shape(dataMatrix)
    alpha = 0.001
    maxCycles = 500
    weights = ones((n, 1))
    for k in range(maxCycles):              #heavy on matrix operations
        h = sigmoid(dataMatrix*weights)     #matrix mult

        # 梯度上升法
        error = (labelMat - h)              #vector subtraction
        weights = weights + alpha * dataMatrix.transpose() * error  # matrix mult

        # 梯度下降法
        #error = (h - labelMat)  # vector subtraction
        #weights = weights - alpha * dataMatrix.transpose() * error  # matrix mult

        weights_arr.append(weights.tolist())
    return weights, weights_arr

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()

plt_util.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import matplotlib.pyplot as plt

def plot31(x, y0, y1, y2):
    plt.figure(1)
    plt.title("show example")

    plt.subplot(311)
    plt.plot(x, y0, marker='.', mec='r')
    plt.ylabel("w0")

    plt.subplot(312)
    plt.plot(x, y1, marker='.', mec='r')
    plt.ylabel("w1")

    plt.subplot(313)
    plt.plot(x, y2, marker='.', mec='r')
    plt.ylabel("w2")

    plt.xlabel(u"iterations")

    plt.show()

main.py

import numpy as np
import logRegres
import plt_util as plutil

# 批量梯度上升
dataArr, labelMat = logRegres.loadDataSet()
weights, weights_arr = logRegres.gradAscent(dataArr, labelMat)
print(weights)
logRegres.plotBestFit(weights.getA())

weights_mat = np.array(weights_arr)
y0 = weights_mat[0::1, 0]
y1 = weights_mat[0::1, 1]
y2 = weights_mat[0::1, 2]
x = range(0, len(y0))
plutil.plot31(x, y0, y1, y2)

拟合出的最佳分类直线如下图所示

训练参数w和迭代次数的关系如下图:


随机梯度上升算法

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

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

{

    所有回归系数初始化为1

    重复R次:   # (文献[2]将这里的迭代R次算作是改进的随机梯度上升法)

        对数据集中的每个样本:

            计算该样本的梯度

            w := w + alpha * gradient更新回归系数值

    返回回归系数

}

更新到logRegres.py

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

main.py

from numpy import *
import logRegres

dataArr, labelMat = logRegres.loadDataSet()
weights = logRegres.stocGradAscent0(array(dataArr), labelMat)
print(weights)
logRegres.plotBestFit(weights)

拟合出的最佳分类直线如下图所示:

训练参数w和迭代次数的关系如下图:

和采用批量梯度上升法相比,对于使参数w达到某一数值,随机梯度上升会更快。

若记录每一次参数更新后的w值,则得图如下:

观察上图可知,在大的波动停止后,还有一些小的周期性波动,这是由于存在一些不能正确分类的样本点(数据并非线性可分),在每次迭代时会引发系数的剧烈改变。我们期望算法能避免来回波动,从而收敛到某个值。另外,收敛速度也需要加快。

改进的随机梯度上升法

针对上述问题,对梯度上升法作如下改进。

1. 学习率alpha在每次迭代时都会调整。这会缓解上图中的数据波动或者高频波动。

2. 随机选取样本来更新回归系数。这将减少周期性的波动。

更新到logRegres.py

def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    weights_arr = []
    m,n = shape(dataMatrix)
    weights = ones(n)   #initialize to all ones
    for j in range(numIter):
        dataIndex = range(m)
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.0001    #apha decreases with iteration, does not 
            randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            del(dataIndex[randIndex])
        weights_arr.append(weights.tolist())
    return weights, weights_arr

main.py

import numpy as np
import logRegres
import plt_util as plutil


# 随机梯度上升
dataArr, labelMat = logRegres.loadDataSet()
weights, weights_arr = logRegres.stocGradAscent1(np.array(dataArr), labelMat)
logRegres.plotBestFit(weights)

print(weights)
weights_mat = np.array(weights_arr)
y0 = weights_mat[0::1, 0]
y1 = weights_mat[0::1, 1]
y2 = weights_mat[0::1, 2]
x = range(0, len(y0))

plutil.plot31(x, y0, y1, y2)
拟合出的最佳分类直线如下:

训练参数w和迭代次数的关系如下图:


若记录每一次参数更新后的w值,则得图如下:


参考文献

[1] 黄海广. MIT 机器学习教程

[2] Peter. 机器学习实战


猜你喜欢

转载自blog.csdn.net/u011362297/article/details/80615752