Logistics Regression 逻辑回归及Python代码

徘徊边缘 提交于 2021-02-12 11:05:02

  逻辑回归(Logistics Regression)是广义线性模型中的一种,其取值为0或1,服从伯努利分布。而伯努利家族的正则响应函数就是sigmoid函数,因此逻辑回归为什么选用sigmoid函数的理论原因。同时,sigmoid函数好处有:

  1. 将现行分类器的响应值 <w , x> (内积) 映射到一个概率上;

  2. 将实域上的数映射到P(y=1|w,x)上,满足逻辑回归的要求。

  逻辑回归可以用于二分类问题,只能解决线性可分的情况,不能用于线性不可分。

  对于输入向量X,其属于y=1的概率为:

  $P(y=1|X,W)=h(X)=\frac{1}{1+{{e}^{-WX}}}$

其属于y=0的概率为:

  $P(y=0|X,W)=1-P(y=0|X,W)=1-h(X)=\frac{{{e}^{-WX}}}{1+{{e}^{-WX}}}$

对于逻辑回归函数,其属于y的概率为:

  $P(y|X,W)=h{{(X)}^{y}}\cdot {{(1-h(X))}^{1-y}}.$

  逻辑回归模型需要求得参数向量W,可以使用极大似然估计求解。假设有m个样本,则似然函数为:

  \[{{\text{L}}_{\text{W}}}=\prod\limits_{i=1}^{m}{\left[ h{{({{X}_{i}})}^{{{y}_{i}}}}\cdot {{(1-h({{X}_{i}}))}^{1-{{y}_{i}}}} \right]}\]

取对数可得损失函数:

  ${{l}_{W}}=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}\log (h({{X}_{i}}))+(1-{{y}_{i}})\log (1-h({{X}_{i}})) \right]}$

根据极大似然估计的理论,要使得参数最优,则似然函数需要最大,因此上式需要求最大值:  $\underset{W}{\mathop{\max }}\,\text{ }{{l}_{W}}$

  逻辑回归损失函数是一个凸函数,其凸优化求解方法有很多,如梯度下降、随机梯度下降、牛顿法等。

   逻辑函数的损失函数进一步化简过程如下:

${{l}_{W}}=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}\log (h({{X}_{i}}))+(1-{{y}_{i}})\log (1-h({{X}_{i}})) \right]}$

$\text{   }=\text{ }\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}(\log (h({{X}_{i}})-\log (1-h({{X}_{i}}))))+\log (1-h({{X}_{i}})) \right]}$

$\text{ }=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}\log (\frac{h({{X}_{i}})}{1-h({{X}_{i}})})+\log (1-h({{X}_{i}})) \right]}$

$\text{ }=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}\log (\frac{1}{{{e}^{-W{X}_{i}}}})+\log (\frac{{{e}^{-W{X}_{i}}}}{1+{{e}^{-W{X}_{i}}}}) \right]}$

$\text{ }=\sum\limits_{i=1}^{m}{\left[ -{{y}_{i}}\log ({{e}^{-W{X}_{i}}})+\log (\frac{1}{1+{{e}^{W{X}_{i}}}}) \right]}$

$\text{ }=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}W{X}_{i}-\log (1+{{e}^{W{X}_{i}}}) \right]}$

其中,第四步到第五步的化简,上下同除$e^{W{X}_{i}}$。

  对损失函数求导,可得到其梯度,过程如下:

$\frac{\partial {{l}_{W}}}{\partial W}=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}W{{X}_{i}}-\log (1+{{e}^{W{{X}_{i}}}}) \right]}$

$=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}{{X}_{i}}-\frac{{{e}^{W{{X}_{i}}}}\cdot {{X}_{i}}}{1+{{e}^{W{{X}_{i}}}}} \right]}$

$=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}{{X}_{i}}-{{X}_{i}}\cdot \frac{{{e}^{W{{X}_{i}}}}}{1+{{e}^{W{{X}_{i}}}}} \right]}=\sum\limits_{i=1}^{m}{\left[ {{y}_{i}}{{X}_{i}}-{{X}_{i}}\cdot \frac{1}{1+{{e}^{-W{{X}_{i}}}}} \right]}$

$\text{=}\sum\limits_{i=1}^{m}{\left[ {{X}_{i}}({{y}_{i}}-P(y|X,W)) \right]}$

  由上式可以看出,逻辑回归的参数W的梯度是每个样本标签值与其计算值的偏差乘以该样本的对应值,然后对所有样本累计求和,如下图所示。

   逻辑回归的Python代码如下:

 

# -*- coding: utf-8 -*-
"""
Created on Wed Jan 17 13:33:28 2018

@author: zhang
"""

##针对梯度上升求最大似然估计,可先设置大一点的步长与0.02, 求取一个初步的最优解, 然后设置小一点的步长,进一步求取精度更高的参数

import numpy as np
import time 
from sklearn.datasets import load_breast_cancer
from sklearn.cross_validation import train_test_split

def load_data ():
     dataSet = load_breast_cancer()
     
     return dataSet.data, dataSet.target
     
     
def sigmoid (X):
     return 1.0 / (1.0 + np.exp(-X))

def LR_train (train_x, train_y, maxCycle, alpha):
     
     numSamples, numFeatures = np.shape(train_x)
     weights = np.ones((numFeatures, ))  
     
#     需要注意 np.ones((numFeatures, )) 和 np.ones((numFeatures, 1)) 的区别,虽然其数值是一样的,但是索引不同
#     此处选择第一种是为了与数据集中的形式匹配
     
     for i in range(maxCycle):
          
          #梯度下降,用到所有样本, 通过所有样本找到梯度,然后调整权重, 需要多次迭代才能收敛(实验为500次)
#          output =sigmoid(np.dot(train_x, weights))
#          err = train_y - output
#          weights = weights + alpha * np.dot(train_x.transpose(), err)
          
          # SGD  随机梯度下降,每次用单个样本计算梯度方向,此处为因此用到所有样本计算梯度,收敛快,迭代次数少(实验为10次)。因样本少,所以
          # 两者计算时间相差不大,当数据量大时,SGD理论收敛要快计算时长要少
          for i in range(numSamples):
               output =sigmoid(np.dot(train_x[i,:], weights))
               err = train_y[i,] - output
               weights = weights + alpha * np.dot(train_x[i,:].transpose(), err)   
          
     return weights


def LR_test (test_x, test_y, weights):
     numSamples, numFeatures = np.shape(test_x)
     count = 0
     for i in range(numSamples):   
          if sigmoid(np.dot(test_x[i,:], weights)) > 0.5:  
               predict = 1
          else: 
               predict = 0
          if predict == test_y[i,]:
                count = count + 1
     return float(count/numSamples)

if __name__ == "__main__":
     data, label = load_data()  
     startTime = time.time()       
     train_x, test_x , train_y, test_y=train_test_split(data, label,test_size=0.25, random_state=33)
        
     weights = LR_train (train_x, train_y, 10, 0.01) 

     accuracy = LR_test (test_x, test_y, weights)
     
     print("准确率:"+ str(accuracy))
     print("计算时长:" +str(time.time() - startTime)) 
     

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!