無聊的言論
自己其實(shí)聽過挺多課的,之前也會亂七八糟報(bào)一堆課,一直堅(jiān)信著一個(gè)原則,沒有輸出再多的輸入也沒什么卵用,其實(shí)也真的是這樣,輸入再多都不如實(shí)際的輸出一波,希望大家可以認(rèn)真的把老師的每個(gè)例子都能實(shí)踐出來,我也會給大家說怎么實(shí)踐以及如何看到自己的結(jié)果,大綱就是21天入門的大綱,跟著猴哥走起來!
emmm 其實(shí)寫了好多又都刪了,不知道覺得將老師的課本重復(fù)一遍又沒什么太大的用途,我們就從算法開始講起,原理部分就當(dāng)大家都理解啦,就從第一個(gè)算法,梯度下降法,我會把老師講的東西掰開揉碎寫清楚,也許會有些瑣碎,選擇自己需要的看,這篇文章也是我看了好多文章論文寫出來的,希望能有幫助!
梯度下降法快速入門
原理方面請看老師的講解,這里我們主要來學(xué)習(xí)實(shí)際原理以及實(shí)現(xiàn)
前言
梯度下降法(Gradient Descent)是機(jī)器學(xué)習(xí)中最常用的優(yōu)化方法之一,常用來求解目標(biāo)函數(shù)的極值。
其基本原理非常簡單:沿著目標(biāo)函數(shù)梯度下降的方向搜索極小值(也可以沿著梯度上升的方向搜索極大值)。
但是如何調(diào)整搜索的步長(也叫學(xué)習(xí)率,Learning Rate)、如何加快收斂速度以及如何防止搜索時(shí)發(fā)生震蕩卻是一門值得深究的學(xué)問。接下來本文將分析第一個(gè)問題:學(xué)習(xí)率的大小對搜索過程的影響


來梳理梯度下降法的原理
梯度下降的相關(guān)概念
在詳細(xì)了解梯度下降的算法之前,我們先看看相關(guān)的一些概念。
1. 步長(Learning rate):步長決定了在梯度下降迭代的過程中,每一步沿梯度負(fù)方向前進(jìn)的長度。用上面下山的例子,步長就是在當(dāng)前這一步所在位置沿著最陡峭最易下山的位置走的那一步的長度。
2.特征(feature):指的是樣本中輸入部分,比如2個(gè)單特征的樣本(x(0),y(0)),(x(1),y(1)),則第一個(gè)樣本特征為x(0),第一個(gè)樣本輸出為y(0)。
3. 假設(shè)函數(shù)(hypothesis function):在監(jiān)督學(xué)習(xí)中,為了擬合輸入樣本,而使用的假設(shè)函數(shù),記為hθ(x)。比如對于單個(gè)特征的m個(gè)樣本(x(i),y(i))(i=1,2,...m),可以采用擬合函數(shù)如下: hθ(x)=θ0+θ1x。
4. 損失函數(shù)(loss function):為了評估模型擬合的好壞,通常用損失函數(shù)來度量擬合的程度。損失函數(shù)極小化,意味著擬合程度最好,對應(yīng)的模型參數(shù)即為最優(yōu)參數(shù)。在線性回歸中,損失函數(shù)通常為樣本輸出和假設(shè)函數(shù)的差取平方。比如對于m個(gè)樣本(xi,yi)(i=1,2,...m),采用線性回歸,損失函數(shù)為:
J(θ0,θ1)=∑i=1m(hθ(xi)?yi)2
其中xi表示第i個(gè)樣本特征,yi表示第i個(gè)樣本對應(yīng)的輸出,hθ(xi)為假設(shè)函數(shù)。
解決實(shí)際問題
是為了解決最優(yōu)化問題,而最優(yōu)化問題其實(shí)就是我們大學(xué)中,來求極大值以及極小值的問題
初中時(shí)我們就學(xué)會了求解二次函數(shù)的極值(拋物線的頂點(diǎn)),高中時(shí)學(xué)習(xí)了冪函數(shù),指數(shù)函數(shù),對數(shù)函數(shù),三角函數(shù),反三角函數(shù)等各種類型的函數(shù),求函數(shù)極值的題更是頻頻出現(xiàn)。這些方法都采用了各種各樣的技巧,沒有一個(gè)統(tǒng)一的方案。
大學(xué)的時(shí)候我們使用微積分,而這個(gè)就是一個(gè)通用的方案:
找到函數(shù)的導(dǎo)數(shù)等于0的點(diǎn),因?yàn)樵跇O值(請注意極值不一定是全局極值),導(dǎo)數(shù)必然為0.
機(jī)器學(xué)習(xí)中,我們一般將最優(yōu)化問題統(tǒng)一表述為求解函數(shù)的極小值問題:

其中x稱為優(yōu)化變量,f稱為目標(biāo)函數(shù)。極大值問題可以轉(zhuǎn)換成極小值問題來求解,只需要將目標(biāo)函數(shù)加上負(fù)號即可:

有些時(shí)候會對優(yōu)化變量x有約束,包括等式約束和不等式約束,它們定義了優(yōu)化變量的可行域,即滿足約束條件的點(diǎn)構(gòu)成的集合。在這里我們先不考慮帶約束條件的問題。
一個(gè)優(yōu)化問題的全局極小值x是指對于可行域里所有的x,有:

