机器学习|逻辑回归模型


by - YH
Date - 22/11/2019

上一篇博客主要通过代码实现了线性回归,本篇博客讲解一下逻辑回归(Logistic),主要分为两个部分,一个是基本概念原理,另外一个是基于Tensorflow对该算法进行一个代码实战。

一、基本概念简介以及理论讲解

1.1 基本概念简介以及理论讲解

  • 回归一般用于解决那些连续变量的问题,如:线性回归,它是通过最小化误差函数,来不断拟合一条直线,以计算出权重w和偏差b的过程,他的目标是基于一个连续方程去预测一个值。

  • 这里要讲解的是Logistic回归,却经常用于分类问题,也就是说将观测值贴上某个标签,或者是分入某个预先定义的类别之中。故理解常用于分类问题的 Logistic 回归的关键在于,我们是将先寻找到的该样本属于某个类可能性的连续值映射到了某一个类(我们一般将某一个类的 label 建模成离散值)。这就是 Logistic 常用于分类却叫做回归的原因。

1.2 Logistic函数的你函数->Logit函数

  • 在了解Logistic函数之前我们先来了解一下它的逆函数 Logit 函数,即对数几率函数。正如我们所了解的一样逆函数之间关于 y = x 直线对称,自变量 x 和因变量 y 互为对偶位置,因此,Logit 函数和 Logistic 函数有很多性质都有关联。

  • Logit 函数的变量需要一个概率值 p 作为自变量,如果是二分类问题,确切一点是伯努利分布(即二项分布),如果是多分类问题,则是多项分布。

1.2.1 伯努力分布

它又称为二项分布,也就是说它只能表示成功和失败两种情况。当是二分类问题时,都可用该分布。

   取1,表示成功,以概率 p 表示
   取 0,即失败,以概率 q = 1-p 表示 
   伯努利分布的概率函数可以表示为: Pr(X=1) = 1 - Pr(X=0) = 1-q = p 

  此外,Logistic 函数也是属于广义线性模型(GLM)的一种,在建立广义线性模型之前,我们还需要从线性函数开始,从独立的连续变量映射到一个概率分布。 如果是针对二分类的 Logistic 回归,由于是二分类属于二值选项问题,我们一般会将上面的概率分布建模成一个伯努利分布(即二项分布),而将上述的独立的连续变量建模成线性方程回归后的 y 值,最后再通过连接函数,这里采用对数函数Logit ,将连续的 y = wx +b 的线性连续变量映射到二项分布。
  由于Logit函数是从自变量p到函数值的映射,故逆函数Logistic 函数即上一段所讲,便可以将连续值映射到二项分布,从而用做分类问题。

1.2.2 Logit函数

  Logit 函数又称对数几率函数,其中 p 是二项分布中事件发生的概率,用发生的概率 p 除以不发生的概率 1-p, 即(p /1-p)称为事件的发生率,对其取对数,就成了对数几率函数(Logit 函数) 。

l o g i t ( p ) = l o g ( p 1 p ) logit(p)=log(\frac{p}{1-p})\, Logit

扫描二维码关注公众号,回复: 10087287 查看本文章

从图中我们可以看到,该函数实现了从区间 [ 0 , 1 ] [0,1]\, 到区间 [ , ] [-\infty,\infty]\, 之间的映射,我们只要将y用一个输入的线性函数替代,那么就实现了从输入到区间 [ 0 , 1 ] [0,1]\, 之间的映射。

1.3 对数几率函数的逆函数 Logistic 函数

  由上面的讲解可知,我们先计算对数几率函数的逆函数后得如下结果:这里我们记logistic(z)为g(z),转换之后, l o g i t 1 ( z ) logit^{-1}(z)\, 就是上面提到的二项分布发生的概率p:

l o g i t 1 ( z ) = l o g i s t i c ( z ) = g ( z ) = 1 1 + e z logit^{-1}(z)=logistic(z)=g(z)=\frac{1}{1+e^{-z}}\,

