Cet article est le dernier jour du calendrier de l'Avent Wacul 2016.
En préparation, nous avons implémenté une machine Boltsman restreinte. Cette fois, nous allons "empiler" cette machine Boltzmann restreinte pour former une machine Boltzmann profonde.
[Avec une explication simple] Implémentation Scratch d'une machine Boltzmann profonde avec Python ① http://qiita.com/yutaitatsu/items/a9478841357b10789514
C'est une sorte de machine Boltzmann (avec couche cachée) que j'ai expliqué la dernière fois. Il représente un modèle de génération dans son ensemble. Il a un calque visible en bas et un calque caché est empilé au-dessus. La machine Boltzmann profonde est un type de machine Boltzmann avec une couche cachée générale, vous pouvez donc apprendre par la méthode du gradient telle qu'elle est. Cependant, plus la couche est profonde, plus il y a de paramètres à estimer et plus le risque de tomber dans une solution locale est grand.
Par conséquent, *** chaque couche est conditionnellement indépendante (il n'y a pas de lien entre les nœuds de la même couche) ***. Voici la plus grande astuce de l'apprentissage automatique profond de Boltzmann introduit cette fois. La raison en est que ***, dans l'ordre du bas, considère deux couches chacune comme une machine Boltzmann restreinte *** </ font>. La paire de deux couches du bas est considérée comme une machine de Boltzmann restreinte, et le but est d'obtenir une bonne valeur initiale des paramètres en pré-apprenant chaque paire. Plus précisément, suivez les étapes suivantes.
[Étapes de pré-apprentissage]
① Tout d'abord, apprenez la machine Boltzmann restreinte (en bas), y compris la couche visible
② Dans la machine Boltzmann restreinte apprise dans ①, échantillonnez la valeur de la couche cachée en utilisant la «distribution conditionnelle de la couche visible à la couche cachée».
③ Apprenez en considérant l'échantillon obtenu dans ② comme l'entrée de la couche visible de la prochaine machine Boltsman restreinte (la seconde à partir du bas).
(Répétez ci-dessous)
Après avoir obtenu une bonne valeur initiale des paramètres, réapprendre l'orthodoxe avec la méthode du gradient. Cependant, si la couche est profonde, le coût de calcul de la valeur attendue augmentera de manière explosive, donc une méthode d'approximation est nécessaire pour obtenir une solution dans un temps réaliste.
Une approximation est nécessaire lors du calcul du gradient pour calculer la valeur attendue sous les paramètres actuels. Cette zone est la même que la précédente machine Boltzmann restreinte. Il existe deux méthodes principales pour estimer la valeur attendue de la distribution. L'une est une méthode d'échantillonnage *** telle que "l'échantillonnage Gyps" utilisée lors du précédent apprentissage automatique limité de Boltzmann, et l'autre est une méthode de *** variation telle que "l'approximation du champ moyen" utilisée cette fois *. **est. Cette fois, nous utiliserons les deux méthodes.
La différence entre la méthode d'échantillonnage et la méthode de variation est que la première "génère en fait un grand nombre d'échantillons à partir de la distribution cible elle-même et prend la moyenne pour se rapprocher de la valeur attendue", tandis que la seconde "valeur attendue". Utilise celle qui est la plus proche de la distribution cible parmi les distributions qui peuvent être calculées analytiquement (facilement) à la place. " Il convient de noter que le premier garantit que la valeur convergera vers la vraie valeur attendue si un nombre infini d'échantillons est obtenu, tandis que le second ne le fait pas.
Plus précisément, la méthode d'échantillonnage utilise la méthode de Monte Carlo en chaîne de Markov (MCMC), et la méthode de variation estime les paramètres qui minimisent la KLdivergence avec la distribution cible pour les groupes de distribution tels que la distribution gaussienne mixte, et l'utilise. Faire.
La machine Botulman à couche profonde peut être utilisée comme "auto-encodeur" en plus d'être utilisée comme modèle de génération tel quel. Cela signifie que chaque calque masqué peut être considéré comme une *** réduction de la dimension du calque visible *** </ font>. En d'autres termes, la valeur de la couche cachée peut être considérée comme une quantité de caractéristiques de dimension réduite tout en préservant l'essence des informations possédées par chaque nœud de la couche visible. Par conséquent, la machine Deep Boltsman peut être appliquée en tant que prétraitement de données pour appliquer des réseaux neuronaux déterministes et d'autres techniques d'apprentissage automatique orthodoxes.
Mise en œuvre de la machine Boltsman profonde
from typing import List
from itertools import chain
Layer = List[int]
Input = List[int]
from pprint import pprint
def sigmoid(z):
return 1/(1+np.exp(-z))
def get_array_from_structure(base_array, structure):
result = []
current_index = 0
for index in range(len(structure)):
accumlator = []
if index == 0:
accumlator = base_array[0:structure[current_index]]
else:
accumlator = base_array[current_index: current_index + structure[index]]
current_index += structure[index]
result.append(accumlator)
return result
class DeepBolzmannMachine:
learningRate = 0.05
sample_num = 100
def __init__(self,layer_structure:Layer):
#Générez une liste de RBM en les associant à partir de la couche visible dans l'ordre
##Structure des couches
self.layer_structure = layer_structure
self.layer_num = len(layer_structure)
##Faire une paire
self.pairs = []
for i in range(len(layer_structure) -1):
self.pairs.append((layer_structure[i],layer_structure[i+1]))
##Générer RBM pour chaque paire
self.rbms = []
for pair in self.pairs:
rbm = RBM(pair[0],pair[1])
self.rbms.append(rbm)
self.link_structure = list(map(lambda x :x[0] *x[1], self.pairs))
def preTrain(self,initaial_data_for_learning:Input, sample_num : int):
current_learning_data = []
##Calculez la valeur initiale de chaque RBM
for rbm_index in range(len(self.rbms)):
#Lorsque le calque est le calque inférieur (visible ou masqué)
if rbm_index == 0:
self.rbms[rbm_index].train(initaial_data_for_learning,100)
for i in range(sample_num):
hidden_value = self.rbms[rbm_index].getHiddenValue(initaial_data_for_learning[i])
current_learning_data.append(hidden_value)
#Lorsque le calque n'est pas le calque du bas (Caché ou Caché)
else:
#Apprenez avec les données d'entrée
self.rbms[rbm_index].train(current_learning_data,100)
#Créer des données pour le prochain RBM
data_for_learning = []
for i in range(sample_num):
hidden_value = self.rbms[rbm_index].getHiddenValue(current_learning_data[i])
data_for_learning.append(hidden_value)
current_learning_data = data_for_learning
#Mettre à jour les paramètres obtenus dans le pré-apprentissage aux valeurs initiales par la méthode ascendante de gradient
#Trouvez le premier terme de différenciation par le paramètre de lien de la fonction de vraisemblance logarithmique par approximation de champ moyen et le deuxième terme par divergence contrastive
def train(self, data_for_learning:Input, iteration_times:int):
samples = []
for iteration_times in range(iteration_times):
#Calculez la valeur attendue de chaque couche / nœud masqué pour une entrée d'échantillon en calculant la moyenne de l'approximation des champs [Note]:Mettez à jour uniquement le lien, pas le biais]
##Données intermédiaires des valeurs attendues(mean_field)Est conservé sous forme de tableau tridimensionnel (qch échantillon/couche r/Valeur attendue de la jème variable)
sample_length = len(data_for_learning)
max_node_num = max(self.layer_structure)
mean_field = np.zeros((sample_length, self.layer_num, max_node_num))
for s in range(sample_length):
for r in range(self.layer_num):
if r == 0:
pass
elif r == 1:
for j in range(self.rbms[0].Hn):
first_paragraph = sum(map(lambda i : self.rbms[0].link[i][j] * data_for_learning[s][i] , range(self.rbms[0].Vn)))
second_paragraph = sum(map(lambda k : self.rbms[1].link[j][k] * mean_field[s][1][k] , range(self.rbms[1].Hn)))
mean_field[s][1][j] = sigmoid(first_paragraph + second_paragraph)
elif r == self.layer_num - 1:
for j in range(self.rbms[r -1].Hn):
mean_field[s][r][j] = sigmoid(sum(map(lambda k : self.rbms[r-1].link[k][j] * mean_field[s][r-1][k] , range(self.rbms[r-2].Hn))))
else :
for j in range(self.rbms[r -1].Hn):
mean_field[s][r][j] = sigmoid(sum(map(lambda k : self.rbms[r -1].link[k][j] * mean_field[s][r-2][k] , range(self.rbms[r - 2].Hn))) + sum(map(lambda l : self.rbms[r].link[j][l] * mean_field[s][r+1][l] , range(self.rbms[r].Hn))))
#Calculer le premier terme de la pente du lien
gradient_1 = []
for r in range(len(self.rbms)):
gradient_1_element = []
if r == 0:
for i in range(self.rbms[0].Vn):
for j in range(self.rbms[0].Hn):
gradient_1_element.append((sum(map(lambda s : data_for_learning[s][i] * mean_field[s][1][j] , range(sample_length)))) / sample_length)
else:
for i in range(self.rbms[r].Vn):
for j in range(self.rbms[r].Hn):
gradient_1_element.append((sum(map(lambda s : mean_field[s][r][i] * mean_field[s][r+1][j] , range(sample_length)))) / sample_length)
gradient_1.append(gradient_1_element)
#Calculer le deuxième terme de la pente du lien
##Échantillonner
new_samples = []
for s in range(DeepBolzmannMachine.sample_num):
sample = []
##Initialiser la valeur de sample
for layer in self.layer_structure:
accumlator = []
for i in range(layer):
accumlator.append(np.empty)
sample.append(accumlator)
##Définissez la valeur initiale de l'échantillonnage (à ce stade, l'échantillon précédent est utilisé pour la deuxième fois et les instants suivants de la boucle ascendante de gradient)
initial_randoms = []
if iteration_times == 0:
for layer in self.layer_structure:
accumlator = []
for i in range(layer):
accumlator.append(np.random.rand(1)[0])
initial_randoms.append(accumlator)
else:
initial_randoms = samples[s]
##Estimer un échantillon (valeur de chaque nœud)
sigmoid_belief = []
for layer_index in range(len(self.layer_structure)):
for node_index in range(self.layer_structure[layer_index]):
####Calculer la croyance sigmoïde en utilisant les messages de tous les nœuds attachés à ce nœud
if layer_index == 0:
sigmoid_belief = sigmoid(sum(map(lambda j : initial_randoms[1][j] * self.rbms[0].link[node_index][j] , range(self.layer_structure[1]))))
elif layer_index == self.layer_num -1:
sigmoid_belief = sigmoid(sum(map(lambda j : initial_randoms[self.layer_num - 2][j] * self.rbms[self.layer_num -2].link[j][node_index] , range(self.layer_structure[self.layer_num -2]))))
else :
belief_downstair = sum(map(lambda j : initial_randoms[layer_index - 1][j] * self.rbms[layer_index -1].link[j][node_index] , range(self.layer_structure[layer_index -1])))
belief_upstair = sum(map(lambda j : initial_randoms[layer_index + 1][j] * self.rbms[layer_index].link[node_index][j] , range(self.layer_structure[layer_index + 1])))
sigmoid_belief = sigmoid(belief_upstair + belief_downstair)
####Générer des nombres aléatoires
r = np.random.rand(1)[0]
####Définir 1 si la croyance sigmoïde est plus grande que le nombre aléatoire, 0 si elle est plus petite que le nombre aléatoire
if sigmoid_belief > r :
sample[layer_index][node_index] = 1
else:
sample[layer_index][node_index] = 0
#Ajouter un échantillon
new_samples.append(sample)
#Supprimez l'échantillon de la mise à jour du dégradé précédente et remplacez-le par cet exemple
samples = new_samples
#Approximer le deuxième terme de gradient de l'échantillon
gradient_2 = []
for r in range(len(self.rbms)):
gradient_2_element = []
if r == 0:
for i in range(self.rbms[0].Vn):
for j in range(self.rbms[0].Hn):
gradient_2_element.append(sum(map(lambda m : samples[m][0][i] * samples[m][1][j], range(DeepBolzmannMachine.sample_num))) / DeepBolzmannMachine.sample_num)
else :
for i in range(self.rbms[r].Vn):
for j in range(self.rbms[r].Hn):
gradient_2_element.append(sum(map(lambda m : samples[m][r][i] * samples[m][r+1][j], range(DeepBolzmannMachine.sample_num))) / DeepBolzmannMachine.sample_num)
gradient_2.append(gradient_2_element)
#Calculer le gradient
gradient_1_flatten = np.array(list(chain.from_iterable(gradient_1)))
gradient_2_flatten = np.array(list(chain.from_iterable(gradient_2)))
gradient_flatten = gradient_1_flatten + gradient_2_flatten
gradient = get_array_from_structure(gradient_flatten, self.link_structure)
#Mettre à jour les paramètres avec un dégradé
for rbm_index in range(len(self.rbms)):
self.rbms[rbm_index].link += DeepBolzmannMachine.learningRate * gradient[rbm_index].reshape((self.rbms[rbm_index].Vn,self.rbms[rbm_index].Hn))
Exemple d'exécution
#Réseau à 4 couches avec 5, 4, 3, 2 nœuds dans l'ordre à partir de la couche visible
layer_structure = [5,4,3,2]
#Nombre d'échantillons à échantillonner lors du calcul de la valeur attendue
sample_num = 100
#Instanciation de la machine Boltzmann
dbm = DeepBolzmannMachine(layer_structure)
#Créez des données d'entraînement appropriées
data_for_learning = np.random.rand(100,layer_structure[0])
#Pré-apprentissage
dbm.preTrain(data_for_learning,sample_num)
#Post-apprentissage
dbm.train(data_for_learning,sample_num)
C'est pourquoi je l'ai implémenté.
L'apprentissage probabiliste profond ne semble pas être aussi répandu que l'apprentissage profond dans les réseaux neuronaux déterministes, mais personnellement *** "Comprendre ce qui se passe, y compris ses incertitudes" *** Bayes Je pense que le mérite de cette façon de penser est grand, alors j'espère que cela deviendra la norme de facto.
Cette année approche à grands pas. Bonne fin d'année, tout le monde ...
Recommended Posts