├── assets ├── model.gif ├── result_0.png ├── result_1.png ├── weights.png ├── result_0_custom.png └── energy.svg ├── LICENSE ├── network.py ├── train.py ├── train_custom.py └── README.md /assets/model.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crypto-code/Hopfield-Network/HEAD/assets/model.gif -------------------------------------------------------------------------------- /assets/result_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crypto-code/Hopfield-Network/HEAD/assets/result_0.png -------------------------------------------------------------------------------- /assets/result_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crypto-code/Hopfield-Network/HEAD/assets/result_1.png -------------------------------------------------------------------------------- /assets/weights.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crypto-code/Hopfield-Network/HEAD/assets/weights.png -------------------------------------------------------------------------------- /assets/result_0_custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crypto-code/Hopfield-Network/HEAD/assets/result_0_custom.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Atin Sakkeer Hussain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /network.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import pyplot as plt 3 | import matplotlib.cm as cm 4 | from tqdm import tqdm 5 | 6 | class HopfieldNetwork(object): 7 | def train_weights(self, train_data): 8 | print("Start to train weights...") 9 | num_data = len(train_data) 10 | self.num_neuron = train_data[0].shape[0] 11 | 12 | # initialize weights 13 | W = np.zeros((self.num_neuron, self.num_neuron)) 14 | rho = np.sum([np.sum(t) for t in train_data]) / (num_data*self.num_neuron) 15 | 16 | # Hebb rule 17 | for i in tqdm(range(num_data)): 18 | t = train_data[i] - rho 19 | W += np.outer(t, t) 20 | 21 | # Make diagonal element of W into 0 22 | diagW = np.diag(np.diag(W)) 23 | W = W - diagW 24 | W /= num_data 25 | 26 | self.W = W 27 | 28 | def predict(self, data, num_iter=20, threshold=0, asyn=False): 29 | print("Start to predict...") 30 | self.num_iter = num_iter 31 | self.threshold = threshold 32 | self.asyn = asyn 33 | 34 | # Copy to avoid call by reference 35 | copied_data = np.copy(data) 36 | 37 | # Define predict list 38 | predicted = [] 39 | for i in tqdm(range(len(data))): 40 | predicted.append(self._run(copied_data[i])) 41 | return predicted 42 | 43 | def _run(self, init_s): 44 | if self.asyn==False: 45 | """ 46 | Synchronous update 47 | """ 48 | # Compute initial state energy 49 | s = init_s 50 | 51 | e = self.energy(s) 52 | 53 | # Iteration 54 | for i in range(self.num_iter): 55 | # Update s 56 | s = np.sign(self.W @ s - self.threshold) 57 | # Compute new state energy 58 | e_new = self.energy(s) 59 | 60 | # s is converged 61 | if e == e_new: 62 | return s 63 | # Update energy 64 | e = e_new 65 | return s 66 | else: 67 | """ 68 | Asynchronous update 69 | """ 70 | # Compute initial state energy 71 | s = init_s 72 | e = self.energy(s) 73 | 74 | # Iteration 75 | for i in range(self.num_iter): 76 | for j in range(100): 77 | # Select random neuron 78 | idx = np.random.randint(0, self.num_neuron) 79 | # Update s 80 | s[idx] = np.sign(self.W[idx].T @ s - self.threshold) 81 | 82 | # Compute new state energy 83 | e_new = self.energy(s) 84 | 85 | # s is converged 86 | if e == e_new: 87 | return s 88 | # Update energy 89 | e = e_new 90 | return s 91 | 92 | 93 | def energy(self, s): 94 | return -0.5 * s @ self.W @ s + np.sum(s * self.threshold) 95 | 96 | def plot_weights(self, path): 97 | plt.figure(figsize=(6, 5)) 98 | w_mat = plt.imshow(self.W, cmap=cm.coolwarm) 99 | plt.colorbar(w_mat) 100 | plt.title("Network Weights") 101 | plt.tight_layout() 102 | plt.savefig(path+"/weights.png") 103 | plt.show() 104 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | np.random.seed(1) 3 | from matplotlib import pyplot as plt 4 | import skimage.data 5 | from skimage.color import rgb2gray 6 | from skimage.filters import threshold_mean 7 | from skimage.transform import resize 8 | import network 9 | import os 10 | 11 | # Helper Functions 12 | def get_corrupted_input(input, corruption_level): 13 | corrupted = np.copy(input) 14 | inv = np.random.binomial(n=1, p=corruption_level, size=len(input)) 15 | for i, v in enumerate(input): 16 | if inv[i]: 17 | corrupted[i] = -1 * v 18 | return corrupted 19 | 20 | def reshape(data): 21 | dim = int(np.sqrt(len(data))) 22 | data = np.reshape(data, (dim, dim)) 23 | return data 24 | def split(l, n): 25 | for i in range(0,len(l), n): 26 | yield l[i:i+n] 27 | 28 | def plot(data, test, predicted, figsize=(5, 6)): 29 | data = [reshape(d) for d in data] 30 | test = [reshape(d) for d in test] 31 | predicted = [reshape(d) for d in predicted] 32 | if not os.path.exists('results'): 33 | os.mkdir('results') 34 | count=0 35 | file_count=0 36 | for d in split(data, 4): 37 | if not len(d)is 1: 38 | fig, axarr = plt.subplots(len(d), 3) 39 | for i in range(len(d)): 40 | if i==0: 41 | axarr[i, 0].set_title('Train data') 42 | axarr[i, 1].set_title("Input data") 43 | axarr[i, 2].set_title('Output data') 44 | 45 | axarr[i, 0].imshow(data[count]) 46 | axarr[i, 0].axis('off') 47 | axarr[i, 1].imshow(test[count]) 48 | axarr[i, 1].axis('off') 49 | axarr[i, 2].imshow(predicted[count]) 50 | axarr[i, 2].axis('off') 51 | count = count+1 52 | 53 | plt.tight_layout() 54 | plt.savefig("results/result_"+str(file_count)+".png") 55 | file_count=file_count+1 56 | plt.show() 57 | else: 58 | fig, axarr = plt.subplots(1, 3) 59 | axarr[0].set_title('Train data') 60 | axarr[1].set_title("Input data") 61 | axarr[2].set_title('Output data') 62 | 63 | axarr[0].imshow(data[count]) 64 | axarr[0].axis('off') 65 | axarr[1].imshow(test[count]) 66 | axarr[1].axis('off') 67 | axarr[2].imshow(predicted[count]) 68 | axarr[2].axis('off') 69 | count = count+1 70 | plt.tight_layout() 71 | plt.savefig("results/result_"+str(file_count)+".png") 72 | file_count=file_count+1 73 | plt.show() 74 | 75 | def preprocessing(img, w=128, h=128): 76 | # Resize image 77 | img = resize(img, (w,h), mode='reflect') 78 | 79 | # Thresholding 80 | thresh = threshold_mean(img) 81 | binary = img > thresh 82 | shift = 2*(binary*1)-1 # Boolian to int 83 | 84 | # Reshape 85 | flatten = np.reshape(shift, (w*h)) 86 | return flatten 87 | 88 | def main(): 89 | # Load data 90 | 91 | import cv2 92 | import glob 93 | img_dir = "train/" # Enter Directory of all images 94 | data_path = os.path.join(img_dir,'*g') 95 | files = glob.glob(data_path) 96 | data = [] 97 | for f1 in files: 98 | img = rgb2gray(cv2.imread(f1)) 99 | data.append(img) 100 | 101 | # Preprocessing 102 | print("Start to data preprocessing...") 103 | data = [preprocessing(d) for d in data] 104 | 105 | # Create Hopfield Network Model 106 | model = network.HopfieldNetwork() 107 | model.train_weights(data) 108 | 109 | # Generate testset 110 | test = [get_corrupted_input(d, 0.3) for d in data] 111 | 112 | predicted = model.predict(test, threshold=0, asyn=False) 113 | print("Show prediction results...") 114 | plot(data, test, predicted) 115 | #print("Show network weights matrix...") 116 | #model.plot_weights("results/") 117 | 118 | if __name__ == '__main__': 119 | main() 120 | -------------------------------------------------------------------------------- /train_custom.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | np.random.seed(1) 3 | from matplotlib import pyplot as plt 4 | import skimage.data 5 | from skimage.color import rgb2gray 6 | from skimage.filters import threshold_mean 7 | from skimage.transform import resize 8 | import network 9 | import os 10 | 11 | # Helper Functions 12 | def get_corrupted_input(input, corruption_level): 13 | corrupted = np.copy(input) 14 | inv = np.random.binomial(n=1, p=corruption_level, size=len(input)) 15 | for i, v in enumerate(input): 16 | if inv[i]: 17 | corrupted[i] = -1 * v 18 | return corrupted 19 | 20 | def reshape(data): 21 | dim = int(np.sqrt(len(data))) 22 | data = np.reshape(data, (dim, dim)) 23 | return data 24 | def split(l, n): 25 | for i in range(0,len(l), n): 26 | yield l[i:i+n] 27 | 28 | def plot(data, test, predicted): 29 | data = [reshape(d) for d in data] 30 | test = [reshape(d) for d in test] 31 | predicted = [reshape(d) for d in predicted] 32 | if not os.path.exists('results_custom'): 33 | os.mkdir('results_custom') 34 | count=0 35 | file_count=0 36 | for d in split(data, 4): 37 | if not len(d)is 1: 38 | fig, axarr = plt.subplots(len(d), 3) 39 | for i in range(len(d)): 40 | if i==0: 41 | axarr[i, 0].set_title('Train data') 42 | axarr[i, 1].set_title("Input data") 43 | axarr[i, 2].set_title('Output data') 44 | 45 | axarr[i, 0].imshow(data[count]) 46 | axarr[i, 0].axis('off') 47 | axarr[i, 1].imshow(test[count]) 48 | axarr[i, 1].axis('off') 49 | axarr[i, 2].imshow(predicted[count]) 50 | axarr[i, 2].axis('off') 51 | count = count+1 52 | 53 | plt.tight_layout() 54 | plt.savefig("results_custom/result_"+str(file_count)+".png") 55 | file_count=file_count+1 56 | plt.show() 57 | else: 58 | fig, axarr = plt.subplots(1, 3) 59 | axarr[0].set_title('Train data') 60 | axarr[1].set_title("Input data") 61 | axarr[2].set_title('Output data') 62 | 63 | axarr[0].imshow(data[count]) 64 | axarr[0].axis('off') 65 | axarr[1].imshow(test[count]) 66 | axarr[1].axis('off') 67 | axarr[2].imshow(predicted[count]) 68 | axarr[2].axis('off') 69 | count = count+1 70 | plt.tight_layout() 71 | plt.savefig("results_custom/result_"+str(file_count)+".png") 72 | file_count=file_count+1 73 | plt.show() 74 | 75 | def preprocessing(img, w=128, h=128): 76 | # Resize image 77 | img = resize(img, (w,h), mode='reflect') 78 | 79 | # Thresholding 80 | thresh = threshold_mean(img) 81 | binary = img > thresh 82 | shift = 2*(binary*1)-1 # Boolian to int 83 | 84 | # Reshape 85 | flatten = np.reshape(shift, (w*h)) 86 | return flatten 87 | 88 | def main(): 89 | # Load data 90 | 91 | import cv2 92 | import glob 93 | img_dir = "train_custom/" # Enter Directory of all images 94 | data_path = os.path.join(img_dir,'*g') 95 | files = glob.glob(data_path) 96 | data = [] 97 | for f1 in files: 98 | img = rgb2gray(cv2.imread(f1)) 99 | data.append(img) 100 | 101 | # Preprocessing 102 | print("Start to data preprocessing...") 103 | data = [preprocessing(d) for d in data] 104 | 105 | # Create Hopfield Network Model 106 | model = network.HopfieldNetwork() 107 | model.train_weights(data) 108 | 109 | # Generate testset 110 | img_dir = "test_custom/" # Enter Directory of all images 111 | data_path = os.path.join(img_dir,'*g') 112 | files = glob.glob(data_path) 113 | test = [] 114 | # Since same name order will be the same 115 | for f1 in files: 116 | img = rgb2gray(cv2.imread(f1)) 117 | test.append(img) 118 | test = [preprocessing(d) for d in test] 119 | 120 | predicted = model.predict(test, threshold=0, asyn=False) 121 | print("Show prediction results...") 122 | plot(data, test, predicted) 123 | #print("Show network weights matrix...") 124 | #model.plot_weights("results_custom/") 125 | 126 | if __name__ == '__main__': 127 | main() 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hopfield-Network 2 | Create a Hopfield Network for Image Reconstruction 3 | 4 | ## What is a Hopefield Network ? 5 | 6 | At its core a Hopfield Network is a model that can reconstruct data after being fed with corrupt versions of the same data. This neural network proposed by Hopfield in 1982 can be seen as a network with associative memory. 7 | 8 | Lets say you hear a melody of a song and suddenly remember when you where on a concert hearing your favorite band playing just that song. That is associative memory. You get an input, which is a fragment of a memory you have stored in your brain, and get an output of the entire memory you have stored in your brain. 9 | 10 |
11 |
22 |
34 |
37 |
49 |
57 |