zoukankan      html  css  js  c++  java
  • Efficient Knowledge Graph Accuracy Evaluation 实现记录

    介绍

    本文记录了 Efficient Knowledge Graph Accuracy Evaluation 的实现过程。目前实现了在随机生成的三元组上进行 static evaluation 和 incremental evaluation。

    Github 地址:https://github.com/zzk0/KGAccEval

    整体结构

    整体结构采用论文中描述的结构。

    文件结构:

    KnowledgeGraph, SamplePool, SampleCollector, Estimation 这几个类相对比较独立,由 Evaluation 这个类来组织和调用。

    Static Evaluation

    具体可参考 Evalution.java 这个文件。


    1, 抽样条件

    根据以下条件决定是否需要继续抽样,一个是检查误差界限是否大于目标值,一个是检查是否还能继续抽取。

    this.estimation.getMarginOfError() > this.epsilon && remainTriples
    

    2,抽样过程

    根据方法去抽样,调用对应的 SampleCollector,将得到的样本放入 SamplePool,同时计算每一次抽样的估计量。计算估计量,比如 SRS 中估计量就是每一个样本正确与否,正确为 1,错误为 0。再比如 TWCS 中估计量就是一个实体的样本正确率。


    3,调用例子

    double suggestedAccuracy = 0.73;
    
    KnowledgeGraph kg = new KnowledgeGraph();
    kg.init(suggestedAccuracy, 1000);
    
    Evaluation evaluation = new Evaluation(epsilon, alpha, Method.TWCS);
    acc = evaluation.evaluate(kg);
    System.out.println(acc);
    

    Incremental Evaluation

    增量评估在实现的时候,总想着不去改动代码来做,但是面对变化,保持不变是不行的。

    增量评估分为 ReservoirIncrementalEvaluation 和 StratifiedIncrementalEvaluation,两个是独立的类,虽然可以抽象出共同的 evalute 接口。这个部分实现的时候,根据论文中描述的两个算法的需要,去获取 Evalutaion 中的信息,所以需要在 Evalutaion 中增加一些获取信息的方法。

    这个部分没有实现完全,对于蓄水池算法的增量评估,如果 MoE 小于目标值了呢?我没有做处理。

    调用例子:

    // base evaluation
    Evaluation evaluation = new Evaluation(epsilon, alpha, Method.TWCS);
    acc = evaluation.evaluate(kg);
    System.out.println(acc);
    
    ReservoirIncrementalEvaluation incrementalEvaluation = new ReservoirIncrementalEvaluation();
    incrementalEvaluation.init(evaluation, kg);
    
    StratifiedIncrementalEvaluation incrementalEvaluation1 = new StratifiedIncrementalEvaluation();
    incrementalEvaluation1.init(evaluation, kg);
    
    double[] accs = {0.27, 0.8, 0.2, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.9};
    for (int i = 0; i < accs.length; i++) {
        // update evaluation
        KnowledgeGraph update = new KnowledgeGraph();
        update.init(accs[i], 1000);
    
        System.out.println("---------------------------------------");
    
        double newAccuracy = incrementalEvaluation.evaluate(update);
        System.out.println("Reservoir Incremental Evaluation: " + newAccuracy);
    
        newAccuracy = incrementalEvaluation1.evaluate(update);
        System.out.println("Stratified Incremental Evaluation: " + newAccuracy);
    }
    

    实现细节


    如何随机生成知识图谱数据

    我们随机产生知识图谱的三元组,三元组的内容是空的,但是预先给它确定是正确还是错误。

    KnowledgeGraph 类中有一个方法 init,需要传入准确率和实体个数。对每一个实体,随机确定实体的三元组个数,并且随机改变这个实体的准确率,之后进行伯努利实验,产生一个 0 到 1 之间的随机数,检查是否小于准确率(也在 0 和 1 之间),如果是,那么产生的三元组为正确的三元组,如果否,那么产生的三元组为错误的三元组。


    随机采样

    简单随机采样,假设三元组一共有 n 个。有两种简单随机采样的方法。

    第一种,产生一个 [0, n) 的整数随机数,用散列表来记录这个随机数是否产生过,如果没有,那么抽取这个随机数对应的三元组;如果有,那么重新执行这个过程。不过这种方法有问题,当我们抽取的三元组越来越多,重新抽取的次数会越来越多,如果只有 100 个三元组,并且我们抽取了 99 个,那么要抽取到最后一个三元组的期望次数是 100.

    第二种,产生一个 List,这个 List 顺序放入 [0, n),然后使用 Collections.shuffle 打乱。从头开始抽取即可。


    加权采样

    加权采样的方法有二种。

    第一种,类似随机采样的第一种。因为抽取的是实体,所以直接产生一个 [0, n) 的整数随机数,这个随机数落到的那个实体就被抽取出来。如果实体的三元组个数越多,那么被抽取的概率越大。同样,使用一个散列表记录是否抽取过,这个方法同样存在缺陷。

    第二种,蓄水池算法。给每个实体一个 key,(r = Rand(0,1), key = r^{1 / size}),选取前 k 大个 key。


    正态分布分位点

    浙大版概率论中第 50 页有分位点的定义。

    我们要找 (alpha) 的置信区间,需要找到 (z_frac{1-alpha}{2}) 这个点,使到正态分布落在大于(z_frac{1-alpha}{2}) 的概率为 (frac{1-alpha}{2}),这样可以构造一个对称区域,落到两边的概率为 (1-alpha),落到中间的概率就是 (alpha) 了。

    那么这个分位点如何计算呢?查了一下,有一个叫做 Hastings formula 的东西[1],可以计算上 (alpha) 分位点。

    比如对于 95% 的置信区间,我们需要调用 computeZx(0.025) 去计算分位点。

    private double computeZx(double alpha) {
        double[] c = {2.515517, 0.802853, 0.010328};
        double[] d = {0.0, 1.432788, 0.189269, 0.001308};
        double y = Math.sqrt(-2 * Math.log(alpha));
        double cy = 0, dy = 0;
        for (int i = 0; i < 2; i++) {
            cy = cy + c[i] * Math.pow(y, i);
            dy = dy + d[i + 1] * Math.pow(y, i+1);
        }
        return y - cy / (dy + 1);
    }
    

    static evaluation 正确性

    假设我们需要求解 90% 的置信区间,5% 的误差界限,这意味着准确率误差范围在 5% 以内的概率为 90%。

    在 Example.java 这个文件中,我们重复进行了 100 次实验,计算准确落在误差范围内的比例,大致接近 90%。

    BTW,误差界限含义,顾名思义就是误差的范围。


    Reservoir Incremental Evaluation

    假设我们要采样总体 (G + Delta) 的样本,可是我们只有采样了 (G) 的样本,如何采样 (Delta) 才能得到 (G + Delta) 的样本呢?

    这就是蓄水池算法的优越之处了:蓄水池算法可以在不了解总体的情况下,进行采样!

    假设 (G) 是 TWCS 采样的结果,我们是根据实体三元组多少加权随机抽样的。我们可以将其视为对 (G) 使用蓄水池算法计算的结果,所以当新的三元组加进来的时候,我们按照蓄水池算法运行下去,就可以得到 (G + Delta) 的结果。

    我在实验的时候,一开始我的做法是保持原来的加权抽样的代码不变。我们可以拿到加权采样的结果,比如有 60 个实体。于是,我对这 60 个实体计算蓄水池算法的 key。可是这些 key 的大小其实分布得比较散,在进行增量更新的时候,计算的 key 大部分会覆盖原来的那些,把原来的样本替换掉。

    道理很简单,我加权挑出来的这 60 个 key,是先挑再算权值,不是先算权值再挑的。假如增量更新有 1000 个实体,那么相当于随机产生 1060 个key,再去挑选最大的几个。这里有点强者生存的感觉,对于原来的那 60 个 key,如果是先计算 key 再挑选,那么留下来的 key 都是比较“强”的。如果是先挑,再计算 key,那么这些 key 比较随机,容易被后面的强者替代。所以这个部分,我将原来加权抽样的代码做了更改,先计算 key,再挑选。在增量评估的初始化的时候,获取这些 key。


    增量评估

    开始做这个部分时,一直在想如何尽可能不要改懂代码来实现。

    这种想法是徒然的,妄图在不改变代码的情况下增加新功能。代码还是要改,要加点代码。不过现在回想起来,其实,原来的代码结构还行吧,至少做到了一点:对扩展开放,对修改封闭。修改代码是尽量减少了,除了那个加权采样的算法必须得改之外,其他地方保持不动。


    有待解决的问题

    1. 在蓄水池算法的增量评估当中,这个部分实现的不完整。当蓄水池算法抽样完成之后,还需要重新计算 MoE,检查是否在范围内,如果不在,那么还需要再抽样。
    2. 在蓄水池算法的增量评估当中,当进行多次增量的时候,比如原知识图谱正确率为 0.5,不断地添加正确率为 0.9 的知识图谱,此时会发现正确率上升的很慢。因为蓄水池当中的那些个随机数都是身经百战,强者生存下来的,所以很难被替代。
    3. 论文提供了增量评估的方法,却没有提供“减量”评估的方法。我认为可以这样处理,假设使用的是 TWCS 评估的结果,并且 TWCS 使用的加权采样是由蓄水池算法来实现的,那么我们可以去掉“减量”,重新挑选前 k 大的实体。
    4. 论文中说,按照实体的三元组多少的分层是不理想的。那么该如何分层呢?论文中直接使用实体的正确率来分层,得出的实验结果很美好,暗示了合理的分层有助于提高评估的效率。
    5. 使用正态分布还是使用 t 分布?虽然在样本多的情况下,差异是不大的,但是仍然有可能出现样本小的情况。

    参考文献

    [1] https://wenku.baidu.com/view/3ca32836581b6bd97f19ea8c.html?re=view

  • 相关阅读:
    【LeetCode 15】三数之和
    【LeetCode 14】最长公共前缀
    【LeetCode 13】罗马数字转整数
    【LeetCode 12】整数转罗马数字
    【LeetCode 11】盛最多水的容器
    【LeetCode 10】正则表达式匹配
    【LeetCode 9】回文数
    【LeetCode 8】字符串转换整数 (atoi)
    【LeetCode 7】整数反转
    【LeetCode 6】Z 字形变换
  • 原文地址:https://www.cnblogs.com/zzk0/p/13446970.html
Copyright © 2011-2022 走看看