这个式子是一个Sigmoid函数,图像如下:
Sigmoid

其中,我们认为 Sigmoid 函数最为漂亮的是它的求导后形式非常简介、易用。如下:
函数: f ( z ) = 1 1 + e z f(z)=\frac{1}{1+e^{-z}}\,

倒数: f ( z ) = f ( z ) ( 1 f ( z ) ) f'(z)=f(z)(1-f(z))\,

推导比较简单,读者自行推导。

  得到上述式子之后,将z换成 θ 1 x + θ 0 \theta_{1}x+\theta_{0}\, ,可以得到逻辑回归模型的参数形式:

p ( x ; a , b ) = 1 1 + e ( θ 1 x + θ 0 ) p(x;a,b)=\frac{1}{1+e^{(-\theta_{1}x+\theta_{0})}}\,

(注:当然也可以将 z 替换成多维度的线性回归方程如: z = θ T x = θ 0 x 0 + θ 1 x 1 + . . . + θ n x n = i = 0 n θ i x i z=\theta^{T}x=\theta_{0}x_{0}+\theta_{1}x_{1}+...+\theta_{n}x_{n}=\sum_{i=0}^{n}\theta_{i}x_{i}\, )

于是可以构造如下预测函数:

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

Logistic 回归的损失函数这里采用交叉熵,此外常见的还有均方误差作为损失函数。它的定义如下:

J ( θ ) = 1 m [ y i l o g h θ ( x i ) + ( 1 y i ) l o g ( 1 h θ ( x i ) ) ] J(\theta)=-\frac{1}{m}[y^{i}logh_{\theta}(x^{i})+(1-y^{i})log(1-h_{\theta}(x^{i}))]\,

  • 参数说明:
    m:训练样本的个数
    h θ ( x ) h_{\theta}(x)\, :用参数 θ \theta 和x预测出来的y值
    y:原训练样本中的y值,也就是标准答案
    上角标(i):第i个样本;

1.4 多分类应用 — Softmax 回归

1.4.1 Softmax 函数定义

  到目前为止,我们都是针对二分类问题进行处理的,能够得到发生或者不发生的概率p。在Logistic regression二分类问题中,我们可以使用sigmoid函数将输入 W x + b Wx+b 映射到(0,1)区间中,从而得到属于某个类别的概率。将这个问题进行泛化,推广到多分类问题中,我们可以使用softmax函数,对输出的值归一化为概率值。
  我们需要将训练样本的标签 label 做出改变从二值标签到向量标签, 假设K是类的数目,y 可以取 K 个不同的值,针对它我们可以对于每个由前面 Sigmoid 函数输出的属于某一个类的概率 X 估算出 P ( y = k x ) P(y = k | x)\, 的概率,其中 k = 1 … K(即进一步将 Logistic 回归泛化,通过 Softmax 函数对输出的值做归一化为新的概率值)。它的输出将会是一个 K 维的向量,每个数值对应的是它属于当前类的概率。

  • 这里假设在进入softmax函数之前,已经有模型输出C值,其中C是要预测的类别数,模型可以是全连接网络的输出a,其输出个数为C,即输出 a 1 , a 2 , a 3 , . . . , a C a_{1},a_{2},a_{3},...,a_{C}

所以对于每个样本,它属于类别 i i 的概率为:
y i = e a i k = 1 C e a k y_i=\frac{e^{a_i}}{\sum_{k=1}^{C}e^{a_{k}}}\,

通过上式可以保证 i = 1 C y i = 1 \sum_{i=1}^{C}y_{i}=1\, ,即属于各个类别的概率和为1。

  • 求导数

对softmax函数进行求导,即求: y i a j \frac{\partial y_i}{\partial a_j}\, ,第 i i 项的输出对第 j j\, 项输入的偏导。
代入softmax函数表达式,可以得到:
y i a j = e a i k = 1 C e a k a j \frac{\partial y_i}{\partial a_j}=\frac{\partial\frac{e^{a_i}}{\sum_{k=1}^{C}e^{a_{k}}}}{\partial a_j} \,

