AI基础:逻辑回归与梯度下降和基于逻辑回归的分类实践

逻辑回归原理

什么是逻辑回归

注意,本文里的y_pred指的是y预测值

逻辑回归是用来做分类算法的,大家都熟悉线性回归,一般形式是y_pred=aX+b,y的取值范围是[-∞, +∞],有这么多取值,怎么进行分类呢?不用担心,伟大的数学家已经为我们找到了一个方法。

也就是把Y的结果带入一个非线性变换的Sigmoid函数中,即可得到(0,1)之间取值范围的数S,S可以把它看成是一个概率值,如果我们设置概率阈值为0.5,那么S大于0.5可以看成是正样本,小于0.5看成是负样本,就可以进行分类了。

再简短说下:

  • 为啥叫逻辑回归:创造者名叫逻辑斯谛(Logistic )
  • 线性回归的输出是[-∞, +∞],通过Sigmoid函数转换成(0,1)之间的值
  • 分类模型
  • 输出目标:(0,1)之间的值
  • 模型:直观上看就是线性模型加入sigmoid函数:y_pred = sigmoid(W0 + W1*X1+…+WnXn)

Sigmoid函数

公式:
g ( z ) = 1 1 + e − z g\left( z \right) =\frac{1}{1+e^{-z}} g(z)=1+ez1
其中 z 是 wx + b,(其实就是线性模型中的预测值y,只不过这里习惯写成 z ),扩展开来 z = w0+w1x1…+wnxn

图像:

在这里插入图片描述

性质:

  • 单调性。必须单调性,保持和原线性模型输出的结果单调性的一致性。因为Sigmoid函数只是把结果压缩到(0,1)而已,并不改变结果值的走向
  • 两边梯度趋于饱和,即导数趋于0(作为激活函数在神经网络中的弊端)
  • 1/2处的导数最大
  • 设f(x)=sigmoid(x),f(x)导数为f(x)(1-f(x))
  • 不以原点为中心(作为激活函数在神经网络中的弊端)

逻辑回归的损失函数

任何一个模型都应该有个损失函数,就是描述预测值与实际值的差值的函数。

先上损失函数(这里假设样本服从伯努利分布(0-1分布)):
l o s s = − ( Σ y i log ⁡ y ^ i + ( 1 − y i ) log ⁡ ( 1 − y ^ i ) ) 其中 y ^ = s i g m o i d ( W T X ) loss=-\left( \varSigma y_i\log \hat{y}_i+\left( 1-y_i \right) \log \left( 1-\hat{y}_i \right) \right) \\ \text{其中}\hat{y}=sigmoid\left( W^TX \right) loss=(Σyilogy^i+(1yi)log(1y^i))其中y^=sigmoid(WTX)

损失函数变换过程:从极大似然估计理解逻辑回归的损失函数

逻辑回归的损失函数别名1:负对数似然损失

