Dieser Artikel ist der 18. Tag des Wacul Adventskalenders.
Ich arbeite seit einem Jahr im Analyseteam von WACUL Co., Ltd. Python-Geschichte: ca. 3 Wochen
Das Üben von Python und ich werden versuchen, eine Deep Boltsman-Maschine zu implementieren, hauptsächlich um probabilistisches Deep Learning zu studieren. Alle theoretischen Aspekte sind in den folgenden Büchern enthalten.
"Deep Learning (Machine Learning Professional Series)" Takayuki Okaya (Autor) " "Deep Learning" (betreut von der Society of Artificial Intelligence)
In diesem Artikel werden wir zunächst eine eingeschränkte Boltsman-Maschine als Vorbereitung implementieren. Nächstes Mal werden wir eine tiefe Boltsman-Maschine bauen, indem wir eingeschränkte Boltsman-Maschinen stapeln.
Da die Wahrscheinlichkeitsverteilung einfach eine "Funktion ist, die einen Satz von Werten mit der Wahrscheinlichkeitsdichte (Wahrscheinlichkeitsmasse) verknüpft, aus der sie erhalten wird", kann sie als "Mechanismus hinter der Erzeugung eines Satzes von Werten" als Ganzes angesehen werden. Ich kann es schaffen Dies wird als *** generiertes Modell *** </ font> bezeichnet. Wenn Sie ein Generierungsmodell erstellen können, können Sie die Beziehungen zwischen Variablen verstehen und erwartete Werte berechnen, wodurch der Anwendungsbereich erweitert wird. Die Boltzmann-Maschine, die wir dieses Mal diskutieren werden, ist ein Generationsmodell, das die Wahrscheinlichkeitsdichte unter Verwendung der Boltzmann-Verteilung berechnet. Die spezifische Wahrscheinlichkeitsdichte ist der standardisierte Wert nach der Exponentialfunktion der unten definierten Energiefunktion (Φ) multipliziert mit minus. (Mit einer Konstanten multiplizieren, um in das geschlossene Intervall von [0,1] zu passen) Die Energiefunktion ist eine gewichtete lineare Summe jeder Knotenvorspannung und -verbindung.
Sichtbare Variablen sind Variablen, die direkt beobachtet werden können, und versteckte Variablen sind Variablen, die nicht beobachtet werden können. Die Einführung versteckter Variablen verbessert die Ausdruckskraft der Boltzmann-Maschine. Im Fall eines Modells, das versteckte Variablen enthält, wird zum Zeitpunkt der wahrscheinlichsten Schätzung die Verteilung nur sichtbarer Variablen verwendet, die in Bezug auf versteckte Variablen </ strong> marginalisiert sind.
Die eingeschränkte Boltzmann-Maschine ist ein Modell, das die Parameterschätzung vereinfacht, indem versteckte Variablen in eine allgemeine Boltzmann-Maschine eingeführt und bestimmte Einschränkungen hinzugefügt werden. Was sind die Einschränkungen? ① Es gibt keine Verbindung zwischen sichtbaren Knoten ② Es gibt keine Verbindung zwischen versteckten Knoten Es gibt zwei. Unter diesen beiden Bedingungen wird die eingeschränkte Boltsman-Maschine als Modell ausgedrückt, das zwei Ebenen aufweist, eine "sichtbare Ebene", die nur aus sichtbaren Knoten besteht, und eine "verborgene Ebene", die nur aus verborgenen Knoten besteht, und nur Verknüpfungen zwischen verschiedenen Ebenen aufweist. Es wird sein.
Dies bedeutet, dass *** </ font> bedeutet, dass eine Ebene bedingt unabhängig von einer Ebene *** </ font> ist.
Sobald Sie sich für die Form Ihres Modells entschieden haben, müssen Sie als Nächstes die Parameter lernen. Das Parameterlernen für eingeschränkte Boltzmann-Maschinen verwendet die Methode des orthodoxen Gradienten (aufsteigend), um die logarithmische Wahrscheinlichkeitsfunktion zu maximieren. Das Gradientenerhöhungsverfahren ist ein Verfahren, das darauf abzielt, die Funktion zu maximieren, während der Vorgang des Aktualisierens der Parameter nach und nach in der Richtung wiederholt wird, in der der Wert der Zielfunktion zunimmt. Verteilung). Um dies zu maximieren, wird die Zielfunktion durch die Zielparameter differenziert, um den Gradienten zu erhalten. Erwartete Werte für sichtbare und verborgene Schichten sind bei der Berechnung des Gradienten erforderlich. Da dies jedoch schwierig direkt zu berechnen ist, wird es angenähert, indem tatsächlich eine Probe unter Verwendung der Gibbs-Abtastung erzeugt und gemittelt wird. Zu diesem Zeitpunkt kann die Berechnung leicht durch den Vorteil der "bedingten Unabhängigkeit zwischen Schichten" der oben erwähnten eingeschränkten Boltzmann-Maschine ausgeführt werden.
Das Lernen durchläuft den folgenden Prozess, wenn man es allgemein betrachtet.
⚫️ Initialisieren Sie die Parameter nach dem Zufallsprinzip
⚫️ Wiederholen Sie unten----------------------------------------------
① Probieren Sie abwechselnd die sichtbare und die verborgene Ebene mit den aktuellen Parameterwerten ab
② Berechnen Sie den erwarteten Wert jedes Knotens in der sichtbaren und der verborgenen Ebene mit ①
③ Berechnen Sie den Gradienten (Differential) der Vorspannung der sichtbaren Schicht, der Vorspannung der verborgenen Schicht und der Verknüpfung mit ②
④ Aktualisieren Sie die Parameter mit ③
---------------------------------------------------------
Ich verwende beim Abtasten die Methode der persistenten kontrastiven Divergenz. Die Konvergenzbeurteilung entfällt, da die Verarbeitungseffizienz nicht berücksichtigt wird.
Implementierung der RBM-Klasse
from typing import List
Input = List[int]
def sigmoid(z):
return 1/(1+np.exp(-z))
class RBM:
learningRate = 0.005
sample_num = 100
#Vn ist die Anzahl der sichtbaren Knoten, Hn ist die Anzahl der versteckten Knoten
def __init__(self , Vn : int , Hn : int):
self.Vn = Vn
self.Hn = Hn
self.bias_v = np.array(np.random.rand(Vn)) #Zufällig initialisiert
self.bias_h = np.array(np.random.rand(Hn)) #Zufällig initialisiert
self.link = np.array(np.random.rand(Vn,Hn)) #Zufällig initialisiert
self.hidden_value = []
def train(self, data_for_learning , iteration_times):
for iteration_times in range(iteration_times):
# samples_vh ist v*h Probe
samples_h =[]
samples_v = []
samples_vh = np.zeros((self.Vn,self.Hn))
messege_in_sampling = 0
sigmoid_belief = 0
#① Probieren Sie abwechselnd die sichtbare und die verborgene Ebene mit den aktuellen Parameterwerten ab
for index in range(RBM.sample_num):
current_v = data_for_learning[0]
current_h = np.zeros(self.Hn)
##Abtasten der verborgenen Ebene
for j in range(self.Hn) :
messege_in_sampling = self.bias_h[j] + sum(list(map(lambda i: current_v[i] * self.link[i][j] , range(self.Vn))))
sigmoid_belief = 1/(1+ np.exp(-1 * messege_in_sampling))
r = np.random.rand(1)
if sigmoid_belief > r[0] :
current_h[j] = 1
else :
current_h[j] = 0
samples_h.append(current_h)
##Abtasten der sichtbaren Schicht
for i in range(self.Vn):
messege_in_sampling = self.bias_v[i] + sum(list(map(lambda j: current_h[j] * self.link[i][j] , range(self.Hn))))
sigmoid_belief = 1/(1+ np.exp(-1 * messege_in_sampling))
r = np.random.rand(1)
if sigmoid_belief > r[0] :
current_v[i] = 1
else :
current_v[i] = 0
samples_v.append(current_v)
##Sichtbare Schicht ✖️ Versteckte Schicht abgetastet
z = itertools.product(current_v,current_h)
product = []
for element in z:
product.append(element)
current_vh = (np.array(list(map(lambda x : x[0] * x[1] ,product))) ) .reshape(self.Vn,self.Hn)
samples_vh += current_vh
#② Berechnen Sie den erwarteten Wert jedes Knotens in der sichtbaren und der verborgenen Ebene
E_V = np.sum(np.array(samples_v),axis=0) / RBM.sample_num
E_H = np.sum(np.array(samples_h),axis=0) / RBM.sample_num
E_VH = samples_vh / RBM.sample_num
#③ Berechnen Sie den Gradienten (Differential) jeder sichtbaren Schichtvorspannung, verborgenen Schichtvorspannung und Verknüpfung
##Differenzierung der sichtbaren Schichtvorspannung
gradient_v = sum(np.array(data_for_learning)) / len(data_for_learning) - E_V
##Differenzierung der versteckten Schichtvorspannung
gradient_h = []
for j in range(len(self.bias_h)):
gradient_h_1 = []
sigmoid_beliefs_h_1 = []
for n in range(len(data_for_learning)):
messege_h_1 = self.bias_h[j] + sum(list(map(lambda i: data_for_learning[n][i] * self.link[i][j] , range(self.Vn))))
sigmoid_belief_h_1 = 1/(1+ np.exp(-1 * messege_h_1))
sigmoid_beliefs_h_1.append(sigmoid_belief_h_1)
gradient_h_1 = sum(sigmoid_beliefs_h_1) / len(data_for_learning)
gradient_h_2 = E_H[j]
gradient_h_j = gradient_h_1 + gradient_h_2
gradient_h.append(gradient_h_j)
##Linkdifferenzierung
gradient_vh = []
for i in range(len(self.bias_v)):
for j in range(len(self.bias_h)):
gradient_vh_1 = []
sigmoid_beliefs_vh_1 = []
for n in range(len(data_for_learning)):
messege_vh = self.bias_h[j] + sum(list(map(lambda i: data_for_learning[n][i] * self.link[i][j] , range(self.Vn))))
sigmoid_belief_vh_1 = 1/(1+ np.exp(-1 * messege_vh))
sigmoid_beliefs_vh_1.append(sigmoid_belief_vh_1 * data_for_learning[n][i])
gradient_vh_1 = sum(sigmoid_beliefs_vh_1) / len(data_for_learning)
gradient_vh_2 = E_VH[i][j]
gradient_vh_ij = gradient_vh_1 + gradient_vh_2
gradient_vh.append(gradient_vh_ij)
#④ Aktualisieren Sie die Parameter mit ③
self.bias_v += RBM.learningRate * np.array(gradient_v)
self.bias_h += RBM.learningRate * np.array(gradient_h)
self.link += RBM.learningRate * np.array(gradient_vh).reshape(self.Vn,self.Hn)
def energy(self,input : Input) -> float:
#Sichtbare Schichtenergie
energy_v = sum(list(map(lambda i : self.bias_v[i] * input[i] , range(self.Vn))))
#Versteckte Schichtenergie
##Geschätzter Wert der verborgenen Ebene
hidden_layer = self.getHiddenValue(input)
energy_h = sum(list(map(lambda j : self.bias_h[j] * hidden_layer[j] , range(self.Hn))))
#Energie verbinden
accumlator = []
for i in range(self.Vn):
for j in range(self.Hn):
accumlator.append(input[i]*hidden_layer[j] * self.link[i][j])
energy_vh = sum(accumlator)
energy = -1 * (energy_v + energy_h + energy_vh)
return energy
def estimate(self,input:Input) -> float:
#Energiefunktion berechnen
energy = self.energy(input)
#Verteilungsfunktion berechnen
all_patterns = list(itertools.product(range(0,2), repeat=self.Vn))
partition = sum(list(map(lambda x: np.exp(-1 *self.energy(list(x))) , all_patterns)))
return np.exp(-1* energy ) / partition
def getHiddenValue(self,input:Input) :
hidden_layer = np.zeros(self.Hn)
for j in range(self.Hn) :
messege_in_sampling = self.bias_h[j] + sum(list(map(lambda i: input[i] * self.link[i][j] , range(self.Vn))))
sigmoid_belief = 1/(1+ np.exp(-1 * messege_in_sampling))
r = np.random.rand(1)
if sigmoid_belief > r[0]:
hidden_layer[j] = 1
else :
hidden_layer[j] = 0
self.hidden_value = hidden_layer
return hidden_layer
Ausführungsbeispiel
#Bauen
visible_nord = 3
hidden_nord = 4
rbm = RBM(visible_nord,hidden_nord)
#Erstellung von Trainingsdaten
training_data = np.array(list(map(lambda x : np.random.randint(0,2) , range(0,300)))).reshape(100,3)
#Parameter lernen
rbm.train(training_data,10)
print(rbm.link)
input = [1,0,1]
#Ermitteln Sie den Wert der ausgeblendeten Ebene
rbm.getHiddenValue(input)
#Energiefunktionsausgabe
rbm.energy(input)
#Ausgabe der Wahrscheinlichkeitsdichte
rbm.estimate()
Es scheint zu funktionieren, aber es ist unklar, ob es passt. .. .. Ich werde vorerst weitermachen.
Nächstes Mal werden wir diese eingeschränkte Boltzmann-Maschine kombinieren, um eine tiefe Boltzmann-Maschine zu konfigurieren. Je tiefer die Ebenen werden, desto schwieriger wird das Lernen. Daher können Sie gute Anfangswerte von Parametern erhalten, indem Sie zuerst mit der eingeschränkten Boltzmann-Maschine vorlernen und dann das gesamte Modell optimieren. Wird sein.
Recommended Posts