机器学习(1)泛化误差上界的实现及分析

        本文在假设空间有限情况下的泛化误差上界分析,并给出了简要的C语言实现。参考文献是李航老师写的《统计学习方法》。
        简单引出泛化误差是什么。对于任意给定的数据,选定模型对数据监督学习,得到学习后的模型,并使用此模型对新样本预测,以便对新数据、新样本预测分析。事实上,无论如何学习,经过学习产生的模型和实际数据产生的情况总会有一定偏差,这种偏差可以通过更大量样本的学习、更精确的模型选择、更高效的算法优化以削减,但几乎无法消除。我们总是期望能够从多种假设的模型中选择出最高效、误差最小的模型,并将其应用到现实问题中;或者仅仅是希望对某种模型的组合进行定量的分析,判断该模型是否可能具有产生较大误差的风险。这时候就需要用到泛化误差上界的分析。

        泛化误差说的是训练数据训练完模型之后,需要将该模型迁移到另一组类似数据时候,这个过程叫做泛化(Generalization),我们希望能够分析出来这种误差大概是多少,那么泛化误差就应该表示为所有可能出现的泛化误差值乘上他出现的可能性,也就是泛化误差的数学期望。
        选定模型为 f ^ \hat{f} f^,用这个模型来预测未知新样本,可能产生的风险为泛化风险,泛化风险表示为:
R e x p ( f ^ ) = E p [ L ( Y , f ^ ( X ) ) ] (1) R_{exp}(\hat{f})=E_{p}[L(Y,\hat{f}(X))]\tag{1} Rexp(f^)=Ep[L(Y,f^(X))](1)
         L ( Y , f ^ ( X ) ) L(Y,\hat{f}(X)) L(Y,f^(X))表示的是选定模型输出的结果与真实标签之间的误差,这种误差可以表示为0-1误差、平方误差、绝对值误差、对数误差等。误差的期望称为期望风险。泛化误差数学期望越低,说明在当前学习模式下,该模型的学习方法迁移过程中犯错的可能性会更低一些,也就是说这种模型会更好。了解泛化误差的上界在哪里,也就是说,既然要犯错,那最多能犯多大的错,这也就是标题中说的泛化误差上界。

        然而,理想是丰满的,现实是骨感的。想要求数学期望,我们需要知道每个误差产生的概率是多少,这在普遍情况下是难以提前预知的,因而我们只能退而求其次,使用已有的数据来推测数学期望,即经验风险。
        经验风险表示为
R ^ ( f ) = 1 N ∑ i = 1 N L ( y i , f ( x i ) ) (2) \hat{R}_(f)=\frac{1}{N}\sum_{i=1}^NL(y_i,f(x_i))\tag{2} R^(f)=N1i=1NL(yi,f(xi))(2)
        意思是根据已有的训练数据集里面的多个样本数据,模型的输出类别和期望类别之间损失的平均值。用这个平均值来代替数学期望,显然,当样本只有两三个的时候,用这个平均值来代替数学期望是很不靠谱的,所以泛化风险的上界会更高,随着训练样本的数量不断增多,泛化风险的上界也会逐渐下降,逐步趋于零。我们称所有备选模型组成的集合为假设空间,当假设空间容量越高,也就是需要从更多的模型中挑选一个最好的模型的时候,学习的成本也会相应提升,因而犯错误的可能性也就会提升,所以误差的上界也就会相应提升。
        因此,泛化误差的上界表示为两个部分,一部分是由已知的经验风险来表示,另一部分则是由训练样本数量、假设空间容量决定的误差函数组成,记为:
R ( f ) ≤ R ^ ( f ) + ϵ ( d , N , δ ) (3) R(f)\leq\hat{R}(f)+\epsilon(d,N,\delta)\tag{3} R(f)R^(f)+ϵ(d,N,δ)(3)
        上式的左侧表示选用 f f f的泛化误差,右侧两式之和则为泛化误差上界,其中 R ^ ( f ) \hat{R}(f) R^(f)已经在上文中给出,误差函数 ϵ \epsilon ϵ中的参数 N N N为样本的容量, d d d为假设空间的容量, δ \delta δ一般为一个较小的数字,例如0.1,0.05等,表示用这种方式来度量泛化误差的上界,只有10%或5%的可能性会犯错。 ϵ \epsilon ϵ表示如下:
ϵ ( d , N , δ ) = 2 N ( l n d − l n δ ) (4) \epsilon(d,N,\delta)=\sqrt{\frac{2}{N}(lnd-ln\delta)}\tag{4} ϵ(d,N,δ)=N2(lndlnδ) (4)
        泛化误差上界不等式的证明用到了Hoeffding不等式,这是一个概率上的不等式,Hoffeding’s Inequality的证明论文参见Hoeffding’s Inequality。泛化误差上界关于 ϵ \epsilon ϵ具体证明过程参见《统计学习方法》。特别地,用 1 − δ 1-\delta 1δ的概率来表示我们相信用这种方式推导出来的上界是没有问题的,当然也会有 δ \delta δ的概率犯错误,也就是泛化误差的实际数值超过了我们在这里通过经验风险和分析给出来的上界。
        至此,我们已经给出了泛化误差上界的计算公式,下面简单给出经验风险、泛化误差上界计算的C++简要实现。

#include <vector>
#include <cmath>
#include <iostream>
#include <cstdio>
using namespace std;

class Loss {
public:
    double loss0_1(vector<int>& pred, vector<int>& vexp){
        int N = pred.size();
        double loss = 0;
        for (int i = 0; i < N; i++) {
            loss += (pred[i] != vexp[i]);
        }
        return loss / N;
    }
    
    double loss_quadratic(vector<int>& pred, vector<int>& vexp) {
        int N = pred.size();
        double loss = 0;
        for (int i = 0; i < N; i++) {
            double temp = (pred[i] - vexp[i]);
            loss += temp * temp;
        }
        return loss / N;
    }

    double loss_absolute(vector<int>& pred, vector<int>& vexp) {
        int N = pred.size();
        double loss = 0;
        for (int i = 0; i < N; i++) {
            loss += abs(pred[i] - vexp[i]);
        }
        return loss / N;
    }

    double loss_logarithmic() {
        return 0;
    }
};

class Analysis {
public:
    double loss(vector<int>& vpred, vector<int>& vexp) {
        // 这里的误差函数是基于N个样本得到的经验风险值。
        // 经验风险empirical risk
        return Loss().loss0_1(vpred, vexp);
    }

    double epsilon(int d, int N, double delta) {
        double ans = sqrt(1.0 / (2.0 * N) * (log((double)d) - log(delta)));
        return ans;
    }

    // Generalization Error Bound: geb;
    double geb(vector<int>& vpred, vector<int>& vexp, int d, double prob) {
        // vpred: 由训练得到模型预测的结果
        // vexp:  原始数据希望得到的真实标签
        int N = vpred.size();
        // N表示样本容量。
        // d表示假设空间中模型的数量。
        // prob为置信度,也就是以多大概率相信该经验风险上界的分析结果。
        double delta = 1 - prob;
        return loss(vpred, vexp) + epsilon(d, N, delta);
    }
};



int main()
{
    vector<int> vexp(10), vpred(10);
    for (int i = 0; i < 10; i++) {
        if (i < 5) {
            vexp[i] = vpred[i] = i % 2;
        }
        else {
            vexp[i] = i % 2;
            vpred[i] = (i + 1) % 2;
        }
    }
    int d = 5;
    double prob = 1;
    double geb;
    for (int i = 0; i < 50; i++) {
        prob -= 0.05;
        geb = Analysis().geb(vpred, vexp, d, prob);
        printf("prob = %lf, geb = %lf. \n", prob, geb);
    }
    return 0;
}

        在 m a i n main main函数里面,随机编造了一组数据,前5个是两两相同的,而后五个数据是两两不同的。我同时输出了不同给定概率条件下面,泛化误差上界的数值,可以看到,越要求不可能犯错误(概率越接近1),泛化误差的上界给的就越高。

prob = 0.950000, geb = 0.979853.
prob = 0.900000, geb = 0.942268.
prob = 0.850000, geb = 0.918722.
prob = 0.800000, geb = 0.901178.

        以上就是泛化误差上界的分析过程以及C++的版本实现,如果看完觉得有用,就请不吝点赞吧!欢迎在评论区留言讨论。
        附:统计学习原理,该链接容纳了密歇根大学2014年冬季统计学习原理课程中中的全部资料,有Hoffeding不等式、贝叶斯分类器、决策树等相关英文资料,可自行阅读查验。
        我的Github链接为Statistic-Learning-Theory,在这个库中,我将陆续更新《统计学习方法》里每章核心代码的C++语言实现,欢迎大家订阅。现已更新至第二章。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ProfSnail

谢谢老哥嗷

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

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

打赏作者

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

抵扣说明:

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

余额充值