一、最小二乘法
先來解釋幾個概念
擬合函數/估值函數
:在回歸問題中,當給定一組樣本時,找到一個最佳的函數來匹配所有的樣本,這個函數就是擬合函數/估值函數
損失函數
:判斷函數擬合的好不好的函數,損失函數越小,說明擬合值與真實值越接近,誤差越小,就越能用擬合函數來進行預測,損失函數的標準有以下幾種:
a) 殘差和: 指擬合值與真實值的差的和,有正有負會存在抵消的情況,不能反應真實誤差
b) 殘差絕對值和:這個可以解決殘差和有正有負的問題,但是絕對值在后續的求導會異常麻煩
c) 殘差的平方和:這就是最小二乘法,通過殘差平方和求解極值,找到最佳的擬合函數
如何對損失函數求解呢?
① 代數求導法
假設擬合函數為一元線性函數:
其中ei為樣本(Xi, Yi)的誤差
平方和損失函數:
則通過Q最小確定這條直線,解決辦法:將兩個參數作為未知數,分別求偏導,代入所有的樣本值,當偏導=0時,就求得極值
解為:
② 矩陣法
當樣本特征量較多時,會有非常多參數需要一一求解,會相當費時間,這時可以用更快的矩陣方法來求解。
矩陣可以理解為多個方程組同時求解,當我們用方程組表達時
將其轉化為矩陣形式:
損失函數就變為:
最優解為:
python中有封裝好的最小二乘法的求解函數 leastsq,可以直接用:
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
# 真實函數
def real_func(x):
return np.sin(2*np.pi*x)
# 擬合函數
def fit_func(p, x):
f = np.poly1d(p) # polyid是多項式函數,p決定了多項式的次數
return f(x)
# 誤差函數
def residuals_func(p, y, x):
ret = fit_func(p,x) - y
return ret
np.random.seed(0) # 隨機數種子
x = np.linspace(0,2,20)
x_points = np.linspace(0,2,100)
y0 = real_func(x)
y1 = [np.random.normal(0, 0.5) + y for y in y0] # 制造噪音
n = 9
p_init = np.random.randn(n) # 隨機生成多項式參數
plsq = leastsq(residuals_func, p_init, args=(y0, x)) #最小二乘法黑箱計算,參數為(誤差函數,誤差函數的參數,樣本點)
print(plsq)
plt.plot(x_points, real_func(x_points),c='r',label='real')
plt.plot(x_points,fit_func(plsq[0], x_points),c='b',label='fit')
plt.plot(x, y1,'bo', label='with noise')
plt.legend(loc='best') # 如果畫圖中有加lable,記得加上plt.legend(loc='best')
plt.show()
最小二乘法通過代數和矩陣兩種方法都能求得擬合函數的參數,但是不是所有的擬合函數都能通過最小二乘法求解呢?No,比如非線性函數就失效了,那有沒有一種更通用的線性和非線性函數都能求參數的方法呢?那就是梯度下降法
二、梯度下降法
擬合函數,損失函數模型以及求偏導與最小二乘法都一樣,唯一區別在于如何獲得最優解,最小二乘法是直接令導數等于0求解,而梯度下降法是沿著梯度下降的方向逐步迭代,不斷逼近最小值來求解。
先解釋下梯度下降:梯度其實就是函數對各變量的偏導的元組,如果從圖上看,就是在特定點的切線,梯度是函數增加最快的方向,那么梯度下降的意思就是沿著梯度的反方向走,那么就是沿著函數減小最快的方向,就像在下山,每個路口都選擇一條最陡峭的路走(當然懸崖峭壁就別考慮了),那么是不是就能以最快的速度到達山腳呢
可能有人會問:為什么不用最小二乘法直接求,多快呀,還要一步一步迭代?這個問題可以這么理解,有一些函數是無法通過通過求導=0這種方式獲得極值的,這就限制了最小二乘的使用;并且哪怕可以通過這種方式求,計算也相當麻煩時, 逐步迭代反而會更快。
梯度下降法求解過程:
①隨機給定一組初始參數值和學習率(學習率是控制每一次迭代的步長,太小了需要很長的時間才能收斂,太大了容易震蕩,可以無法獲得最優解)
②對損失函數各參數求偏導,得到梯度
③更新參數:參數 = 初始參數-學習率
梯度
④不斷迭代,直接誤差達到所要求的范圍停止
*
# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
x_train = np.array([[1, 2], [2, 1], [2, 3], [3, 5],[1, 3], [4, 2], [7, 3], [4, 5], [11, 3], [8, 7]])
y_train = np.array([7, 8, 10, 14, 8, 13, 20, 16, 28, 26])
x_test = np.array([[1, 4],[2, 2],[2, 5],[5, 3],[1, 5],[4, 1]])
a = np.random.normal()
b = np.random.normal()
c = np.random.normal()
error_list = []
# 擬合函數
def h(x):
return a*x[0] + b*x[1] + c
rate = 0.001 # 學習率
for i in range(8000):
sum_a = sum_b = sum_c = 0
for x, y in zip(x_train, y_train): # 將全部樣本代入求梯度
sum_a = sum_a + rate*(y - h(x))*x[0]
sum_b = sum_b + rate*(y - h(x))*x[1]
sum_c = sum_c + rate*(y - h(x))
a = a+sum_a
b = b+sum_b
c = c+sum_c
plt.plot([h(xi) for xi in x_test])
error = 0
for x, y in zip(x_train, y_train):
error += 0.5*((h(x) - y)**2)
error_list.append(error)
if error<0.000001: # 當誤差小于0.00001 即可停止
break
print(error_list)
print(len(error_list))
print(a)
print(b)
print(c)
result = [h(xi) for xi in x_train]
print(result)
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax1.plot(y_train,label='標簽')
ax2 = fig.add_subplot(122)
ax2.plot(result,label='預測')
plt.legend()
plt.show()
通過迭代調整參數,曲線一開始變化很快,后面變得緩慢,等誤差小到一定程度時,曲線不再變化,這就是最佳的擬合函數曲線
這個是誤差列表,可以看出共迭代了4508次,誤差一開始下降特別快,后面逐漸變慢
這個是真實曲線和擬合曲線,肉眼上基本已經看不出分別了。
上面的例子只有10個樣本,所以每次迭代都使用所有的樣本來計算梯度,速度也還挺快的,這種方法叫
批量梯度下降
;但是如果樣本成千上萬,這樣計算的時間開銷就有點太大了,難道一定要每一次迭代都用所有的樣本才能獲得最優解嗎?針對這個問題,提出了2個解決方案:
①
隨機梯度下降
:顧名思義,每次只挑選一個樣本進行梯度計算然后更新參數,這樣就可以大大節省時間了!是的,可是這樣真的可以嗎?還真的可以! 雖然正確度肯定比不上每次都用所有樣本,但是在迭代多次之后,仍然可能達能一個比較好的效果,但是也存在一個問題,就是噪音要更多,使得并不是每次迭代都向著整體最優化方向
②
小批量梯度下降
:這是批量和隨機梯度下降兩種方法的折中,每次選取一組訓練樣本進行關于參數的梯度,即能減少批量帶來的計算冗余的問題,又能解決隨機帶來的震蕩問題。
總結:
1、最小二乘法與梯度下降法的區別?
都是最小化風險函數、損失函數的方法,兩者都需要求偏導,最小二乘法是以誤差平方和最小作為損失函數,直接讓偏導等于0獲得全局最優解,多用于處理一元或多元線性回歸問題,梯度下降法包括但不限于最小平方和作為損失函數,是沿著負梯度方向不斷迭代逼近最優解,線性和非線性問題都能處理
2、與批量梯度下降對比,隨機梯度下降求解的會是最優解嗎?
(1)批量梯度下降—最小化所有訓練樣本的損失函數,使得最終求解的是全局的最優解,即求解的參數是使得風險函數最小。
(2)隨機梯度下降—最小化每條樣本的損失函數,雖然不是每次迭代得到的損失函數都向著全局最優方向, 但是大的整體的方向是向全局最優解的,最終的結果往往是在全局最優解附近
3、梯度下降用來求最優解,哪些問題可以求得全局最優?哪些問題可能局部最優解?
如果損失函數只有一個峰,那么可能獲得全局最優,如果有多個峰,則可能獲得局部最優
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
