随着数据科学的不断发展,机器学习已经成为我们日常生活的一部分。今天,我想分享一下我对Logistic回归的理解和应用。Logistic回归是一种用于分类问题的统计方法,特别适用于二分类问题。
一、Logistic回归简介
- logistics回归=线性回归+sigmoid函数。
- 线性回归的形式z=w*x+b
- sigmoid函数
将回归模型的连续的值映射到[0,1]之间,划分为离散的分类标准。 以下为sigmoid函数图像。
- 对于二分类问题,当z>0.5时,归为1,z<0.5时,归为0.
- 那么logistics回归的总体公式就是:
Logistic回归是一种广义线性模型,它通过一个转换函数将预测变量的线性组合转换为概率值。这个转换函数是逻辑函数,即S型曲线。因此,Logistic回归主要用于二分类问题,它可以处理分类变量和连续变量,同时也可以处理分类变量之间的相互作用。
二、Logistic回归的优点和缺点
Logistic回归的优点包括:
- 模型简单,易于实现。
- 结果易于解释。
- 可以处理不同类型的输入变量,包括连续变量和类别变量。
- 通过输出概率,提供了决策阈值的灵活性。
不过,Logistic回归也有其局限性:
- 假设特征和输出之间有线性关系。
- 对于非线性问题,可能需要特征工程来提高模型性能。
- 对于有多个类别的分类问题,需要进行扩展,如使用一对多或多项式Logistic回归
三、最佳回归系数的确定(极大似然估计+最优化方法)
上一节我们已经确定了logisti分类函数,有了分类函数,我们输入特征向量就可以得出实例属于某个类别的概率。但这里有个问题,权重w(回归系数)我们是不确定的。正如我们想的那样,我们需要求得最佳的回归系数,从而使得分类器尽可能的精确。
如何才能获得最佳的回归系数呢?这里就要用到最优化方法。前面我们提到过某件事情发生的概率为p,在logistics回归中所定义的损失函数就是定义一个似然函数做概率的连乘,数值越大越好,也就是某个样本属于其真实标记样本的概率越大越好。如,一个样本的特征x所对应的标记为1,通过逻辑斯蒂回归模型之后,会给出该样本的标记为1和为-1的概率分别是多少,我们当然希望模型给出该样本属于1的概率越大越好。既然是求最大值,那我们用到的最优化算法就是梯度上升,其实也就是与梯度下降相反而已。
- 对于多分类问题:
四、梯度上升法
梯度上升法基于的思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。如果梯度记为∇ ,则函数f(x,y)的梯度由下式表示:
▽
这个梯度意味着要沿x的方向移动▽,沿y的方向移动▽
。其中,函数f(x,y)必须要在待计算的点上有定义并且可微。一个具体的函数例子见下图。
梯度上升算法到达每个点后都会重新估计移动的方向。从P0开始,计算完该点的梯度,函数就根据梯度移动到下一点P1。在P1点,梯度再次被重新计算,并沿新的梯度方向移动到P2.如此循环迭代,知道满足通知条件。迭代的过程中,梯度算子总是保证我们能选取到最佳的移动方向。
上图中的梯度上升算法沿梯度方向移动了一步。可以看到,梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量值称为步长,记作α 。用向量来表示的话,梯度上升算法的迭代公式如下:
w=w+α▽wf(w)
该公式将一直迭代执行,直至达到某个停止条件为止,b比如迭代次数达到某个值或者算法达到某个可以允许的误差范围。
五、代码实现
import pandas as pd
# pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin.data")
# -*- coding:UTF-8 -*-
# {{{
import matplotlib.pyplot as plt
import numpy as np
# 加载波士顿房屋数据集
data_url = "boston.csv"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
dataMat = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
labelMat = raw_df.values[1::2, 2]
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
def gradAscent(dataMatIn, classLabels):
"""
梯度上升算法
:param dataMatIn: 数据矩阵
:param classLabels: 标签矩阵
:return: 最有参数数组
"""
dataMatrix = np.mat(dataMatIn)
""" 转置 """
labelMat = np.mat(classLabels).transpose()
""" 获取矩阵行数 m 和列数 n """
m, n = np.shape(dataMatrix)
""" 迭代步长 """
alpha = 0.001
""" 迭代次数 """
maxCycles = 500
# 初始化权重为全1的矩阵
weights = np.ones((n, 1))
'''在梯度下降算法中,我们计算的是误差函数关于权重的梯度(或者叫偏导数),并将其乘以学习率来更新权重。这里有两种常见的更新权重的方式: 1.
直接使用误差与学习率的乘积来更新权重:'''
for k in range(maxCycles):
""" 带入数学公式求解 """
h = sigmoid(dataMatrix * weights)
# 更新权重,这里使用了dataMatrix的转置与误差相乘,然后乘以学习率alpha
weights = weights + alpha * dataMatrix.transpose() * (labelMat - h)
""" 转换为 array """
return weights.getA()
def plotBestFit(weights):
"""
绘制数据集与结果分类线
:param weights: 权重参数数组
:return:
"""
# dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
""" 数据个数 """
n = np.shape(dataMat)[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)
""" 离散绘制分类结果为 1 的样本 """
ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)
""" 离散绘制分类结果为 0 的样本 """
ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)
x1 = np.arange(-3.0, 3.0, 0.1)
x2 = (-weights[0] - weights[1] * x1) / weights[2]
ax.plot(x1, x2)
plt.title('BestFit')
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
if __name__ == '__main__':
# dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
plotBestFit(weights)
# }}}
结果为: