作为一个刚刚接触机器学习的小白,最近看了些Andrew Ng课中回归的知识,写一写自己的一些心得。
分析一个简单的小例子
回归的目的是为了使预测的函数能够更好的拟合我们给定的数据。我们先假设只在二维平面中,用一条直线去拟和一条曲线。
假设我们需要用一条直线:
hθ(X)= θ0 + θ1X1 ①
去拟合一条抛物线
Y = X^2 ②
假设现在我们取抛物线上的10个点作为我们的训练集:
( 1,1)、(2,4)、(3,9)、(4,16)、(5,25)、(6,36)、(7,49)、(8,64)、(9,81)、(10,100)
既然目的是为了使预测的函数与实际的数据的拟合最好,我们只需要尽量做到使他们的误差和最小即可。于是,我们可以用到最小二乘法来达到我们的目的。
误差项:E0 = (hθ(X)- Y)^2 ③
m个误差项的平均:E = 1/(2*m)Σ(hθ(X)- Y)^2 ④
(多除了一个2是为了使得之后的计算简便,对结果并没有什么影响)
现在我们只要使得函数
J(θ)= E = 1/(2*m)Σ(hθ(X)- Y)^2 ⑤
能够趋向最小值即可
问题转化到函数的J(θ)的分析,函数J(θ)含有两个参数θ0和θ1,从④式中可以看出θ0和θ1都是平方级别的,如果我们分别以θ0,θ1,J(θ)为X,Y,Z轴构建一个空间坐标系,他们构成的图形应当为一个曲面,这个曲面应当长这样:
显然,我们只需要找到最低点的θ0和θ1的值便能使得函数J(θ)的值最小
处理函数J(θ)的方法
一个小疑问:就是使得J(θ)取得最小值时的θ0和θ1能不能直接求出?(我觉得我应该回去好好看看高数有关空间曲面的那章????)
梯度下降法
梯度的意思就是函数上升最快的方向,我们想要较快的达到函数J(θ)的底部,我们可以任取(θ0,θ1),然后通过减去他们的梯度(往上升最快的反方向走),不断更新(θ0,θ1)的值就能找到最适合的θ0和θ1了,如果正视从起始点到终点的路径大概是这样子:
⑤式可化为:
J(θ0,θ1)= 1/(2*m)Σ(θ0 + θ1X1 - Y)^2 ⑥
⑥式分别对θ0和θ1求偏导:
∂J(θ0)/∂θ0 = 1/mΣ((θ0+θ1X1)- Y)) ⑦
∂J(θ1)/∂θ1 = 1/mΣ((θ0+θ1X1)- Y))X1 ⑧
为了方便表达与运算,我们令X0 = [1,1,1,1,1,1,1,1,1,1,1],可得X1 = [1,2,3,4,5,6,7,8,9,10],X = [[X0] ,[X1]];θ = [θ0,θ1];Y = [1,4,9,25,36,49,64,81,100]
于是⑦式,⑧式的表达为:
∂J(θ0)/∂θ0 = 1/m(θX-Y) ⑨
∂J(θ1)/∂θ1 = 1/m(θX- Y))X1 ⑩
两式综合起来表达为
J(θ)的导数为1/m(θX-Y)X
所以更新公式为θ = θ - α*a(α为学习率,即下降一次的步长)
此时任取θ=[0,0],将代码实现就能得出我们想要拟合的曲线啦
## 牛顿方法
牛顿方法又叫切线法,相比梯度下降算法来说,它能更快的收敛。
在分析方面牛顿方法也与梯度下降法相似的处理,最后得出的迭代公式为:
*θ = θ - (H^-1)a
H^-1为hessian矩阵的逆,至于hessian矩阵什么的,小伙伴们自行可以百度。
代码以及实现
梯度下降代码及实现
import numpy as np
from matplotlib import pyplot as plt
a = np.linspace(0, 11)
b = a**2
plt.plot(a, b)
X0 = np.mat(np.ones((1, 10))) #X0中的值全为1
X1 = np.mat(np.arange(1, 11)) #X1中的值为1-10,生成X1
X = np.hstack((np.transpose(X0), np.transpose(X1))) #将两个向量组合起来
X = np.transpose(X)
y = np.multiply(X1, X1)
plt.scatter(np.array(X1), np.array(y))
alpha = 0.01
m = 10
def gradient_function(theta, X, y):
dif = np.dot(theta, X) - y
return (1./m)*np.dot(dif, np.transpose(X)) #计算坡度值
def gradient_descent(X, y, alpha, turns):
theta = np.mat(np.zeros((1, 2)))
gradient = gradient_function(theta, X, y)
for i in range(turns):
theta = theta - alpha * gradient
gradient = gradient_function(theta, X, y)
return theta #返回两个theta的值
test = np.array(gradient_descent(X, y, alpha, 1))
x_test = np.array([1, 10])
y_test = x_test*test[0][1] + test[0][0]
plt.plot(x_test, y_test, color='r', label='1')
test = np.array(gradient_descent(X, y, alpha, 20))
x_test = np.array([1, 10])
y_test = x_test*test[0][1] + test[0][0]
plt.plot(x_test, y_test, color='b', label='20')
test = np.array(gradient_descent(X, y, alpha, 200))
x_test = np.array([1, 10])
y_test = x_test*test[0][1] + test[0][0]
plt.plot(x_test, y_test, color='g', label='200')
test = np.array(gradient_descent(X, y, alpha, 2000))
x_test = np.array([1, 10])
y_test = x_test*test[0][1] + test[0][0]
plt.plot(x_test, y_test, color='g', label='2000')
plt.legend()
plt.show()
## 牛顿算法
上面已经说明,牛顿算法与梯度下降在最后迭代式上的差别紧将学习率α换成hessian矩阵的逆。
修改代码表示为:
def L_function(X, y, theta): #计算L(θ)一阶导/L(θ)二阶导
dif1 = np.dot(theta, X) - y
dif2 = (1./m)*np.dot(dif1, np.transpose(X)) #计算L(θ)一阶导数
h1 = (1./m)*np.dot(X0, np.transpose(X1))
h2 = (1./m)*np.dot(X1, np.transpose(X1))
dif3 = np.mat([[1, h1], [h1, h2]]) #计算L(θ)二阶导数的值
return np.dot(dif2, dif3.I)
def Newtown_methon(X, y, turns):
theta = np.mat(np.zeros((1, 2)))
for i in range(turns):
para1 = L_function(X, y, theta)
theta = theta - para1
return theta
结尾
感觉在这里面比较难以理解的是数学推导式,我觉得我应该去把高数线代概率论重新去看一遍