梯度下降法求极值

梯度下降法的基本原理

梯度下降是迭代法的一种,可以用于求解最小二乘问题(线性和非线性都可以)。在求解机器学习算法的模型参数,即无约束优化问题时,梯度下降(Gradient Descent)是最常采用的方法之一,另一种常用的方法是最小二乘法。在求解损失函数的最小值时,可以通过梯度下降法来一步步的迭代求解,得到最小化的损失函数和模型参数值。反过来,如果我们需要求解损失函数的最大值,这时就需要用梯度上升法来迭代了。在机器学习中,基于基本的梯度下降法发展了两种梯度下降方法,分别为随机梯度下降法和批量梯度下降法。顾名思义,梯度下降法的计算过程就是沿梯度下降的方向求解极小值(也可以沿梯度上升方向求解极大值)。其迭代公式为,
在这里插入图片描述
其中代表梯负方向,表示梯度方向上的搜索步长。梯度方向我们可以通过对函数求导得到,步长的确定比较麻烦,太大了的话可能会发散,太小收敛速度又太慢。一般确定步长的方法是由线性搜索算法来确定,即把下一个点的坐标看做是ak+1的函数,然后求满足f(ak+1)的最小值的ak+1即可。因为一般情况下,梯度向量为0的话说明是到了一个极值点,此时梯度的幅值也为0.而采用梯度下降算法进行最优化求解时,算法迭代的终止条件是梯度向量的幅值接近0即可,可以设置个非常小的常数阈值。

牛顿法

牛顿迭代法是以微分为基础的,微分就是用直线来代替曲线,由于曲线不规则,那么我们来研究直线代替曲线后,剩下的差值是不是高阶无穷小,如果是高阶无穷小,那么这个差值就可以扔到不管了,只用直线就可以了,这就是微分的意义。
牛顿法是牛顿在17世纪提出的一种求解方程f(x)=0.多数方程不存在求根公式,从而求精确根非常困难,甚至不可能,从而寻找方程的近似根就显得特别重要。
牛顿迭代法是取x0之后,在这个基础上,找到比x0更接近的方程的跟,一步一步迭代,从而找到更接近方程根的近似跟。方法使用函数f(x)的泰勒级数的前面几项来寻找方程f(x) = 0的根。牛顿迭代法是求方程根的重要方法之一,其最大优点是在方程f(x) = 0的单根附近具有平方收敛,而且该法还可以用来求方程的重根、复根。另外该方法广泛用于计算机编程中。

设r是f(x)=0的根,选取x0作为r初始近似值,过点(x0,f(x0))做曲线y=f(x)的切线L,L的方程为y=f(x0)+f’(x0)(x-x0),求出L与x轴交点的横坐标 x1=x0-f(x0)/f’(x0),称x1为r的一次近似值,过点(x1,f(x1))做曲线y=f(x)的切线,并求该切线与x轴的横坐标 x2=x1-f(x1)/f’(x1)称x2为r的二次近似值,重复以上过程,得r的近似值序列{Xn},其中Xn+1=Xn-f(Xn)/f’(Xn),称为r的n+1次近似值。上式称为牛顿迭代公式。

梯度下降法求极值

在这里插入图片描述

在excel中求极值

在这里插入图片描述
在这里插入图片描述

利用python代码求极值

import math

#使用梯度下降法求函数的最小值
# f = x1^2+2x2^2-4x1-2x1x2  初始点为(1,1)



#设计函数

def function_one(x1,x2):   # 函数的输入 x,y
    f =  x1*x1+2*x2*x2-4*x1-2*x1*x2 # 算出f 的值
    dx = 2*x1-4-2*x2                          # 算出 一阶x导数的值
    dy = 4*x2-2*x1                      # 算出 一阶y导数的值
    
    return f, dx, dy                           # 返回三个值

