Uczenie się ciężarów w maszynie Boltzmanna

14

Próbuję zrozumieć, jak działają maszyny Boltzmann, ale nie jestem pewien, w jaki sposób uczone są ciężary i nie byłem w stanie znaleźć jasnego opisu. Czy poniższe informacje są prawidłowe? (Również wskazania do każdego dobrego wyjaśnienia maszyny Boltzmanna byłyby również świetne.)

Mamy zestaw jednostek widocznych (np. Odpowiadających czarno-białym pikselom na obrazie) i zestaw jednostek ukrytych. Wagi są w jakiś sposób inicjalizowane (np. Równomiernie od [-0,5, 0,5]), a następnie przełączamy się między kolejnymi dwiema fazami, aż do osiągnięcia pewnej reguły zatrzymania:

  1. Faza zaciśnięta - W tej fazie wszystkie wartości widocznych jednostek są stałe, dlatego aktualizujemy tylko stany ukrytych jednostek (zgodnie z regułą aktywacji stochastycznej Boltzmanna). Aktualizujemy, aż sieć osiągnie równowagę. Kiedy osiągniemy równowagę, kontynuujemy aktualizację więcej razy (dla niektórych predefiniowanych N ), śledząc średnią z x i x j (gdzie x i , x j są stanami węzłów i i j ). Po tych aktualizacjach N równowagi aktualizujemy w i j = w i j +N.N.xjaxjotxja,xjotjajotN., gdzieCoznacza pewną szybkość uczenia się. (Czy zamiast przeprowadzać aktualizację wsadową na końcu, czy aktualizujemy po kroku równowagi?)wjajot=wjajot+1doZAvmirzasolmi(xjaxjot)do

  2. Faza wolna - W tej fazie stany wszystkich jednostek są aktualizowane. Kiedy osiągniemy równowagę, podobnie kontynuujemy aktualizację N 'więcej razy, ale zamiast dodawać korelacje na końcu, odejmujemy: .wjajot=wjajot-1doZAvmirzasolmi(xjaxjot)

Więc moje główne pytania to:

  1. Za każdym razem, gdy jesteśmy w fazie zaciśniętej, resetujemy widoczne jednostki do jednego z wzorców, których chcemy się nauczyć (z pewną częstotliwością, która reprezentuje znaczenie tego wzoru), czy też pozostawiamy widoczne jednostki w stanie, w jakim były pod koniec fazy wolnej?

  2. Czy przeprowadzamy okresową aktualizację wag na końcu każdej fazy, czy aktualizujemy wagi na każdym etapie równowagi w fazie? (Lub czy jedno jest w porządku?)

raegtin
źródło

Odpowiedzi:

6

Intuicyjnie możesz postrzegać jednostki widoczne jako „to, co widzi model”, a jednostki ukryte jako „stan umysłu modelu”. Gdy ustawisz wszystkie widoczne jednostki na niektóre wartości, „pokaż dane modelowi”. Następnie, kiedy aktywujesz ukryte jednostki, model dostosowuje swój stan umysłu do tego, co widzi.

Następnie pozwól modelowi uwolnić się i fantazjować. Zostanie zamknięty i dosłownie zobaczy pewne rzeczy, które generuje umysł, i wygeneruje nowe stany umysłu na podstawie tych obrazów.

To, co robimy, dostosowując wagi (i uprzedzenia), sprawia, że ​​model wierzy bardziej w dane, a mniej we własne fantazje. W ten sposób po pewnym szkoleniu uwierzy w jakiś (miejmy nadzieję) całkiem niezły model danych, a my możemy na przykład zapytać: „czy wierzysz w tę parę (X, Y)? Maszyna Boltzmanna? ”

Na koniec znajduje się krótki opis modeli opartych na energii, które powinny dać ci intuicję, skąd pochodzą fazy zaciśnięte i wolne oraz jak chcemy je uruchomić.

http://deeplearning.net/tutorial/rbm.html#energy-based-models-ebm

Zabawnie jest widzieć, że intuicyjnie jasne reguły aktualizacji wynikają z wyliczenia prawdopodobieństwa wygenerowania danych przez model.

Mając na uwadze te intuicje, łatwiej jest teraz odpowiedzieć na pytania:

  1. Musimy zresetować widoczne jednostki do niektórych danych, w które chcielibyśmy wierzyć modelowi. Jeśli użyjemy wartości z końca fazy wolnej, po prostu będzie nadal fantazjować, koniec egzekwować swoje błędne przekonania.

  2. Lepiej robić aktualizacje po zakończeniu fazy. Zwłaszcza jeśli jest to faza zaciśnięta, lepiej dać modelowi trochę czasu na „skupienie się” na danych. Wcześniejsze aktualizacje spowalniają konwergencję, ponieważ wymuszają połączenia, gdy model nie dostosował jeszcze swojego stanu umysłu do rzeczywistości. Aktualizowanie wagi po każdym kroku równowagi podczas fantazjowania powinno być mniej szkodliwe, chociaż nie mam z tym doświadczenia.

