一、最小二乘法
先来解释几个概念
拟合函数/估值函数
:在回归问题中,当给定一组样本时,找到一个最佳的函数来匹配所有的样本,这个函数就是拟合函数/估值函数
损失函数
:判断函数拟合的好不好的函数,损失函数越小,说明拟合值与真实值越接近,误差越小,就越能用拟合函数来进行预测,损失函数的标准有以下几种:
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、梯度下降用来求最优解,哪些问题可以求得全局最优?哪些问题可能局部最优解?
如果损失函数只有一个峰,那么可能获得全局最优,如果有多个峰,则可能获得局部最优