-
Notifications
You must be signed in to change notification settings - Fork 0
/
models.py
168 lines (148 loc) · 5.52 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import numpy as np
import random
class Hopfield:
def __init__(self, memories):
''' Neurons takes binary value 0 or 1.
args:
N (int): dimension of the net
memories (nparray): memory patterns with dimension [number of examples, N]
returns: None
'''
N = memories.shape[1]
self.weights = np.zeros([N, N])
self.N = N
for m in memories:
self.weights += np.outer(m, m)
for i in range(N):
self.weights[i][i] = 0.
self.neurons = np.zeros(N)
def set(self, state):
'''
Set neuron state of the network
Args:
state (list): Neuron state
Returns: None
'''
self.neurons = np.array(state)
def energy(self, neurons = None):
'''
Args:
neurons: input neurons used to calculate the energy. None for using the network's neuron
Returns: energy of the network
'''
if neurons is None:
neurons = self.neurons
return -0.5*float(np.dot(np.matmul(neurons[np.newaxis,:], self.weights), neurons))
def update(self, num_updates = 1000, rule = 'energy_diff', sync = False):
''' Update network
Args:
num_updates (int): update of updates to perform
rule (str): update rule.
'energy_diff': energy difference
'field': flip by effective field
Returns:
'''
for n in range(num_updates):
old_neuron = self.neurons.copy()
update_order = np.arange(0, self.N)
random.shuffle(update_order)
if rule == 'energy_diff':
for i in update_order:
# flip a neuron state if energy is lower after flipping
new_neurons = self.neurons.copy()
new_neurons[i] *= -1
energy_diff = self.energy(new_neurons) - self.energy()
if energy_diff < 0:
self.neurons[i] = new_neurons[i]
elif rule == 'field':
threshold = 0.
if sync:
# update all at the same time
self.neurons = (self.field(None) > threshold).astype('float')
self.neurons[self.neurons == 0.] = -1
else:
# update one at a time
for i in update_order:
if self.field(i) > threshold:
self.neurons[i] = 1.
else:
self.neurons[i] = -1
else:
raise ValueError('Invalid update rule %s.'%rule)
if np.array_equal(self.neurons, old_neuron):
#print('converges after %d updates'%(n+1) )
return n+1
return n
def field(self, i):
'''return field at the i-th neuron'''
if i is None:
return self.weights@(self.neurons)
return np.dot(self.weights[i], self.neurons)
class ModernHopfield:
'''As in paper "Dense Associative Memory for Pattern Recognition" '''
def __init__(self, memories):
''' Note that this model has no weights but stores the training examples (memories) directly.
args:
N (int): dimension of the net
memories (nparray): memory patterns with dimension [number of examples, N]
returns: None
'''
self.memories = memories
self.N = memories.shape[1]
self.neurons = np.zeros(self.N)
def set(self, state):
'''
Set neuron state of the network
Args:
state (list): Neuron state
'''
self.neurons = np.array(state)
def update(self, num_updates = 1000, rule = 'energy_diff', sync = False):
''' Update network
Args:
num_updates (int): update of updates to perform
rule (str): update rule.
'energy_diff': energy difference
'field': flip by effective field
Returns:
'''
for n in range(num_updates):
old_neuron = self.neurons.copy()
update_order = np.arange(0, self.N)
random.shuffle(update_order)
if rule == 'energy_diff':
for i in update_order:
# flip a neuron state if energy is lower after flipping
new_neurons = self.neurons.copy()
new_neurons[i] *= -1
energy_diff = self.energy(new_neurons) - self.energy()
if energy_diff < 0:
self.neurons[i] = new_neurons[i]
else:
raise ValueError('Invalid update rule %s.'%rule)
if np.array_equal(self.neurons, old_neuron):
return n+1
return n
def energy(self, neurons = None, n = 2):
'''
Polynomial energy
Args:
neurons: state of neurons, None for the current state
n: power of norm
Returns: scalar energy
'''
if neurons is None:
neurons = self.neurons
x = (np.tile(neurons, (len(self.memories), 1)) * self.memories).sum(1)
E = - self._F(x, n).sum()
return E
def _F(self, x, n):
'''Rectified polynomial'''
x[x < 0] = 0.
return x**n
def addNoise(X, prob = 0.05):
Xnew = X.copy()
for i in range(len(Xnew)):
if random.random() < prob:
Xnew[i] *= -1
return Xnew