# coding:utf-8 import logging import numpy as np from scipy.special import expit from mla.base import BaseEstimator from mla.utils import batch_iterator np.random.seed(9999) sigmoid = expit """ References: A Practical Guide to Training Restricted Boltzmann Machines https://www.cs.toronto.edu/~hinton/absps/guideTR.pdf """ class RBM(BaseEstimator): y_required = False def __init__(self, n_hidden=128, learning_rate=0.1, batch_size=10, max_epochs=100): """Bernoulli Restricted Boltzmann Machine (RBM) Parameters ---------- n_hidden : int, default 128 The number of hidden units. learning_rate : float, default 0.1 batch_size : int, default 10 max_epochs : int, default 100 """ self.max_epochs = max_epochs self.batch_size = batch_size self.lr = learning_rate self.n_hidden = n_hidden def fit(self, X, y=None): self.n_visible = X.shape[1] self._init_weights() self._setup_input(X, y) self._train() def _init_weights(self): self.W = np.random.randn(self.n_visible, self.n_hidden) * 0.1 # Bias for visible and hidden units self.bias_v = np.zeros(self.n_visible, dtype=np.float32) self.bias_h = np.zeros(self.n_hidden, dtype=np.float32) self.errors = [] def _train(self): """Use CD-1 training procedure, basically an exact inference for `positive_associations`, followed by a "non burn-in" block Gibbs Sampling for the `negative_associations`.""" for i in range(self.max_epochs): error = 0 for batch in batch_iterator(self.X, batch_size=self.batch_size): positive_hidden = sigmoid(np.dot(batch, self.W) + self.bias_h) hidden_states = self._sample(positive_hidden) # sample hidden state h1 positive_associations = np.dot(batch.T, positive_hidden) negative_visible = sigmoid( np.dot(hidden_states, self.W.T) + self.bias_v ) negative_visible = self._sample( negative_visible ) # use the sampled hidden state h1 to sample v1 negative_hidden = sigmoid( np.dot(negative_visible, self.W) + self.bias_h ) negative_associations = np.dot(negative_visible.T, negative_hidden) lr = self.lr / float(batch.shape[0]) self.W += lr * ( (positive_associations - negative_associations) / float(self.batch_size) ) self.bias_h += lr * ( negative_hidden.sum(axis=0) - negative_associations.sum(axis=0) ) self.bias_v += lr * ( np.asarray(batch.sum(axis=0)).squeeze() - negative_visible.sum(axis=0) ) error += np.sum((batch - negative_visible) ** 2) self.errors.append(error) logging.info("Iteration %s, error %s" % (i, error)) logging.debug("Weights: %s" % self.W) logging.debug("Hidden bias: %s" % self.bias_h) logging.debug("Visible bias: %s" % self.bias_v) def _sample(self, X): return X > np.random.random_sample(size=X.shape) def _predict(self, X=None): return sigmoid(np.dot(X, self.W) + self.bias_h)