机器学习基础(3)—— 泛化能力、过拟合与欠拟合

  • 参考:
    1. 西瓜书第二章
    2. 李航《统计学习方法(第二版)》第一章
    3. 《动手学深度学习》第三章

1. 训练误差、测试误差与泛化性能

1.1 训练误差与测试误差

  • 统计学习的目的是使学到的模型不仅在训练集上有良好的表现,更要能对未知数据给出良好的预测,要从训练样本中尽可能找出适用于所有潜在样本的 “普遍规律”
  • 对于学得的基于损失函数的模型 Y = f ^ ( X ) Y=\hat{f}(X) Y=f^(X),当损失函数给定时
    1. 训练误差是模型关于训练数据集的平均损失(就是经验风险,反映了模型在训练集上的预测性能。训练样本容量为 N N N 时,训练误差为
      R e m p ( f ^ ) = 1 N ∑ i = 1 N L ( y i , f ^ ( x i ) ) R_{emp}(\hat{f}) = \frac{1}{N}\sum_{i=1}^N L(y_i,\hat{f}(x_i)) Remp(f^)=N1i=1NL(yi,f^(xi))
    2. 测试误差是模型关于训练数据集的平均损失,反映了模型的泛化性能。测试样本容量为 N ′ N' N 时,测试误差为
      R t e s t ( f ^ ) = 1 N ′ ∑ i = 1 N ′ L ( y i , f ^ ( x i ) ) R_{test}(\hat{f}) = \frac{1}{N'}\sum_{i=1}^{N'} L(y_i,\hat{f}(x_i)) Rtest(f^)=N1i=1NL(yi,f^(xi))
  • 对于学习器来说,它能参考的只有训练样本,实际能做到只是让训练误差最小。我们可以轻松地获得一个在训练集上经验误差很小甚至为 0 的学习器,但这种学习器的泛化性能可能很差
    1. 训练误差的大小,可以用来判断给定的问题是不是一个容易学习的问题,但本质上并不重要
    2. 测试误差反映了模型对于未知测试数据的预测能力,是体现泛化性能的重要指标

1.2 泛化能力

  • 泛化能力generalization ability:学习方法的泛化能力指由此方法学到的模型对未知数据的预测能力,是学习方法本质上的重要性质
  • 现实应用中常用测试误差来评价泛化能力,但这种评价是依赖于测试集的,因为测试数据集大小有限,很有可能由此得到的评价结果不可靠。统计学习理论试图从理论上对学习方法的泛化能力进行分析

1.2.1 泛化误差

  • 泛化误差generalization error:设学得的模型是 f ^ \hat{f} f^,用这个模型对未见数据预测的误差即为泛化误差
    R e x p ( f ^ ) = E P ( L ( Y , f ^ ( X ) ) ) = ∫ X × Y L ( y , f ^ ( x ) ) P ( x , y ) d x d y \begin{aligned} R_{exp}(\hat{f}) &= \mathbb{E}_P(L(Y,\hat{f}(X))) \\ &=\int_{\mathcal{X\times Y}}L(y,\hat{f}(x))P(x,y)dxdy \end{aligned} Rexp(f^)=EP(L(Y,f^(X)))=X×YL(y,f^(x))P(x,y)dxdy
  • 泛化误差本质上就是学得模型的期望风险,反映了模型的泛化能力。如果方法 A 比方法 B 的泛化误差小,那么我们就认为方法 A 更有效
  • 我们之前分析过,机器学习的目标就是最小化期望风险(即上述泛化误差),注意到其中联合分布 P ( X , Y ) P(X,Y) P(X,Y) 未知,所以我们使用数据集上的经验分布估计它,也就是使用经验风险估计期望风险,通过最小化经验风险(即上述训练误差)来学习,详见 机器学习基础(2)—— 统计学习方法三要素 第 2.1 节

1.2.2 泛化误差上界

  • 学习方法泛化能力的分析往往是通过研究泛化误差的概率上界来进行的,简称为 泛化误差上界generalization error bound。具体来说,就是通过比较两算法的泛化误差上界来比较它们的优劣
  • 泛化误差上界通常具有以下性质
    1. 样本容量增加时,泛化误差上界 → 0 \to 0 0
    2. 是关于假设空间容量 (capacity) 的函数,假设空间越\大,模型越难学,泛化误差上界越大
  • 举例来说,对于二分类问题,当假设空间是有限个函数的集合 F = { f 1 , f 2 , . . . , f d } \mathcal{F}=\{f_1,f_2,...,f_d\} F={f1,f2,...,fd} 时,对任意一个函数 f ∈ F f\in\mathcal{F} fF,至少以概率 1 − δ 1-\delta 1δ 0 < δ < 1 0<\delta<1 0<δ<1 ,以下不等式成立
    R ( f ) ≤ R ^ ( f ) + ε ( d , N , δ )   其 中   ε ( d , N , δ ) = 1 2 N ( log ⁡ d + log ⁡ 1 δ ) R(f)\leq \hat{R}(f) +\varepsilon(d,N,\delta) \\ \space\\ 其中\space \varepsilon(d,N,\delta) = \sqrt{\frac{1}{2N}(\log d+\log \frac{1}{\delta})} R(f)R^(f)+ε(d,N,δ)  ε(d,N,δ)=2N1(logd+logδ1) 证明见李航《统计学习方法(第二版)》1.6

2. 过拟合和欠拟合

2.1 基础概念

  • 过拟合overfitting
    1. 定义:学习器把训练样本 “学得太好” ,以至于把训练样本自身的一些特点当作了潜在样本都具有的一般特性。
    2. 危害:导致泛化性能下降。
    3. 成因:学习性能太强,以至于把训练样本中包含的不太一般的特性或数据噪音都学到了(学习性能是否 “过于强大” 是由学习算法和数据内涵共同决定的),学得模型复杂度太高
    4. 解决过拟合是不可避免的。可以这样理解:机器学习问题通常是NP难甚至更难,而有效的学习算法必然是在多项式时间内运行完成的。若可以彻底避免过拟合,则通过经验误差最小化即可求得最优解,这就构造化地证明了P=NP各类学习算法中,都采取了一些针性措施来缓解其影响,比如正则化、早停等,关键是要找到复杂度合适的模型
  • 欠拟合underfitting
    1. 定义:学习器对训练样本的一般特性尚未学好
    2. 危害:无论训练集还是新样本,性能都不是最佳
    3. 成因:学习能力低下,或是在模型尚未收敛时就停止学习,尚未学得训练样本的主要特征,学得模型复杂度太低
    4. 解决:增强学习能力,比如在决策树中增加分支、在神经网络中增加训练轮数等,关键是要找到复杂度合适的模型
  • 直观示例
    在这里插入图片描述

2.2 加深理解

  • 模型的参数个数越多,我们说它的复杂度越高,能够拟合的目标函数也就越复杂。理想情况下,我们希望从假设空间中选出的最优模型的参数个数和真实模型参数个数相同(复杂度相同),且参数向量和真实模型参数向量相近

    1. 模型复杂度超过真实复杂度时,学到的输入空间到输出空间的映射比真实映射更复杂,函数曲线更加弯曲
    2. 模型复杂度低于真实复杂度时,学到的输入空间到输出空间的映射比真实映射更简单,函数曲线更加平直
  • 例如我们做一个多项式回归任务,假设数据是由 M M M 次多项式生成,样本 x x x 的真实标记为
    y = f M ( x , w ) = w 0 + w 1 x + w 2 x 2 + . . . + + w M x M = ∑ j = 0 M w j x j y = f_M(x,w) = w_0+w_1x+w_2x^2+...++w_Mx^M = \sum_{j=0}^Mw_jx^j y=fM(x,w)=w0+w1x+w2x2+...++wMxM=j=0Mwjxj 其中 w 1 , w 2 , . . . . , w M w_1,w_2,....,w_M w1,w2,....,wM 是模型参数。通常采取以下步骤解决此问题

    1. 确定模型复杂度,即确定多项式次数 M M M
    2. 在给定的复杂度下,按照经验风险最小化策略求解参数(即多项式系数),具体来说,最小化以下损失函数
      L ( w ) = 1 2 ∑ i = 1 N ( f ( x i , w ) − y i ) 2 = 1 2 ∑ i = 1 N ( ∑ j = 0 M w j x j − y i ) 2 \begin{aligned} L(w) &= \frac{1}{2}\sum_{i=1}^N(f(x_i,w)-y_i)^2\\ &= \frac{1}{2}\sum_{i=1}^N(\sum_{j=0}^Mw_jx^j-y_i)^2 \end{aligned} L(w)=21i=1N(f(xi,w)yi)2=21i=1N(j=0Mwjxjyi)2 其中 1 2 \frac{1}{2} 21 是为了求梯度时消去常数部分,简化计算。此优化问题可以使用最小二乘法求得解析解,也可以用梯度下降法求出数值解,不再详细说明

    假定给出 10 个数据点,使用 0~9 次多项式函数进行拟合,数据点、真实分布(正弦曲线)以及 M = 0 , 1 , 3 , 9 M=0,1,3,9 M=0,1,3,9 时的拟合曲线如下所示
    在这里插入图片描述

    1. 信息论告诉我们数据 = 信息 + 噪音,训练数据是真实分布信息(正弦曲线)的体现,受噪声影响,训练数据点分散在真实曲线周围
    2. M = 0 M=0 M=0 M = 1 M=1 M=1 时,模型过于简单,拟合效果很差,属于欠拟合
    3. M = 9 M=9 M=9 时,模型过于复杂,虽然学到的多项式曲线穿过了所有数据点,训练误差为 0,但是曲线整体和真实曲线差别很大,测试集上性能会很差,属于过拟合。这里可以理解为复杂模型表示能力太强,以至于把训练数据上的噪音也学到了
    4. M = 3 M=3 M=3 时,多项式曲线对训练数据拟合得足够好,模型也比较简单,是较好的选择
  • 从上例可见,随着多项式次数(模型复杂度)的增加,训练误差会不断减少到趋于 0;测试误差先减小而后增大
    在这里插入图片描述
    下面是我在做 BERT 预训练时观察到的真实过拟合现象
    在这里插入图片描述

  • 特别明确一点,所谓 “模型复杂度” 是一个相对概念,它其实考虑的是 “模型复杂度” 和 “训练数据复杂程度” 的相对关系通常情况下数据是固定的,所以只考虑模型复杂度,其实反过来固定模型,训练数据复杂程度的变化(受真实分布、采样均匀程度、数量量影响)也会导致过拟合和欠拟合,见下面 2.4.5 节

2.3 探究本质:偏差-方差分解

  • 本节从数学原理上分析测试误差呈现 U 型的原因,我们知道测试误差是对泛化误差的估计,所以我们从泛化错误率的角度入手,使用 偏差-方差分解(bias-variance decomposition) 这个数学工具对学习算法的期望泛化错误率进行拆解.

  • 对测试样本 x \pmb{x} xxx,令 y D y_D yD x \pmb{x} xxx 在数据集中的标记, y y y x \pmb{x} xxx 的真实标记, f ( x , D ) f(\pmb{x},D) f(xxx,D) 为训练集 D D D 上学得模型 f f f x \pmb{x} xxx 的预测输出。考虑上面那样的回归问题,学习算法的期望预测为
    f ˉ ( x ) = E D [ f ( x ; D ) ] \bar{f}(\pmb{x}) = \mathbb{E}_D[f(\pmb{x};D)] fˉ(xxx)=ED[f(xxx;D)] 固定训练使用的样本数量,训练集的不同采样会导致预测方差
    v a r ( x ) = E D [ ( f ( x ; D ) − f ˉ ( x ) ) 2 ] var({\pmb{x}}) = \mathbb{E}_D\Big[\big(f(\pmb{x};D)-\bar{f}(\pmb{x})\big)^2\Big] var(xxx)=ED[(f(xxx;D)fˉ(xxx))2] 同时,采样噪声会使得样真实标记 y y y 和采样标记 y D y_D yD 不同,噪声体现为
    E 2 = E D [ ( y D − y ) 2 ] \mathcal{E}^2 = \mathbb{E}_D\Big[\big(y_D-y\big)^2\Big] E2=ED[(yDy)2] 最后,模型输出和真实标记之间有偏差
    b i a s 2 ( x ) = ( f ˉ ( x ) − y ) 2 bias^2(\pmb{x}) = (\bar{f}(\pmb{x})-y)^2 bias2(xxx)=(fˉ(xxx)y)2 为了便于讨论,假设噪声的期望为零,即 E D [ y d − y ] = 0 \mathbb{E}_D[y_d-y]=0 ED[ydy]=0,用多项式展开对算法的期望泛化误差 E ( f ; D ) E(f ; D) E(f;D) 进行分解
    E ( f ; D ) = E D [ ( f ( x ; D ) − y D ) 2 ] = E D [ ( f ( x ; D ) − f ˉ ( x ) + f ˉ ( x ) − y D ) 2 ] = E D [ ( f ( x ; D ) − f ˉ ( x ) ) 2 ] + E D [ ( f ˉ ( x ) − y D ) 2 ] + E D [ 2 ( f ( x ; D ) − f ˉ ( x ) ) ( f ˉ ( x ) − y D ) ] = E D [ ( f ( x ; D ) − f ˉ ( x ) ) 2 ] + E D [ ( f ˉ ( x ) − y D ) 2 ] = E D [ ( f ( x ; D ) − f ˉ ( x ) ) 2 ] + E D [ ( f ˉ ( x ) − y + y − y D ) 2 ] = E D [ ( f ( x ; D ) − f ˉ ( x ) ) 2 ] + E D [ ( f ˉ ( x ) − y ) 2 ] + E D [ ( y − y D ) 2 ] + 2 E D [ ( f ˉ ( x ) − y ) ( y − y D ) ] = E D [ ( f ( x ; D ) − f ˉ ( x ) ) 2 ] + ( f ˉ ( x ) − y ) 2 + E D [ ( y D − y ) 2 ] , \begin{aligned} E(f ; D)=& \mathbb{E}_D\left[\left(f(\boldsymbol{x} ; D)-y_D\right)^2\right] \\ =& \mathbb{E}_D\left[\left(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x})+\bar{f}(\boldsymbol{x})-y_D\right)^2\right] \\ =& \mathbb{E}_D\left[(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x}))^2\right]+\mathbb{E}_D\left[\left(\bar{f}(\boldsymbol{x})-y_D\right)^2\right] +\mathbb{E}_D\left[2(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x}))\left(\bar{f}(\boldsymbol{x})-y_D\right)\right] \\ =& \mathbb{E}_D\left[(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x}))^2\right]+\mathbb{E}_D\left[\left(\bar{f}(\boldsymbol{x})-y_D\right)^2\right] \\ =& \mathbb{E}_D\left[(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x}))^2\right]+\mathbb{E}_D\left[\left(\bar{f}(\boldsymbol{x})-y+y-y_D\right)^2\right] \\ =& \mathbb{E}_D\left[(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x}))^2\right]+\mathbb{E}_D\left[(\bar{f}(\boldsymbol{x})-y)^2\right]+\mathbb{E}_D\left[\left(y-y_D\right)^2\right] +2 \mathbb{E}_D\left[(\bar{f}(\boldsymbol{x})-y)\left(y-y_D\right)\right] \\ =& \mathbb{E}_D\left[(f(\boldsymbol{x} ; D)-\bar{f}(\boldsymbol{x}))^2\right]+(\bar{f}(\boldsymbol{x})-y)^2+\mathbb{E}_D\left[\left(y_D-y\right)^2\right], \end{aligned} E(f;D)=======ED[(f(x;D)yD)2]ED[(f(x;D)fˉ(x)+fˉ(x)yD)2]ED[(f(x;D)fˉ(x))2]+ED[(fˉ(x)yD)2]+ED[2(f(x;D)fˉ(x))(fˉ(x)yD)]ED[(f(x;D)fˉ(x))2]+ED[(fˉ(x)yD)2]ED[(f(x;D)fˉ(x))2]+ED[(fˉ(x)y+yyD)2]ED[(f(x;D)fˉ(x))2]+ED[(fˉ(x)y)2]+ED[(yyD)2]+2ED[(fˉ(x)y)(yyD)]ED[(f(x;D)fˉ(x))2]+(fˉ(x)y)2+ED[(yDy)2], 于是有
    E ( f ; D ) = b i a s 2 ( x ) + v a r ( x ) + E 2 E(f ; D) = bias^2(\pmb{x}) + var(\pmb{x})+\mathcal{E}^2 E(f;D)=bias2(xxx)+var(xxx)+E2

  • 我们得到一个重要结论:泛化误差可以分解为偏差、方差与噪声之和,其中

    1. 偏差度量了学习算法的期望预测与真实结果的偏离程度,刻画的是学习算法本身的拟合能力
    2. 方差度量了同样大小的训练集变动所导致的学习性能的变化,刻画了数据扰动所造成的影响(采样误差)
    3. 噪声表达了在当前任务上任何学习算法所能达到的期望泛化误差的下界(即使预测和数据集标记完全一致,还是和真实标记差一个噪声),刻画了学习问题本身的难度

    这说明泛化性能是由 “学习算法的能力”、“数据的充分性” 以及 “学习任务本身的难度” 所共同决定的。给定学习任务,为了取得好的泛化性能,需要

    1. 使偏差较小,即能够充分拟合数据
    2. 使方差较小,即使得数据扰动产生的影响小.
  • 偏差-方差窘境bias-variance dilemma:一般而言,偏差和方差是有冲突的,如下图所示
    在这里插入图片描述

    大多数学习算法都可以控制训练程度,给定学习任务,随着训练程度不断提高

    1. 训练不足时,学习器的拟合能力不够强,训练数据的扰动不足以便学习器产生显著变化,此时偏差主导泛化错误率,即发生欠拟合
    2. 随着训练程度的加深,学习器的拟合能力逐渐增强,训练数据发生的扰动逐渐被学到,方差逐渐主导泛化错误率;
    3. 训练程度充足后,学习器的拟合能力已非常强,训练数据的轻微扰动就会使学习器发生显著变化,若训练数据自身的、非全局的特性被学习器学到了,则将发生过拟合

