机器学习之逻辑回归

目录

一、逻辑回归组成

        1.1线性回归

        1.2Sigmoid函数

二、逻辑回归算法介绍

        2.1逻辑回归算法实质

        2.2逻辑回归算法步骤

三、逻辑回归代码实现及分析

        3.1总体代码实现思路

        3.2各部分代码实现及效果图

        3.3梯度下降算法优化

        3.4整体代码实现及效果图

四、总结


一、逻辑回归组成

        1.1线性回归

线性回归:描述一个连续型因变量和一系列自变量之间的关系。它的作用是通过许多实验观察到的值回归统计变量的真实值,它属于有监督学习算法。它的核心是建立一个模型,该模型能够描述自变量与因变量之间的线性关系(也就是在多个点分布的情况下,拟合出一条回归的直线)。它一般的表示形式如下:

y = f(x) = w_{0}+w_{1}x_{1}+...+w_{n}x_{n}

其中,y为预测的因变量,w为系数,x为自变量,n为自变量的个数。若转化为矩阵形式,则能表示成以下形式:

x^{i} = [x_{1}^{i} x_{2}^{i}....x_{n}^{i}]

我们通常通过最小二乘法来进行估计回归系数,它通过最小化误差平方和来寻找最佳的参数估计。通常我们会使用决定系数(R^{2})、均方误差(MSE)和均方根误差(RMSE)等来衡量模型的拟合优度。

线性回归的优点:模型易于理解、计算简单和可解释性强

线性回归的缺点:不能很好地处理非线性关系和多重共线性问题。

        1.2Sigmoid函数

Sigmoid函数是一个在生物学中常见的S型函数,也称为S型生长曲线。在深度学习中,由于其单增以及反函数单增等性质,Sigmoid函数常被用作神经网络的激活函数,将变量映射到[0,1]之间。其表示如下:

S(x) = \frac{1}{1+e^{-x}}

Sigmoid函数图像如下:

我们可以将线性回归输出的y作为Sigmoid函数的输入,而后y的范围会从映射到[0,1],当我们假设0.5为阈值,则映射的y值大于0.5的我们就会将它定义为类别1,否则为类别2,使得她在表示概率或者是二分类问题中非常有用。

Sigmoid函数优点:它是连续且光滑的,严格单调递增,易于求导,在梯度下降等优化算法中非常实用。

Sigmoid函数缺点:当输入值非常大或非常小的时候,函数的梯度会接近于0,可能导致梯度消失问题,从而影响神经网络的学习效果;由于其饱和性,当输入值处于极端时,会导致网络学习缓慢。

二、逻辑回归算法介绍

        2.1逻辑回归算法实质

首先我们需要知道逻辑回归虽然名字中有回归一词,但它实际上是个二分类问题。而回归问题与二分类问题的区别就在于:回归问题解决的是连续问题,分类问题解决的是离散问题。奇妙之处在于,我们对回归问题增加一个阈值的条件,也可以将其变为分类问题。所以其实逻辑回归的实质就是将线性回归与Sigmoid函数进行相结合,从而解决分类问题。下图为离散问题与分类问题的区别比对图:

        2.2逻辑回归算法步骤

(1)线性模型与回归

f(x) = w_{1}x_{1}+...+w_{n}x_{n}+b

其中x = (x_{1},x_{2},...,x_{n})是由n维属性描述的样本,向量化表示为:f(x) = w^{T}x+b。(也就是在多个样本点中拟合出一条回归直线)

        (2)通过最小二乘法实现参数求解

参数的求解其实也是根据线性回归目标决定(即想要回归预测值与真实值的误差最小)

(w^{*},b^{*}) = argmin_{(w,b)}\sum_{i=1}^{m}(f(x_{i})-y_{i})^{2}

其中f(x_{i})为预测值,y_{i}为真实值。因此我们需要对参数w和b求偏导求解误差最小值。求解的结果如下:

w = \frac{\sum_{i=1}^{m}y_{i}(x_{i}-\bar{x})}{\sum_{i=1}^{m}x_{i}^{2}-\frac{1}{m}(\sum_{i=1}^{m}x_{i})^{2}}

b = \frac{1}{m}\sum_{i=1}^{m}(y_{i}-wx_{i})

其中:\bar{x} = \frac{1}{m}\sum_{i=1}^{m}x_{i}

(3)结合Sigmoid函数

我们通过线性回归模型产生的预测值z = w^{T}x + b作为输入,传入到Sigmoid函数中,将其值映射到[0,1]区间,因此我们最理想的是“单位阶跃函数”,其输出标记我们即为y,则有以下关系。