def main():
    x = 1 #初始点
    y = 1 #初始点
    
    a = 0.3  # 设置步长
    
    error = 0.000001  # 设置误差函数
    
    f_old, dx, dy = function_one(x,y) # 算出初始的值
    
    for i in range(100):    # 开始循环 梯度下降 设置循环的次数
        x -= a*dx
        y -= a*dy
        f_result, dx, dy = function_one(x,y)  # 获得第一次计算的初始值
        
        if abs(f_result - f_old) < error:       # 下降的结果绝对值与误差进行比较 如果再允许范围内则终止
            f_final = f_result
            print("极值点为: %.5f ,最小值为: %.5f, 循环次数: %d, 误差:%8f" % (x,f_final, i , abs(f_result - f_old)))
            break
        print("第 %d 次循环, 函数值为 %f" % (i, f_result))
        f_old = f_result
        
        
if __name__ == '__main__':
    main()

第 0 次循环, 函数值为 -5.400000
第 1 次循环, 函数值为 -6.576000
第 2 次循环, 函数值为 -7.193280
第 3 次循环, 函数值为 -7.533504
第 4 次循环, 函数值为 -7.727005
第 5 次循环, 函数值为 -7.839158
第 6 次循环, 函数值为 -7.904877
第 7 次循环, 函数值为 -7.943626
第 8 次循环, 函数值为 -7.966552
第 9 次循环, 函数值为 -7.980142
第 10 次循环, 函数值为 -7.988206
第 11 次循环, 函数值为 -7.992994
第 12 次循环, 函数值为 -7.995838
第 13 次循环, 函数值为 -7.997527
第 14 次循环, 函数值为 -7.998531
第 15 次循环, 函数值为 -7.999127
第 16 次循环, 函数值为 -7.999481
第 17 次循环, 函数值为 -7.999692
第 18 次循环, 函数值为 -7.999817
第 19 次循环, 函数值为 -7.999891
第 20 次循环, 函数值为 -7.999935
第 21 次循环, 函数值为 -7.999962
第 22 次循环, 函数值为 -7.999977
第 23 次循环, 函数值为 -7.999986
第 24 次循环, 函数值为 -7.999992
第 25 次循环, 函数值为 -7.999995
第 26 次循环, 函数值为 -7.999997
第 27 次循环, 函数值为 -7.999998
极值点为: 3.99862 ,最小值为: -8.00000, 循环次数: 28, 误差:0.000001

由此可知,该函数的极值点为4.000,最小值为-8.000

店铺多元回归

先求方程和偏导

#求第一组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(469-10*a1-80*a2-b)*(469-10*a1-80*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
200*a1 + 1600*a2 + 20*b - 9380
1600*a1 + 12800*a2 + 160*b - 75040
20*a1 + 160*a2 + 2*b - 938
##求第2组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(366-8*a1-0*a2-b)*(366-8*a1-0*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
128*a1 + 16*b - 5856
0
16*a1 + 2*b - 732
##求第3组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(371-8*a1-200*a2-b)*(371-8*a1-200*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
128*a1 + 3200*a2 + 16*b - 5936
3200*a1 + 80000*a2 + 400*b - 148400
16*a1 + 400*a2 + 2*b - 742
##求第4组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(208-5*a1-200*a2-b)*(208-5*a1-200*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
50*a1 + 2000*a2 + 10*b - 2080
2000*a1 + 80000*a2 + 400*b - 83200
10*a1 + 400*a2 + 2*b - 416
##求第5组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(246-7*a1-300*a2-b)*(246-7*a1-300*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
98*a1 + 4200*a2 + 14*b - 3444
4200*a1 + 180000*a2 + 600*b - 147600
14*a1 + 600*a2 + 2*b - 492
##求第6组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(297-8*a1-230*a2-b)*(297-8*a1-230*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
128*a1 + 3680*a2 + 16*b - 4752
3680*a1 + 105800*a2 + 460*b - 136620
16*a1 + 460*a2 + 2*b - 594
##求第7组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(363-7*a1-40*a2-b)*(363-7*a1-40*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
98*a1 + 560*a2 + 14*b - 5082
560*a1 + 3200*a2 + 80*b - 29040
14*a1 + 80*a2 + 2*b - 726
##求第8组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(436-9*a1-0*a2-b)*(436-9*a1-0*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
162*a1 + 18*b - 7848
0
18*a1 + 2*b - 872
##求第9组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(198-6*a1-330*a2-b)*(198-6*a1-330*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
72*a1 + 3960*a2 + 12*b - 2376
3960*a1 + 217800*a2 + 660*b - 130680
12*a1 + 660*a2 + 2*b - 396
##求第10组数据的
from sympy import *
a1,a2,b=symbols('a1 a2 b')
S=(364-9*a1-180*a2-b)*(364-9*a1-180*a2-b)
print(diff(S,a1))
print(diff(S,a2))
print(diff(S,b))
162*a1 + 3240*a2 + 18*b - 6552
3240*a1 + 64800*a2 + 360*b - 131040
18*a1 + 360*a2 + 2*b - 728

