【优化理论】 梯度下降算法及其python实现——以Logistic回归为例

写在之前

  这是本人第一次编写博客,技术不是很娴熟,若造成阅读困扰十分抱歉。其次,编写博客的内容是基于研究生第一年所学课程作业而言的,部分时间已经比较久远,引用和转载上可能找不到当初借鉴的原文了,十分抱歉!编写博客的目的一方面是对自己所学有个总结和归处,另一方面在完成某些作业时受很多博客的影响,向他们学习、致敬!最后就是代码的正确性在当时是可以得到保证的,但不一定适用于所有场景,且效率上可能存在一定问题,因此仅供参考,谢谢!

实验目的

  有一组数据记录了学生复习小时数h和通过考试与否Y,如图1所示。图 1 复习时长与是否通过记录图

图 1 复习时长与是否通过记录图

  现欲找出通过考试的概率与复习时长之间的关系,即用Logistic函数(sigmod函数)模拟p(x),其中
p ( x ) = P { Y = 1 h = x } , 0 p ( x ) 1. p(x) = P\left\{Y=1|h=x\right\},0≤p(x)≤1.   Logistic函数如图2所示,其函数表示见公式(1),构造函数p(x)如公式(2)所示,实验目的转化为通过图1中的数据学习出参数θ01,得到最终的hY的关系函数Y = f (h)。

在这里插入图片描述

图 2 Logistic函数图像

L o g i s t i c : σ ( t ) = 1 1 + e t ( 1 ) p ( x ) = p ( x ; θ ) = σ ( θ 0 + θ 1 x ) = 1 1 + e ( θ 0 + θ 1 x ) ( 2 ) Logistic 函数 :σ(t)=\frac{1}{1+e^{-t}}……………………………(1)\\ p(x)=p(x;θ)=σ(θ_0+θ_1 x)=\frac{1}{1+e^{-(θ_0+θ_1 x) } }………………(2)

实验步骤

  求公式(2)中的参数θ,在数理统计里问题转化为给定样本,对参数进行估计,常用方法为极大似然估计。按以下步骤构造似然函数:
P { Y = y i x i , θ } = p ( x i ; θ ) y i ( 1 p ( x i ; θ ) ) 1 y i ( 3 ) L ( θ ) = i = 1 n P { Y = y i x i , θ } = i = 1 n p ( x i ; θ ) y i ( 1 p ( x i ; θ ) ) 1 y i . ( 4 ) P\left\{Y=y_i |x_i,θ\right\} = p(x_i;θ)^{y_i}(1-p(x_i;θ))^{1-y_i }………………(3) \\ L(θ)=∏_{i=1}^nP\left\{Y=y_i |x_i,θ\right\}=∏_{i=1}^np(x_i;θ)^{y_i } (1-p(x_i;θ))^{1-y_i }.…(4)
  对似然函数L(θ)取自然对数并化简得到公式(5)。

l ( θ ) = l o g L ( θ ) = i = 1 n y i l o g p ( x i ; θ ) + i = 1 n ( 1 y i ) l o g ( 1 p ( x i ; θ ) ) = i = 1 n y i l o g p ( x i ; θ ) 1 p ( x i ; θ ) + i = 1 n l o g ( 1 p ( x i ; θ ) ) = i = 1 n y i ( θ 0 + θ 1 x i ) i = 1 n l o g ( 1 + e θ 0 + θ 1 x i ) . ( 5 ) \begin{aligned} l(θ)=log⁡L (θ)&=∑_{i=1}^ny_i log⁡p (x_i;θ)+∑_{i=1}^n(1-y_i)log⁡( 1-p(x_i;θ))\\ &=∑_{i=1}^ny_i log⁡\frac{p(x_i;θ)}{1-p(x_i;θ)} +∑_{i=1}^nlog⁡( 1-p(x_i;θ))\\ &=∑_{i=1}^ny_i (θ_0+θ_1 x_i ) -∑_{i=1}^nlog⁡( 1+e^{θ_0+θ_1 x_i })….…(5) \end{aligned}
  现在问题转化为求公式(5)的最大值,即最优化问题,如公式(6)所示。