2.4 线性回归实验

  • 利用 pytorch 进行上面的多项式拟合实验。*以下代码从 jupyter 文档转换而来,可能无法直接运行
  • 拟合的目标函数为三次多项式 y = 1.2 x − 3.4 x 2 + 5.6 x 3 + 5 y=1.2x - 3.4x^2 + 5.6x^3 + 5 y=1.2x3.4x2+5.6x3+5,增加一个来自高斯分布的采样噪声 ϵ ∼ N ( 0 , 1.5 ) \epsilon \sim N(0,1.5) ϵN(0,1.5),如下生成样本标签
    y = 1.2 x − 3.4 x 2 + 5.6 x 3 + 5 + ϵ y=1.2x - 3.4x^2 + 5.6x^3 + 5 +\epsilon y=1.2x3.4x2+5.6x3+5+ϵ

2.4.1 生成数据

  • 用以下代码生成尺寸均为 100 的训练集和验证集,注意做数据预处理,把原始数据转换为非线性成分 polynomial_features

    %matplotlib notebook
    import numpy as np
    import math
    import scipy.stats as st
    import matplotlib.pylab as plt
    import numpy as np
    from scipy import stats
    import torch
    import random
    from IPython import display
    
    # 真实参数
    n_train, n_test = 100, 100
    true_w, true_b = [1.2, -3.4, 5.6], 5
    
    # 构造样本集(训练集 & 验证集)
    features = torch.randn((n_train + n_test, 1))
    polynomial_features = torch.cat((features, torch.pow(features, 2), torch.pow(features, 3)), dim=1) # y = 1.2x - 3.4x^2 + 5.6x^3 + 5 + epsilon
    labels = (true_w[0] * polynomial_features[:, 0] + true_w[1] * polynomial_features[:, 1] + true_w[2] * polynomial_features[:, 2] + true_b)
    labels += torch.tensor(np.random.normal(0, 1.5, size=labels.size()), dtype=torch.float)
    
    # 绘制带噪的目标函数 & 样本集
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False    # 用来正常显示负号
    
    x = torch .linspace(-1,1,5000).view(5000,1)
    y_items = torch.cat((x, torch.pow(x, 2), torch.pow(x, 3)), dim=1) # y = 1.2x - 3.4x^2 + 5.6x^3 + 5 + epsilon
    y = (true_w[0] * y_items[:, 0] + true_w[1] * y_items[:, 1] + true_w[2] * y_items[:, 2] + true_b)
    y += torch.tensor(np.random.normal(0, 1.5, size=y.size()), dtype=torch.float)
    
    fig = plt.figure(figsize = (10,5))
    a0 = fig.add_subplot(1,2,1,label='a0')         
    a0.plot(x, y, "r-",label='带噪的目标函数', linewidth=0.05)
    a0.set_title('带高斯噪声的目标函数 $y=1.2x - 3.4x^2 + 5.6x^3 + 5 + \epsilon$')
    a1 = fig.add_subplot(1,2,2,label='a1')         
    a1.scatter(features[:n_train, :], labels[:n_train], s=1,alpha=1,cmap="rainbow", label='训练集')
    a1.scatter(features[n_train:, :], labels[n_train:], s=1,alpha=1,cmap="rainbow", label='测试集')
    a1.set_title('训练集 & 验证集')
    a1.legend()
    

    在这里插入图片描述

