编程语言
首页 > 编程语言> > LogisticRegression -逻辑回归/对率回归与三种优化算法(梯度下降/牛顿/拟牛顿与sklearn实现

LogisticRegression -逻辑回归/对率回归与三种优化算法(梯度下降/牛顿/拟牛顿与sklearn实现

作者:互联网

文章目录




1. 简介

逻辑回归即考察在样本各属性值前加上一个系数后的和(类似于加权平均),通过与阈值的比较实现分类




2. 实现思路

主要分为两个步骤

2.1 迭代计算系数





a. 梯度下降法


特点

python实现

def sag(old_beta,alpha):
    
    '''
    使用梯度下降法得到更新的系数值
    
    param old_beta: 初始系数值
    param alpha: 梯度下降学习步长
    return cur_beta: 迭代结束后计算得到的系数值
    '''
    cur_beta = old_beta
    cur_lb = 0
    
    while 1:
        for j in range(num_a):
            for i in range(number):
                wtxi = np.dot(datax[i,:],old_beta.reshape(-1,1))
                hx = np.exp(wtxi)/(1+np.exp(wtxi))    # 2.2.2
                cur_beta[0,j]-= alpha/number*(hx-datay[i])*datax[i,j]    # 2.2.1
    
    # 计算代价函数,判断迭代终止条件
        lb = 0
        for i in range(number):
            wtxi_cur = np.dot(datax[i,:],cur_beta.reshape(-1,1)) 
            # 即当前迭代后beta对number号样本的预测值
            lb += -datay[i]*wtxi_cur + np.log(1+np.exp(wtxi_cur))
        if np.abs(cur_lb-lb)<1e-5:
            return cur_beta
        old_beta = cur_beta
        cur_lb = lb
        # 不满足结束条件,继续进行迭代


b. 牛顿迭代法

思路

特点

def newton_cg(old_beta):
    
    '''
    使用牛顿迭代法得到系数 beta
    param old_beta: 系数的初始值
    return cur_beta: 迭代结束得到的系数值
    '''
    
    beta_d1 = 0    # 一阶导
    beta_d2 = 0    # 二阶导(Hessian矩阵
    cur_lb = 0    # 代价损失函数(用于判断迭代是否终止
    
    while 1:
        
        for i in range(number):
            wtxi = np.dot(datax[i,:],old_beta.reshape(-1,1))
            # 即当前beta对number号样本的预测值
            p1 = np.exp(wtxi)/(1+np.exp(wtxi))   # 2.2.4
            beta_d1 -= datax[i,:].reshape(-1,1)*(datay[i]-p1)    # 2.2.2
            beta_d2 += np.dot(datax[i,:].reshape(-1,1),datax[i,:].reshape(1,-1))*p1*(1-p1)    # 2.2.3
        
        # 进行beta更新
        cur_beta = old_beta - np.dot(np.linalg.inv(beta_d2),beta_d1).reshape(1,-1)    # 2.2.1

        # 计算代价函数,判断迭代终止条件
        lb = 0
        for i in range(number):
            wtxi_cur = np.dot(datax[i,:],cur_beta.reshape(-1,1)) 
            # 即当前迭代后beta对number号样本的预测值
            lb += -datay[i]*wtxi_cur + np.log(1+np.exp(wtxi_cur))
        if np.abs(cur_lb-lb)<1e-5:
            return cur_beta
        old_beta = cur_beta
        cur_lb = lb
        # 不满足结束条件,继续进行迭代
            


c. 拟牛顿法(BFGS


a) 考虑使用另一不断迭代的矩阵表示黑塞矩阵逆的近似
使用 G_k 表示第k次迭代更新beta时的黑塞矩阵的逆的近似


b) 注意具体迭代时与牛顿法存在差异:拟牛顿法在迭代时使用阻尼牛顿法的思路


在每次迭代beta的时候迭代一次黑塞矩阵
在每次迭代beta的时候都要完整迭代完步长因子

def BFGS(old_beta,sigma,delta):
    
    '''
    使用拟牛顿法得到更新迭代的系数值
    param old_beta : 初始系数值
    param sigma, delta : 步长因子迭代部分调参
    return cur_beta
    '''
    
    G = np.identity(num_a)    # 使用 G 逼近黑塞矩阵的逆,初始从单位矩阵开始
    I = np.identity(num_a)
    g_k = 0    # 一阶导
    cur_lb = 0
    for i in range(number):    # 计算迭代开始前用到的 g_k
            wtxi = np.dot(old_beta,datax[i,:].reshape(-1,1))
            p1 = np.exp(wtxi)/(1+np.exp(wtxi))
            g_k -= datax[i,:].reshape(-1,1)*(datay[i]-p1)
            
            
    while 1:
        cur_beta = old_beta-np.dot(G,g_k).reshape(1,-1)    # 默认步长为1时的迭代
        
        # 寻找最佳步长因子
        for m in range(30):
            lam = np.power(delta,m)
            beta_new = old_beta-lam*np.dot(G,g_k).reshape(1,-1)
            left = 0
            right = 0
            for i in range(number):    # armijo搜索
                wtxi_new = np.dot(beta_new,datax[i,:].reshape(-1,1))
                wtxi_old = np.dot(old_beta,datax[i,:].reshape(-1,1))
                left += -datay[i]*wtxi_new + np.log(1+np.exp(wtxi_new))
                right += -datay[i]*wtxi_old + np.log(1+np.exp(wtxi_old))
            if left<= right+sigma*np.dot(beta_new,g_k.reshape(-1,1)):
                cur_beta = beta_new
                break
       
        # 计算 g_k+1
        g_k1 = 0
        for i in range(number):
            wtxi = np.dot(cur_beta,datax[i,:].reshape(-1,1))
            p1 = np.exp(wtxi)/(1+np.exp(wtxi))
            g_k1 -= datax[i,:].reshape(-1,1)*(datay[i]-p1)
        # 判断迭代结束条件
        lb = 0
        for i in range(number):
            wtxi_cur = np.dot(datax[i,:],cur_beta.reshape(-1,1)) 
            # 即当前迭代后beta对number号样本的预测值
            lb += -datay[i]*wtxi_cur + np.log(1+np.exp(wtxi_cur))
        if np.abs(cur_lb-lb)<1e-5:
            return cur_beta
            
        # 更新黑塞矩阵的近似矩阵
        yk = g_k1-g_k
        sk = old_beta-cur_beta
        cur_lb = lb
        G = np.dot(np.dot((I-np.dot(sk.reshape(-1,1),yk.reshape(1,-1))/np.dot(yk.reshape(1,-1),\
                                                                                 sk.reshape(-1,1))),G),\
                   (I-np.dot(yk,sk.reshape(1,-1)/np.dot(yk.reshape(1,-1),sk.reshape(-1,1)))))+\
            np.dot(sk.reshape(-1,1),sk.reshape(1,-1))/np.dot(yk.reshape(1,-1),sk.reshape(-1,1))



2.2 Sigmoid 函数转化


考虑到此时计算得到的结果本身是不能用作判断的
考虑一个 Sigmoid 函数将计算出的预测值转化为 0-1 之间的数字

def predict(beta,test_data):
    '''
    结合sigmoid函数对测试数据进行预测
    param beta: 即计算出的系数集
    param test_data: 即需要进行预测的数据,每行为一个样本,ndarray形式,注意此时最后一列并没有置1
    return result: 一个长为待预测样本数的列表,记录分类结果 0/1
    '''
    
    num,num_a = test_data.shape()
    result = [0]*num
    for i in range(num):
        for j in range(num_a):
            result[i] += test_data[i,j]*beta[j]
        result[i] = 1/1+np.exp(-result[i])
        if result[i]>0.5:
            result[i] = 1
        else:
            result[i] = 0
            
    return result


3. 数据尝试


密度 含糖率 好瓜
0.697 0.460 1
0.774 0.376 1
0.634 0.264 1
0.608 0.318 1
0.556 0.215 1
0.403 0.237 1
0.481 0.149 1
0.437 0.211 1
0.666 0.091 0
0.243 0.267 0
0.245 0.057 0
0.343 0.099 0
0.639 0.161 0
0.657 0.198 0
0.360 0.370 0
0.593 0.042 0
0.719 0.103 0
# 预先载入与处理数据
# 此时使用西瓜数据集 3.0a

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time 

data = pd.read_csv('data+wm3a.csv')
datax = np.array(data.iloc[:,1:].copy())    #注意第一列为编号
datax[:,-1]=1    # 处理为 x^
datay = np.array(data.iloc[:,-1].copy())

number,num_a = datax.shape
# number:样本数
# num_a: 属性个数+1

inibeta = np.zeros((1,num_a))
inibeta[0,-1]=1
# 初始化系数

alpha = 0.5
sigma = 0.3
delta = 0.5

# 梯度下降法
start = time.time()
old_beta = inibeta
beta_sag = sag(inibeta,alpha)
end = time.time()
print('beta_sag:',beta_sag)
print('sag cost time:',end-start)

# 牛顿迭代法
start = time.time()
old_beta = inibeta
beta_newton = newton_cg(inibeta)
end = time.time()
print('\nbeta_newton:',beta_newton)
print('newton cost time:',end-start)

# BFGS法
start = time.time()
old_beta = inibeta
beta_BFGS = BFGS(inibeta,sigma,delta)
end = time.time()
print('\nbeta_BFGS:',beta_BFGS)
print('BFGS cost time:',end-start)

输出结果

beta_sag: [[ 3.60304639 11.98140328 -4.5915188 ]]
sag cost time: 5.659311532974243

beta_newton: [[ 2.97700622 12.75249236 -4.36482012]]
newton cost time: 0.06464886665344238

beta_BFGS: [[ 4.14063382 11.7053009  -4.85396699]]
BFGS cost time: 0.8432471752166748
# 作图表示
for i in range(17):
    if i<8:
        plt.plot(datax[i,0],datax[i,1],'*r')
        continue
    plt.plot(datax[i,0],datax[i,1],'+b')

listx = np.arange(0,1,0.002)
listy = np.arange(0,0.6,0.0012)
X,Y = np.meshgrid(listx,listy)

def predict_draw(beta,x,y):
    result = 0
    result+= beta[0,0]*x+beta[0,1]*y+beta[0,2]
    return result

# 用不同计算结果展示一下
Z = predict_draw(beta_BFGS,X,Y)
cline = plt.contour(X,Y,Z,[0.5],colors = 'orange')
plt.clabel(cline)

plt.xlabel('density')
plt.ylabel('sugar ratio')




4. sklearn 实现

参考链接


4.1 参数介绍

from sklearn import linear_model
# linear_model.LogisticRegression


4.2 常用调用方法

from sklearn import linear_model
clf = linear_model.LogisticRegression(solver = 'lbfgs',tol = 1e-5, C = 1e5)
clf.fit(datax[:,:-1],datay)

Z = clf.predict(datax[:,:-1])

此时 Z 直接是分类结果 0/1
但这里得到的分类结果并不是很好:

[1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0]


——————————————————————————
*存在不足欢迎指正

转载请保留作者信息

Blacksheep103221 发布了1 篇原创文章 · 获赞 0 · 访问量 32 私信 关注

标签:LogisticRegression,old,迭代,回归,牛顿,beta,xi,np,2.2
来源: https://blog.csdn.net/weixin_44914063/article/details/104512292