ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

深度学习系列(9)——node2vec算法中的alias采样介绍

2020-04-05 15:56:55  阅读:468  来源: 互联网

标签:采样 idx node2vec large alias small prob


1、说在前面

2、详细介绍

问题

  比如一个随机事件包含四种情况,每种情况发生的概率分别为: 1/2,1/3,1/12,1/12问怎么用产生符合这个概率的采样方法。

最容易想到的方法

  我之前有在【数学】均匀分布生成其他分布的方法中写过均匀分布生成其他分布的方法,这种方法就是产生0~1之间的一个随机数,然后看起对应到这个分布的CDF中的哪一个,就是产生的一个采样。比如落在0~ 1/2之间就是事件A,落在1/2~5/6之间就是事件B,落在5/6~11/12之间就是事件C,落在11/12~1之间就是事件D。
  但是这样的复杂度,如果用BST树来构造上面这个的话,时间复杂度为O(logN),有没有时间复杂度更低的方法。

一个Naive的办法

  1. 可以像上图这样采样,将四个事件排成4列:1~4,扔两次骰子,第一次扔1~4之间的整数,决定落在哪一列。

  2. 如上如所示,将其按照最大的那个概率进行归一化。在1步中决定好哪一列了之后,扔第二次骰子,0~1之间的任意数,如果落在了第一列上,不论第二次扔几,都采样时间A,如果落在第二列上,第二次扔超过2/3则采样失败,重新采样,如果小于2/3则采样时间B成功,以此类推。
  3. 这样算法复杂度最好为O(1)最坏有可能无穷次,平均意义上需要采样O(N)
那怎么去改进呢?

Alias Method

  还是如上面的那样的思路,但是如果我们不按照其中最大的值去归一化,而是按照其均值归一化。即按照1/N(这里N是情况的种数,这里四种)归一化,即为所有概率乘以N,得到如下图:

  其总面积为N,然后可以将其分成一个1*N的长方形,如下图:

  将前两个多出来的部分补到后面两个缺失的部分中。
  先将1中的部分补充到4中:

  这时如果,将1,2中多出来的部分,补充到3中,就麻烦了,因为又陷入到如果从中采样超过2个以上的事件这个问题中,所以Alias Method一定要保证:每列中最多只放两个事件
  所以此时需要将1中的补满3中去:

  再将2中的补到1中:

至此整个方法大功告成

Alias Method具体算法如下:

  1. 按照上面说的方法,将整个概率分布拉平成为一个1*N的长方形即为Alias Table,构建上面那张图之后,储存两个数组,一个里面存着第ii列对应的事件ii矩形站的面积百分比【也即其概率】,上图的话数组就为Prab[2/3, 1, 1/3, 1/3],另一个数组里面储存着第ii列不是事件ii的另外一个事件的标号,像上图就是Alias[1 0 0 0]
2.产生两个随机数,第一个产生1~N 之间的整数i,决定落在哪一列。扔第二次骰子,0~1之间的任意数,判断其与Prab[i]大小,如果小于Prab[i],则采样i,如果大于Prab[i],则采样Alias[i]

3、算法实现

import numpy as np

def alias_table(prob_table):
    n = len(prob_table)
    prob_norm = np.array(prob_table) * n
    small, large = [], []
    for i, prob in enumerate(prob_norm):
        if prob < 1:
            small.append(i)
        else:
            large.append(i)

    accept, alias = [0] * n, [0] * n
    while small and large:
        small_idx, large_idx = small.pop(), large.pop()
        # 存放第i列对应的事件i的概率;
        accept[small_idx] = prob_norm[small_idx]
        # 存放不是事件i的另外事件的标号;
        alias[small_idx] = large_idx

        prob_norm[large_idx] = prob_norm[large_idx] - (1 - prob_norm[small_idx])
        if prob_norm[large_idx] < 1.0:
            small.append(large_idx)
        else:
            large.append(large_idx)

    while small:
        small_idx = small.pop()
        accept[small_idx] = 1

    while large:
        large_idx = large.pop()
        accept[large_idx] = 1
    print(accept)
    print(alias)
    return accept, alias

def alias_sample(accept, alias):
    n = len(accept)
    i = int(np.random.random() * n)
    r = np.random.random()
    if r < accept[i]:
        return i
    else:
        return alias[i]

if __name__ == '__main__':
    prob_table = [1 / 2, 1 / 3, 1 / 12, 1 / 12]
    ac, a = alias_table(prob_table)
    i = alias_sample(ac, a)
    print(i)

————————————————
版权声明:本文为CSDN博主「哈乐笑」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/haolexiao/article/details/65157026

标签:采样,idx,node2vec,large,alias,small,prob
来源: https://www.cnblogs.com/SupremeBoy/p/12637530.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有