Press "Enter" to skip to content

逻辑回归(Logistic Regression)

逻辑回归(Logistic Regression)

 

线性模型

 

逻辑回归,本质是一种线性模型 y = w x + b y=wx+b w x + b 。

线性模型的来龙去脉,请参考:《神经网络的结构》。

不过逻辑回归和线性模型不同的是,为了实现二分类任务,加了 s i g m o d sigmod s i g m o d 函数。

这个不仅要满足映射到 [0,1],还要满足:当 x = 0 时;y 正好为 0.5;且 x = 负无穷 和 x = 正无穷的时候,y 分别趋近于 0 和 1。
s i g m o i d sigmoid s i g m o i d 是连续可导函数,为我们计算带来了方便。
s i g m o i d sigmoid s i g m o i d 是最简单的满足这样条件的函数,更“自然”,数学上的解释 —— 满足最大似然估计的结果

s i g m o i d sigmoid s i g m o i d 函数,使得预测的结果从样本成绩变成了样本成绩的概率。

样本成绩: y = w x + b y=wx+b w x + b
样本成绩的概率: p = σ ( y ) = 1 1 + e w x + b p=\sigma(y) =\frac{1}{1+e^{wx+b}} σ ( y ) = 1 + e w x + b 1 ​

如果这个概率 p > 0.5 p > 0.5 0.5 ,把这个数据分类为 y = 1 y=1 1 (正样本)。

 

如果这个概率 p < 0.5 p < 0.5 0.5 ,就把这个数据分类为 y = 0 y=0 0 (负样本)。

 

损失函数

 

这个损失函数的意义是模型预测出来的值 p p p 要和样本的标签值 y y y 尽量一致,即正样本的预测值要尽量为正,负样本要为负,否则会产生一个损失值。

 

按照 y 、 p y、p y 、 p 之间的关系,我们反过来设计:

如果样本的标签值 y = 1 y=1 1 ,模型预测出来的值 p 越小 p 越小 p 越小 ,损失值越大
如果样本的标签值 y = 0 y=0 0 ,模型预测出来的值 p 越大 p 越大 p 越大 ,损失值越小

用数学语言表达:

− l o g ( p )      i f      y = 1 -log(p)~~~~if~~~~y=1 − l o g ( p )         i f         y = 1
− l o g ( 1 − p )      i f      y = 0 -log(1-p)~~~~if~~~~y=0 − l o g ( 1 − p )         i f         y = 0

函数图像:

 

 

p p p

 

接近 1 时,损失函数接近正无穷,会分为正样本,但实际值却是负样本。

 

所以,我们会给一个正无穷的惩罚,直到 p p p 的减小,惩罚值会越来越低,直到当 p = 0 p=0 0 时,会分为负样本,实际值也是负样本,就没有惩罚。

 

不过目前这个损失函数是分类函数形式,我们需要合并为一个函数:

L ( p , y ) = − y   l o g ( p ) − ( 1 − y )   l o g ( 1 − p ) ) L(p, y)=-y~log(p) – (1-y)~ log(1-p)) L ( p , y ) = − y   l o g ( p ) − ( 1 − y )   l o g ( 1 − p ))

这是对单个训练样本(一张图)的损失函数,但我们输入都神经网络的通常是一个训练集(大量的图片)。

 

我们需要累加单个训练样本的结果,再求平均值:

1 m ∑ i = 1 m L ( p i , y i ) \frac{1}{m}\sum_{i=1}^{m}L(p_{i}, y_{i}) ∑ i = 1 m ​ L ( p i ​ , y i ​ )

为什幺使用这个损失函数呢?

 

这个损失函数(逻辑回归)的函数图像是一个向下凸的图形。

 

因为学习,就是找到一组 w 和 b,使这个损失函数最小。

 

也就是在这个函数图像的底部找到一组 w 和 b。

 

这个损失函数没有局部最优解,只存在唯一的全局最优解。

 

我们要做的,是求出成本函数的梯度,而后运用梯度下降法求出损失最小的一组 w 和 b。

 

损失函数的梯度

 

损失函数 L o s s ( p , y ) Loss(p, y) L oss ( p , y ) ,是指对单个样本的做的损失。

 

