深度學習系列(9)——node2vec算法中的alias采樣介紹


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM