机器学习实战(四)逻辑回归LR(Logistic Regression)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhq9695/article/details/83185608

目录

0. 前言

1. Sigmoid 函数

2. 梯度上升与梯度下降

3. 梯度下降法(Gradient descent)

4. 梯度上升法(Gradient ascent)

5. 梯度下降/上升法的数学推导

6. 随机梯度下降/上升法(Stochastic gradient descent/ascent)

7. 实战案例

7.1. 简单案例

7.2. 病马死亡率案例


学习完机器学习实战的逻辑回归,简单的做个笔记。文中部分描述属于个人消化后的理解,仅供参考。

本篇综合了先前的文章,如有不理解,可参考:

吴恩达机器学习(二)多元线性回归

吴恩达机器学习(四)逻辑回归

吴恩达机器学习(五)正则化

吴恩达机器学习(十五)大规模机器学习

所有代码和数据可以访问 我的 github

如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~

0. 前言

逻辑回归(Logistic Regression)是一种基于最优化思想的二分类监督学习算法。

其主要思想是根据现有数据对分类边界线建立回归公式,以此分类

其目的是寻找一个非线性函数 Sigmoid 的最佳拟合参数,求解过程可以通过最优化算法完成。

  • 优点:计算代价不高,易于理解和实现
  • 缺点:容易造成欠拟合,分类精度不高
  • 适用数据类型:数值型和标称型数据

1. Sigmoid 函数

线性回归中,对数据进行拟合,是在每个特征上都乘以一个回归系数,然后把所有结果再相加,即 h_{\theta}=\theta_{0}x_{0}+\theta_{1}x_{1}+...+\theta_{n}x_{n} ,向量化即 h_{\theta}(x)=\theta^{T}x 。

逻辑回归中,再将 h_{\theta}(x)=\theta^{T}x 带入 Sigmoid 函数,可得到属于 (0,1) 范围的一个数值,若大于 0.5 则属于正类,若小于则反之。

Sigmoid 函数的定义如下:

g(z)=\frac{1}{1+e^{-z}}

Sigmoid 函数的图像如下:

综合上述,整体公式表示为:

h_{\theta}(x)=g(\theta^{T}x)=\frac{1}{1+e^{-\theta^{T}x}}

2. 梯度上升与梯度下降

梯度:某一函数在该点处的方向导数沿该方向取得最大值,即在该点变化率(斜率)最大。

  • 梯度上升法:寻找某函数的最大值,沿着该函数的梯度方向寻找
  • 梯度下降法:寻找某函数的最小值,沿着该函数的梯度方向寻找

梯度符号记为 \nabla ,f(x) 的梯度可表示为,对各个方向求偏导数:

\nabla f(x)=\begin{bmatrix} \frac{\partial f(x)}{\partial x_1}\\ ...\\ \frac{\partial f(x)}{\partial x_n} \end{bmatrix}

如下图所示,求最小值,可采用梯度下降法:

梯度下降: x:=x-\alpha\frac{\partial f(x)}{\partial x} ,当 x 在最小值左边时,导数为负数,x 向右移;当 x 在最小值右边时,导数为正数,x 向左移。

如下图所示,求最大值,可采用梯度上升法:

梯度上升: x:=x+\alpha\frac{\partial f(x)}{\partial x} ,当 x 在最小值左边时,导数为正数,x 向右移;当 x 在最小值右边时,导数为负数,x 向左移。

3. 梯度下降法(Gradient descent)

梯度下降法的代价函数如下定义:

J(\theta)= -\frac{1}{m}\sum_{i=1}^{m}[y^{(i)}log(h_{\theta}(x^{(i)}))+(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))] + \frac{\lambda}{2m}\sum_{j=1}^{n}\theta_{j}^{2}

代价函数是指,函数值越大,付出的代价越大,即精度越差。

第二项是正则化项,是为了防止过拟合而设定,先忽略这项。

第一项是所有样本计算结果的求平均,若 y^{(i)}=1 ,则取 -log(h_{\theta}(x^{(i)})) ,若 y^{(i)}=0 ,则取 -log(1-h_{\theta}(x^{(i)})) 。

由上图易得,y=1 时,若 h_{\theta}(x) 越接近 0 代价越大,y=0 时,若 h_{\theta}(x) 越接近 1 代价越大。

为了降低代价函数 J(\theta) ,可采取梯度下降法,定义如下:

\theta_{j}:=\theta_{j}-\alpha \frac{\partial }{\partial \theta_{j} }J(\theta)\\ \Rightarrow \theta_{j}:=\theta_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_{j}^{(i)}

4. 梯度上升法(Gradient ascent)