成本函数 J ( θ ) J(\theta) J ( θ ) ,是数据集上总的成本和损失。

J ( θ ) = − 1 m ∑ m i = 1 y i l o g ( σ ( X b i θ ) ) + ( 1 − y i ) l o g ( 1 − σ ( X b i θ ) ) J(\theta)=-\frac{1}{m}\sum_{m}^{i=1}y_{i}log(\sigma(X^{i}_{b}\theta))+(1-y_{i})log(1-\sigma(X^i_{b}\theta)) J ( θ ) = − ∑ m i = 1 ​ y i ​ l o g ( σ ( X b i ​ θ )) + ( 1 − y i ​ ) l o g ( 1 − σ ( X b i ​ θ ))

求损失函数的梯度: Δ J = { ∂ J ( θ ) ∂ θ 0 ∂ J ( θ ) ∂ θ 1 ⋅ ⋅ ⋅ ∂ J ( θ ) ∂ θ n } \Delta J= \begin{Bmatrix} \frac{\partial J (\theta)}{\partial\theta_{0}} \\ \frac{\partial J (\theta)}{\partial\theta_{1}} \\ ··· \\ \frac{\partial J (\theta)}{\partial\theta_{n}} \end{Bmatrix} Δ J = ⎩ ⎨ ⎧ ​ ∂ θ 0 ​ ∂ J ( θ ) ​ ∂ θ 1 ​ ∂ J ( θ ) ​ ⋅⋅⋅ ∂ θ n ​ ∂ J ( θ ) ​ ​ ⎭ ⎬ ⎫ ​

 

比较难处理的就是 σ ( t ) \sigma(t) σ ( t ) :

σ ( t ) = 1 1 + e − t = ( 1 + e − t ) − 1 \sigma(t)=\frac{1}{1+e^{-t}}=(1+e^{-t})^{-1} σ ( t ) = 1 + e − t 1 ​ = ( 1 + e − t ) − 1

对 σ ( t ) \sigma(t) σ ( t ) 求导:

σ ( t ) ′ = − ( 1 + e − t ) − 2 ⋅ e − t ⋅ ( − 1 ) = ( 1 + e − t ) − 2 ⋅ e − t \sigma(t)’=-(1+e^{-t})^{-2}·e^{-t}·(-1)=(1+e^{-t})^{-2}·e^{-t} σ ( t ) ′ = − ( 1 + e − t ) − 2 ⋅ e − t ⋅ ( − 1 ) = ( 1 + e − t ) − 2 ⋅ e − t

对 l o g   σ ( t ) log~\sigma(t) l o g   σ ( t ) 求导:

l o g   σ ( t ) = 1 σ ( t ) ⋅ σ ( t ) ′ = 1 σ ( t ) ⋅ ( 1 + e − t ) − 2 ⋅ e − t log~\sigma(t)=\frac{1}{\sigma(t)}·\sigma(t)’=\frac{1}{\sigma(t)}·(1+e^{-t})^{-2}·e^{-t} l o g   σ ( t ) = ⋅ σ ( t ) ′ = ⋅ ( 1 + e − t ) − 2 ⋅ e − t
l o g   σ ( t ) = 1 ( 1 + e − t ) − 1 ⋅ ( 1 + e − t ) − 2 ⋅ e − t log~\sigma(t)=\frac{1}{(1+e^{-t})^{-1}}·(1+e^{-t})^{-2}·e^{-t} l o g   σ ( t ) = ( 1 + e − t ) − 1 1 ​ ⋅ ( 1 + e − t ) − 2 ⋅ e − t
l o g   σ ( t ) = ( 1 + e − t ) − 1 ⋅ e − t log~\sigma(t)=(1+e^{-t})^{-1}·e^{-t} l o g   σ ( t ) = ( 1 + e − t ) − 1 ⋅ e − t
l o g   σ ( t ) = e − t 1 + e − t = 1 + e − t − 1 1 + e − t = 1 − 1 1 + e − t = 1 − σ ( t ) log~\sigma(t)=\frac{e^{-t}}{1+e^{-t}}=\frac{1+e^{-t}-1}{1+e^{-t}}=1-\frac{1}{1+e^{-t}}=1-\sigma(t) l o g   σ ( t ) = 1 + e − t e − t ​ = 1 + e − t 1 + e − t − 1 ​ = 1 + e − t 1 ​ = σ ( t )