2.4.2 定义模型和训练方法

  • 要做多项式回归,注意到多项式可以看做非线性成分的线性组合,因此可以把数据预处理扩展为非线性成分 [ x , x 2 , x 3 ] [x,x^2,x^3] [x,x2,x3],把原问题转换成这些成分的线性回归问题

  • 先定义一个在一张图中绘制两条曲线的函数,用来对比训练误差和验证误差随训练进度的变化

    # 绘图函数,在一张图中绘制两条曲线
    def semilogy(x_vals, y_vals, x_label, y_label, x2_vals=None, y2_vals=None, legend=None, semilogy=True, figsize=(3.5, 2.5)):
        # 设置图像尺寸
        display.set_matplotlib_formats('svg')  # Use svg format to display plot in jupyter
        fig = plt.figure(figsize = figsize)
        	    
        # 坐标轴文本
        plt.xlabel(x_label)
        plt.ylabel(y_label)
        
        # 绘制第一组数据
        if semilogy: plt.semilogy(x_vals, y_vals)  # y轴使用对数尺度的点线图
        else: plt.plot(x_vals, y_vals)             # 普通点线图
        
        # 绘制第二组数据,y轴使用对数尺度的点线图(如果有的话)
        if x2_vals != None and y2_vals != None:
            if semilogy: plt.semilogy(x2_vals, y2_vals, linestyle=':')
            else: plt.plot(x2_vals, y2_vals, linestyle=':')
            plt.legend(legend)
        
        plt.show()
    
  • 用全连接层作为线性模型,MSE 损失,随机梯度下降优化器,训练100个 epoch,记录训练误差和验证误差的变化过程

    def fit_and_plot(train_features, test_features, train_labels, test_labels):
        # 多项式回归转化为线性回归进行拟合
        # 设置一个线性层,输入维度是多项式次数(不含零次/偏置项),输出维度是1,通过设置 bias=True 要求学习偏置项
        # torch.nn.Linear 会自动初始化权重和偏置参数
        net = torch.nn.Linear(in_features=train_features.shape[-1], out_features=1, bias=True)
        
        # 先把训练样本/标记拼成 dataset,然后对它设置 DataLoader,这是个迭代器,每次访问返回 batch_size 量的数据
        batch_size = min(10, train_labels.shape[0])    
        dataset = torch.utils.data.TensorDataset(train_features, train_labels)
        train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)
        
        # 随机梯度下降优化器
        optimizer = torch.optim.SGD(net.parameters(), lr=0.01) 
        
        # MSE 损失函数
        loss = torch.nn.MSELoss()         
        
        # 进行训练
        num_epochs = 100              # 训练迭代次数
        train_ls, test_ls = [], []    # 记录训练损失和验证损失
        for _ in range(num_epochs):
            # 进行一步小批量随机梯度下降
            for X, y in train_iter:
                l = loss(net(X), y.view(-1, 1))
                optimizer.zero_grad()
                l.backward()
                optimizer.step()
            
            # 评估并记录训练损失 & 测试损失
            train_labels = train_labels.view(-1, 1)
            test_labels = test_labels.view(-1, 1)
            train_ls.append(loss(net(train_features), train_labels).item())
            test_ls.append(loss(net(test_features), test_labels).item())
        
        # 打印
        print('final epoch train loss:', train_ls[-1], '\nfinal epoch test loss:', test_ls[-1])
        print('weight:', net.weight.data, '\nbias:', net.bias.data)
        
        # 训练损失和测试损失变化曲线
        semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
                 range(1, num_epochs + 1), test_ls, ['train', 'test'], semilogy=True)
    
        return net
    

