Logistic回归的实现

时间:2022-09-13 19:51:15

摘要:

  Logistic回归也称为对率线性回归,需要涉及到最优化算法。此回归应用十分广泛,对于回归的预测也是概率的值,既属于回归模型也属于分类模型。


Logistic回归的基本过程

收集数据

准备数据:由于需要距离的计算所以需要为数值同时需要格式化

分析数据:任意方法

训练算法:找到最佳回归系数

测试算法

使用算法:根据输入转换成对应的结构化数据

而如何进行最优系数的求解就需要用到梯度上升等算法


Logistic以及Sigmoid函数分类

我们需要的函数是这样的,接受所有输入然后预测出类别,我们采用数学上的sigmoid函数  g(z) = 1/(1+exp(-z))

当x等于0的时候这个函数的值是0.5,当大于0.5的时候被分为类型1,小于0,5的时候被分为类型0

那么确定了分类标准就需要确定最优化的分类系数



回归系数的确定最优化方法

梯度下降的方法:  z= w0x0+w1x1+w2x2+----2n

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

def sigmoid(inX):
    return 1.0/(1+exp(-inX))
#下面是模型以及参数设定过程
def sigmoid(inX):
    return 1.0/(1+exp(-inX))

def compute_grad(theta,xMat,yMat):
    # h = sigmoid(xMat*theta)
    # delta = yMat-h
    # m = shape(xMat)[0]
    # #若是batch_gradient_descent
    # grad = (-1.0)*delta.T*xMat
    # return grad.flatten()
    m = shape(yMat)[0]
    h = sigmoid(xMat.dot(theta.reshape(-1, 1)))

    grad = (1.0) * xMat.T.dot(h - yMat)

    return (grad.flatten())

#自定义梯度下降
def batch_gradient(J,X,maxIter=500,alpha=0.001):
    X = mat(X)
    m = shape(X)[1]
    theta = ones((m,1))
    Jmin,grad = J(theta)
    for k in range(maxIter):
        theta = theta - alpha*grad.T
        Jmin,grad = J(theta)
    #Jmin = J(theta)#返回梯度下降后的最小值
    return theta

 

测试如下结果:

dataArr,labelMat = logRegres.loadDataSet()
logRegres.gradAscent(dataArr,labelMat)

得到答案是:

[[ 4.12414349]
 [ 0.48007329]
 [-0.6168482 ]]

需要注意的是这里的更新是同步更新并不是异步更新,同时采用梯度上升是由于这里是求的是极大似然估计的值


分析数据:画出决策边界

如何对已经划分出的不同类别数据的分割线呢。

目标是画出如下的图:Logistic回归的实现


绘制程序如下:

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]  #这里 0 = w0x0 + w1x1 + w2x2
    ax.plot(x,y)
    plt.xlabel('X1');plt.ylabel('X2')
    plt.show()


训练算法:随机梯度上升

同步的梯度上升算法,每次计算回归系数的时候需要遍历整个数据集合,但是如果有很多个特征,那么计算复杂度会相当的高,改进的方法是一次只用一个回归样本点继续进行更新,该方法是随机梯度上升算法,是一种“在线学习”算法与一次处理所有数据不同,那是一种“批处理算法”

随机梯度上升算法:

  对数据集的每个样本点

       计算梯度

        使用alpha*gradient更新回归系数

 返回回归系数

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

注意和一般梯度下降算法中h ,error区别,这里这两个均是数值而不是向量


测试以及绘制后发现错分了1/3的样本由于一般的梯度上升算法根据迭代次数的来得到更优的划分,所以我们可以改进让该算法迭代200次

但是会出现的问题就是在极值点附近的波动,所以我们可以修改alpha的值,减少波动,同时可以随机选择样本增加多样性减少周期性的波动

增加了分类的效率避免了周期性的波动

 
def random_gradient(X,y,maxIter=500,alpha=0.001):
    Xmat = mat(X)
    ymat = mat(y).transpose()
    n, m = Xmat.shape
    theta = mat(ones(m))
    for j in range(maxIter):
        dataIndex = range(m)
        for i in range(m):
            alpha = 4/(1.0+j+i) + 0.01 #alpha每次调整越后面越小
            randIndex = int(random.uniform(0,n)) #均匀分布随机取其中一个数值
            h = sigmoid(Xmat[randIndex]*theta.T)
            delta = h - ymat[randIndex]
            theta = theta - alpha*delta*Xmat[randIndex]
    return theta.T

 

Logistic回归的实现

得到了相当不错的效果

p = LogRegression.predict(theta,dataArr)
Pmat = mat(labelArr).transpose()
print 'Train Accuracy: %f'% float(mean(p== Pmat)*100)
定义一个预测函数
def predict(theta,X,threshold=0.5):
    X = mat(X)
    p = X*theta>0
    return p.astype('int')


Train Accuracy: 96.000000


从疝气病预测病马的死亡率

我们直接解析这个例子的数据。有368样本和28个特征。而这数据集是包含了这个得了这个病的一些指标,有些指标比较主观,而有的难以测量,例如疼痛等级。

数据预处理

数据缺失值是这个数据集的主要问题,如果因为某一维特征的确实丢掉所有特征是十分不值当的.所以需要一些方法处理缺失值。数据挖掘中有许多处理手段。特征均值填补

特殊值填补,忽略缺失值样本呢,使用相似样本均值填补,使用机器学习算法预测.

此处我们采用特殊值来代替,为什么可以用0来代替呢。引入如果为0的话weights*X对结果的预测没有倾向性。因为sigmoid(0) = 0.5。但是如果是类别标签缺失那么就没有办法用特殊值代替了。我们处理后的样本为test.txt和train.txt


测试算法

def colicTest():
    frTrain = open('horseColicTraining.txt')
    frTest = open('horseColicTest.txt')
    trainingSet=[]
    trainingLabels=[]
    for line in frTrain.readlines():
        curLine = line.strip().split('\t')
        curArr = []
        for i in range(len(curLine)-1):
            curArr.append(float(curLine[i]))
        trainingSet.append(curArr)
        trainingLabels.append(float(curLine[-1]))
    weights = random_gradient(trainingSet,trainingLabels,maxIter=1000)
    #predict(weights,trainingSet,trainingLabels)
    testSet=[]
    testLabels=[]
    for line in frTest.readlines():
        curLine = line.strip().split('\t')
        curArr = []
        for i in range(len(curLine)-1):
            curArr.append(float(curLine[i]))
        testSet.append(curArr)
        testLabels.append(float(curLine[-1]))
    predict(weights,testSet,testLabels)

可以用交叉验证来算一下这个平均错误率35%

Train Accuracy: 74.626866