成本函数 J J J 分成了俩段,先对前段求导:

前半段求导结果 : d ( y i   l o g   σ ( X b i θ ) ) d θ j = y i ( 1 − σ ( X b i θ ) ) ⋅ X j i \frac{d(y_{i}~log~\sigma(X^{i}_{b}\theta))}{d\theta_{j}}=y_{i}(1-\sigma(X^{i}_{b}\theta))·X^{i}_{j} d θ j ​ d ( y i ​   l o g   σ ( X b i ​ θ )) ​ = y i ​ ( 1 − σ ( X b i ​ θ )) ⋅ X j i ​

对后段求导:

l o g ( 1 − σ ( t ) ) ′ = 1 1 − σ ( t ) ⋅ ( − 1 ) ⋅ σ ( t ) ′ = − 1 1 − σ ( t ) ⋅ ( 1 + e − t ) − 2 ⋅ e − t log(1-\sigma(t))’=\frac{1}{1-\sigma(t)}·(-1)·\sigma(t)’=-\frac{1}{1-\sigma(t)}·(1+e^{-t})^{-2}·e^{-t} l o g ( 1 − σ ( t ) ) ′ = 1 − σ ( t ) 1 ​ ⋅ ( − 1 ) ⋅ σ ( t ) ′ = − 1 − σ ( t ) 1 ​ ⋅ ( 1 + e − t ) − 2 ⋅ e − t

其中: − 1 1 − σ ( t ) = 1 1 + e − t 1 + e − t − 1 1 + e − t = 1 + e − t e − t -\frac{1}{1-\sigma(t)}=\frac{1}{\frac{1+e^{-t}}{1+e^{-t}}-\frac{1}{1+e^{-t}}}=\frac{1+e^{-t}}{e^{-t}} − 1 − σ ( t ) 1 ​ = 1 + e − t 1 + e − t ​ − 1 + e − t 1 ​ 1 ​ = e − t 1 + e − t ​

 

代入: l o g ( 1 − σ ( t ) ) ′ = 1 + e − t e − t ⋅ ( 1 + e − t ) − 2 ⋅ e − t = − ( 1 + e − t ) − 1 = − σ ( t ) log(1-\sigma(t))’=\frac{1+e^{-t}}{e^{-t}}·(1+e^{-t})^{-2}·e^{-t}=-(1+e^{-t})^{-1}=-\sigma(t) l o g ( 1 − σ ( t ) ) ′ = e − t 1 + e − t ​ ⋅ ( 1 + e − t ) − 2 ⋅ e − t = − ( 1 + e − t ) − 1 = − σ ( t )

后半段的求导结果 : d ( 1 − y i )   l o g ( 1 − σ ( X b i θ ) ) d θ j = ( 1 − y i ) ⋅ ( − σ ( X b i θ ) ) ⋅ X j i \frac{d(1-y_{i})~log(1-\sigma(X^{i}_{b}\theta))}{d\theta_{j}}=(1-y_{i})·(-\sigma(X^{i}_{b}\theta))·X^{i}_{j} d θ j ​ d ( 1 − y i ​ )   l o g ( 1 − σ ( X b i ​ θ )) ​ = ( 1 − y i ​ ) ⋅ ( − σ ( X b i ​ θ )) ⋅ X j i ​

对成本函数 J ( θ ) J(\theta) J ( θ ) 求导,得先把俩段导数相加:

前半段: y i X j i − y i σ ( X b i θ ) ⋅ X j i y_{i}X^{i}_{j}-y_{i}\sigma(X^{i}_{b}\theta)·X^{i}_{j} y i ​ X j i ​ − y i ​ σ ( X b i ​ θ ) ⋅ X j i ​
后半段: − σ ( X b i θ ) ⋅ X j i + y i σ ( X b i θ ) ⋅ X j i -\sigma(X^{i}_{b}\theta)·X^{i}_{j}+y_{i}\sigma(X^{i}_{b}\theta)·X^{i}_{j} − σ ( X b i ​ θ ) ⋅ X j i ​ + y i ​ σ ( X b i ​ θ ) ⋅ X j i ​