arg max θ l ( θ ) = i = 1 n y i ( θ 0 + θ 1 x i ) i = 1 n l o g ( 1 + e θ 0 + θ 1 x i ) ( 6 ) \mathop{\arg\max_{θ}}l(θ)=∑_{i=1}^ny_i (θ_0+θ_1 x_i)-∑_{i=1}^nlog⁡( 1+e^{θ_0+θ_1 x_i })………(6)
  该问题的等价优化问题如公式(7)所示。
arg min θ l ( θ ) = i = 1 n l o g ( 1 + e θ 0 + θ 1 x i ) i = 1 n y i ( θ 0 + θ 1 x i ) ( 7 ) \mathop{\arg\min_{θ}}-l(θ)=∑_{i=1}^nlog⁡( 1+e^{θ_0+θ_1 x_i })-∑_{i=1}^ny_i (θ_0+θ_1 x_i )………(7)
  终于,将最开始的回归问题转换成了求目标函数(公式(7))的最小值问题,可以用到我们优化理论与方法所学的梯度下降算法。让公式(7)分别对θ0,θ1求偏导,得到公式(8)(9),整个似然函数的梯度如公式(10)所示。
l θ 0 = i = 1 n [ y i p ( x i , θ ) ] ( 8 ) l θ 1 = i = 1 n [ y i p ( x i , θ ) ] x i ( 9 ) l = [ l θ 0 , l θ 1 ] T ( 10 ) \frac{∂l}{∂θ_0 }=∑_{i=1}^n[y_i-p(x_i,θ)] …………………………………(8)\\ \frac{∂l}{∂θ_1 }=∑_{i=1}^n[y_i-p(x_i,θ)]x_i …………………………………(9)\\ \nabla l =[\frac{∂l}{∂θ_0},\frac{∂l}{∂θ_1}]^T………………………………………(10)

基本实现原理

  梯度下降的思想是其基本思想是从某一个初始点x0出发,每次沿着函数值下降最快(即负梯度方向)的方向前进一小步,随着执行次数不断增加,xn 不断逼近局部极小点x*,如图3所示。

图 3 梯度下降示意图

图 3 梯度下降示意图
  因此,给出一个简单的梯度下降算法如图4所示。图中的x可理解为待求参数,这里给出的循环条件为迭代次数n小于预设的最大迭代次数N~max~ ,除了这个条件以外,我们还可以使用当前参数得到的数据值与真实数据产生的均方误差小于一个预设的非常小的值ξ,或者梯度的二泛数小于一个预设的非常小的值ε。   图中的α~n~代表每次走的步长,可以设置为固定步长,即预先设置一个常数,一般在0.01~0.3之间取值;还可以根据不同的自适应步长算法得到,如回溯线搜索。

图 4 简单梯度下降算法

图 4 简单梯度下降算法
  基于以上分析,现给出本次实验的算法,如图5所示。

图 5 Logistic回归 梯度下降算法求解算法图

图 5 Logistic回归 梯度下降算法求解算法图

  此图里的n有个小问题,图中算法步骤的第3步的Σ符号上的的n代表数据集的记录数,其他的n代表迭代次数。另外值得注意的是,此时的循环终止条件用的是梯度的二范数小于给定阈值ε。

实现代码

代码如下,也可观看我GitHub

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 27 12:09:23 2018

