@yuichirominato 2019.02.26更新 268views

【RBM】イジングRBMとnetworkx表示とJijの準備


はじめに

量子アニーリングやD-WaveでRBM、制限付きボルツマンマシンをやりたい時には必要なツールはあまりありませんが、とりあえず最低限を揃えてみます。

networkx

RBMを量子アニーリングでやる際には無向グラフで十分ですので、networkxとmatplotlibを使ってやってみたいと思います。

import matplotlib.pyplot as plt
import networkx as nx

#無向グラフ
G = nx.Graph()

#RBMで使うエッジを指定、ノードは自動的に指定される
G.add_edges_from([(0,4),(0,5),(0,6),(0,7),(1,4),(1,5),(1,6),(1,7),(2,4),(2,5),(2,6),(2,7),(3,4),(3,5),(3,6),(3,7)])

#綺麗に並べたいので場所指定
pos = {0:[0.1,0.4],1:[0.1,0.3],2:[0.1,0.2],3:[0.1,0.1],4:[0.2,0.4],5:[0.2,0.3],6:[0.2,0.2],7:[0.2,0.1]}

nx.draw_networkx(G,pos)
plt.show()

ぼちぼちいい感じではないでしょうか。

次に、量子ビットを指定し、バイアスと結合荷重を初期化してみます。使用するのは量子ビットを表すリストのqとバイアスと結合荷重が上三角行列のmatrixになったJです。

class rbm:
    def __init__(self):
        self.J = []
        self.v = 0
        self.h = 0
        self.n = 0
     
    def setRBM(self,v=4,h=4):
        n = v+h
        self.v = v
        self.h = h
        self.n = n
        self.J = np.diag([np.random.randint(99)/100 for i in range(n)])
        for i in range(v):
            for j in range(v,n):
                self.J[i][j] = np.random.randint(99)/100
        return self
     
    def network(self):
        G= nx.Graph()     
        edges = []
        for i in range(self.v):
            edges += [(i,j) for j in range(self.v,self.n)]
        G.add_edges_from(edges)
        pos = {}
        for i in range(self.n):
            if i<self.v:
                pos[i] = [0.1,0.1*(self.v-i)]
            else:
                pos[i] = [0.2,0.1*(self.h+self.v-i)]
        nx.draw_networkx(G,pos)
        plt.show()

ついでにネットワークまで設定してくれるようにしました。こんな感じです。

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np

a = rbm().setRBM(3,6).network()

まぁ、こんなもんでしょう。

アニーリング計算

この状態でアニーリングをかけてみます。アニーリングは任意の状態からスタートして、バイアスと結合荷重で結果が決まります。また、RBMの場合には近い値での局所解がたくさんありそうです。

from blueqat.opt import Opt

a = rbm().setRBM(4,4)
a.J

#=>
array([[0.  , 0.  , 0.  , 0.52, 0.87, 0.09],
       [0.  , 0.08, 0.  , 0.21, 0.35, 0.22],
       [0.  , 0.  , 0.31, 0.51, 0.62, 0.3 ],
       [0.  , 0.  , 0.  , 0.7 , 0.  , 0.  ],
       [0.  , 0.  , 0.  , 0.  , 0.55, 0.  ],
       [0.  , 0.  , 0.  , 0.  , 0.  , 0.81]])

#これをアニーリングにかけてみます。
#複数回を使うカウンター
import collections
def counter(narr):
    dis = []
    for i in range(len(narr)):
        dis.append(''.join([str(x) for x in narr[i]]))
    return collections.Counter(dis)

c = Opt().add(a.J).run(shots=100)

#カウント
counter(c)

#=>
Counter({'000000': 48, '100000': 49, '010000': 2, '110000': 1})

全体の分布

上記は4つの状態が出てきました。この分布を使って更新を行います。分布は温度によって影響されます。最近ではpause+quenchなどの機能もでていますが、今回は逆温度パラメータと呼ばれるものを使って分布を調整してみます。

def bars(cc):
    index = []
    value = []
    for k,v in counter(cc).items():
        index.append(k)
        value.append(v)
    plt.bar(index,value)
    plt.show()

bars(c)

これで、

こんな感じです。逆温度パラメータはjijの前にパラメータ$\beta$をつけます。これによって、分布を変えてみます。$\beta=0.1$としてみて、

beta = 0.1
c = Opt().add(beta*a.J).run(shots=100)
bars(c)

これにより、

より複雑な分布をとることができました。

Counter({'011000': 4,
         '000000': 12,
         '001000': 7,
         '100000': 29,
         '010000': 15,
         '110000': 24,
         '000001': 1,
         '101000': 3,
         '111000': 5})

学習について

学習に関しては、学習すべきパラメータが3種類あります。
1、visible layerのbias
2、hidden layerのbias
3、v*hの結合荷重

1は簡単でデータの入力値を使います。

2は一旦visible layerからsigmoidを通してhiddenlayerの値を計算します。

3がめんどいのですが、CD法を使う代わりにアニーリングを使って、v*hの期待値をだしてからの1/Nして平均を取ります。

次回はこちらの学習を中心にしてまずはパターン認識をしてみたいと思います。以上です。