相加结果:

y i X j i − σ ( X b i θ ) ⋅ X j i = ( y i − σ ( X b i θ ) ) ⋅ X j i y_{i}X^{i}_{j}-\sigma(X^{i}_{b}\theta)·X^{i}_{j}=(y_{i}-\sigma(X^{i}_{b}\theta))·X^{i}_{j} y i ​ X j i ​ − σ ( X b i ​ θ ) ⋅ X j i ​ = ( y i ​ − σ ( X b i ​ θ )) ⋅ X j i ​

对某个 θ j \theta_{j} θ j ​ 求导,消去负号,结合相加结果,得出:

J ( θ ) θ j = 1 m ∑ i = 1 m ( σ ( X b i θ ) − y i ) X j i \frac{J(\theta)}{\theta_{j}}=\frac{1}{m}\sum_{i=1}^{m}(\sigma(X^{i}_{b}\theta)-y_{i})X^{i}_{j} θ j ​ J ( θ ) ​ = ∑ i = 1 m ​ ( σ ( X b i ​ θ ) − y i ​ ) X j i ​

其中的 σ ( X b i θ ) \sigma(X^{i}_{b}\theta) σ ( X b i ​ θ ) 就是预测值 p   o r   y ^ p~or~\hat{y} p   or   y ^ ​ 。

J ( θ ) θ j = 1 m ∑ i = 1 m ( y i ^ − y i ) X j i \frac{J(\theta)}{\theta_{j}}=\frac{1}{m}\sum_{i=1}^{m}(\hat{y_{i}}-y_{i})X^{i}_{j} θ j ​ J ( θ ) ​ = ∑ i = 1 m ​ ( y i ​ ^ ​ − y i ​ ) X j i ​

要求 Δ J \Delta J Δ J 的梯度:

Δ J ( θ ) = { ∂ J ∂ θ 0 ∂ J ∂ θ 1 ⋅ ⋅ ⋅ ∂ J ∂ θ n } = 1 m ⋅ { ∑ i = 1 m σ ( X b i θ ) − y i ∑ i = 1 m ( σ ( X b i θ ) − y i ) ⋅ X 1 i ⋅ ⋅ ⋅ ∑ i = 1 m ( σ ( X b i θ ) − y i ) ⋅ X n i } = 1 m ⋅ X b T ⋅ ( σ ( X b θ ) − y ) \Delta J(\theta)= \begin{Bmatrix} \frac{\partial J}{\partial\theta_{0}} \\ \frac{\partial J}{\partial\theta_{1}} \\ ··· \\ \frac{\partial J}{\partial\theta_{n}} \end{Bmatrix} = \frac{1}{m}·\begin{Bmatrix} \sum_{i=1}^{m} \sigma(X^{i}_{b}\theta)-y_{i}\\ \sum_{i=1}^{m} (\sigma(X^{i}_{b}\theta)-y_{i})·X^{i}_{1} \\ ··· \\ \sum_{i=1}^{m} (\sigma(X^{i}_{b}\theta)-y_{i})·X^{i}_{n} \end{Bmatrix}=\frac{1}{m}·X^{T}_{b}·(\sigma(X_{b}\theta)-y) Δ J ( θ ) = ⎩ ⎨ ⎧ ​ ∂ θ 0 ​ ∂ J ​ ∂ θ 1 ​ ∂ J ​ ⋅⋅⋅ ∂ θ n ​ ∂ J ​ ​ ⎭ ⎬ ⎫ ​ = ⋅ ⎩ ⎨ ⎧ ​ ∑ i = 1 m ​ σ ( X b i ​ θ ) − y i ​ ∑ i = 1 m ​ ( σ ( X b i ​ θ ) − y i ​ ) ⋅ X 1 i ​ ⋅⋅⋅ ∑ i = 1 m ​ ( σ ( X b i ​ θ ) − y i ​ ) ⋅ X n i ​ ​ ⎭ ⎬ ⎫ ​ = ⋅ X b T ​ ⋅ ( σ ( X b ​ θ ) − y )

解决二分类问题:鸢尾花分类

 

编程实现:

 