假设样本服从伯努利分布(0-1分布):
P ( Y ∣ X ) = { y ^ , y = 1 1 − y ^ , y = 0 } 合并成一个形式: y ^ y ( 1 − y ^ ) 1 − y 那么所有样本: Π i y ^ i y i ( 1 − y ^ i ) 1 − y i 目标函数: max ⁡ Π i y ^ i y i ( 1 − y ^ i ) 1 − y i 取 log ⁡ 并加上负号变成取最小值(方便优化): l o s s = − ( Σ y i log ⁡ y ^ i + ( 1 − y i ) log ⁡ ( 1 − y ^ i ) ) 求解参数 w ∗ ,使得 l o s s 最小化: w ∗ = a r g min ⁡ − ( Σ y i log ⁡ y ^ i + ( 1 − y i ) log ⁡ ( 1 − y ^ i ) ) 解释下相关符号: y 表示实际值; y ^ 表示预测值; w ∗ 和 a r g min ⁡ 表示当参数 w 为 w ∗ 时使得 l o s s 最小,这只是一种符号化形式 P\left( Y|X \right) =\left. \left\{ \begin{array}{c} \hat{y}, y=1\\ 1-\hat{y}, y=0\\ \end{array} \right. \right\} \\ \text{合并成一个形式:}\hat{y}^y\left( 1-\hat{y} \right) ^{1-y} \\ \text{那么所有样本:}\underset{i}{\varPi}{\hat{y}_i}^{y_i}\left( 1-\hat{y}_i \right) ^{1-y_i} \\ \text{目标函数:}\max \underset{i}{\varPi}{\hat{y}_i}^{y_i}\left( 1-\hat{y}_i \right) ^{1-y_i} \\ \text{取}\log\text{并加上负号变成取最小值(方便优化):}loss=-\left( \varSigma y_i\log \hat{y}_i+\left( 1-y_i \right) \log \left( 1-\hat{y}_i \right) \right) \\ \text{求解参数}w^*\text{,使得}loss\text{最小化:}w^*=arg\min -\left( \varSigma y_i\log \hat{y}_i+\left( 1-y_i \right) \log \left( 1-\hat{y}_i \right) \right) \\ \text{解释下相关符号:}y\text{表示实际值;}\hat{y}\text{表示预测值;} \\ w^*\text{和}arg\min\text{表示当参数}w\text{为}w^*\text{时使得}loss\text{最小,这只是一种符号化形式} P(YX)={y^,y=11y^,y=0}合并成一个形式:y^y(1y^)1y那么所有样本:iΠy^iyi(1y^i)1yi目标函数:maxiΠy^iyi(1y^i)1yilog并加上负号变成取最小值(方便优化):loss=(Σyilogy^i+(1yi)log(1y^i))求解参数w,使得loss最小化:w=argmin(Σyilogy^i+(1yi)log(1y^i))解释下相关符号:y表示实际值;y^表示预测值;wargmin表示当参数ww时使得loss最小,这只是一种符号化形式
小结一下

  • 1)样本服从什么分布:伯努利(0-1)
  • 2)损失函数的由来:伯努利分布的极大似然估计
    • 这里怎么理解呢:回到P(Y|X),这表示的是在X情况下Y的概率,在0-1分布的式子里,当然希望预测值越大越好,放到整个样本来描述就是,当x1,x2…xn情况下预测值的情况就是联合概率P(Y|x1,x2,x3…xn),就是所有概率值连乘。并且,我们仍然是希望预测值越大越好,即希望概率值连乘的结果越大越好,上述式子就能看明白了吧! 这就是极大似然函数,也是我们的目标函数。
    • 为啥加log:log是单调性的,能够和原式子保持一致的结果单调性,不会影响式子求解。连乘法加log变成加法,方便求解
    • 为啥加负号:一般求极小值更容易有优化方法,方便求解

损失函数变换过程:从交叉熵的角度理解逻辑回归的损失函数

逻辑回归的损失函数别名2:交叉熵损失

参考网上文章叙述

先引入一个概念:KL散度衡量两个概率分布的差异

逻辑回归模型最后的计算结果(通过sigmoid或softmax函数)是各个分类的概率(可以看做是各个分类的概率分布)。那么假设真实的概率分布是,估计得到的概率分布是, 这两个概率分布的距离如何去衡量?