梯度上升法求解逻辑回归参数,和梯度下降法求解参数原理是一样的。

只是在梯度上升法中,J(\theta) 定义如下:

J(\theta)= \frac{1}{m}\sum_{i=1}^{m}[y^{(i)}log(h_{\theta}(x^{(i)}))+(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))] + \frac{\lambda}{2m}\sum_{j=1}^{n}\theta_{j}^{2}

与梯度下降法只相差一个负号,所以是求解最大值,采用梯度上升法:

\theta_{j}:=\theta_{j}+\alpha \frac{\partial }{\partial \theta_{j} }J(\theta)\\ \Rightarrow \theta_{j}:=\theta_{j}+\alpha \frac{1}{m}\sum_{i=1}^{m}(y^{(i)}-h_{\theta}(x^{(i)}))x_{j}^{(i)}

5. 梯度下降/上升法的数学推导

以梯度上升法为例子,不考虑正则化项:

\begin{align*} & J(\theta)= \frac{1}{m}\sum_{i=1}^{m}[y^{(i)}log(h_{\theta}(x^{(i)}))+(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))]=\frac{1}{m}\sum_{i=1}^{m}J_{temp}(\theta) \\ & h_{\theta}(x)=g(\theta^Tx)=\frac{1}{1+e^{-\theta^Tx}} \\ \\ & \frac{\partial J_{temp}(\theta)}{\partial \theta_j}=\frac{\partial J_{temp}(\theta)}{\partial g(\theta^Tx)}\cdot\frac{\partial g(\theta^Tx)}{\partial \theta^Tx}\cdot\frac{\partial \theta^Tx}{\partial \theta_j} \end{align*}

对三项分别计算:

\begin{align*} \frac{\partial J_{temp}(\theta)}{\partial g(\theta^Tx)} &= y\cdot\frac{1}{g(\theta^Tx)}+(1-y)\cdot\frac{-1}{1-g(\theta^Tx)} \\ &= y\cdot\frac{1}{g(\theta^Tx)}+(y-1)\cdot\frac{1}{1-g(\theta^Tx)} \end{align*}

\begin{align*} \frac{\partial g(\theta^Tx)}{\partial \theta^Tx} &= \frac{\partial g(z)}{\partial z} \\ &= -\frac{1}{(1+e^{-z})^2}\cdot e^{-z} \cdot (-1) \\ &= \frac{1}{(1+e^{-z})^2}\cdot e^{-z} \\ &= \frac{1}{1+e^{-z}}(1- \frac{1}{1+e^{-z}}) \\ &= g(z)(1-g(z)) \\ &= g(\theta^Tx)(1-g(\theta^Tx)) \end{align*}

\begin{align*} \frac{\partial \theta^Tx}{\partial \theta_j} &= \frac{\partial (\theta_0x_0+\theta_1x_1+...+\theta_nx_n)}{\partial \theta_j} \\ &= x_j \end{align*}

综合三项可得:

\begin{align*} \frac{\partial J_{temp}(\theta)}{\partial \theta_j} &= \frac{\partial J_{temp}(\theta)}{\partial g(\theta^Tx)}\cdot\frac{\partial g(\theta^Tx)}{\partial \theta^Tx}\cdot\frac{\partial \theta^Tx}{\partial \theta_j} \\ &= (y\cdot\frac{1}{g(\theta^Tx)}+(y-1)\cdot\frac{1}{1-g(\theta^Tx)})\cdot g(\theta^Tx)(1-g(\theta^Tx))\cdot x_j \\ &= (y(1-g(\theta^Tx))+(y-1)g(\theta^Tx))x_j \\ &= (y-g(\theta^Tx))x_j \\ &= (y-h_{\theta}(x))x_j \end{align*}

\begin{align*} \frac{\partial J(\theta)}{\partial \theta_j} &= \frac{1}{m}\sum_{i=1}^{m} \frac{\partial J_{temp}(\theta)}{\partial \theta_j} \\ &= \frac{1}{m}\sum_{i=1}^{m}(y^{(i)}-h_{\theta}(x^{(i)}))x_j^{(i)} \end{align*}

综上所述,在梯度上升法中:

\theta_{j}:=\theta_{j}+\alpha \frac{\partial }{\partial \theta_{j} }J(\theta)\\ \Rightarrow \theta_{j}:=\theta_{j}+\alpha \frac{1}{m}\sum_{i=1}^{m}(y^{(i)}-h_{\theta}(x^{(i)}))x_{j}^{(i)}

6. 随机梯度下降/上升法(Stochastic gradient descent/ascent)