import numpy as np
from .metrics import accuracy_score
class LogisticRegression:
    def __init__(self):
        """初始化Logistic Regression模型"""
        self.coef_ = None
        self.intercept_ = None
        self._theta = None
    def _sigmoid(self, t):
        return 1. / (1. + np.exp(-t))
    def fit(self, X_train, y_train, eta=0.01, n_iters=1e4):
        """根据训练数据集X_train, y_train, 使用批量-梯度下降法训练Logistic Regression模型"""
        assert X_train.shape[0] == y_train.shape[0], \
            "the size of X_train must be equal to the size of y_train"
        def J(theta, X_b, y):
            y_hat = self._sigmoid(X_b.dot(theta))
            try:
                return - np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat)) / len(y)
            except:
                return float('inf')
        def dJ(theta, X_b, y):
            return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(y)
        def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):
            theta = initial_theta
            cur_iter = 0
            while cur_iter < n_iters:
                gradient = dJ(theta, X_b, y)
                last_theta = theta
                theta = theta - eta * gradient
                if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
                    break
                cur_iter += 1
            return theta
        X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
        initial_theta = np.zeros(X_b.shape[1])
        self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)
        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]
        return self
    def predict_proba(self, X_predict):
        """给定待预测数据集X_predict,返回表示X_predict的结果概率向量"""
        assert self.intercept_ is not None and self.coef_ is not None, \
            "must fit before predict!"
        assert X_predict.shape[1] == len(self.coef_), \
            "the feature number of X_predict must be equal to X_train"
        X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
        return self._sigmoid(X_b.dot(self._theta))
    def predict(self, X_predict):
        """给定待预测数据集X_predict,返回表示X_predict的结果向量"""
        assert self.intercept_ is not None and self.coef_ is not None, \
            "must fit before predict!"
        assert X_predict.shape[1] == len(self.coef_), \
            "the feature number of X_predict must be equal to X_train"
        proba = self.predict_proba(X_predict)
        return np.array(proba >= 0.5, dtype='int')
    def score(self, X_test, y_test):
        """根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""
        y_predict = self.predict(X_test)
        return accuracy_score(y_test, y_predict)
    def __repr__(self):
        return "LogisticRegression()"

 

sklearn 调包:

 

import numpy as np
import matplotlib.pyplot as plt
 
from sklearn.datasets import load_iris                 # 导入鸢尾花数据集
from sklearn.model_selection import train_test_split   # 导入数据划分函数
from sklearn.linear_model import LogisticRegression    # 导入逻辑回归
 
# 导入评价指标
from sklearn.metrics import accuracy_score   
iris = load_iris()
 
iris_X = iris.data[:100, ]     # x有4个属性,共有100个样本,鸢尾花的label原本是3类,这里为了展示二分类,我只取了鸢尾花的前100个数据,也就是label只有0和1
iris_y = iris.target[:100, ]   # y的取值有2个,分别是0,1
model = LogisticRegression()   # 选逻辑回归作为分类器
model.fit(X_train, y_train)    # 训练模型
y_test_pred = model.predict(X=X_test)   # 预测测试集的label
 
print(y_test_pred)             # 模型预测的测试集label
print(y_test)                  # 测试集实际label 
accuracy_score(y_test, y_test_pred) # 查看模型预测的准确率

 

解决多分类问题:OvR、OvO

 

逻辑回归只能解决二分类的问题,现在我们可以通过 OvR、OvO 实现多分类。

OvR: 一对剩余所有

如下图解决四分类问题:

n n n

个类别就进行

 

n n n

 

次分类,选择分类得分最高的。

 

from sklearn.multiclass import OneVsRestClassifier
ovr = OneVsRestClassifier(log_reg)
ovr.fit(X_train, y_train)
ovr.score(X_test, y_test)

OvO:一对一

如下图解决四分类问题:

n n n

个类别就进行

 

C ( n ,   2 ) C(n,~2) C ( n ,   2 )

 

次分类,选择赢数最高的分类。

 

from sklearn.multiclass import OneVsOneClassifier
ovo = OneVsOneClassifier(log_reg)
ovo.fit(X_train, y_train)
ovo.score(X_test, y_test)

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注