RBM ist eine Methode, die als Vorlernen verwendet wird, wenn beim Deep Learning mehrere Ebenen verwendet werden. Der Quellcode wird auf theano veröffentlicht. http://deeplearning.net/tutorial/rbm.html Diesmal habe ich diesen Code auf Chainer portiert.
Im Fall von RBM ist die Fehlerfunktion eine Funktion der freien Energie und ist kompliziert (?). Aufgrund der inversen Fehlerausbreitung erhalten Sie eine schöne Berechnungsformel, wenn Sie mit der Gewichtsmatrix und den Bias-Variablen unterscheiden. Sie kann aus dem Wert der CD-Verarbeitung (Contrastive Divergence) berechnet werden. Bei der Implementierung mit Chainer, anstatt eine Fehlerfunktion zu erstellen (Verlust) und eine inverse Fehlerausbreitung durchzuführen (Verlust.rückwärts) Es wäre einfacher, es einfach mit cupy (numpy + GPU) zu implementieren, aber ich habe es gewagt, es mit der Fehlerfunktion zu implementieren. Der Quellcode für cupy (numpy + GPU) wird nur veröffentlicht, wenn sich die Gelegenheit ergibt. Der folgende Quellcode verwendet jedoch auch cupy, um die CD-Verarbeitung zu beschleunigen. .. ..
# -*- coding: utf-8 -*-
import os,sys
import argparse
import time
import numpy as np
import chainer
from chainer import computational_graph
from chainer import cuda, Variable
import chainer.functions as F
import chainer.links as L
import chainer.optimizers as O
import pickle
import gzip
parser = argparse.ArgumentParser()
parser.add_argument('--gpu', '-g', default=-1, type=int,
help='GPU ID (negative value indicates CPU)')
parser.add_argument('--pcd', '-p', default=1, type=int,
help='pcd_flag')
parser.add_argument('--kcd', '-k', default=1, type=int,
help='cd-k')
'''Bei der Berechnung mit GPU Cupy= numpy +Prozess mit GPU.'''
args = parser.parse_args()
if args.gpu >= 0:
cuda.check_cuda_available()
xp = cuda.cupy if args.gpu >= 0 else np
def sigmoid(x):
return 1. / (1 + xp.exp(-x))
class RBM(chainer.Chain):
'''
n_visbile:Sichtbare Schichtdimension
n_hidden:Versteckte Ebenendimension
l.W:Gewichtsmatrix
l.b:hbias
l.a:vbaias
real:Die sichtbare Schicht ist 0,Die Fälle werden danach unterteilt, ob es sich um 1 oder eine reelle Zahl handelt. Die erste Schicht nimmt eine reelle Zahl an, die mittlere Schicht ist jedoch 0,Ich denke nur an einen.
h in chainer= l(v)Die Gewichtsmatrix, die im Allgemeinen RBM erscheint, wird transponiert, um sich zu multiplizieren.
'''
def __init__(self,n_visible,n_hidden,real=0,k=1,pcd_flag=0):
super(RBM,self).__init__(
l=L.Linear(n_visible,n_hidden),
# chainer 1.Diese Verwendung wurde in 5 Handbuch veraltet, also hinzufügen_Verwenden Sie param.
# a=L.Parameter(xp.zeros(n_visible,dtype=xp.float32)),
)
self.l.add_param("a",(n_visible),dtype=xp.float32)
''' l.Lernt es nicht, wenn a nicht mit Hilfe von Chainer initialisiert und initialisiert wird?'''
self.l.a.data.fill(0)
self.n_visible = n_visible
self.n_hidden = n_hidden
self.real = real
self.k = k
self.pcd_flag = pcd_flag
def __call__(self,v_data,v_prev_data=None):
batch_size = v_data.shape[0]
if self.pcd_flag == 0:
v_prev_data = v_data
elif self.pcd_flag ==1 and v_prev_data is None:
v_prev_data = v_data
vh_data = self.constrastive_divergence(v_prev_data,self.k)
v = Variable(v_data)
vh = Variable(vh_data.astype(np.float32))
'''
http://deeplearning.net/tutorial/rbm.HTML-Ausdruck(5)Von
Fehlerfunktion(loss)Ist(Freie Energie sichtbarer Schichtdaten)Wann(Sichtbare Schichtdaten-CD-k freie Energie)の差Wannなる。
loss = (self.free_energy(v) - self.free_energy(vh)) / batch_size
v->Die Zuordnung zu vh ist nicht im Lernen enthalten, also die CD-Die Variableisierung nach Abschluss der k-Verarbeitung ist abgeschlossen
'''
loss = (self.free_energy(v) - self.free_energy(vh)) / batch_size
return loss
def free_energy(self,v):
''' Function to compute the free energy '''
'''
Der Eingabewert v muss variabel sein
Ursprünglich nach der Aufnahme von SUM in Zeileneinheiten, Spalteneinheiten(Anzahl der Chargen)Es ist ein Prozess, der SUM dauern sollte
Immerhin werde ich SUM nehmen, also SUM auf einmal
'''
batch_size = v.data.shape[0]
n_visible = self.n_visible
real = self.real
if real == 0:
'''
Die sichtbare Schicht[0,1]Wann
vbias_term = -1 * SUM((a(i) * v(i))
'''
vbias_term = F.sum(F.matmul(v,self.l.a))
else:
'''
Wenn die sichtbare Ebene eine reelle Zahl ist
vbias_term = -0.5 * SUM((v(i)-a(i)) * (v(i)-a(i)))
Anzahl der Chargen in Chainer*In der Eingabeebene zu handhaben, die Vorspannung von jeder Zeile zu subtrahieren
Verwenden Sie zwangsweise m * n
'''
m = Variable(xp.ones((batch_size,1),dtype=xp.float32))
n = F.reshape(self.l.a,(1,n_visible))
v_ = v - F.matmul(m,n)
vbias_term = -F.sum(0.5 * v_ * v_)
wx_b = self.l(v)
hidden_term = F.sum(F.log(1+F.exp(wx_b)))
return -vbias_term-hidden_term
def propup(self,vis):
'''
Berechnen Sie die Wahrscheinlichkeitsverteilung versteckter Ebenen aus sichtbaren Ebenendaten
Die versteckte Schicht[0,1]Es gibt also die Wahrscheinlichkeit zurück, 1 zu sein.
Der Eingabewert vis ist immer xp(np)
'''
pre_sigmoid_activation = xp.dot(vis,self.l.W.data.T) + self.l.b.data
return sigmoid(pre_sigmoid_activation)
def propdown(self,hid):
'''
Berechnen Sie die Wahrscheinlichkeitsverteilung der sichtbaren Ebene aus den Daten der verborgenen Ebene
Die sichtbare Schicht[0,1]Wann ist die Wahrscheinlichkeit, 1 zu werden, zurückgegeben.
Wenn die sichtbare Ebene eine reelle Zahl ist, gibt sie den Durchschnitt der Normalverteilung zurück. Die Verteilung ist auf 1 festgelegt.
Der versteckte Eingabewert ist immer xp(np)
'''
real = self.real
if real == 0:
pre_sigmoid_activation = xp.dot(hid,self.l.W.data) + self.l.a.data
v_mean = sigmoid(pre_sigmoid_activation)
else:
v_mean = xp.dot(hid,self.l.W.data) + self.l.a.data
return v_mean
def sample_h_given_v(self,v0_sample):
'''
v0_sample → h1_Gibbs-Probenahme
[0,1]Erstellen Sie einen zufälligen Wert von und vergleichen Sie ihn mit der Wahrscheinlichkeit von 1, die durch Popup berechnet wird
h1_mean[i] > p[i]Dann h1_sample[i] = 1
h1_mean[i] <= p[i]Dann h1_sample[i] = 0
'''
h1_mean = self.propup(v0_sample)
h1_sample = xp.random.binomial(size=h1_mean.shape,n=1,p=h1_mean)
return h1_mean,h1_sample
def sample_v_given_h(self,h0_sample):
'''
h0_sample → v1_Gibbs-Probenahme
Die sichtbare Schicht[0,1]Im Falle von
[0,1]Der Wert von ist der Zufallswert p[i]Und vergleichen Sie es mit der Wahrscheinlichkeit von 1, die durch Popdown berechnet wird
v1_mean[i] > p[i]Dann v1_sample[i] = 1
v1_mean[i] <= p[i]Dann v1_sample[i] = 0
Wenn die sichtbare Ebene eine reelle Zahl ist
v1_sample[i]Ist durchschnittlich v1_mean[i]Weil es sich um eine Normalverteilung mit Varianz 1 handelt
Zufallswert u aus Normalverteilung mit Mittelwert 0 und Varianz 1[i]Und addieren Sie den durch Popdown berechneten Wert
v1_sample[i] = v1_mean[i] + u[i]
'''
v1_mean = self.propdown(h0_sample)
if self.real == 0:
v1_sample = xp.random.binomial(size=v1_mean.shape,n=1,p=v1_mean)
else:
batch_number = h0_sample.shape[0]
v1_sample = v1_mean + xp.random.randn(batch_number,self.n_visible)
return v1_mean,v1_sample
def gibbs_hvh(self,h0_sample):
''' h->v->Gibbs Sampling h'''
v1_mean,v1_sample = self.sample_v_given_h(h0_sample)
h1_mean,h1_sample = self.sample_h_given_v(v1_sample)
return v1_mean,v1_sample,h1_mean,h1_sample
def gibbs_vhv(self,v0_sample):
''' v->h->v Gibbs Sampling'''
h1_mean,h1_sample = self.sample_h_given_v(v0_sample)
v1_mean,v1_sample = self.sample_v_given_h(h1_sample)
return h1_mean,h1_sample,v1_mean,v1_sample
def constrastive_divergence(self,v0_sample,k=1):
''' CD-Verarbeitung von k, cupy ist erforderlich, um es GPU zu machen'''
vh_sample = v0_sample
for step in range(k):
ph_mean,ph_sample,vh_mean,vh_sample = self.gibbs_vhv(vh_sample)
return vh_sample
def reconstruct(self, v):
h = sigmoid(xp.dot(v,self.l.W.data.T) + self.l.b.data)
reconstructed_v = sigmoid(xp.dot(h,self.l.W.data) + self.l.a.data)
return reconstructed_v
Im Fall von Chainer können Sie chainer.links.Linear verwenden, um die Gewichtsmatrix (W) und die Vorspannung (b) der Abbildung jeder Schicht zu definieren. Im Fall von RBM ist eine zusätzliche Vorspannung erforderlich. l.add_param("a",(n_visible),dtype=xp.float32) Ich habe einen Parameter hinzugefügt. l.W: Gewichtsmatrix l.b:hbias l.a:vbaias Es wird sein. So verwalten Sie die Zuordnung von der sichtbaren Ebene zur verborgenen Ebene mit chainer.links.Linear Dies ist eine Umsetzung der in einem allgemeinen RBM-Buch beschriebenen Gewichtsmatrix. (Wij → Wji)
Ich habe mit dem folgenden Quellcode mit MNIST-Daten experimentiert. Da es keinen GPU-Server gab, wurde dieser von der CPU trainiert, sodass die Anzahl der Schulungen 50-mal beträgt. Dieses Mal wird die verborgene Schicht auf 500 Dimensionen eingestellt und die Ergebnisse mit einem Lernkoeffizienten von 0,01, einem Impuls von 0,5 und PCD-10 werden verwendet.
Das Folgende sind die Originaldaten. Tatsächlich gibt es 60.000 Fälle, aber nur 150 Fälle werden angezeigt
Das Folgende ist ein Bild des oben rekonstruierten Bildes. Es ist fast reproduziert. Es werden nur Elemente angezeigt.
Als nächstes ist ein Bild, das die Gewichtsmatrix visualisiert. Die verborgene Schicht hat 500 Dimensionen in Bezug auf die Basis jedes Vektors Es wird in 28 * 28 Bilder konvertiert.
Recommended Posts