上一期给出的散点可以根据seed()种子函数的不同,可以得出不同的散点图,并拟合出最接近给定参数的拟合曲线,现在我们想获取200组数据但是我们只使用前面50组数据对后面150组数据进行预测,看看准确性
先进行散点图的绘制
import numpy as np
import matplotlib.pyplot as plt
'''
返回数据集,形如[[x_1, y_1], [x_2, y_2], ..., [x_N, y_N]]
保证 bound[0] <= x_i < bound[1].
- N 数据集大小, 默认为 100
- bound 产生数据横坐标的上下界, 应满足 bound[0] < bound[1]
'''
def get_dataset(N=200, bound=(0, 100)):
l, r = bound
x = sorted(np.random.rand(N) * (r - l) + l)
y = np.sin(x) + np.random.randn(N) / 3
return np.array([x, y]).T
'''
最小二乘求出解析解, m 为多项式次数
最小二乘误差为 (XW - Y)^T*(XW - Y)
- dataset 数据集
- m 多项式次数, 默认为 5
'''
def fit(dataset, m=5):
X = np.array([dataset[:, 0] ** i for i in range(m + 1)]).T
Y = dataset[:, 1]
return np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)), X.T), Y)
'''
绘制给定系数W的, 在数据集上的多项式函数图像
- dataset 数据集
- w 通过上面四种方法求得的系数
- color 绘制颜色, 默认为 red
- label 图像的标签
'''
def draw(dataset, w, color='red', label=''):
X = np.array([dataset[:, 0] ** i for i in range(len(w))]).T
Y = np.dot(X, w)
plt.plot(dataset[:, 0], Y, c=color, label=label)
if __name__ == '__main__':
dataset = get_dataset(bound=(-3, 3))
# 绘制数据集散点图
for [x, y] in dataset:
plt.scatter(x, y, color='blue')
plt.show()
可以看到散点图总体还是比较分散的,这对预测的难度大大增强了
下一卜,进行全部200个散点的曲线拟合
for [x, y] in dataset:
plt.scatter(x, y, color='blue')
coef1 = fit(dataset)
draw(dataset, coef1, color='black', label='OLS')
plt.legend()
plt.show()
可以看到曲线还是比较符合散点走向的总体趋势的
接下来我们只对前面150个点进行曲线拟合,看看后50个点的预测效果,代码只需要把原来的范围修改一下即可
coef1 = fit(dataset[:150], m=3)
#coef1 = fit(dataset)
通过图片明显可以看到后面曲线拟合效果明显变差,我们再试试前50个点
效果很明显,预测效果差
前面两张图当m值超过进入计算的值时,过拟合严重。
可以看到拟合在前50或150个点表现很好;而在测试集上表现就很差,为了防止过拟合,可以引入正则化项
我们使用简单的梯度下降的方法
def GD(dataset, m = 3, max_iteration = 1000, lr = 0.01):
# 初始化参数
w = np.random.rand(m + 1)
N = len(dataset)
X = np.array([dataset[:, 0] ** i for i in range(len(w))]).T
Y = dataset[:, 1]
try:
for i in range(max_iteration):
pred_Y = np.dot(X, w)
# 均方误差(省略系数2)
grad = np.dot(X.T, pred_Y - Y) / N
w -= lr * grad
except RuntimeWarning:
print('梯度下降法溢出, 无法收敛')
return w
可以看到梯度下降的方法也就是黄色线,效果更好一点,也就验证了正则化方法是可以对预测效果进行准确性优化的