由求导规则可知:对于 f ( x ) = g ( x ) / h ( x ) f(x)=g(x)/h(x) 的求导公式为:
f ( x ) = g ( x ) h ( x ) g ( x ) h ( x ) [ h ( x ) ] 2 f'(x)=\frac{g'(x)h(x)-g(x)h'(x)}{[h(x)]^{2}}

在上式当中, g ( x ) = e a i g(x)=e^{a_{i}} , h ( x ) = k = 1 C e a k h(x)=\sum_{k=1}^{C}e^{a_{k}}
要实现 g ( x ) g(x) a j a_j 进行求导,要分情况讨论:

  1. 如果 i = j i=j ,则求导结果为 e a i e^{a_{i}}
  2. 如果 i j i \neq j ,则求导结果为0
  3. k = 1 C e a k \sum_{k=1}^{C}e^{a_{k}} a j a_{j} 求导,结果为 e a i e^{a_{i}}
  4. 最后结合上述两者,代入求导公式
  • i j i \neq j 时:
    y i a j = e a i k = 1 C e a k a j = e a i e a i e a j 2 = e a i e a j = y i ( 1 y j ) \frac{\partial y_i}{\partial a_j}=\frac{\partial\frac{e^{a_i}}{\sum_{k=1}^{C}e^{a_{k}}}}{\partial a_j}=\frac{e^{a_{i}}\sum-e^{a_{i}}e^{a_{j}}}{\sum^{2}}=\frac{e^{a_{i}}}{\sum}\frac{\sum-e^{a_{j}}}{\sum}=y_{i}(1-y_j)
  • i = j i = j 时:

y i a j = e a i k = 1 C e a k a j = 0 e a i e a j 2 = e a i e a j = y i y j \frac{\partial y_i}{\partial a_j}=\frac{\partial\frac{e^{a_i}}{\sum_{k=1}^{C}e^{a_{k}}}}{\partial a_j}=\frac{0-e^{a_{i}}e^{a_{j}}}{\sum^{2}}=-\frac{e^{a_{i}}}{\sum}\frac{e^{a_{j}}}{\sum}=-y_{i}y_{j}
其中 = k = 1 C e a k \sum=\sum_{k=1}^{C}e^{a_{k}}

1.4.2 损失函数

  机器学习里面,对模型的训练都是对Loss function进行优化,在分类问题中,我们一般使用最大似然估计(Maximum likelihood estimation)来构造损失函数。对于输入的 x,其对应的类标签为 t t ,我们的目标是找到这样的θ使得p(t|x)最大。在二分类的问题中,我们有:

p ( t x ) = ( y ) t ( 1 y ) 1 t p(t|x)=(y)^{t}(1-y)^{1-t}

其中, y = f ( x ) y=f(x) 是模型预测的概率值,t是样本对应的类标签。
  将问题泛化为更一般的情况,多分类问题:
p ( t x ) = i = 1 C P ( t i x ) t i = i = 1 C y i t i p(t|x)=\prod_{i=1}^{C}P(t_i|x)^{t_i}=\prod_{i=1}^{C}y_{i}^{t_i}

由于连乘可能导致最终结果接近0的问题,一般对似然函数取对数的负数,变成最小化对数似然函数:

l o g p ( t x ) = l o g i = 1 C y i t i = i = 1 C t i l o g ( y i ) -log p(t|x)=-log\prod_{i=1}^{C}y_{i}^{t_i}=-\sum_{i=1}^{C}t_ilog(y_i)

1.4.3 交叉熵

经过推理可得,交叉熵和上面的对数似然函数的形式一样 !
对一个样本来说,真实类标签分布与模型预测的类标签分布可以用交叉熵来表示:

l C E = i = 1 C t i l o g ( y i ) l_{CE}=-\sum_{i=1}^{C}t_ilog(y_i)

1.4.4 Loss function 求导

对单个样本来说,loss function l C E l_CE 对输入 a j a_j 的导数为:

l C E a j = i = 1 C t i l o g ( y i ) a j = i = 1 C t i l o g ( y i ) a j = i = 1 C t i 1 y i y i a j \frac{\partial l_{CE}}{\partial a_j}=-\sum_{i=1}^{C}\frac{\partial t_{i}log{(y_i)}}{\partial a_j}=-\sum_{i=1}^{C}t_i\frac{\partial log{(y_i)}}{\partial a_j}=-\sum_{i=1}^{C}t_i\frac{1}{y_i}\frac{\partial y_i}{\partial a_j}

上面对 y i a j \frac{\partial y_i}{\partial a_j} 求导结果已经算出来了:
i = j i=j 时: y i a j = y i ( 1 y i ) \frac{\partial y_i}{\partial a_j}=y_i(1-y_i)
i j i\neq j 时: y i a j = y i y j \frac{\partial y_i}{\partial a_j}=-y_iy_j

所以将求导结果代入上式:

i = 1 C t i 1 y i y i a j = t i y i y i a i i j C y i a j = t j y i y i ( 1 y i ) i j C t i y i ( y i y j ) = t j + t j y j + i j C t i y j = t j + i = 1 C t i y j = t j + y j i = 1 C t i = y j t j -\sum_{i=1}^{C}t_{i}\frac{1}{y_i}\frac{\partial y_i}{\partial a_j} =-\frac{t_i}{y_i}\frac{\partial y_i}{\partial a_i}-\sum_{i \neq j}^{C}\frac{\partial y_i}{\partial a_j} =-\frac{t_j}{y_i}y_i(1-y_i)-\sum_{i \neq j}^{C}\frac{t_i}{y_i}(-y_iy_j) =-t_j+t_jy_j+\sum_{i \neq j}^{C}t_iy_j =-t_j+\sum_{i=1}^{C}t_iy_j =-t_j+y_j\sum_{i=1}^{C}t_i=y_j-t_j

1.5 其他相关的 Softmax 回归的一些操作

  • 其一 :正则化,正如之前提到的逻辑回归使用的梯度下降方法来最小化损失函数,但是该方法对特征数据的分布和形式非常敏感。因此,我们一般都会对数据进行预处理,以期待获得更好、更快的收敛结果。其实,数据正则化就相当平滑误差表面,使得梯度下降迭代更快地收敛到最小误差。
  • 其二 :one-hot编码,此外,我们在做 Softmax 编码的时候,可能会使用 one-hot 编码。它将一个表示类的数值,转变成一个阵列,而原表示类数值的列表编码后成为矩阵列表。对应某一列的某一个类别,为1,其他都为0,用法可以参见 tf.ont_hot API
  • Tensoflow 中的 skflow发布的目的就是以模拟 sklearn 的接口运行 TensorFlow,这样会比 TensorFlow 的会话环境中运行简化掉很多工作。他是抽象上层API接口,提供了完全类似于sklearn的API接口。使用非常简单,只要有sklearn的使用经验,构建一个模型就是简单几个步骤,模型参数配置,fit,predict等等。它已经被完全整合到了 TensorFlow 框架中了。
参考:

二、Logistic回归应用实战

2.1 逻辑回归与线性回归的对比
%matplotlib inline
print(__doc__)


# Code source: Gael Varoquaux
# License: BSD 3 clause

import numpy as np
import matplotlib.pyplot as plt

from sklearn import linear_model

# General a toy dataset:s it's just a straight line with some Gaussian noise:
xmin, xmax = -5, 5
n_samples = 100
X = np.random.normal(size=n_samples)
y = (X > 0).astype(np.float)   #X>0,y=1 X<0,y=0
X[X > 0] *= 4
X += .3 * np.random.normal(size=n_samples)

X = X[:, np.newaxis]   #X转换为矩阵

# Fit the classifier
clf = linear_model.LogisticRegression(C=1e5, solver='lbfgs')
clf.fit(X, y)

# and plot the result
plt.figure(1, figsize=(4, 3))
plt.clf()
plt.scatter(X.ravel(), y, color='black', zorder=20)
X_test = np.linspace(-5, 10, 300)