在信息论中,「相对熵」,也就是KL散度,可以衡量两个概率分布的差异性。具体公式为:
D K L ( p ∣ ∣ q ) = Σ x p ( x ) log ⁡ p ( x ) q ( x ) = Σ x p ( x ) ( log ⁡ p ( x ) − log ⁡ q ( x ) ) = − Σ x p ( x ) log ⁡ q ( x ) − ( − Σ x p ( x ) log ⁡ p ( x ) ) = H ( p , q ) − H ( p ) 其中 H ( p , q ) = − Σ x p ( x ) log ⁡ q ( x ) 就是交叉熵, H ( p ) 是真实概率分布的信息熵 所以: K L 散度 = 交叉熵 − 真实概率分布的信息熵 \\ D_{KL}\left( p||q \right) =\underset{x}{\varSigma}p\left( x \right) \log \frac{p\left( x \right)}{q\left( x \right)}=\underset{x}{\varSigma}p\left( x \right) \left( \log p\left( x \right) -\log q\left( x \right) \right) \\ =-\underset{x}{\varSigma}p\left( x \right) \log q\left( x \right) -\left( -\underset{x}{\varSigma}p\left( x \right) \log p\left( x \right) \right) \\ =H\left( p,q \right) -H\left( p \right) \\ \text{其中}H\left( p,q \right) =-\underset{x}{\varSigma}p\left( x \right) \log q\left( x \right) \text{就是交叉熵,}H\left( p \right) \text{是真实概率分布的信息熵} \\ \text{所以:}KL\text{散度}=\text{交叉熵}-\text{真实概率分布的信息熵} DKL(pq)=xΣp(x)logq(x)p(x)=xΣp(x)(logp(x)logq(x))=xΣp(x)logq(x)(xΣp(x)logp(x))=H(p,q)H(p)其中H(p,q)=xΣp(x)logq(x)就是交叉熵,H(p)是真实概率分布的信息熵所以:KL散度=交叉熵真实概率分布的信息熵
因为交叉熵越大,KL散度越大,也可以用交叉熵来衡量两个概率分布之间的距离,所以逻辑回归使用交叉熵作为逻辑回归的损失函数。

逻辑回归损失函数求解

一个模型在一个特定样本分布上只有一个损失函数

为啥逻辑回归要用梯度下降?因为参数项太多了,无法直接求导得出最优解

先明确几个概念:

  • 1)梯度下降属于优化算法来求解逻辑回归的损失函数,牛顿法也是属于优化算法,都是迭代算法
  • 2)LR中,梯度下降求解是参数W
  • 3)梯度下降中的“梯度”针对的是损失函数loss

这里就是使用梯度下降来求解逻辑回归的损失函数的参数

梯度的定义:
g r a d i e n t ( w ) = ∂ l o s s ( w ) ∂ w w t + 1 = w t − α . g r a d i e n t ( w ) 符号解释: w t 和 w t + 1 表示进行梯度下降的前后两个值(点); α 表示学习率或者叫步长; g r a d i e n t ( w ) 在公式中就是梯度,也是学习方向或者梯度方向,就是你要往哪个方向进行学习更新参数 w ; 在式子里含义就是 w t 进行 α . g r a d i e n t ( w ) 梯度下降值后的新位置 w t + 1 gradient\left( w \right) =\frac{\partial loss\left( w \right)}{\partial w} \\ w_{t+1}=w_t-\alpha .gradient\left( w \right) \\ \text{符号解释:}w_t\text{和}w_{t+1}\text{表示进行梯度下降的前后两个值(点);} \\ \alpha \text{表示学习率或者叫步长;} \\ gradient\left( w \right) \text{在公式中就是梯度,也是学习方向或者梯度方向,就是你要往哪个方向进行学习更新参数}w; \\ \text{在式子里含义就是}w_t\text{进行}\alpha .gradient\left( w \right) \text{梯度下降值后的新位置}w_{t+1} gradient(w)=wloss(w)wt+1=wtα.gradient(w)符号解释:wtwt+1表示进行梯度下降的前后两个值(点);α表示学习率或者叫步长;gradient(w)在公式中就是梯度,也是学习方向或者梯度方向,就是你要往哪个方向进行学习更新参数w在式子里含义就是wt进行α.gradient(w)梯度下降值后的新位置wt+1

图示梯度下降样例:如图,当我要往山底走时(求最小值);我的方向四面八方,参考的东西(特征)很多,比如树的稀疏程度、草的茂盛程度、动物出没情况等等;你不清楚啥情况的时候,不知道往哪个方向,那么梯度公式就告诉你当前这一步你尝试往gradient(w)这个方向走a步(阿尔法符号)看看(梯度–>方向),每走完一步又继续确认方向再往前试探的走,直到走到谷底(求到最小值)。

在这里插入图片描述

LR中梯度法的推导:

推导流程来源于网络,但是意思能表示清楚

在这里插入图片描述

