• 最大熵源码解读


    最大熵源码解读

    先简要介绍一下最大熵,主要的参考资料是:

    • 《自然语言处理的最大熵模型》常宝宝
    • 《统计自然语言处理》第二章
    • 《条件随机场综述》韩雪东
    • 《Classical Probabilistic Models and Conditional Random Fields》 Roman Klinger

    一个问题

    假设已知随机变量X的一些特征,需要求解X的概率分布,怎么求解?X的概率分布长什么样子?

    最大熵原理的思想就是:

    在只掌握关于未知分布的部分知识时,应该选取符合这些知识但熵值最大的分布

    什么是熵值最大?如何衡量熵值?

    可参考《统计自然语言处理》第二章

    1. 我们不是对随机变量(X)一无所知,而是知道一些特征

    2. 选择那些符合特征的 概率分布,这个选择的过程,相当于最大熵模型的训练过程

    3. 将 符合特征的概率分布 并且 熵值 最大的概率分布,作为最终结果

      这些特征相当于约束条件,即:需要选择符合这些特征的 概率分布。那如何衡量一个概率分布是否符合特征呢?

      这就要靠收集的样本数据了。

      把样本数据拿过来,对每个特征(f_j)计算数学期望 (E^~_{p}f_j),这个数学期望称为:经验期望。

      待求解的概率分布(X)的数学期望,应该等于 经验期望----约束条件

      待求解的概率分布(X) 熵值最大----通过GIS算法迭代选取----熵值最大

    经过证明,符合上述约束条件且熵值最大的概率分布可写成如下形式:

    [p(x)=pi*prod_{j=1}^{k}alpha_j^{f_j(x)} ]

    至于如何证明的,感兴趣的就去找相关参考文献吧。

    特征函数(f_j(x))是个二值函数,因此从上式可看出:要想知道 (p(x)), 求出参数 (alpha_j)即可(特征(f_j(x))的权重)。而 (alpha_j)可使用GIS算法迭代求解出来。

    本文以最大熵的JAVA实现最大熵模型.pdf 论文记录最大熵模型的源码实现细节。

    整理好的训练样本 train.txt 如下:

    Outdoor Sunny Happy
    Outdoor Sunny Happy Dry
    Outdoor Sunny Happy Humid
    Indoor Rainy Happy Dry
    Indoor Rainy Sad Dry
    ....
    

    第一列代表事件,比如 在家、外出,用论文中的集合A表示。

    其他列代表环境(上下文),用论文中的集合B表示。

    Feature类代表特征,是个二值函数,封装了某事件对应的一个环境。一个事件,可对应多个环境。

        /**
         * 特征(二值函数)
         */
        class Feature
        {
            /**
             * 事件,如Outdoor
             */
            String label;
            /**
             * 事件发生的环境,如Sunny
             */
            String value;
    

    一个Instance对象代表一个观测实例。封装了一个事件及触发该事件的上下文环境,一个事件可由多个环境触发,故上下文环境用ArrayList保存。一个Instance对象相当于训练样本train.txt中的一行。

        /**
         * 一个观测实例,包含事件和时间发生的环境
         */
        class Instance
        {
            /**
             * 事件(类别),如Outdoor
             */
            String label;
            /**
             * 事件发生的环境集合,如[Sunny, Happy]
             */
            List<String> fieldList = new ArrayList<String>();
    

    训练的目的:

    训练的目的其实是计算出一组最优的拉格朗日乘子,对应表示每个特征函数有多重要。

    这里的拉格朗日乘子,就是代码中的 weight[] 数组保存。

        /**
         * 训练模型
         * @param maxIt 最大迭代次数
         */
        public void train(int maxIt)
        {
            int size = featureList.size();
            weight = new double[size];               // 特征权重 (训练之后得到的模型)
    

    计算经验期望

    根据《自然语言处理的最大熵模型》论文,对于第 j 个特征而言,经验期望的计算公式如下:

    [E^{''}f_j=Sigma_{i=1}^{N}{frac{1}{N}}f(a_i,b_i) ]

    经验期望 相当于 约束条件,代码实现如下:

            for (int i = 0; i < size; ++i)
            {
    	    /**
    	     * 特征i的经验期望 = 1/N*Sigma_{j=1}^{N}(f_i(a_j, b_j))
    	     * N = instanceList.size()
    	     * Sigma_{j=1}^{N}f(_i(a_j,b_j)) = featureCountList.get(i)
    	     */
                empiricalE[i] = (double) featureCountList.get(i) / instanceList.size();
            }
    

    经验期望计算出来后,保存起来就可以了,不需要重复计算。

    计算模型期望

    模型期望的计算公式可参考《自然语言处理的最大熵模型》论文的第五小节。需要说明一下的是:代码实现中,求解的是条件分布的最大熵。

    本文实现的最大熵模型属于条件最大熵模型。

    训练过程如下:

            for (int i = 0; i < maxIt; ++i)
            {
                computeModeE(modelE);
                for (int w = 0; w < weight.length; w++)
                {
                    lastWeight[w] = weight[w];
                    weight[w] += 1.0 / C * Math.log(empiricalE[w] / modelE[w]);
                }
                if (checkConvergence(lastWeight, weight)) break;
            }
    
    • maxIt 是迭代次数

    • computeModeE(modelE);计算模型期望

    • 接下来的for循环更新权重

    • checkConvergence(lastWeight, weight)检查是否收敛以停止迭代

    具体的实现细节不说了(大概了解一下,等以后碰到了再深入研究一下)。整个最大熵JAVA实现的流程是:

    根据训练样本:

    Outdoor Sunny Happy
    Outdoor Sunny Happy Dry
    Outdoor Sunny Happy Humid
    Indoor Rainy Happy Dry
    Indoor Rainy Sad Dry

    统计出 Outdoor 有多少次、Indoor 有多少次、在Sunny的条件下Outdoor又有多少次、在Rainy的条件下Indoor又有多少次……然后再根据论文中的公式,计算条件概率、联合概率、经验期望、模型期望啊……各种数据。然后再根据论文中的GIS算法来迭代,求解权重(alpha_j),最终将得到的权重保存下来weight[]数组,就是模型了,然后再拿它来进行预测。

        public Pair<String, Double>[] predict(List<String> fieldList)
        {
            double[] prob = calProb(fieldList);
    

    计算一个新样本的预测的概率:计算 calProb(fieldList)

        public double[] calProb(List<String> fieldList)
        {
            //....
                for (String field : fieldList)
                {
                    Feature feature = new Feature(labels.get(i), field);
                    int index = featureList.indexOf(feature);
                    if (index != -1)
                        weightSum += weight[index];
                }
        //....
        p[i] = Math.exp(weightSum);
    

    weightSum += weight[index]使用weight数组(保存的特征的权重)进行预测。

    总结

    有时候理论看多了,每次看到书上说:训练模型、用什么EM算法、迭代算法…求解代价函数的最优值、计算出模型的参数、使用模型参数对新样本预测……这些概念的时候,觉得挺抽象的。而看一个具体的实例,就能知道怎么从训练样本数据如何 转化成 论文中说的 各种需要的数据,模型的参数又是如何 用代码实现计算得到的,又是如何存储的……其实是如何把数学公式转化成编程语言,然后放在计算机上运行吧。

    最大熵JAVA实现github
    原文:https://www.cnblogs.com/hapjin/p/9093545.html

  • 相关阅读:
    个人阅读2
    代码复审
    PairProject 总结
    Pairproject 移山之道 阅读随笔和一些问题
    M1/M2个人总结
    团队项目个人总结
    个人阅读作业2
    代码互审
    《移山之道》读后感
    Individual Project
  • 原文地址:https://www.cnblogs.com/hapjin/p/9093545.html
Copyright © 2020-2023  润新知