python实现 logistic 回归 二分类算法 (通俗讲解逻辑回归本质与由来)

logistic回归

  • 将数据样本看作是欧式空间的点,尝试找到一个超平面,将空间分成两部分,如果样本点在”正面“,则它被分为0类;如果样本点在”负面“,则它被分为1类。
  • 怎么判断样本点在超平面的哪一面?将样本点坐标 x x x代入超平面方程 a T x = 0 a^Tx=0 aTx=0的等式左边 a T x a^Tx aTx,如果大于0,则在”阳面“;小于0,则在”阴面“;等于0,则在超平面上。
  • 根据上面大于0 or 小于0 已经能判断属于哪一类了,再代入符号函数(机器学习里叫阶跃函数,数学人觉得就是个符号函数),直接输出类别0 or 1,就更好了。这样就有了一个完整模型,即 s i g n ( a T x ) {\rm sign}(a^Tx) sign(aTx)
  • 为什么非要套一个符号函数呢?我直接用大于0,小于0判断不行嘛?不行,因为后面要进行优化选参数 a a a,有完整模型才能建立评价指标,比如风险函数,然后这个风险函数作为优化目标,取其极值点不是吗?
  • s i g n ( a T x ) {\rm sign}(a^Tx) sign(aTx)这个模型并不是很好,因为后续要进行优化,绝大多数优化算法依靠的是导数信息(虽然现在有很多非光滑优化算法),参数 a a a作为变量,函数 s i g n ( a T x ) {\rm sign}(a^Tx) sign(aTx)最好要是光滑的,但是它不是,因为原点是符号函数的跳跃间断点,它甚至都不连续啊…性质太差了!
  • 怎么解决上面的问题?光滑化呗,取一个光滑函数去近似符号函数,下图是符号函数图像:
    python实现 logistic 回归 二分类算法 (通俗讲解逻辑回归本质与由来)_第1张图片
    我想让它变成下面这样光滑去近似符号函数:
    python实现 logistic 回归 二分类算法 (通俗讲解逻辑回归本质与由来)_第2张图片
  • 正好,sigmoid函数完美符合要求,连续光滑,长得像符号函数。
  • 把上面模型的符号函数替换成sigmoid函数就是逻辑回归模型了。