最后,带入梯度下降公式:这也就是LR的w参数关系式:
w t + 1 = w t − α ( Σ i ( f ( w , x ) − y i ) x ) w_{t+1}=w_t-\alpha \left( \underset{i}{\varSigma}\left( f\left( w,x \right) -y_i \right) x \right) wt+1=wtα(iΣ(f(w,x)yi)x)

解释下相关符号: J ( w , x ) 等价与 l o s s ( w ) ,就是损失函数的另一种写法; y _ p r e d i 等价于我上述损失函数的 y ^ ,表示的是预测值 y ; y i 就是实际值 y ; s i g m o i d ( w T x ) = s i g m o i d ( w 0 + w 1 x 1 + . . . w n x n ) , 其中 w T x 是向量的表示方式 ; 推导式子里有些有下角标有些没有 虽不严谨,但不影响推导理解,没有角标的都表示的是向量形式 \text{解释下相关符号:}J\left( w,x \right) \text{等价与}loss\left( w \right) \text{,就是损失函数的另一种写法;} \\ y\_pred_i\text{等价于我上述损失函数的}\hat{y}\text{,表示的是预测值}y; \\ y_i\text{就是实际值}y; \\ sigmoid\left( w^Tx \right) =sigmoid\left( w_0+w_1x_1+...w_nx_n \right) ,\text{其中}w^Tx\text{是向量的表示方式}; \\ \text{推导式子里有些有下角标有些没有} \\ \text{虽不严谨,但不影响推导理解,没有角标的都表示的是向量形式} 解释下相关符号:J(w,x)等价与loss(w),就是损失函数的另一种写法;y_predi等价于我上述损失函数的y^,表示的是预测值yyi就是实际值ysigmoid(wTx)=sigmoid(w0+w1x1+...wnxn),其中wTx是向量的表示方式推导式子里有些有下角标有些没有虽不严谨,但不影响推导理解,没有角标的都表示的是向量形式

逻辑斯特回归为什么要对特征进行离散化

  1. 引入非线性:逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合; 离散特征的增加和减少都很容易,易于模型的快速迭代;
  2. 速度快:稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展;
  3. 鲁棒性:离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
  4. 方便交叉与特征组合:离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力;
  5. 稳定性:特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问;
  6. 简化模型:特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。

逻辑回归应用

优缺点(特点)

  • 轻量级训练快:因为是线性的套了个sigmoid,故计算很快。
  • 易存储易扩展:稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展
  • 鲁棒性:离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
  • LR的可解释性强:参数代表每个特征对输出的影响,可解释性强
  • 因为结果是概率,可以做ranking model
  • 解决过拟合的方法很多,如L1、L2正则化
  • 具有参数记忆从而进行特征选择的用途
  • 缺点:因为它本质上是一个线性的分类器,所以处理不好特征之间相关的情况,即更喜欢特征独立的样本数据
  • 缺点:特征空间很大时,性能不好。
  • 缺点:容易欠拟合,精度不高

一般应用场景

  • 模型的baseline
  • CTR预估/推荐系统的learning to rank/各种分类场景。
  • 某搜索引擎厂的广告CTR预估基线版是LR。
  • 某电商搜索排序/广告CTR预估基线版是LR。
  • 某电商的购物搭配推荐用了大量LR。
  • 某现在一天广告赚1000w+的新闻app排序基线是LR

对于过拟合和欠拟合等优化方案

  • 梯度下降实际上就是一种优化方案,同类的还有牛顿法,这里不展开了
  • 对于过拟合和欠拟合通常使用正则化方式处理,对于正则化与逻辑回归的优化过程,后面单独开一篇详细描述

基于逻辑回归的分类示例

本示例代码取用sklearn的鸢尾花数据集,并取用标签结果为0,1的数据集来测试分类

手动实现逻辑回归

from math import exp
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# data
def create_data():
    # 加载鸢尾花数据集(sklearn中的数据集)
    iris = load_iris()
    # 通过feature_names构造dataFrame
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    # 把iris的结果放到dataFrame的label属性中
    df['label'] = iris.target
    # 声明dataFrame的新列项
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
    # 切取dataFrame前100行,index为0,1,-1的列
    data = np.array(df.iloc[:100, [0,1,-1]])
    # data[:,:2]切取所有行和前两列 ;data[:,-1]切取所有行和index=-1的那列
    return data[:,:2], data[:,-1]

