机器学习中欠拟合和过拟合是一个很常见的问题,以下通过python代码模型对欠拟合、拟合和过拟合进行一个简单的示例。

%matplotlib inline # 在Jupyternote中运行的话,这条语句使得所生成的图直接嵌入在notebook中
import matplotlib.pyplot as plt
import numpy as np

n_dots = 20
x = np.linspace(0, 1, n_dots)                # [0, 1] 之间创建 20 个点
y = x**3 + 0.2*np.random.rand(n_dots) - 0.1; # 立方函数,加上一点随机噪声,再加上一个固定offset

plt.figure(figsize=(18, 8), dpi=200)
titles = ['Under Fitting', 'Fitting', 'Over Fitting', 'completely remembered']
models = [None, None, None, None]
for index, order in enumerate([1, 3, 10, 19]):
    plt.subplot(2, 2, index + 1)
    p = np.poly1d(np.polyfit(x, y, order))
    # 画出拟合出来的多项式所表达的曲线以及原始的点
    t = np.linspace(0, 1, 200)
    plt.plot(x, y, 'ro', t, p(t), '-', t, t**3, 'r--')
    models[index] = p
    plt.title(titles[index], fontsize=20)

        以上代码中,首先生成一个立方函数序列,然后加上一点随机噪声,以及一个固定的offset。

        然后,分别调用numpy中的polyfit函数,分别使用4种不同的阶数参数进行拟合。

        最后将不同拟合结果绘图表示。

        运行结果如下图所示:

欠拟合(Under-Fitting)、拟合(Fitting)、过拟合(Over-Fitting)示例-LMLPHP

        由于我们已知原曲线是3阶函数(虽然加了一些噪声),所以用1阶线性拟合肯定是不够的,如左上角所示这是一个欠拟合的例子。

        用3阶函数拟合有噪声的3阶数据序列恰好,如右上图所示,虽然由于有噪声的影响,拟合曲线没有经过所有的点,但是目测也可以看出拟合曲线基本上跟随了原序列的变化趋势。如果将数据生成中的噪声项去掉的话,比如说修改如下:

        则会得到3阶拟合时的曲线如下所示(确实是完美的拟合):

欠拟合(Under-Fitting)、拟合(Fitting)、过拟合(Over-Fitting)示例-LMLPHP

        左下和右下两个图是典型的过拟合的情况,其中右下角的图由于参数个数等于数据点数,拟合曲线精确地穿过了原数据序列的每一个数据点,通俗一点说就是,拟合模型记住了每一个数据点!

        以上代码段还把拟合模型记录下来了,以下将这4个模型的参数打印出来看看从参数情况能不能看出一些过拟合和欠拟合的迹象来。

for m in models:
    print('model coeffs: {0}'.format(m.coeffs))
    
for m in models:
    print('model coeffs sum-of-square: {0}'.format(np.sum(np.square(m.coeffs))))

        运行结果如下: 

        如上所示,关于欠拟合,从模型参数是看不出什么来的,但是从模型参数可以看出过拟合的迹象!过拟合体现在参数上就是会出现(绝对)值很大的参数。越大的参数意味着越复杂的模型,对于一个简单的数据(for example, generated from simple underlying function)用一个复杂的模型去拟合当然会产生过拟合的现象!

        以上将所有参数的平方和打印出来,可以看出后面两种过拟合情况的参数平方和非常大! 事实上机器学习中常用的L1正则化和L2正则化就是通过对很大的参数(将参数绝对值和或者参数平方和计入损失函数中)进行惩罚来规避或者缓和过拟合效果的。

11-12 10:26