2.4.3 模型复杂度合适

  • 使用与数据生成函数同阶的三阶多项式函数拟合。这时模型复杂度合适,训练误差和在验证误差都较低,训练出的模型参数也接近真实值
    # 三阶多项式函数拟合(正常)
    net = fit_and_plot(polynomial_features[:n_train, :], polynomial_features[n_train:, :], labels[:n_train], labels[n_train:])
                                                                                                              
    # 绘制拟合结果
    fig = plt.figure(figsize = (5,5))
    fitted_x = torch .linspace(features.min().item(),features.max().item(),800).view(800,1)
    fitted_y_items = torch.cat((fitted_x, torch.pow(fitted_x, 2), torch.pow(fitted_x, 3)), dim=1) # y = 1.2x - 3.4x^2 + 5.6x^3 + 5 + epsilon
    plt.scatter(features, labels, s=1, cmap="rainbow", label='raw')
    plt.scatter(fitted_x, net(fitted_y_items).detach().numpy(), s=0.1, alpha=0.5, cmap="rainbow", label='fitted')
    plt.legend()
    plt.show()
    
    在这里插入图片描述

2.4.4 模型复杂度过低导致欠拟合

  • 使用线性函数拟合,这时模过于简单,训练误差在迭代早期下降后便很难继续降低,此时很容易欠拟合
    # 线性函数拟合(欠拟合)
    net = fit_and_plot(features[:n_train, :], features[n_train:, :], labels[:n_train], labels[n_train:])
    
    # 绘制拟合结果
    fig = plt.figure(figsize = (5,5))
    fitted_x = torch.linspace(features.min().item(),features.max().item(),800).view(800,1)
    plt.scatter(features, labels, s=1, cmap="rainbow", label='raw')
    plt.scatter(fitted_x, net(fitted_x).detach().numpy(), s=0.1, alpha=0.5, cmap="rainbow", label='fitted')
    plt.legend()
    plt.show()
    