def model(x):
    return 1 / (1 + np.exp(-x))

 
loss = model(X_test * clf.coef_ + clf.intercept_).ravel()   #coef_为模型参数w,intercept_为模型参数b
plt.plot(X_test, loss, color='red', linewidth=3)

ols = linear_model.LinearRegression()
ols.fit(X, y)
plt.plot(X_test, ols.coef_ * X_test + ols.intercept_, linewidth=1)
plt.axhline(.5, color='.5')   #绘制出平行于x轴的水平线

plt.ylabel('y')
plt.xlabel('X')
plt.xticks(range(-5, 10))
plt.yticks([0, 0.5, 1])
plt.ylim(-.25, 1.25)
plt.xlim(-4, 10)
plt.legend(('Logistic Regression Model', 'Linear Regression Model'),
           loc="lower right", fontsize='small')
plt.tight_layout()
plt.show()

png

2.2 在mnist数据集上运用Softmax模型
import tensorflow as tf
import os
from tensorflow.examples.tutorials.mnist import input_data

os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

mnist=input_data.read_data_sets('mnist_data/',one_hot=True)

print('开始构建计算图:')
with tf.Graph().as_default():
    with tf.name_scope('Input'):
        X=tf.placeholder(tf.float32,shape=[None,784],name='X')
        Y_true=tf.placeholder(tf.float32,shape=[None,10],name='Y_true')
    with tf.name_scope('Inference'):
        w=tf.Variable(tf.zeros([784,10]),name='weight')
        b=tf.Variable(tf.zeros([10]),name='Bias')
        logits=tf.add(tf.matmul(X,w),b)
    with tf.name_scope('Softmax'):
        Y_pred=tf.nn.softmax(logits=logits)
    with tf.name_scope('Loss'):
        TrainLoss=tf.reduce_mean(-tf.reduce_sum(Y_true*tf.log(tf.nn.softmax(Y_pred)),axis=1))
    with tf.name_scope('Train'):
        Optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.01)
        TrainOp=Optimizer.minimize(TrainLoss)
    with tf.name_scope('Evaluate'):
        correct_prediction=tf.equal(tf.argmax(Y_pred,1),tf.argmax(Y_true,1))
        accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

    Init=tf.global_variables_initializer()

    writer=tf.summary.FileWriter(logdir='LOG_Softmax_Regression',graph=tf.get_default_graph())
    writer.flush()

    print('计算图构建完毕!在TensorBoard中查看!')

    print('开始运行计算图:')

    with tf.Session() as sess:
        sess.run(Init)
        for step in range(3000):
            batch_xs,batch_ys=mnist.train.next_batch(100)
            _,train_loss,train_w,train_b=sess.run([TrainOp,TrainLoss,w,b],feed_dict={X:batch_xs,Y_true:batch_ys})
            print('train step:',step,'Train_Loss:',train_loss)

        accuracy_score=sess.run(accuracy,feed_dict={X:mnist.test.images,Y_true:mnist.test.labels})
        print('模型预测正确率:',accuracy_score)

train step: 2986 Train_Loss: 1.7659888

train step: 2987 Train_Loss: 1.7344866
train step: 2988 Train_Loss: 1.8374661
train step: 2989 Train_Loss: 1.7589226
train step: 2990 Train_Loss: 1.7642982
train step: 2991 Train_Loss: 1.7779152
train step: 2992 Train_Loss: 1.7818173
train step: 2993 Train_Loss: 1.7704663
train step: 2994 Train_Loss: 1.7911502
train step: 2995 Train_Loss: 1.8432688
train step: 2996 Train_Loss: 1.7856534
train step: 2997 Train_Loss: 1.7969577
train step: 2998 Train_Loss: 1.8482801
train step: 2999 Train_Loss: 1.7773836
模型预测正确率: 0.794

发布了59 篇原创文章 · 获赞 25 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/m0_37570854/article/details/103215044
今日推荐