Apprentissage des poids dans une machine Boltzmann

14

J'essaie de comprendre comment fonctionnent les machines Boltzmann, mais je ne sais pas trop comment les poids sont appris et je n'ai pas pu trouver de description claire. Est-ce que ce qui suit est correct? (En outre, des pointeurs vers de bonnes explications de la machine Boltzmann seraient également excellents.)

Nous avons un ensemble d'unités visibles (par exemple, correspondant aux pixels noirs / blancs dans une image) et un ensemble d'unités cachées. Les poids sont initialisés d'une manière ou d'une autre (par exemple, uniformément à partir de [-0,5, 0,5]), puis nous alternons entre les deux phases suivantes jusqu'à ce qu'une règle d'arrêt soit atteinte:

  1. Phase bloquée - Dans cette phase, toutes les valeurs des unités visibles sont fixes, nous ne mettons donc à jour que les états des unités cachées (selon la règle d'activation stochastique de Boltzmann). Nous mettons à jour jusqu'à ce que le réseau soit à l'équilibre. Une fois que nous atteignons l'équilibre, nous continuons à mettre à jour plusieurs fois (pour certains prédéfinis ), en gardant une trace de la moyenne de (où sont les états des nœuds et ). Après ces mises à jour d'équilibre, nous mettons à jour , oùNNXjeXjXje,XjjejNwjej=wjej+1CUNEverunege(XjeXj)Cest un certain taux d'apprentissage. (Ou, au lieu de faire une mise à jour par lots à la fin, mettons-nous à jour après l'étape d'équilibre?)

  2. Phase libre - Dans cette phase, les états de toutes les unités sont mis à jour. Une fois que nous atteignons l'équilibre, nous continuons également à mettre à jour N 'plusieurs fois, mais au lieu d'ajouter des corrélations à la fin, nous soustrayons: .wjej=wjej-1CUNEverunege(XjeXj)

Donc mes principales questions sont:

  1. Chaque fois que nous sommes dans la phase bloquée, réinitialisons-nous les unités visibles à l'un des modèles que nous voulons apprendre (avec une fréquence qui représente l'importance de ce modèle), ou laissons-nous les unités visibles dans l'état où elles étaient à la fin de la phase libre?

  2. Faisons-nous une mise à jour par lots des poids à la fin de chaque phase, ou mettons-nous à jour les poids à chaque étape d'équilibre de la phase? (Ou bien, est-ce une amende?)

raegtin
la source

Réponses:

6

Intuitivement, vous pouvez considérer les unités visibles comme «ce que le modèle voit» et les unités cachées comme «l'état d'esprit du modèle». Lorsque vous définissez toutes les unités visibles sur certaines valeurs, vous "montrez les données au modèle". Ensuite, lorsque vous activez des unités cachées, le modèle ajuste son état d'esprit à ce qu'il voit.

Ensuite, vous laissez le modèle libre et fantasmez. Il deviendra enfermé et verra littéralement certaines choses que son esprit génère, et générera de nouveaux états d'esprit basés sur ces images.

Ce que nous faisons en ajustant les pondérations (et les biais), c'est que le modèle croit davantage aux données et moins à ses propres fantasmes. De cette façon, après une certaine formation, il croira en un (espérons-le) assez bon modèle de données, et nous pouvons par exemple demander "croyez-vous à cette paire (X, Y)? Quelle est la probabilité que vous la trouviez? Quelle est votre opinion mr. Machine Boltzmann? "

Enfin, voici une brève description des modèles basés sur l'énergie, qui devrait vous donner une idée de l'origine des phases Clamped et Free et comment nous voulons les exécuter.

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

Il est amusant de voir que les règles de mise à jour intuitivement claires proviennent de la dérivation de la probabilité de génération de données par le modèle.

Avec ces intuitions à l'esprit, il est maintenant plus facile de répondre à vos questions:

  1. Nous devons réinitialiser les unités visibles à certaines données auxquelles nous aimerions que le modèle croie. Si nous utilisons les valeurs de la fin de la phase libre, il continuera à fantasmer, et à appliquer ses propres croyances erronées.

  2. Il est préférable de faire des mises à jour après la fin de la phase. Surtout s'il s'agit de la phase bloquée, il est préférable de laisser au modèle le temps de se "concentrer" sur les données. Les mises à jour antérieures ralentissent la convergence, car elles renforcent les connexions lorsque le modèle n'a pas encore ajusté son état d'esprit à la réalité. Mettre à jour le poids après chaque étape d'équilibre tout en fantasmant devrait être moins nocif, bien que je n'en ai aucune expérience.

Si vous souhaitez améliorer votre intuition sur EBM, BM et RBM, je vous conseillerais de regarder certaines des conférences de Geoffrey Hinton sur le sujet, il a de bonnes analogies.

sjm.majewski
la source
2
  1. Oui, "nous remettons (fixons) les unités visibles à l'un des motifs que nous voulons apprendre (avec une fréquence qui représente l'importance de ce motif)".

  2. Oui, "nous faisons une mise à jour par lots des poids à la fin de chaque phase." Je ne pense pas que la mise à jour "des poids à chaque étape d'équilibre de la phase" conduira à une convergence rapide car le réseau "se laisse distraire" par des erreurs instantanées - j'ai implémenté des machines Boltzmann de cette façon, et je me souviens que ça ne fonctionne pas très bien jusqu'à ce que je le change en une mise à jour par lots.

Neil G
la source
0

Voici un exemple de code Python pour les machines Boltzmann basé sur le code de Paul Ivanov de

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

Cela fonctionne en créant des correctifs de données, mais cela peut être modifié pour que le code fonctionne sur toutes les données tout le temps.

BBDynSys
la source