在这里插入图片描述

2.4.5 模型复杂度过高导致过拟合

  • 注意 2.2 节最后说明了模型复杂度的相对性,2.4.4 节我们是控制数据不变,减小模型复杂度来造成欠拟合;本节我们控制模型不变,减小数据复杂度使模型复杂度相对过高,从而造成过拟合。这里使用减少数据复杂度的最简单方法 —— 减少训练集样本量,这里我们只使用两个样本来训练模型,训练样本甚至少于模型参数的数量,模型过于相对复杂,以至于容易被训练数据中的噪声影响。迭代过程中尽管训练误差较低,但是测试数据集上的误差却很高,这是典型的过拟合现象

    # 样本不足情况下过拟合
    net = fit_and_plot(polynomial_features[:2, :], polynomial_features[n_train:, :], labels[:2], labels[n_train:])
    
    # 绘制拟合结果
    fig = plt.figure(figsize = (5,5))
    fitted_x = torch .linspace(features.min().item(),features.max().item(),800).view(800,1)
    fitted_y_items = torch.cat((fitted_x, torch.pow(fitted_x, 2), torch.pow(fitted_x, 3)), dim=1) # y = 1.2x - 3.4x^2 + 5.6x^3 + 5 + epsilon
    plt.scatter(features, labels, s=1, cmap="rainbow", label='raw')
    plt.scatter(fitted_x, net(fitted_y_items).detach().numpy(), s=0.1, alpha=0.5, cmap="rainbow", label='fitted')
    plt.legend()
    plt.show()
    