y = \left\{\begin{matrix} 0,z<0; & \\ 0.5,z=0;& \\ 1,z>0;& \end{matrix}\right.

若预测值z大于0,我们就潘伟正例,反之则判为反例,如下图所示。

我们可以看出,单位阶跃函数不连续,因此不能直接使用。因此我们希望找到能在一定程度上近似单位阶跃函数的“替代函数”,并希望它单调可微,因此Sigmoid函数就显得十分合适,Sigmoid函数如下:

y = \frac{1}{1+e^{-z}}

这样,我们就能将原本z的值从负无穷大到正无穷大,映射到[0,1]的区间,除此之外,我们可以通过添加一个阈值,从而达到分类的结果。

(4)优化损失函数

为了求出好的逻辑回归,损失函数是体现“预测值”和“真实值”,相似程度的函数。我们的损失函数越小,证明我们训练的模型越好。在对逻辑回归的时候,我们通常会使用交叉熵来衡量模型。

而我们通常用均方误差来衡量回归的效果,用交叉熵来衡量分类的效果。

均方误差(MSE):常用于回归问题,用以评估预测模型的预测值与实际值之间的差异,其公式如下:

E = \sum_{i=1}^{n}(y_{i}-(wx_{i}+b))^{2}

交叉熵损失函数:用于衡量两个概率分布之间的差异。在逻辑回归中,这两个概率分布分别是模型的预测概率和真实的标签概率。假设p是真是的概率分布,而q是模型预测的概率分布。交叉熵损失函数则可以表示为:

L = -\frac{1}{n}\sum_{i=1}^{n}[y_{i}log(\hat{y_{i}})+(1-y_{i})log(1-\hat{y_{i}})]

其中n是样本数量,y_{i}是真是标签,\hat{y_{i}}是预测概率。在二分类逻辑回归中,如果样本的真实标签是1,则真实概率分布p为(1,0);如果标签是0,则p为(0,1)。模型预测的概率分布q则是由逻辑回归模型输出的概率。

(5)优化算法(梯度下降法)

梯度下降法:该算法是一个一阶最优化算法,通过向函数上当前点对应梯度(或近似梯度)的反方向的规定步长距离点进行迭代搜索找到一个函数的局部极小值。梯度下降图解如下所示:

在逻辑回归中,我们需要训练两个参数w和b。然后根据计算出的梯度和学习率α,更新模型的参数对于这两个参数我们需要重复以下两个步骤:

w = w -\partial \frac{\partial f(w,b)}{\partial w}

b = b - \partial \frac{\partial f(w,b)}{\partial b}

假设一个样本有两个特征,分别是x1和x2,则逻辑回归有以下步骤:

其中y是真实值,a是预测值。

在实现逻辑回归的算法中包括向前求预测值 a,以及利用损失函数L(a,y)反向更新w和b的过程。而后根据计算出的梯度和学习率α,更新模型的参数。重复上述步骤,直到满足迭代的次数达到我们所设定的次数时停止。

三、逻辑回归代码实现及分析

        3.1总体代码实现思路

(1)构建模型:逻辑回归是一种基于线性回归的分类算法,通过添加一个Sigmoid函数将线性回归的输出映射到概率空间。具体来说,逻辑回归模型可以表示为:

P(y=1|X) = \frac{1}{1+exp(-z)}

其中,P(y=1|X)表示在给定自变量X的条件下,因变量y取值为1的概率;exp()是指数函数;z是线性组合,即z = w_{0}+w_{1}x_{1}+...+w_{n}x_{n},其中w是模型参数,x是自变量。

(2)损失函数:为了优化模型,需要定义一个损失函数来衡量模型预测的概率与实际标签之间的差异。常用的损失函数是交叉熵损失函数,其表达式为:

L(y,y_pred) = -y[y*log(y_pred)+(1-y)*log(1-y_pred)]

其中,y是实际标签,y_pred是模型预测的概率。

(3)梯度下降法求解最优参数:通过梯度下降法来最小化损失函数,找到最优的模型参数。具体步骤如下:

(1)初始化模型参数(如权重和偏置项)

(2)计算损失函数关于每个参数的梯度

(3)根据学习率和梯度更新参数值

(4)重复步骤2和3直到满足停止条件(如迭代次数或损失函数收敛)

(4)模型评估:在得到最优参数后,需要对模型进行评估,以检验其泛化能力。

(5)应用模型进行预测:最后,使用训练好的模型对新的数据进行分类预测。

        3.2各部分代码实现及效果图

(1)加载数据集

def loadDataSet():
    # 数据列表
    dataMat = []
    # 标签列表
    labelMat = []
    # 打开文件
    fr = open('D:\\AI\\watermelon.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]))
    # 关闭文件
    fr.close()
    return dataMat, labelMat

(2)然后我们将输入的数据集和标签转换为numpy矩阵,然后初始化权重向量为全1。接下来进行迭代更新权重,每次迭代使用梯度上升矢量化公式更新权重,最后返回求得的权重数组。

def plotDataSet():
    # 加载数据集
    dataMat, labelMat = loadDataSet()
    # 转换成numpy的array数组
    dataArr = np.array(dataMat)
    # 数据个数
    n = np.shape(dataMat)[0]
    # 正样本
    xcord1 = []
    ycord1 = []
    # 负样本
    xcord2 = []
    ycord2 = []
    for i in range(n):
        # 1为正样本
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i, 1])
            ycord1.append(dataArr[i, 2])
        # 0为负样本
        else:
            xcord2.append(dataArr[i, 1])
            ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    # 添加subplot
    ax = fig.add_subplot(111)
    # 绘制正样本
    ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)
    # 绘制负样本
    ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)
    plt.title('DataSet')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.show()


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