把结果加起来就是各自的偏导

a1偏导=1226a1+22440a2+154b-53306
a2偏导=22440a1+744400a2+3120b-881620
b偏导=154a1+3120a2+20b-6636
S=(469-10a1-80a2-b)(469-10a1-80a2-b)+(371-8a1-200a2-b)(371-8a1-200a2-b)+(371-8a1-200a2-b)(371-8a1-200a2-b)+(208-5a1-200a2-b)(208-5a1-200a2-b)+(246-7a1-300a2-b)(246-7a1-300a2-b)+(297-8a1-230a2-b)(297-8a1-230a2-b)+(363-7a1-40a2-b)(363-7a1-40a2-b)+(436-9a1-0a2-b)(436-9a1-0a2-b)+(198-6a1-330a2-b)(198-6a1-330a2-b)+(364-9a1-180a2-b)(364-9a1-180a2-b)

在excel中求:

在这里插入图片描述
在这里插入图片描述

由此可知:当a1=44.944,a2=-0.4475,b=69.9925时Se有极小值。
所以y=44.944x1-0.4475x2+69.9925
漫画书中所得结果y=41.5x1-0.3x2+65.3
两者有一定的误差,不过用梯度下降法得到的结果没有用最小二乘法得到的结果准确。

在Python中求

import math

#使用梯度下降法求函数的最小值

#设计函数

def function_one(x1,x2,b):   # 函数的输入 x,y
    f =  (469-10*x1-80*x2-b)*(469-10*x1-80*x2-b)+(366-8*x1-0*x2-b)*(366-8*x1-0*x2-b)+(371-8*x1-200*x2-b)*(371-8*x1-200*x2-b)+(208-5*x1-200*x2-b)*(208-5*x1-200*x2-b)+(246-7*x1-300*x2-b)*(246-7*x1-300*x2-b)+(297-8*x1-230*x2-b)*(297-8*x1-230*x2-b)+(363-7*x1-40*x2-b)*(363-7*x1-40*x2-b)+(436-9*x1-0*x2-b)*(436-9*x1-0*x2-b)+(198-6*x1-330*x2-b)*(198-6*x1-330*x2-b)+(364-9*x1-180*x2-b)*(364-9*x1-180*x2-b) # 算出f 的值
    dx1 = 1226*x1+22440*x2+154*b-53306                          # 算出 一阶x1导数的值
    dx2 = 22440*x1+744400*x2+3120*b-881620                     # 算出 一阶x2导数的值
    db = 154*x1+3120*x2+20*b-6636
    return f, dx1, dx2 ,db                          # 返回4个值

def main():
    x1 = 45 #初始点
    x2 = 1 #初始点
    b = 70
    
    a = 0.0000001  # 设置步长
    
    error = 0.8  # 设置误差函数
    
    f_old, dx1, dx2,db = function_one(x1,x2,b) # 算出初始的值
    
    for i in range(100):    # 开始循环 梯度下降 设置循环的次数
        x1 -= a*dx1
        x2 -= a*dx2
        b -=b*db
        f_result, dx1, dx2,db = function_one(x1,x2,b)  # 获得第一次计算的初始值
        if abs(f_result-f_old)<error:
            print("a1=",x1)
            print("a2=",x2)
            print("b=",b)
        f_old=f_result
       
        
if __name__ == '__main__':
    main()
发布了9 篇原创文章 · 获赞 4 · 访问量 890

猜你喜欢

转载自blog.csdn.net/weixin_43709601/article/details/105322476