以随机梯度下降法为例子,随机梯度下降法是对梯度下降法的改进。

梯度下降法中,每次更新参数都需要遍历整个数据集,又称作 Batch gradient descent 。

随机梯度下降法中,每次更新参数只随机选择一个样本点,通过每次更新参数的迭代而选择不同的样本点。

算法流程如下:

随机梯度下降/上升法是一个在线算法,可以在新数据来临的时候直接更新参数,而不用遍历整个数据集。 

7. 实战案例

以下将展示书中案例的代码段,所有代码和数据可以在github中下载:

7.1. 简单案例

# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt

"""
简单案例
"""


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


# 梯度上升求解J(\theta)
# Batch gradient ascent
def gradAscent(dataMatIn, classLabels):
    # 转换为矩阵
    dataMatrix = mat(dataMatIn)
    # 转换为矩阵后转置,表示为列向量
    labelMat = mat(classLabels).transpose()
    m, n = shape(dataMatrix)
    alpha = 0.001
    maxCycles = 500
    # \theta 表示为列向量
    weights = ones((n, 1))
    # 梯度上升迭代
    for k in range(maxCycles):
        h = sigmoid(dataMatrix * weights)
        error = (labelMat - h)
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights


# 加载数据集
def loadDataSet():
    dataMat = []
    labelMat = []
    fr = open('testSet.txt')
    for line in fr.readlines():
        # 去除头尾空字符,按照空格分割字符串
        lineArr = line.strip().split()
        # 添加偏置位 w_0(\theta_0) 相乘的 x_0 = 1.0
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
        labelMat.append(int(lineArr[2]))
    return dataMat, labelMat


# 画出数据的分界线
def plotBestFit(weights):
    dataMat, labelMat = loadDataSet()
    dataArr = array(dataMat)
    n = shape(dataArr)[0]
    xcord1 = []
    ycord1 = []
    xcord2 = []
    ycord2 = []
    # 遍历每一条数据,根据类别将x1, x2分别插入不同的List中
    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 = array(arange(-3.0, 3.0, 0.1))
    # x=x1, y=x2 表示的是 w0x0+w1x1+w2x2 = 0 的直线
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()


if __name__ == '__main__':
    dataArr, labelMat = loadDataSet()
    weights = gradAscent(dataArr, labelMat)
    # getA():
    #   将矩阵转换为ndarray
    plotBestFit(weights.getA())

7.2. 病马死亡率案例

# coding:utf-8
from numpy import *

"""
病马死亡率案例
"""


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


# 随机梯度上升
# stochastic gradient ascent
def stocGradAscent0(dataMatrix, classLabels):
    m, n = shape(dataMatrix)
    alpha = 0.01
    weights = ones(n)
    # 遍历每一个数据
    for i in range(m):
        # 数组对应元素相乘再相加
        h = sigmoid(sum(dataMatrix[i] * weights))
        error = classLabels[i] - h
        weights = weights + alpha * error * dataMatrix[i]
    return weights


# 改进后的随机梯度上升
# 学习率随迭代次数减少
# 随机选择样本
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m, n = shape(dataMatrix)
    weights = ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            # 学习率降低
            alpha = 4 / (1.0 + j + i) + 0.0001
            randIndex = int(random.uniform(0, len(dataIndex)))
            h = sigmoid(sum(dataMatrix[dataIndex[randIndex]] * weights))
            error = classLabels[dataIndex[randIndex]] - h
            weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
            del (dataIndex[randIndex])
    return weights


# 分类函数
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))
    if prob > 0.5:
        return 1
    else:
        return 0


# 构件逻辑回归分类器,进行分类测试
def colicTest():
    frTrain = open('horseColicTraining.txt')
    frTest = open('horseColicTest.txt')
    trainingSet = []
    trainingLabels = []
    # 遍历训练集
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        # 创建训练集的特征向量和标签
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[21]))
    # 随机梯度上升求解参数
    trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 1000)
    correctCount = 0
    numTestVec = 0.0
    # 遍历测试集
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(array(lineArr), trainWeights)) == int(currLine[21]):
            correctCount += 1
    accuracy = (float(correctCount) / numTestVec)
    print("the accuracy of this test is: %f" % accuracy)
    return accuracy


# 多次测试分类器
def multiTest():
    numTests = 10
    correctSum = 0.0
    for k in range(numTests):
        correctSum += colicTest()
    print("after %d iterations the average accuracy is: %f" % (numTests, correctSum / float(numTests)))


if __name__ == '__main__':
    multiTest()

如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~

猜你喜欢

转载自blog.csdn.net/zhq9695/article/details/83185608