forked from eriklindernoren/ML-From-Scratch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c75b1a3
commit 05c879a
Showing
8 changed files
with
158 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import logging | ||
|
||
import numpy as np | ||
from sklearn import datasets | ||
from sklearn.datasets import fetch_mldata | ||
import matplotlib.pyplot as plt | ||
|
||
from mlfromscratch.unsupervised_learning import RBM | ||
|
||
logging.basicConfig(level=logging.DEBUG) | ||
|
||
def main(): | ||
|
||
mnist = fetch_mldata('MNIST original') | ||
|
||
X = mnist.data / 255.0 | ||
y = mnist.target | ||
|
||
# Select the samples of the digit 2 | ||
X = X[y == 2] | ||
|
||
# Limit dataset to 500 samples | ||
idx = np.random.choice(range(X.shape[0]), size=500, replace=False) | ||
X = X[idx] | ||
|
||
rbm = RBM(n_hidden=50, n_iterations=200, batch_size=25, learning_rate=0.001) | ||
rbm.fit(X) | ||
|
||
training_gen = rbm.training_recon | ||
|
||
# Plot images showing how the network progresses in getting better at | ||
# reconstructing the digits in the training set | ||
for epoch, batch in enumerate(training_gen): | ||
fig, axs = plt.subplots(5, 5) | ||
plt.suptitle("Restricted Boltzmann Machine") | ||
cnt = 0 | ||
for i in range(5): | ||
for j in range(5): | ||
axs[i,j].imshow(batch[cnt].reshape((28, 28)), cmap='gray') | ||
axs[i,j].axis('off') | ||
cnt += 1 | ||
fig.savefig("rbm_%d.png" % epoch) | ||
plt.close() | ||
|
||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
mlfromscratch/unsupervised_learning/restricted_boltzmann_machine.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import logging | ||
import numpy as np | ||
import progressbar | ||
|
||
from mlfromscratch.utils.misc import bar_widgets | ||
from mlfromscratch.utils import batch_iterator | ||
from mlfromscratch.deep_learning.activation_functions import Sigmoid | ||
|
||
""" | ||
References: | ||
A Practical Guide to Training Restricted Boltzmann Machines https://www.cs.toronto.edu/~hinton/absps/guideTR.pdf | ||
""" | ||
|
||
sigmoid = Sigmoid() | ||
|
||
class RBM(): | ||
"""Bernoulli Restricted Boltzmann Machine (RBM) | ||
Parameters: | ||
----------- | ||
n_hidden: int: | ||
The number of processing nodes (neurons) in the hidden layer. | ||
learning_rate: float | ||
The step length that will be used when updating the weights. | ||
batch_size: int | ||
The size of the mini-batch used to calculate each weight update. | ||
n_iterations: float | ||
The number of training iterations the algorithm will tune the weights for. | ||
""" | ||
def __init__(self, n_hidden=128, learning_rate=0.1, batch_size=10, n_iterations=100): | ||
self.n_iterations = n_iterations | ||
self.batch_size = batch_size | ||
self.lr = learning_rate | ||
self.n_hidden = n_hidden | ||
|
||
self.progressbar = progressbar.ProgressBar(widgets=bar_widgets) | ||
|
||
def _initialize_weights(self, X): | ||
n_visible = X.shape[1] | ||
self.W = np.random.normal(scale=0.1, size=(n_visible, self.n_hidden)) | ||
|
||
self.v0 = np.zeros(n_visible) # Bias visible | ||
self.h0 = np.zeros(self.n_hidden) # Bias hidden | ||
|
||
def fit(self, X, y=None): | ||
'''Contrastive Divergence training procedure''' | ||
|
||
self._initialize_weights(X) | ||
|
||
self.errors = [] | ||
self.training_recon = [] | ||
for i in self.progressbar(range(self.n_iterations)): | ||
batch_errors = [] | ||
for batch in batch_iterator(X, batch_size=self.batch_size): | ||
# Positive phase | ||
positive_hidden = sigmoid(batch.dot(self.W) + self.h0) | ||
hidden_states = self._sample(positive_hidden) | ||
positive_associations = batch.T.dot(positive_hidden) | ||
|
||
# Negative phase | ||
negative_visible = sigmoid(hidden_states.dot(self.W.T) + self.v0) | ||
negative_visible = self._sample(negative_visible) | ||
negative_hidden = sigmoid(negative_visible.dot(self.W) + self.h0) | ||
negative_associations = negative_visible.T.dot(negative_hidden) | ||
|
||
self.W += self.lr * (positive_associations - negative_associations) | ||
self.h0 += self.lr * (positive_hidden.sum(axis=0) - negative_hidden.sum(axis=0)) | ||
self.v0 += self.lr * (batch.sum(axis=0) - negative_visible.sum(axis=0)) | ||
|
||
batch_errors.append(np.mean((batch - negative_visible) ** 2)) | ||
|
||
self.errors.append(np.mean(batch_errors)) | ||
# Reconstruct a batch of images from the training set | ||
idx = np.random.choice(range(X.shape[0]), self.batch_size) | ||
self.training_recon.append(self.reconstruct(X[idx])) | ||
|
||
def _sample(self, X): | ||
return X > np.random.random_sample(size=X.shape) | ||
|
||
def reconstruct(self, X=None): | ||
positive_hidden = sigmoid(X.dot(self.W) + self.h0) | ||
hidden_states = self._sample(positive_hidden) | ||
negative_visible = sigmoid(hidden_states.dot(self.W.T) + self.v0) | ||
return negative_visible | ||
|