Aprendendo pesos em uma máquina Boltzmann

14

Estou tentando entender como as máquinas Boltzmann funcionam, mas não sei bem como os pesos são aprendidos e não consegui encontrar uma descrição clara. O seguinte está correto? (Além disso, seria bom apontar para qualquer boa explicação da máquina de Boltzmann.)

Temos um conjunto de unidades visíveis (por exemplo, correspondendo a pixels preto / branco em uma imagem) e um conjunto de unidades ocultas. Os pesos são inicializados de alguma forma (por exemplo, uniformemente de [-0,5, 0,5]) e, em seguida, alternamos entre as duas fases a seguir até que alguma regra de parada seja atingida:

  1. Fase fixada - Nesta fase, todos os valores das unidades visíveis são fixos; portanto, somente atualizamos os estados das unidades ocultas (de acordo com a regra de ativação estocástica de Boltzmann). Nós atualizamos até que a rede alcance o equilíbrio. Assim que atingir o equilíbrio, continuamos a actualização mais vezes (para alguns predefinidos N ), mantendo-se a par da média de x i x j (onde x i , x j são os estados de nodos i e j ). Após essas N atualizações de equilíbrio, atualizamos w i j = w i j +NNxEuxjxEu,xjEujN, ondeCé uma taxa de aprendizado. (Ou, em vez de fazer uma atualização em lote no final, atualizamos após a etapa de equilíbrio?)WEuj=WEuj+1CUMAverumage(xEuxj)C

  2. Fase livre - Nesta fase, os estados de todas as unidades são atualizados. Quando alcançamos o equilíbrio, continuamos atualizando N 'mais vezes, mas em vez de adicionar correlações no final, subtraímos: .WEuj=WEuj-1CUMAverumage(xEuxj)

Então, minhas principais perguntas são:

  1. Sempre que estamos na fase restrita, redefinimos as unidades visíveis para um dos padrões que queremos aprender (com alguma frequência que represente a importância desse padrão) ou deixamos as unidades visíveis no estado em que estavam no final da fase livre?

  2. Fazemos uma atualização em lote dos pesos no final de cada fase ou atualizamos os pesos em cada etapa de equilíbrio da fase? (Ou uma delas está boa?)

raegtin
fonte

Respostas:

6

Intuitivamente, você pode pensar em unidades visíveis como "o que o modelo vê" e unidades ocultas como "estado de espírito do modelo". Quando você define todas as unidades visíveis para alguns valores, "mostra os dados para o modelo". Então, quando você ativa unidades ocultas, o modelo ajusta seu estado mental ao que vê.

Em seguida, você deixa o modelo livre e fantasia. Ele ficará fechado e literalmente verá algumas coisas que sua mente gera, além de gerar novos estados de espírito com base nessas imagens.

O que fazemos ajustando os pesos (e os vieses) é fazer o modelo acreditar mais nos dados e menos nas próprias fantasias. Dessa forma, após algum treinamento, ele acreditará em um modelo de dados (espero) muito bom, e podemos, por exemplo, perguntar "você acredita nesse par (X, Y)? Qual a probabilidade de encontrá-lo? Qual é a sua opinião, sr. Máquina Boltzmann? "

Finalmente, aqui está uma breve descrição dos Modelos Baseados em Energia, que devem lhe dar alguma intuição de onde vêm as fases Fixada e Livre e como queremos executá-las.

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

É engraçado ver que as regras de atualização intuitivamente claras surgem da derivação da probabilidade de log de gerar dados pelo modelo.

Com essas intuições em mente, agora é mais fácil responder suas perguntas:

  1. Temos que redefinir as unidades visíveis para alguns dados nos quais gostaríamos que o modelo acreditasse. Se usarmos os valores do final da fase livre, ele continuará fantasiando, aplicará suas próprias crenças equivocadas.

  2. É melhor fazer atualizações após o final da fase. Especialmente se for a fase restrita, é melhor dar tempo ao modelo para "focar" nos dados. As atualizações anteriores desacelerarão a convergência, pois reforçam as conexões quando o modelo ainda não ajustou seu estado de espírito à realidade. Atualizar os pesos após cada etapa de equilíbrio durante a fantasia deve ser menos prejudicial, embora eu não tenha experiência com isso.

Se você quiser melhorar sua intuição sobre EBM, BM e RBM, aconselho assistir a algumas das palestras de Geoffrey Hinton sobre o assunto, ele tem algumas boas analogias.

sjm.majewski
fonte
2
  1. Sim, "redefinimos (prendemos) as unidades visíveis para um dos padrões que queremos aprender (com alguma frequência que representa a importância desse padrão)".

  2. Sim, "fazemos uma atualização em lote dos pesos no final de cada fase". Não acho que atualizar "os pesos de cada etapa de equilíbrio da fase" leve a uma convergência rápida, porque a rede "se distrai" com erros instantâneos - implementei as máquinas Boltzmann dessa maneira e lembro que elas não estavam funcionando muito bem. até que eu mudei para uma atualização em lote.

Neil G
fonte
0

Aqui está um exemplo de código Python para Boltzmann Machines com base no código 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

Ele funciona criando patches de dados, mas isso pode ser modificado para que o código funcione em todos os dados o tempo todo.

BBDynSys
fonte