笔记
描述
什么是线性回归?
线性回归初等数学就学过?
回归是通过先通过数据的分布规律,建立一个假设函数,例如线性回归的假设函数:
h
θ
(
x
)
=
θ
0
+
θ
1
x
h_\theta (x) = \theta_0 + \theta_1 x
hθ(x)=θ0+θ1x
其中h是数据集x对应的结果,
θ
0
\theta_0
θ0和
θ
1
\theta_1
θ1是系数。
机器学习的目的是想办法从训练数据集中,得到
θ
0
\theta_0
θ0和
θ
1
\theta_1
θ1的系数,使该假设函数尽可能的拟合数据,最后可以用这个假设函数去预测新数据的结果。
如何让计算机知道什么样的theta值比较合适?
代价函数:可以利用代入大量训练数据
X
i
X_i
Xi,让假设函数先计算得到结果
H
i
H_i
Hi。利用训练集的结果值
Y
i
Y_i
Yi和
H
i
H_i
Hi作差,这就得到每个训练数据
X
i
X_i
Xi的误差,再将每个训练数据的误差求和,最后取平均。
这就可以得到了代价函数J,这就是对于当前theta值的假设函数的拟合效果是怎么样的,做出了量化的评判。
如果只有一个变量决定结果,所以代价函数的表达式:
J
(
θ
0
,
θ
1
)
=
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
−
y
(
i
)
)
)
2
J(\theta_0, \theta_1) = \frac{1}{2m} \sum_{i=1}^m (h_\theta (x^{(i)} - y^{(i)})) ^2
J(θ0,θ1)=2m1∑i=1m(hθ(x(i)−y(i)))2
如何让计算机自动拟合数据?
梯度下降:线性回归这里,我们采用梯度下降的方法。通过高数中求偏导的方式,对一个点求各变量偏导即可得到该点的梯度,我们通过对假设函数中的theta减去这个梯度即可逐步修正theta值(偏导有正有负)。
多次迭代theta的操作后,即可到达代价函数的极小值,达到满意的theta值。
Andrew Ng将梯度下降的方法,想象成一个人下山,如何更快的下山,这就要找梯度最大的方向走。
需要注意的是,求导值太大可能会导致越过一个极小值点,从而无法正确的梯度下降,所以,我们通常会为偏导值乘上一个学习系数alpha来调整梯度下降的速率。
对于一元一次的假设函数,梯度下降算法如下:
θ
0
:
=
θ
j
−
α
∂
∂
θ
0
J
(
θ
0
,
θ
1
)
\theta_0 := \theta_j - \alpha \frac{\partial}{\partial \theta_0} J (\theta_0, \theta_1)
θ0:=θj−α∂θ0∂J(θ0,θ1)
θ
1
:
=
θ
j
−
α
∂
∂
θ
1
J
(
θ
0
,
θ
1
)
\theta_1 := \theta_j - \alpha \frac{\partial}{\partial \theta_1} J (\theta_0, \theta_1)
θ1:=θj−α∂θ1∂J(θ0,θ1)
对高数中的梯度不太熟的,可以观看动画演示(2个变量情况):Gradients and Partial Derivatives
多元梯度下降
推广到多元,仅仅是变量x的系数theta更多了 (采用矩阵的方式处理,易于计算)
多元梯度下降:
θ
j
:
=
θ
j
−
α
∂
∂
θ
j
J
(
θ
0
,
θ
1
)
(
j
=
0
,
1
,
2
,
.
.
.
,
n
)
\theta_j := \theta_j - \alpha \frac{\partial}{\partial \theta_j} J (\theta_0, \theta_1) \\ (j = 0, 1, 2, ... , n)
θj:=θj−α∂θj∂J(θ0,θ1)(j=0,1,2,...,n)
关键点
线性回归模型(Linear Regression Model)
假设函数:
h
θ
(
x
)
=
θ
0
+
θ
1
x
h_\theta (x) = \theta_0 + \theta_1 x
hθ(x)=θ0+θ1x
代价函数:
J
(
θ
)
=
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
−
y
(
i
)
)
)
2
J(\theta) = \frac{1}{2m} \sum_{i=1}^m (h_\theta (x^{(i)} - y^{(i)})) ^2
J(θ)=2m1i=1∑m(hθ(x(i)−y(i)))2
梯度下降算法(Gradient descent algorithm)
θ
j
:
=
θ
j
−
α
∂
∂
θ
j
J
(
θ
)
(
j
=
0
,
1
,
2
,
.
.
.
,
n
)
\theta_j := \theta_j - \alpha \frac{\partial}{\partial \theta_j} J (\theta) \\ (j = 0, 1, 2, ... , n)
θj:=θj−α∂θj∂J(θ)(j=0,1,2,...,n)
注:j = 0时,
x
0
x_0
x0 = 1,即函数的偏移项
代码
python实现
这是一段多元梯度下降的线性回归的代码
代码中,为了便于书写,以W作为
θ
j
\theta_j
θj(j = 1, 2, … , n),以b作为
θ
0
\theta_0
θ0
方法功能:
fit():用来加载数据,并初始化一些变量
partial_derivative():是计算偏导(注意偏导的方程是自己手动先算好的,并不是调用求偏导的库)
gradient_descent():是对theta值一直梯度下降(减去偏导值)
train():开始运行
predict():对测试集做预测
import numpy as np
class LinearRegression:
'''
线性回归
参数:
X - 训练集(需要将训练集的特征缩放到0~1,将参数以列向量重排)
Y - 训练集的结果(取值为0|1)
W - 假设函数的多参数组成矩阵(w1、w2、w3 ...)(W_j对应theta_j,j=1,2,3...)
b - 假设函数的参数(x0 = 1的值)(b对应theta_0)
learning_rate - 学习速率
num_iter - 迭代次数
costs - 代价函数值的集合(非必须操作)
使用:
lg = LinearRegression()
lg.init(X_train, Y_train)
lg.train(0.001, 2000)
predicted = lg.predict(X_test)
'''
X = 0
Y = 0
W = 0
b = 0
learning_rate = 0
num_iter = 0
costs = []
# 初始化变量
def init(self, X, Y):
'''
加载训练集,并设置一些初始值
参数:
X - 训练集
Y - 训练集的结果
'''
self.X = X
self.Y = Y
self.W = np.zeros(shape = (X.shape[0], 1))
self.b = 0
self.costs = []
# 对代价函数J求导
# h(x) = W * x + b
def partial_derivative(self):
'''
对梯度下降公式后半部分的求导(手动计算)数值
返回:
dW,db - 假设函数的参数的偏导值
'''
m = self.X.shape[1]
# 假设函数的激活值(正向传播)
H = np.dot(self.W.T, self.X) + self.b
# 计算代价,记录代价(非必须操作,只是便于观察梯度下降的效果)
cost = (1 / (2 * m)) * np.sum(H - self.Y) ** 2
self.costs.append(cost)
# 求偏导(反向传播)
dW = 1 / m * np.dot(self.X, (H - self.Y).T) # 注:矩阵点乘后,已经求过和
db = 1 / m * np.sum(H - self.Y) # 注:没有进行过矩阵乘法运算,需要手动求和
return dW, db
# 梯度下降
# temp0 = W - alpha * partial_derivative(J0(W, b))
# temp1 = b - alpha * partial_derivative(J1(W, b))
# ...
def gradient_descent(self):
'''
进行梯度下降的运算,公式:W_j = W_j - alpha * partial_derivative(J_j(W_j, b)), j = 1,2,3...
'''
for i in range(self.num_iter):
dW, db = self.partial_derivative()
# 梯度下降,优化参数W、b
self.W = self.W - self.learning_rate * dW
self.b = self.b - self.learning_rate * db
# 开始训练
def train(self, learning_rate = 0, num_iter = 0):
'''
开始训练
参数:
learning_rate - 学习速率
num_iter - 迭代次数
'''
self.learning_rate = learning_rate
self.num_iter = num_iter
self.gradient_descent()
# 预测
def predict(self, X):
'''
预测X数据集
参数:
X - 测试数据集
返回:
predicted - 对于测试数据集X的预测结果
'''
# 带入参数w、b预测测试集
predicted = np.dot(self.W.T, X) + self.b
return predicted
测试
简单的自拟了一些训练数据。
注意,训练数据和测试数据,必须要和代码里写的格式一样。这里采用列向量表示每组数据的各个特征,每一列就是一组数据。
(虽然代码写为矩阵计算,是多元的,但这是一个变量的测试)
先导入上面写好的线性回归的类,加载训练集,设置训练的学习速率和迭代次数,最后预测测试集的结果。然后,还用折线图画出了代价函数的迭代图。
import numpy as np
import matplotlib.pyplot as plt
from linear_regression import LinearRegression
import random
if __name__ == '__main__':
# 生成数据
X_train = np.arange(1, 30, 1)
X_train = X_train.reshape(1, X_train.shape[0])
Y_train = np.array([15, 11, 10, 32, 16, 20, 27, 44, 41, 48, 53, 39, 41, 40, 49, 36, 49, 94, 57, 45, 72, 96, 43, 81, 70, 99, 80, 91, 70])
X_test = np.arange(1, 30, 1)
X_test = X_test.reshape(1, X_test.shape[0])
N = 200 # 迭代次数
# 线性回归
lr = LinearRegression()
lr.init(X_train, Y_train)
lr.train(0.005, N)
predicted = lr.predict(X_test)
# 显示
# 测试集,模型参数w、b的函数
plt.subplot(1,2,1)
Y_test = predicted
plt.scatter(X_train, Y_train)
X_test = X_test.reshape(X_test.shape[1], 1)
Y_test = Y_test.reshape(Y_test.shape[1], 1)
plt.plot(X_test, Y_test, color = 'red')
# 迭代代价图
plt.subplot(1,2,2)
plt.plot([x for x in range(N)], lr.costs)
plt.show()
训练集
# 这是用random随机生产在自定义的线性方程上下有一定浮动的数据,保存成了列表
[15, 11, 10, 32, 16, 20, 27, 44, 41, 48, 53, 39, 41, 40, 49, 36, 49, 94, 57, 45, 72, 96, 43, 81, 70, 99, 80, 91, 70]
附
学习速率的选择
学习速率的选择要适度,如果选择小了,梯度下降太慢,需要更多次迭代才能达到适合的theta值;如果选择大了,每次乘以偏导值后,很可能会一直在极小值附近,并且可能会发散,导致无法收敛到极小值
特征缩放
为了更好的梯度下降,我们应该在进行梯度下降操作之前,将数据进行特征缩放。
(这是为了防止,一个数据的某个特征取值范围很小,另一个特征却很大,那每次梯度下降时,取值大的特征下降的很慢)
局部最优解
还需要注意的是,高数中,我们知道,如果一个函数比较复杂,它也许不止只有一个极小值点,我们通常想要得到的是最小值。梯度下降的方法,如果有多个极小值点,在初始点(初始的theta值)选定后,那将会总是达到同一个极小值点。
但是在线性回归中,因为就是简单的一元函数,故只有一个极小值,即最小值,无论初始的theta选择什么,总会通过多次迭代到同一个最小值。
代价函数的求导步骤
∂
∂
θ
j
J
(
θ
)
=
∂
∂
θ
j
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
−
y
(
i
)
)
)
2
\frac{\partial}{\partial \theta_j} J (\theta) = \frac{\partial}{\partial \theta_j} \frac{1}{2m} \sum_{i=1}^m (h_\theta (x^{(i)} - y^{(i)})) ^2
∂θj∂J(θ)=∂θj∂2m1i=1∑m(hθ(x(i)−y(i)))2
=
∂
∂
θ
j
1
2
m
∑
i
=
1
m
(
θ
0
+
θ
1
x
(
i
)
−
y
(
i
)
)
)
2
= \frac{\partial}{\partial \theta_j} \frac{1}{2m} \sum_{i=1}^m (\theta_0 + \theta_1 x^{(i)} - y^{(i)})) ^2
=∂θj∂2m1i=1∑m(θ0+θ1x(i)−y(i)))2
(
j
=
0
)
(j = 0)
(j=0)
θ
0
=
∂
∂
θ
0
J
(
θ
)
=
∂
∂
θ
0
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
−
y
(
i
)
)
)
2
\theta_0 = \frac{\partial}{\partial \theta_0} J (\theta) = \frac{\partial}{\partial \theta_0} \frac{1}{2m} \sum_{i=1}^m (h_\theta (x^{(i)} - y^{(i)})) ^2
θ0=∂θ0∂J(θ)=∂θ0∂2m1∑i=1m(hθ(x(i)−y(i)))2
(
j
=
1
)
(j = 1)
(j=1)
θ
1
=
∂
∂
θ
1
J
(
θ
)
=
∂
∂
θ
1
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
−
y
(
i
)
)
)
2
\theta_1 = \frac{\partial}{\partial \theta_1} J (\theta) = \frac{\partial}{\partial \theta_1} \frac{1}{2m} \sum_{i=1}^m (h_\theta (x^{(i)} - y^{(i)})) ^2
θ1=∂θ1∂J(θ)=∂θ1∂2m1∑i=1m(hθ(x(i)−y(i)))2
(
j
=
2
)
(j = 2)
(j=2)
θ
2
=
∂
∂
θ
2
J
(
θ
)
=
∂
∂
θ
2
1
2
m
∑
i
=
1
m
(
h
θ
(
x
(
i
)
−
y
(
i
)
)
)
2
\theta_2 = \frac{\partial}{\partial \theta_2} J (\theta) = \frac{\partial}{\partial \theta_2} \frac{1}{2m} \sum_{i=1}^m (h_\theta (x^{(i)} - y^{(i)})) ^2
θ2=∂θ2∂J(θ)=∂θ2∂2m1∑i=1m(hθ(x(i)−y(i)))2
…
(
j
=
0
,
1
,
2
,
.
.
.
,
n
)
(j = 0, 1, 2, ... , n)
(j=0,1,2,...,n)
其中,j = 0 时,
x
0
=
1
x_0 = 1
x0=1