sigmoid函数:
σ ( x ) = 1 1 + e − x \sigma (x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1
顺便记录一下它求导过程:
σ ′ ( x ) = e − x ( 1 + e − x ) 2 = 1 1 + e − x − 1 ( 1 + e − x ) 2 = σ ′ ( x ) − ( σ ′ ( x ) ) 2 = σ ′ ( x ) ( 1 − σ ′ ( x ) ) \sigma^{'} (x)=\frac{e^{-x}}{(1+e^{-x})^2}=\frac{1}{1+e^{-x}}-\frac{1}{(1+e^{-x})^2}=\sigma^{'} (x)-(\sigma^{'} (x))^2=\sigma^{'} (x)(1-\sigma^{'} (x)) σ(x)=(1+ex)2ex=1+ex1(1+ex)21=σ(x)(σ(x))2=σ(x)(1σ(x))

  • 注: 以上很多名词只是为了自己方便记忆随便乱取的。
  • 由于sigmoid函数取值不是离散的0,1,而是一个连续区间(0,1),所以,可以看作是一种概率估计,概率大于0.5分入1类,小于0.5分入0类。

梯度下降法

太熟了,不写。
机器学习里的优化好像并没有给出明确的停机准则,都是靠最大迭代次数来停止的。

训练/优化:使用梯度下降找到模型的最佳参数

有关评价准则,风险函数,损失函数等有时间再补上,下面先只给出训练程序。

  • 下面的梯度grad是对风险函数求导得到,有机会再补充,这本书也没有详细说明。
  • 注意:dataMtrix第一列是1,这是对应于线性函数的截距项。
import numpy as np


## 逻辑回归
# 自定义sigmoid函数
## 逻辑回归
# 自定义sigmoid函数
def sigmoid(inX):
    res = np.zeros(inX.shape)
    for i in range(inX.shape[0]):
        if inX[i] >= 0:
            res[i] = 1 / (1 + np.exp(-inX[i]))
        else:
            res[i] = np.exp(inX[i]) / (1 + np.exp(inX[i]))
    return res


# 获取数据
def loadDataSet():
    """这里数据集使用python list存储"""
    dataMat = []
    labelMat = []
    # 一行一行读取文本,获取数据,读到空串的时候就是EOF
    f_obj = open('testSet.txt')
    lines = f_obj.readlines()
    for line in lines:
        line = line.strip()  # 去掉末尾换行符
        line_splited_list = line.split()
        labelMat.append(int(line_splited_list[-1]))
        dataMat.append([1.0, float(line_splited_list[0]), float(line_splited_list[1])])  # 注意不是用extend,dataMat每个元素就是样本列表
    return dataMat, labelMat


# 使用梯度下降法 训练数据 得到最佳参数
def gradDscent(dataMatIn, labelMatIn):
    # 将数据转换成numpy ndarray
    dataMatrix = np.mat(dataMatIn)
    labelMat = np.mat(labelMatIn).transpose()  # 转置
    m, n = dataMatrix.shape
    alpha = 0.001  # 迭代步长/学习率
    maxItNum = 500  # 最大迭代次数
    weights = np.ones((n, 1))  # 初始化参数为全1,是列向量
    for i in range(maxItNum):
        grad = -((labelMat - sigmoid(dataMatrix * weights)).transpose() * dataMatrix).transpose()
        weights = weights - alpha * grad
    return weights


w = gradDscent(*loadDataSet())
print(w)

结果

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

画出决策边界

def plotBestFit(weights, dataSet, labels):
    # 先划分dataSet中属于不同类的点
    weights = weights.A
    dataMat = np.mat(dataSet)
    lablesArr = np.array(labels)
    aux_1 = lablesArr == 1
    aux_2 = ~aux_1
    class_1_cord1 = (dataMat[aux_1, :][:, 1]).A;
    class_1_cord2 = dataMat[aux_1, :][:, 2].A;
    class_2_cord1 = dataMat[aux_2, :][:, 1].A;
    class_2_cord2 = dataMat[aux_2, :][:, 2].A;
    fig = plt.figure(1)  # 画布对象
    ax = fig.add_subplot(111)  # 坐标对象
    # 画散点图
    ax.scatter(class_1_cord1, class_1_cord2, s=20, c='b', marker='.')
    ax.scatter(class_2_cord1, class_2_cord2, s=20, c='r', marker='v')
    # 画直线
    x = np.arange(-3, 3, 0.1)
    y = -(weights[1]*x + weights[0])/weights[2]
    plt.plot(x,y,'-g')
    plt.show()


plotBestFit(w, *loadDataSet())

python实现 logistic 回归 二分类算法 (通俗讲解逻辑回归本质与由来)_第3张图片

报错记录

  1. sigmoid 函数出现:python overflow encountered in exp。

这是由于如果x的值是负数,且绝对值非常大,那么exp(-x)会非常大,就会出现溢出的现象。
解决办法: 当x<0时,对sigmoid函数分子分母同乘以exp(-x),函数值不变。
注意: 上面sigmoid函数是可以处理向量和矩阵的,就是对向量的每一个分量求函数值,所以判断的时候必须一个值一个值判断。

  1. plot只接受一维的x,y。所以传入列向量(是matrix)会报错。可以用mat.A 或者mat.getA()将mat装换成ndarray。
  2. numpy中 numpy.ndarray和numpy.matrix是不一样的!
  3. suqueeze函数或者方法,可以去掉ndarray中维数是1的那个多余的维度。对matrix不管用!!
  4. matrix must be 2-dimensional numpy中matrix类型数据只能是2维的。
  5. 一个数组的元素全是逻辑值,对所有元素取否定不能前面加 -,而是加 ~

你可能感兴趣的:(机器学习实战个人笔记,逻辑回归,python,回归)