def gradAscent(dataMatIn, classLabels):
    # 转换成numpy的mat
    dataMatrix = np.mat(dataMatIn)
    # 转换成numpy的mat,并进行转置
    labelMat = np.mat(classLabels).transpose()
    # 返回dataMatrix的大小。m为行数,n为列数。
    m, n = np.shape(dataMatrix)
    # 移动步长,也就是学习速率,控制更新的幅度。
    alpha = 0.001
    # 最大迭代次数
    maxCycles = 500
    weights = np.ones((n,1))
    for k in range(maxCycles):
        # 梯度上升矢量化公式
        # g(X)=h(theta) = theta * X
        h = sigmoid(dataMatrix * weights)
        error = labelMat - h
        # theta = theta + alpha * X^T(y - g(X))
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights.getA()

(3)接下来我们利用数据集的标签,绘制其类别分散的散点图

def gradAscent(dataMatIn, classLabels):
    # 转换成numpy的mat
    dataMatrix = np.mat(dataMatIn)
    # 转换成numpy的mat,并进行转置
    labelMat = np.mat(classLabels).transpose()
    # 返回dataMatrix的大小。m为行数,n为列数。
    m, n = np.shape(dataMatrix)
    # 移动步长,也就是学习速率,控制更新的幅度。
    alpha = 0.001
    # 最大迭代次数
    maxCycles = 500
    weights = np.ones((n,1))
    for k in range(maxCycles):
        # 梯度上升矢量化公式
        # g(X)=h(theta) = theta * X
        h = sigmoid(dataMatrix * weights)
        error = labelMat - h
        # theta = theta + alpha * X^T(y - g(X))
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights.getA()

def plotBestFit(weights):
    # 加载数据集
    dataMat, labelMat = loadDataSet()
    # 转换成numpy的array数组
    dataArr = np.array(dataMat)
    # 数据个数
    n = np.shape(dataMat)[0]
    # 正样本
    xcord1 = []
    ycord1 = []
    # 负样本
    xcord2 = []
    ycord2 = []
    # 根据数据集标签进行分类
    for i in range(n):
        # 1为正样本
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
        # 0为负样本
        else:
            xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])
    fig = plt.figure()
    # 添加subplot
    ax = fig.add_subplot(111)
    # 绘制正样本
    ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)
    # 绘制负样
    ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)
    x = np.arange(-3.0, 3.0, 0.1)
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x, y)
    plt.title('BestFit')
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()

(4)利用随机梯度上升算法进行优化,我们通过初始化数据索引列表,对于每个样本,计算学习率alpha,随着迭代次数的增加而减小。随机选择一个样本,计算该样本的预测值h。计算误差 , 更新权重,从数据索引列表中删除已使用的样本,最后返回更新后的权重向量。

def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    # 返回dataMatrix的大小。m为行数,n为列数。
    m, n = np.shape(dataMatrix)
    # 参数初始化
    weights = np.ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            # 降低alpha的大小,每次减小1/(j+i)。
            alpha = 4 / (1.0 + j + i) + 0.01
            # 随机选取样本
            randIndex = int(random.uniform(0, len(dataIndex)))
            # 选择随机选取的一个样本,计算h
            h = sigmoid(sum(dataMatrix[randIndex] * weights))
            # 计算误差
            error = classLabels[randIndex] - h
            # 更新回归系数
            weights = weights + alpha * error * dataMatrix[randIndex]
            # 删除已经使用的样本
            del (dataIndex[randIndex])
    return weights

        3.3整体代码实现及效果图

整体代码如下:

from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
import numpy as np
import random