@author: YLC
"""
import numpy as np #用于矩阵运算
import matplotlib.pyplot as plt #用于画图
import math #用于指数运算
h = np.array([0.5,0.75,1,1.25,1.5,1.75,1.75,2,2.25,2.5,2.75,3,3.25,3.5,4,4.25,4.5,4.75,5,5.50]) #学习小时数
Y = np.array([0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1]) #是否通过标记
matrix = np.vstack((h,Y)) #原始数据
print("原始数据如下:")
for i in range(0,len(h)):
    print("h="+str(matrix[0][i])+"\t p="+str(matrix[1][i]))
n = 0 #迭代次数
arr_len = len(h) #数据量大小
theta0 = 0.5 #第一个要学习的参数
theta1 = 0.5 #第二个要学习的参数
theta = np.array([[theta0],[theta1]])#将两个参数放到参数矩阵,不保留参数的历史记录,只存当前参数值
epsilon = 0.001 # 定义阈值
print("学习前,初始参数:theta0="+str(theta[0])+", theta1="+str(theta[1]))
def p(x,theta): #定义Logistic回归函数
    return 1/(1+math.exp(-theta[0]-theta[1]*x))
def grad(x,y,theta): #定义梯度
    sum1 = sum2 = 0
    for i in range(0,arr_len):
        sum1 = sum1 + y[i]-p(x[i],theta) #相当于theta0参数沿当前梯度的变化量
        sum2 = sum2 + (y[i]-p(x[i],theta))*x[i] #相当于theta1参数沿当前梯度的变化量
    return np.array([[sum1],[sum2]])
#梯度的二范数大于或等于阈值或小于迭代次数则进入循环,小于跳出循环
while (np.linalg.norm(grad(h,Y,theta),ord=2)>=epsilon):#梯度的范数可理解为二维中y=kx+b的斜率,高维中的下降最快的坡度
    if(n >= 1000) : break #迭代超过阈值
    alpha = 0.08 #初始步长通常在0.01~0.3之间取值
    thetaT = theta #临时存放参数
    n = n + 1 #迭代次数+1
    theta = thetaT + alpha * grad(h,Y,theta) #更新参数 
    print("误差(梯度的二范数)为:",np.linalg.norm(grad(h,Y,theta),ord=2))
    #print("第"+str(n)+"次迭代,梯度的二范数为:"+str(np.linalg.norm(grad(h,Y,theta),ord=2)))
print("迭代"+str(n)+"次后结束,学习后,theta0="+str(theta[0])+",theta1="+str(theta[1]))
print("模型为:y = 1/(1+exp("+str(float(-theta[0]))+str(float(-theta[1]))+"*x))")
pp = np.array([])
for i in range(0,len(h)):
    pp = np.append(pp,p(h[i],theta)) 
pmatrix = np.vstack((h,pp))
print("将学习小时数进行输入,获得课程通过概率为:")
for i in range(0,len(h)):
    print("h="+str(pmatrix[0][i])+"\t p="+str(pmatrix[1][i]))
plt.scatter(h,pp,c = 'r',marker = 'o') 
print("\n其散点图为:")
plt.show()
''' 整个模型的图像 '''
print("整个模型的图像")
x=np.linspace(0,5,1000)  #这个表示在-5到5之间生成1000个x值
y=[1/(1+np.exp(-theta[0]-theta[1]*i)) for i in x]  #对上述生成的1000个数循环用sigmoid公式求对应的y
plt.plot(x,y)  #用上述生成的1000个xy值对生成1000个点
plt.show()  #绘制图像 

  在具体实现时,循环条件用的时梯度的二范数与给定最大迭代次数同用,满足其一跳出循环。
  代码的主要输出结果如下:
  学习前,初始参数:theta0=[0.5], theta1=[0.5]
  迭代252次后结束,学习后,theta0=[-4.07451178],theta1=[1.50356165]
  模型为:y = 1/(1+exp(4.0745117791005-1.503561654356568*x))。
  将原始数据的学习时长h代入模型里,得到散点图如图6所示,将最终模型的图像全部画出来如图7。
图 6 散点图

图 6 散点图

图 7 整个模型图像

图 7 整个模型图像

发现与收获

  做完整个实验后,实验的前半部分与所学的工程数学里数理统计的参数估计相应印证,实验的后半部分让我对梯度下降的原理有了更深的掌握。然而,优化理论博大精深,还是有许多一时难以消化的地方。
  起初的难点在于理解循环终止条件,特别是梯度的二范数作为终止条件的具体含义不能有深刻的理解,后来想到其可理解为二次平面中的斜率,或者三维图像里的坡度,沿着最陡的地方走,走着走着变平缓了,说明就找到了一个局部极小值,如果是凸函数,那就找到了最小值。
  实际上后面考虑过自适应步长,使用回溯线搜索时,一直无法收敛,这让我对调参产生了苦恼,应该有可以机器调参的方法,只是我还没学到,就没在代码上放出来,省得误导别人。在设置固定步长时,考虑迭代次数最短,改变固定步长,然后就随缘调到了0.08,多加一点,少加一点都不合适,是粗鲁的最优了。

发布了22 篇原创文章 · 获赞 6 · 访问量 4152

猜你喜欢

转载自blog.csdn.net/qq_34862636/article/details/94432040