class LogisticReressionClassifier:
    def __init__(self, max_iter=200, learning_rate=0.01):
        self.max_iter = max_iter
        self.learning_rate = learning_rate

    # 定义sigmoid函数
    def sigmoid(self, x):
        return 1 / (1 + exp(-x))

    def data_matrix(self, X):
        data_mat = []
        for d in X:
            # 把d所有值放到数组里并append到data_mat里。这里为啥第一项是1.0?因为我们的公式w0+w1x1+....wnxn,公式中没有x0,实际上第一项x0等同于1.0
            data_mat.append([1.0, *d])
        return data_mat

    def fit(self, X, y):
        # 获得数据矩阵,也就是我们公式中的x集合
        data_mat = self.data_matrix(X)  # m*n
        # 初始化参数w,即公式中的w0,w1,w2。为啥这里初始化全为0?因为训练过程中会逐渐根据损失函数调整参数w
        self.weights = np.zeros((len(data_mat[0]), 1), dtype=np.float32)

        # 迭代的根据损失函数优化参数w的取值
        for iter_ in range(self.max_iter):
            for i in range(len(X)):
                # 计算逻辑回归的结果
                result = self.sigmoid(np.dot(data_mat[i], self.weights))
                # 计算损失值
                error = y[i] - result
                # 根据损失函数更新参数w,其中np.transpose表示矩阵转置
                self.weights += self.learning_rate * error * np.transpose([data_mat[i]])
        print('LogisticRegression Model(learning_rate={},max_iter={})'.format(
            self.learning_rate, self.max_iter))

    # 模型得分
    def score(self, X_test, y_test):
        right = 0
        X_test = self.data_matrix(X_test)
        # zip是方便for取到每个x,y
        for x, y in zip(X_test, y_test):
            # 根据训练出来的w参数对测试数据集进行校验真实结果
            result = np.dot(x, self.weights)
            # 由于数据集只取了前100行,故y的值只有0,1,用来分类
            if (result > 0 and y == 1) or (result < 0 and y == 0):
                right += 1
        return right / len(X_test)
    
# ====================================开始训练============
# 调用函数获得样本集
X, y = create_data()
# 划分数据集为训练、测试数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

lr_clf = LogisticReressionClassifier()
# 训练
lr_clf.fit(X_train, y_train)
lr_clf.score(X_test, y_test)
# score结果打印:0.9666666666666667

画图展示

# 画图展示参数划分结果
x_ponits = np.arange(4, 8)
y_ = -(lr_clf.weights[1]*x_ponits + lr_clf.weights[0])/lr_clf.weights[2]
plt.plot(x_ponits, y_)

#lr_clf.show_graph()
plt.scatter(X[:50,0],X[:50,1], label='0')
plt.scatter(X[50:,0],X[50:,1], label='1')
plt.legend()

在这里插入图片描述

使用sklearn逻辑回归模型

# 使用SKlearn里的模型
from sklearn.linear_model import LogisticRegression
# 设置迭代次数为200得到模型
clf = LogisticRegression(max_iter=200)
# 训练(沿用手动实现中的划分数据集)
clf.fit(X_train, y_train)
# 查看得分(沿用手动实现中的划分数据集)
clf.score(X_test, y_test)
# 打印参数w;coef_和intercept_都是模型参数,即为w;intercept_为w0,coef_为w1到wn
print(clf.coef_, clf.intercept_)

score为:1.0

打印的参数w为:[[ 2.71974676 -2.60054221]] [-6.86475951]

画图展示:

# 图像化展示分类效果
x_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_ponits + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_ponits, y_)

plt.plot(X[:50, 0], X[:50, 1], 'bo', color='blue', label='0')
plt.plot(X[50:, 0], X[50:, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

在这里插入图片描述

以上,若有表述错误的地方,希望指出,谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值