Jeśli chcesz poprawić swoją intuicję w EBM, BM i RBM, radziłbym obejrzeć niektóre wykłady Geoffreya Hintona na ten temat, ma on kilka dobrych analogii.

sjm.majewski
źródło
2
  1. Tak, „resetujemy (blokujemy) widoczne jednostki do jednego z wzorców, których chcemy się nauczyć (z pewną częstotliwością, która reprezentuje znaczenie tego wzorca)”.

  2. Tak, „wykonujemy zbiorczą aktualizację wag na końcu każdej fazy”. Nie sądzę, że aktualizacja „wag na każdym etapie równowagi w fazie” doprowadzi do szybkiej konwergencji, ponieważ sieć „rozprasza się” na skutek natychmiastowych błędów - zaimplementowałem maszyny Boltzmanna w ten sposób i pamiętam, że nie działało to zbyt dobrze dopóki nie zmieniłem go na aktualizację wsadową.

Neil G.
źródło
0

Oto przykładowy kod Python dla maszyn Boltzmann oparty na kodzie Paula Iwanowa z

http://redwood.berkeley.edu/wiki/VS265:_Homework_assignments

import numpy as np

def extract_patches(im,SZ,n):
    imsize,imsize=im.shape;
    X=np.zeros((n,SZ**2),dtype=np.int8);
    startsx= np.random.randint(imsize-SZ,size=n)
    startsy=np.random.randint(imsize-SZ,size=n)
    for i,stx,sty in zip(xrange(n), startsx,startsy):
        P=im[sty:sty+SZ, stx:stx+SZ];
        X[i]=2*P.flat[:]-1;
    return X.T

def sample(T,b,n,num_init_samples):
    """
    sample.m - sample states from model distribution

    function S = sample(T,b,n, num_init_samples)

    T:                weight matrix
    b:                bias
    n:                number of samples
    num_init_samples: number of initial Gibbs sweeps
    """
    N=T.shape[0]

    # initialize state vector for sampling
    s=2*(np.random.rand(N)<sigmoid(b))-1

    for k in xrange(num_init_samples):
        s=draw(s,T,b)

    # sample states
    S=np.zeros((N,n))
    S[:,0]=s
    for i in xrange(1,n):
        S[:,i]=draw(S[:,i-1],T,b)

    return S

def sigmoid(u):
    """
    sigmoid.m - sigmoid function

    function s = sigmoid(u)
    """
    return 1./(1.+np.exp(-u));

def draw(Sin,T,b):
    """
    draw.m - perform single Gibbs sweep to draw a sample from distribution

    function S = draw(Sin,T,b)

    Sin:      initial state
    T:        weight matrix
    b:        bias
    """
    N=Sin.shape[0]
    S=Sin.copy()
    rand = np.random.rand(N,1)
    for i in xrange(N):
        h=np.dot(T[i,:],S)+b[i];
        S[i]=2*(rand[i]<sigmoid(h))-1;

    return S

def run(im, T=None, b=None, display=True,N=4,num_trials=100,batch_size=100,num_init_samples=10,eta=0.1):
    SZ=np.sqrt(N);
    if T is None: T=np.zeros((N,N)); # weight matrix
    if b is None: b=np.zeros(N); # bias

    for t in xrange(num_trials):
        print t, num_trials
        # data statistics (clamped)
        X=extract_patches(im,SZ,batch_size).astype(np.float);
        R_data=np.dot(X,X.T)/batch_size;
        mu_data=X.mean(1);

        # prior statistics (unclamped)
        S=sample(T,b,batch_size,num_init_samples);
        R_prior=np.dot(S,S.T)/batch_size;
        mu_prior=S.mean(1);

        # update params
        deltaT=eta*(R_data - R_prior);
        T=T+deltaT;

        deltab=eta*(mu_data - mu_prior);
        b=b+deltab;


    return T, b

if __name__ == "__main__": 
    A = np.array([\
    [0.,1.,1.,0],
    [1.,1.,0, 0],
    [1.,1.,1.,0],
    [0, 1.,1.,1.],
    [0, 0, 1.,0]
    ])
    T,b = run(A,display=False)
    print T
    print b

Działa poprzez tworzenie poprawek danych, ale można to zmodyfikować, aby kod działał na wszystkich danych przez cały czas.

BBDynSys
źródło