在这里插入图片描述

3. 总结

  • 回顾一下本文介绍的各个概念及相关关系
    1. 训练模型的本质是最小化训练误差,即最小化经验风险
    2. 训练模型的目的是最小化泛化误差,即最小化期望风险
    3. 真实的泛化误差无法评估,我们使用测试误差估计泛化误差
    4. 由于测试集大小有限,测试误差不能很好地衡量和对比学习方法的泛化能力,更准确的方法是计算泛化误差上界
    5. 随着模型复杂度从低到高,训练误差不断减小,测试误差会先减少后增大,U型曲线左右分别对应欠拟合过拟合
    6. 模型复杂度是模型和数据间的相对概念,模型复杂度相对高/低对导致过拟合/欠拟合
    7. 偏差-方差分解说明:泛化误差 = 偏差+方差+噪声,欠拟合和过拟合阶段的泛化误差分别由偏差和噪声主导
  • 李宏毅机器学习课里有一张训练模型的 general guide,很好地体现了训练误差、测试误差、欠拟合过拟合等概念
    在这里插入图片描述
    图中显示了欠拟合和过拟合间,即模型和数据间相对复杂度的 trade-off 关系,也指出可以通过 模型选择方法 选出复杂度适当的模型,使其测试误差最小化,从而间接地保证得到最佳泛化性能。接下来用两篇文章来介绍模型选择
    1. 机器学习基础(4)—— 模型选择之评估方法
    2. 机器学习基础(5)—— 模型选择之性能度量
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值