即全局極小值點(diǎn)處的函數(shù)值不大于任意一點(diǎn)處的函數(shù)值。局部極小值x
定義為存在一個(gè)δ鄰域,對于在鄰域內(nèi):

并且在可行域內(nèi)的所有x,有:

即局部極小值點(diǎn)處的函數(shù)值比一個(gè)局部返回內(nèi)所有點(diǎn)的函數(shù)值都小。在這里,我們的目標(biāo)是找到全局極小值。不幸的是,有些函數(shù)可能有多個(gè)局部極小值點(diǎn),因此即使找到了導(dǎo)數(shù)等于0的所有點(diǎn),還需要比較這些點(diǎn)處的函數(shù)值。
導(dǎo)數(shù)與梯度
由于實(shí)際應(yīng)用中一般都是多元函數(shù),因此我們跳過一元函數(shù),直接介紹多元函數(shù)的情況。梯度是導(dǎo)數(shù)對多元函數(shù)的推廣,它是多元函數(shù)對各個(gè)自變量偏導(dǎo)數(shù)形成的向量。多元函數(shù)的梯度定義為:

其中? 稱為梯度算子,它作用于一個(gè)多元函數(shù),得到一個(gè)向量。下面是計(jì)算函數(shù)梯度的一個(gè)例子:

可導(dǎo)函數(shù)在某一點(diǎn)處取得極值的必要條件是梯度為0,梯度為0的點(diǎn)稱為函數(shù)的駐點(diǎn),這是疑似極值點(diǎn)。需要注意的是,梯度為0只是函數(shù)取極值的必要條件而不是充分條件,即梯度為0的點(diǎn)可能不是極值點(diǎn)。
編寫一個(gè)梯度下降的例子
首先先假設(shè)現(xiàn)在我們需要求解目標(biāo)函數(shù)func(x) = x * x的極小值,由于func是一個(gè)凸函數(shù),因此它唯一的極小值同時(shí)也是它的最小值,其一階導(dǎo)函數(shù) 為dfunc(x) = 2 * x。
import numpy as np
import matplotlib.pyplot as plt
# 目標(biāo)函數(shù):y=x^2
def func(x): return np.square(x)
# 目標(biāo)函數(shù)一階導(dǎo)數(shù):dy/dx=2*x
def dfunc(x): return 2 * x
接下來編寫梯度下降法函數(shù):
Gradient Descentdef GD(x_start, df, epochs, lr):
""" 梯度下降法。給定起始點(diǎn)與目標(biāo)函數(shù)的一階導(dǎo)函數(shù),求在epochs次迭代中x 的更新值 :param x_start: x的起始點(diǎn) :param df: 目標(biāo)函數(shù)的一階導(dǎo)函數(shù) :param epochs: 迭代周期 :param lr: 學(xué)習(xí)率 :return: x在每次迭代后的位置(包括起始點(diǎn)),長度為epochs+1 """
xs = np.zeros(epochs+1)
x = x_start
xs[0] = x
for i in range(epochs):
dx = df(x)
# v表示x要改變的幅度
v = - dx * lr
x += v
xs[i+1] = x
return xs
需要注意的是參數(shù)df是一個(gè)函數(shù)指針,即需要傳進(jìn)我們的目標(biāo)函數(shù)一階導(dǎo)函數(shù)。
測試代碼如下,假設(shè)起始搜索點(diǎn)為-5,迭代周期為5,學(xué)習(xí)率為0.3:
def demo0_GD():
"""演示如何使用梯度下降法GD()"""
line_x = np.linspace(-5, 5, 100)
line_y = func(line_x)
x_start = -5
epochs = 5
lr = 0.3
x = GD(x_start, dfunc, epochs, lr=lr)
color = 'r'
plt.plot(line_x, line_y, c='b')
plt.plot(x, func(x), c=color, label='lr={}'.format(lr))
plt.scatter(x, func(x), c=color, )
plt.legend()
plt.show()

學(xué)習(xí)率對梯度下降法的影響
在上節(jié)代碼的基礎(chǔ)上編寫新的測試代碼demo1_GD_lr,設(shè)置學(xué)習(xí)率分別為0.1、0.3與0.9:
def demo1_GD_lr():
# 函數(shù)圖像
line_x = np.linspace(-5, 5, 100)
line_y = func(line_x)
plt.figure('Gradient Desent: Learning Rate')
x_start = -5
epochs = 5
lr = [0.1, 0.3, 0.9]
color = ['r', 'g', 'y']
size = np.ones(epochs+1) * 10
size[-1] = 70
for i in range(len(lr)):
x = GD(x_start, dfunc, epochs, lr=lr[i])
plt.subplot(1, 3, i+1)
plt.plot(line_x, line_y, c='b')
plt.plot(x, func(x), c=color[i], label='lr={}'.format(lr[i]))
plt.scatter(x, func(x), c=color[i])
plt.legend()
plt.show()
從下圖輸出結(jié)果可以看出兩點(diǎn),在迭代周期不變的情況下:
- 學(xué)習(xí)率較小時(shí),收斂到正確結(jié)果的速度較慢。
-
學(xué)習(xí)率較大時(shí),容易在搜索過程中發(fā)生震蕩。
image.png
