1 多变量线性回归应用场景
目前为止,我们探讨了单变量/特征的回归模型,现在我们对房价模型增加更多的特征,例如房间数楼层等,构成一个含有多个变量的模型.。
1.1 单变量线性回归案例
- 模型: hθ(x) = θ0 + θ1x
1.2 多变量线性回归案例
- 模型:
- 新的概念
x(1) = [40, 1, 1, 10]
x(2) = [96, 2, 1, 5]
x(3) = [135, 3, 2, 20]
x(1)1 = 40
x(1)2 = 1
.......
2 多元梯度下降法
- 模型:
- 参数:
- 损失函数:
- 梯度下降公式(重复执行):
2.1 一元梯度下降n=1, 重复执行,直到收敛
2.2 多元梯度下降n>1
2.3 多元批梯度下降代码
import numpy as np
# 1). 模拟数据
X1 = 2 * np.random.randn(100, 1)
X2 = 4 * np.random.rand(100, 1)
X3 = 6 * np.random.rand(100, 1)
y = 4 + 3 * X1 + 4 * X2 + 5 * X3 + np.random.randn(100, 1)
# 2). 实现梯度下降算法
# np.c_是将数据组合成向量格式: (n, 1) (n,1) = (n, 2)
X_b = np.c_[np.ones((100, 1)), X1, X2, X3]
# 初始化theta的值, 需要计算四个theta的值;
theta = np.random.randn(4, 1)
# 设置学习率和收敛次数
learning_rate = 0.1
n_iterations = 1000
# 根据公式计算
for iteration in range(n_iterations):
# 梯度下降公式 = 1/样本数 * (预测值 - 真实值) *Xi
gradients = 1 / 100 * X_b.T.dot(X_b.dot(theta) - y)
# theta = theta - 学习率 * 梯度值
theta = theta - learning_rate * gradients
print(theta)
- 代码执行结果:
3 梯度下降法实践一:特征缩放
3.1 梯度下降法遇到的问题
在我们面对多维特征问题的时候,我们要保证这些特征都具有相近的尺度,这将帮助梯度下降算法更快地收敛。而特征缩放是为了确保特征在一个数量级上。
以房价问题为例,假设我们使用两个特征,房屋的尺寸和房间的数量,其中x1 = 房屋面积(0-400 m2), x2 = 卧室数量(1-5), 以两个参数分别为横纵坐标,绘制代价函数的等高线图能,看出图像会显得很扁,梯度下降算法需要非常多次的迭代才能收敛。
3.2 解决方法
-
解决方法一:
- 尝试将所有特征的尺度都尽量缩放到-1到1之间。比如:
x1 = 房屋面积 / 400
x2 = 卧室数量 / 5
也可以把最大值换成标准差,或者最大值 – 最小值。
4 梯度下降法实践二: 学习率
4.1 梯度下降法遇到的问题
梯度下降算法收敛所需要的迭代次数根据模型的不同而不同,我们不能提前预知,我们可以绘制迭代次数和代价函数的图表来观测算法在何时趋于收敛。
梯度下降算法的每次迭代受到学习率的影响,
- 如果学习率过小,则达到收敛所需的迭代次数会非常高;
- 如果学习率过大,每次迭代可能不会减小代价函数,可能会越过局部最小值导致无法收敛。
4.2 解决方法
- 自动测试是否收敛的方法,例如将代价函数的变化值与某个阀值(例如0.001)进行比较,但通常看上面这样的图表更好。
尝试在如下的数值中选择α : …, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1,…
5 梯度下降算法补充
5.1 三种梯度下降总结
如何选择?
- 训练集比较小: 使用批梯度下降(小于2000个)
- 训练集比较大:使用Mini-bitch梯度下降 一般的Mini-batch size 是64,128,256, 512,1024, Mini-batch size要适用CPU/GPU的内存
5.2 随机梯度下降
随机梯度下降思想:把m个样本分成m份,每次用1份做梯度下降;也就是说,当有m个样本时,批梯度下降只能做一次梯度下降,但是随机梯度下降可以做m次。
- 实现代码
import numpy as np
import random
X = 2 * np.random.rand(100, 1)
Y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# 每轮epochs处理m个样本;
n_epochs = 1000
# 学习率
a0 = 0.1
# 定义衰减率
decay_rate = 1
def learning_schedule(epoch_num):
"""
定义一个学习率衰减的函数
"""
return (1.0 / (decay_rate * epoch_num + 1)) * a0
# 初始化theta值
theta = np.random.randn(2, 1)
# 初始化随机值
num = [i for i in range(100)]
m = 100
for epoch in range(n_epochs):
rand = random.sample(num, 100)
for i in range(m):
random_index = rand[i]
xi = X_b[random_index:random_index + 1]
yi = Y[random_index:random_index + 1]
# 随机梯度下降值
gradients = xi.T.dot(xi.dot(theta) - yi)
# 学习率
learning_rate = learning_schedule(epoch+1)
theta = theta - learning_rate * gradients
print(theta)
- 执行结果展示:
5.3 Mini-batch梯度算法
随机梯度下降会丧失向量带来的加速,所以我们不会太用随机梯度下降。
- 实现代码
import numpy as np
import random
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# print(X_b)
n_epochs = 500
a = 0.03
m = 100
num = [i for i in range(100)]
theta = np.random.randn(2, 1)
batch_num = 5
batch_size = m // 5
# epoch 是轮次的意思,意思是用m个样本做一轮迭代
for epoch in range(n_epochs):
# 生成100个不重复的随机数
for i in range(batch_num):
start = i*batch_size
end = (i+1)*batch_size
xi = X_b[start:end]
yi = y[start:end]
gradients = 1/batch_size * xi.T.dot(xi.dot(theta)-yi)
print(a)
learning_rate = a
theta = theta - learning_rate * gradients
print(theta)
- 执行结果展示:
5.4 Mini-batch梯度算法优化: 学习率衰减
在做Mini-batch的时候,因为噪声的原因,可能训练结果不是收敛的,而是在最低点周围晃动,如果我们要解决这个问题,那我们就需要减少学习率,让他在尽量小的范围内晃动
1 epoch = 1 次遍历所有的数据
- 学习率衰减公式:
- 实现代码
import numpy as np
import random
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# print(X_b)
n_epochs = 500
t0, t1 = 5, 50
m = 100
num = [i for i in range(100)]
def learning_schedule(t):
return float(t0) / (t + t1)
theta = np.random.randn(2, 1)
batch_num = 5
batch_size = m // 5
# epoch 是轮次的意思,意思是用m个样本做一轮迭代
for epoch in range(n_epochs):
# 生成100个不重复的随机数
for i in range(batch_num):
start = i*batch_size
end = (i+1)*batch_size
xi = X_b[start:end]
yi = y[start:end]
gradients = 1/batch_size * xi.T.dot(xi.dot(theta)-yi)
learning_rate = learning_schedule(epoch*m + i)
theta = theta - learning_rate * gradients
print(theta)
- 执行结果展示:
6 特征和多项式回归
6.1 过拟合的问题
过拟合的问题出现在变量(θ)过多的时候,这时候我们没有更多的数据去拟合模型,虽然损失函数的值基本接近于0。
6.2 过拟合的解决方法:
- 减少特征的数量(一般不用)
1)手动选择特征数
2)模型选择 - 正则化(特征缩放)
保留所有特征,但是减少量级或者参数θ_j的大小
6.2 特征缩放
房价预测时, 假设我们不知道房屋面积,但是知道房屋的长宽。
- 模型设计:
hθ(x) = θ0 + θ1 x 房屋的长度 + θ2 x 房屋的宽度 - 特征未缩放图形展示
- 特征缩放图形展示
注:如果我们采用多项式回归模型,在运行梯度下降算法前,特征缩放非常有必要。
6.3 正则化
- 如何不想要theta3和theta4?
然后正则化, 公式如下图:
6.4 L1 正则和 L2 正则的区别
- L1 会趋向于减少特征值
- L2 会趋向于保留特征值
7 正则化算法与代码实现
7.1 Ridge(岭)回归
7.1.1 算法理解
7.1.2 实现公式
7.1.3 代码实现
- 两种实现岭回归的方法:
"""
岭回归
方法一: 岭回归运用了L2正则化
"""
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.linear_model import SGDRegressor
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
# alpha是惩罚项里的alpha, solver处理数据的方法,auto是根据数据自动选择,svd是解析解,sag就是随机梯度下降
ridge_reg = Ridge(alpha=1, solver='auto')
# 学习过程
ridge_reg.fit(X, y)
# 预测
print(ridge_reg.predict([[1.5], [2], [2.5]]))
# 打印截距
print(ridge_reg.intercept_)
# 打印系数
print(ridge_reg.coef_)
"""
方法二: 岭回归和sgd & penalty=2是等价的
"""
sgd_reg = SGDRegressor(penalty='l2')
sgd_reg.fit(X, y.ravel())
print(sgd_reg.predict([[1.5], [2], [2.5]]))
# 打印截距
print("W0=", sgd_reg.intercept_)
# 打印系数
print("W1=", sgd_reg.coef_)
7.2 Lasso(拉索)回归
7.2.1 算法理解
7.2.2 实现公式
7.2.3 代码实现
"""
Lasso 回归
Lasso用的是l1的正则化
"""
import numpy as np
from sklearn.linear_model import Lasso
from sklearn.linear_model import SGDRegressor
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
lasso_reg = Lasso(alpha=0.15)
lasso_reg.fit(X, y)
print(lasso_reg.predict([[1.5]]))
print(lasso_reg.coef_)
sgd_reg = SGDRegressor(penalty='l1', n_iter=1000)
sgd_reg.fit(X, y.ravel())
print(sgd_reg.predict([[1.5]]))
print(sgd_reg.coef_)
7.3 Elastic Net回归
7.3.1 算法理解
7.3.2 实现公式
7.3.3 代码实现
import numpy as np
from sklearn.linear_model import ElasticNet
X = 2 * np.random.rand(100, 1)
Y = 4 + 3 * X + np.random.randn(100, 1)
elastic_reg = ElasticNet(alpha=0.15, l1_ratio=0.5)
elastic_reg.fit(X, Y)
print(elastic_reg.predict([[1.5]]))
print(elastic_reg.coef_)
print(elastic_reg.intercept_)
from sklearn.linear_model import SGDRegressor
elastic_reg = SGDRegressor(penalty='elasticnet')
elastic_reg.fit(X, Y)
print(elastic_reg.predict([[1.5]]))
print(elastic_reg.coef_)
8 正规方程和梯度下降比较
梯度下降:
- 需要选择合适的α
- 需要多次迭代
- 当n很大时,效果很好
正规方程:
- 不需要选择学习率a
- 不需要迭代
- 需要计算X的转置乘X整体的逆
- 当n很大时,计算很慢
总结:根据经验,当特征数量到10000的时候,是会换成梯度下降比较好
8.1 多项式回归的梯度下降代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# 1). 数据准备;
# 样本数
m = 100
X = 6 * np.random.randn(m, 1) - 3
Y = 0.5 * X ** 2 + X + 2 + np.random.randn(m, 1)
# 2). 处理
# 2-1). 将一个高阶方程转化为一个一阶方程;(多元线性回归)
# degree:用几维处理数据;
poly_features = PolynomialFeatures(degree=2, include_bias=False)
# fit_transform === fit() + transform(), 其中transform就是用来做归一化的;
X_poly = poly_features.fit_transform(X, Y)
# 2-2). 处理一阶方程
line_reg = LinearRegression()
line_reg.fit(X_poly, Y)
print(line_reg.coef_)
print(line_reg.intercept_)
8.2 不同维度绘制的图形
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# 1). 数据准备;
# 样本数
m = 100
X = 6 * np.random.randn(m, 1) - 3
Y = 7 * X ** 2 + 5 *X + 2 + np.random.randn(m, 1)
# plt.plot(X, Y, 'b.')
# plt.show()
# 设置图像维度及线条的字体显示
d = {1: 'g-', 2: 'r.', 10: 'y*'}
# d = {2: 'g-'}
for i in d:
# 2). 处理
# 2-1). 将一个高阶方程转化为一个一阶方程;(多元线性回归)
# degree:用几维处理数据;
poly_features = PolynomialFeatures(degree=i, include_bias=False)
# fit_transform === fit() + transform(), 其中transform就是用来做归一化的;
X_poly = poly_features.fit_transform(X)
print(X_poly)
# 2-2). 处理一阶方程
line_reg = LinearRegression()
line_reg.fit(X_poly, Y)
print(line_reg.coef_)
print(line_reg.intercept_)
y_predict = line_reg.predict(X_poly)
plt.plot(X_poly[:, 0], y_predict, d[i])
plt.show()