def loadDataSet():
    # 数据列表
    dataMat = []
    # 标签列表
    labelMat = []
    # 打开文件
    fr = open('D:\\AI\\watermelon.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]))
    # 关闭文件
    fr.close()
    return dataMat, labelMat


def plotDataSet():
    # 加载数据集
    dataMat, labelMat = loadDataSet()
    # 转换成numpy的array数组
    dataArr = np.array(dataMat)
    # 数据个数
    n = np.shape(dataMat)[0]
    # 正样本
    xcord1 = []
    ycord1 = []
    # 负样本
    xcord2 = []
    ycord2 = []
    for i in range(n):
        # 1为正样本
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i, 1])
            ycord1.append(dataArr[i, 2])
        # 0为负样本
        else:
            xcord2.append(dataArr[i, 1])
            ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    # 添加subplot
    ax = fig.add_subplot(111)
    # 绘制正样本
    ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)
    # 绘制负样本
    ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)
    plt.title('DataSet')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.show()

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



def gradAscent(dataMatIn, classLabels):
    # 转换成numpy的mat
    dataMatrix = np.mat(dataMatIn)
    # 转换成numpy的mat,并进行转置
    labelMat = np.mat(classLabels).transpose()
    # 返回dataMatrix的大小。m为行数,n为列数。
    m, n = np.shape(dataMatrix)
    # 移动步长,也就是学习速率,控制更新的幅度。
    alpha = 0.001
    # 最大迭代次数
    maxCycles = 500
    weights = np.ones((n,1))
    for k in range(maxCycles):
        # 梯度上升矢量化公式
        # g(X)=h(theta) = theta * X
        h = sigmoid(dataMatrix * weights)
        error = labelMat - h
        # theta = theta + alpha * X^T(y - g(X))
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights.getA()


def plotBestFit(weights):
    # 加载数据集
    dataMat, labelMat = loadDataSet()
    # 转换成numpy的array数组
    dataArr = np.array(dataMat)
    # 数据个数
    n = np.shape(dataMat)[0]
    # 正样本
    xcord1 = []
    ycord1 = []
    # 负样本
    xcord2 = []
    ycord2 = []
    # 根据数据集标签进行分类
    for i in range(n):
        # 1为正样本
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
        # 0为负样本
        else:
            xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])
    fig = plt.figure()
    # 添加subplot
    ax = fig.add_subplot(111)
    # 绘制正样本
    ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)
    # 绘制负样
    ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)
    x = np.arange(-3.0, 3.0, 0.1)
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x, y)
    plt.title('BestFit')
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()


def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    # 返回dataMatrix的大小。m为行数,n为列数。
    m, n = np.shape(dataMatrix)
    # 参数初始化
    weights = np.ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            # 降低alpha的大小,每次减小1/(j+i)。
            alpha = 4 / (1.0 + j + i) + 0.01
            # 随机选取样本
            randIndex = int(random.uniform(0, len(dataIndex)))
            # 选择随机选取的一个样本,计算h
            h = sigmoid(sum(dataMatrix[randIndex] * weights))
            # 计算误差
            error = classLabels[randIndex] - h
            # 更新回归系数
            weights = weights + alpha * error * dataMatrix[randIndex]
            # 删除已经使用的样本
            del (dataIndex[randIndex])
    return weights



if __name__ == '__main__':
    dataMat, labelMat = loadDataSet()
    weights = stocGradAscent1(np.array(dataMat), labelMat)
    plotBestFit(weights)

四、总结

总的来说逻辑回归算法是一种统计学方法,用于解决二分类问题。尽管名字中有“回归”二字,它实际上是用来进行分类的。逻辑回归基于线性回归,但是通过引入sigmoid函数来实现概率估计,从而预测出属于某一类别的概率。

以下为逻辑回归算法的各部分分解说明:

  1. 线性回归部分:首先使用特征和权重的线性组合来形成原始输出值,即 z = w_{0}+w_{1}x_{1}+...+w_{n}x_{n}

  2. Sigmoid函数:将线性组合的结果输入到sigmoid函数中,得到一个介于0和1之间的概率值。sigmoid函数定义为 y = \frac{1}{1+e^{-z}}

  3. 概率解释:通过sigmoid函数转换后的值可以被解释为正类(通常标记为1)的预测概率。

  4. 损失函数:逻辑回归通常使用对数损失函数(log loss),也叫做交叉熵损失,以衡量模型的性能。

  5. 参数优化:使用如梯度下降(GD)、随机梯度下降(SGD)等优化算法来迭代学习最佳的权重参数,最小化损失函数。

  6. 逻辑回归的优点:(1)实现简单,易于理解。(2)计算成本较低,适用于大型数据集。(3)输出具有概率意义,方便解释。不容易过拟合,尤其是在数据维度不太高的情况下。

  7. 逻辑回归缺点:(1)不能处理非线性决策边界。如果问题是非线性可分的,逻辑回归效果可能不佳。(2)对于多分类问题,需要使用One-vs-All或One-vs-One策略,这会增加模型复杂性。(3)当类别之间严重不平衡时,性能可能会受到影响。

  • 33
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值