@@ -26,7 +26,7 @@ def __init__(
2626 n_visible : int ,
2727 n_hidden : int ,
2828 learning_rate : float = 0.01 ,
29- k : int = 1 ,
29+ cd_steps : int = 1 ,
3030 epochs : int = 10 ,
3131 batch_size : int = 64 ,
3232 mode : str = "bernoulli" ,
@@ -38,15 +38,15 @@ def __init__(
3838 n_visible (int): Number of visible units.
3939 n_hidden (int): Number of hidden units.
4040 learning_rate (float): Learning rate for weight updates.
41- k (int): Number of Gibbs sampling steps.
41+ cd_steps (int): Number of Gibbs sampling steps for Contrastive Divergence .
4242 epochs (int): Number of training epochs.
4343 batch_size (int): Batch size.
4444 mode (str): Sampling mode ('bernoulli' or 'gaussian').
4545 """
4646 self .n_visible = n_visible
4747 self .n_hidden = n_hidden
4848 self .learning_rate = learning_rate
49- self .k = k
49+ self .cd_steps = cd_steps
5050 self .epochs = epochs
5151 self .batch_size = batch_size
5252 self .mode = mode
@@ -58,33 +58,33 @@ def __init__(
5858 self .hidden_bias = np .zeros (n_hidden )
5959 self .visible_bias = np .zeros (n_visible )
6060
61- def sigmoid (self , x : np .ndarray ) -> np .ndarray :
61+ def sigmoid (self , input_array : np .ndarray ) -> np .ndarray :
6262 """
6363 Compute the sigmoid activation function element-wise.
6464
6565 Args:
66- x (np.ndarray): Input array.
66+ input_array (np.ndarray): Input array.
6767
6868 Returns:
6969 np.ndarray: Sigmoid output of input.
7070
7171 >>> rbm = RBM(3, 2)
7272 >>> import numpy as np
7373 >>> np.allclose(
74- ... dbn .sigmoid(np.array([0, 1])),
74+ ... rbm .sigmoid(np.array([0, 1])),
7575 ... np.array([0.5, 1/(1+np.exp(-1))])
7676 ... )
7777 True
7878
7979 """
80- return 1.0 / (1.0 + np .exp (- x ))
80+ return 1.0 / (1.0 + np .exp (- input_array ))
8181
82- def sample_prob (self , probs : np .ndarray ) -> np .ndarray :
82+ def sample_prob (self , probabilities : np .ndarray ) -> np .ndarray :
8383 """
8484 Sample binary states from given probabilities.
8585
8686 Args:
87- probs (np.ndarray): Probabilities of activation.
87+ probabilities (np.ndarray): Probabilities of activation.
8888
8989 Returns:
9090 np.ndarray: Binary sampled values.
@@ -95,87 +95,89 @@ def sample_prob(self, probs: np.ndarray) -> np.ndarray:
9595 >>> set(result).issubset({0., 1.})
9696 True
9797 """
98- return (self .rng .random (probs .shape ) < probs ).astype (float )
98+ return (self .rng .random (probabilities .shape ) < probabilities ).astype (float )
9999
100100 def sample_hidden_given_visible (
101- self , v : np .ndarray
101+ self , visible_batch : np .ndarray
102102 ) -> tuple [np .ndarray , np .ndarray ]:
103103 """
104104 Sample hidden units conditioned on visible units.
105105
106106 Args:
107- v (np.ndarray): Visible unit batch.
107+ visible_batch (np.ndarray): Visible unit batch.
108108
109109 Returns:
110110 tuple: (hidden probabilities, hidden samples)
111111 """
112- hid_probs = self .sigmoid (np .dot (v , self .weights ) + self .hidden_bias )
112+ hid_probs = self .sigmoid (np .dot (visible_batch , self .weights ) + self .hidden_bias )
113113 hid_samples = self .sample_prob (hid_probs )
114114 return hid_probs , hid_samples
115115
116116 def sample_visible_given_hidden (
117- self , h : np .ndarray
117+ self , hidden_batch : np .ndarray
118118 ) -> tuple [np .ndarray , np .ndarray ]:
119119 """
120120 Sample visible units conditioned on hidden units.
121121
122122 Args:
123- h (np.ndarray): Hidden unit batch.
123+ hidden_batch (np.ndarray): Hidden unit batch.
124124
125125 Returns:
126126 tuple: (visible probabilities, visible samples)
127127 """
128- vis_probs = self .sigmoid (np .dot (h , self .weights .T ) + self .visible_bias )
128+ vis_probs = self .sigmoid (
129+ np .dot (hidden_batch , self .weights .T ) + self .visible_bias
130+ )
129131 vis_samples = self .sample_prob (vis_probs )
130132 return vis_probs , vis_samples
131133
132- def contrastive_divergence (self , v0 : np .ndarray ) -> float :
134+ def contrastive_divergence (self , visible_zero : np .ndarray ) -> float :
133135 """
134136 Perform Contrastive Divergence (CD-k) for a single batch.
135137
136138 Args:
137- v0 (np.ndarray): Initial visible units (data batch).
139+ visible_zero (np.ndarray): Initial visible units (data batch).
138140
139141 Returns:
140142 float: Reconstruction loss (mean squared error) for batch.
141143 """
142- h_probs0 , h0 = self .sample_hidden_given_visible (v0 )
143- vk , hk = v0 , h0
144+ h_probs0 , h0 = self .sample_hidden_given_visible (visible_zero )
145+ vk , hk = visible_zero , h0
144146
145- for _ in range (self .k ):
147+ for _ in range (self .cd_steps ):
146148 _v_probs , vk = self .sample_visible_given_hidden (hk )
147149 h_probs , hk = self .sample_hidden_given_visible (vk )
148150
149- positive_grad = np .dot (v0 .T , h_probs0 )
151+ positive_grad = np .dot (visible_zero .T , h_probs0 )
150152 negative_grad = np .dot (vk .T , h_probs )
151153
152154 self .weights += (
153- self .learning_rate * (positive_grad - negative_grad ) / v0 .shape [0 ]
155+ self .learning_rate * (positive_grad - negative_grad ) / visible_zero .shape [0 ]
154156 )
155- self .visible_bias += self .learning_rate * np .mean (v0 - vk , axis = 0 )
157+ self .visible_bias += self .learning_rate * np .mean (visible_zero - vk , axis = 0 )
156158 self .hidden_bias += self .learning_rate * np .mean (h_probs0 - h_probs , axis = 0 )
157159
158- loss = np .mean ((v0 - vk ) ** 2 )
160+ loss = np .mean ((visible_zero - vk ) ** 2 )
159161 return loss
160162
161- def train (self , data : np .ndarray ) -> None :
163+ def train (self , dataset : np .ndarray ) -> None :
162164 """
163165 Train the RBM on the entire dataset.
164166
165167 Args:
166- data (np.ndarray): Training dataset matrix.
168+ dataset (np.ndarray): Training dataset matrix.
167169
168170 >>> rbm = RBM(6, 3, epochs=1, batch_size=2)
169171 >>> data = np.random.randint(0, 2, (4, 6)).astype(float)
170172 >>> rbm.train(data) # runs without error
171173 """
172- n_samples = data .shape [0 ]
174+ n_samples = dataset .shape [0 ]
173175 for epoch in range (self .epochs ):
174- self .rng .shuffle (data )
176+ self .rng .shuffle (dataset )
175177 losses = []
176178
177179 for i in range (0 , n_samples , self .batch_size ):
178- batch = data [i : i + self .batch_size ]
180+ batch = dataset [i : i + self .batch_size ]
179181 loss = self .contrastive_divergence (batch )
180182 losses .append (loss )
181183
@@ -188,7 +190,7 @@ def __init__(
188190 input_size : int ,
189191 layers : list [int ],
190192 mode : str = "bernoulli" ,
191- k : int = 5 ,
193+ cd_steps : int = 5 ,
192194 save_path : str | None = None ,
193195 ) -> None :
194196 """
@@ -198,23 +200,23 @@ def __init__(
198200 input_size (int): Number of features in input layer.
199201 layers (list): list of hidden layer unit counts.
200202 mode (str): Sampling mode ('bernoulli' or 'gaussian').
201- k (int): Number of sampling steps in generate_input_for_layer.
203+ cd_steps (int): Number of sampling steps in generate_input_for_layer.
202204 save_path (str, optional): Path for saving trained model parameters.
203205
204206 """
205207 self .input_size = input_size
206208 self .layers = layers
207- self .k = k
209+ self .cd_steps = cd_steps
208210 self .mode = mode
209211 self .save_path = save_path
210212 self .layer_params = [{"W" : None , "hb" : None , "vb" : None } for _ in layers ]
211213
212- def sigmoid (self , x : np .ndarray ) -> np .ndarray :
214+ def sigmoid (self , input_array : np .ndarray ) -> np .ndarray :
213215 """
214216 Compute sigmoid activation function.
215217
216218 Args:
217- x (np.ndarray): Input array.
219+ input_array (np.ndarray): Input array.
218220
219221 Returns:
220222 np.ndarray: Sigmoid of input.
@@ -228,110 +230,114 @@ def sigmoid(self, x: np.ndarray) -> np.ndarray:
228230 True
229231
230232 """
231- return 1.0 / (1.0 + np .exp (- x ))
233+ return 1.0 / (1.0 + np .exp (- input_array ))
232234
233- def sample_prob (self , probs : np .ndarray ) -> np .ndarray :
235+ def sample_prob (self , probabilities : np .ndarray ) -> np .ndarray :
234236 """
235237 Sample binary states from probabilities.
236238
237239 Args:
238- probs (np.ndarray): Activation probabilities.
240+ probabilities (np.ndarray): Activation probabilities.
239241
240242 Returns:
241243 np.ndarray: Binary sampled values.
242244 """
243245 rng = np .random .default_rng ()
244- return (rng .random (probs .shape ) < probs ).astype (float )
246+ return (rng .random (probabilities .shape ) < probabilities ).astype (float )
245247
246248 def sample_h (
247- self , x : np .ndarray , w : np .ndarray , hb : np .ndarray
249+ self , visible_units : np .ndarray , weights : np .ndarray , hidden_bias : np .ndarray
248250 ) -> tuple [np .ndarray , np .ndarray ]:
249251 """
250252 Sample hidden units given visible units for a DBN layer.
251253
252254 Args:
253- x (np.ndarray): Visible units.
254- w (np.ndarray): Weight matrix.
255- hb (np.ndarray): Hidden bias vector.
255+ visible_units (np.ndarray): Visible units.
256+ weights (np.ndarray): Weight matrix.
257+ hidden_bias (np.ndarray): Hidden bias vector.
256258
257259 Returns:
258260 tuple: Hidden probabilities and binary samples.
259261 """
260- probs = self .sigmoid (np .dot (x , w ) + hb )
262+ probs = self .sigmoid (np .dot (visible_units , weights ) + hidden_bias )
261263 samples = self .sample_prob (probs )
262264 return probs , samples
263265
264266 def sample_v (
265- self , y : np .ndarray , w : np .ndarray , vb : np .ndarray
267+ self , hidden_units : np .ndarray , weights : np .ndarray , visible_bias : np .ndarray
266268 ) -> tuple [np .ndarray , np .ndarray ]:
267269 """
268270 Sample visible units given hidden units for a DBN layer.
269271
270272 Args:
271- y (np.ndarray): Hidden units.
272- w (np.ndarray): Weight matrix.
273- vb (np.ndarray): Visible bias vector.
273+ hidden_units (np.ndarray): Hidden units.
274+ weights (np.ndarray): Weight matrix.
275+ visible_bias (np.ndarray): Visible bias vector.
274276
275277 Returns:
276278 tuple: Visible probabilities and binary samples.
277279 """
278- probs = self .sigmoid (np .dot (y , w .T ) + vb )
280+ probs = self .sigmoid (np .dot (hidden_units , weights .T ) + visible_bias )
279281 samples = self .sample_prob (probs )
280282 return probs , samples
281283
282- def generate_input_for_layer (self , layer_index : int , x : np .ndarray ) -> np .ndarray :
284+ def generate_input_for_layer (
285+ self , layer_index : int , original_input : np .ndarray
286+ ) -> np .ndarray :
283287 """
284288 Generate input for a particular DBN layer by sampling and averaging.
285289
286290 Args:
287291 layer_index (int): Layer index for which input is generated.
288- x (np.ndarray): Original input data.
292+ original_input (np.ndarray): Original input data.
289293
290294 Returns:
291295 np.ndarray: Smoothed input for the layer.
292296 """
293297 if layer_index == 0 :
294- return x .copy ()
298+ return original_input .copy ()
295299 samples = []
296- for _ in range (self .k ):
297- x_dash = x .copy ()
300+ for _ in range (self .cd_steps ):
301+ x_dash = original_input .copy ()
298302 for i in range (layer_index ):
299303 _ , x_dash = self .sample_h (
300304 x_dash , self .layer_params [i ]["W" ], self .layer_params [i ]["hb" ]
301305 )
302306 samples .append (x_dash )
303307 return np .mean (np .stack (samples , axis = 0 ), axis = 0 )
304308
305- def train_dbn (self , x : np .ndarray ) -> None :
309+ def train_dbn (self , training_data : np .ndarray ) -> None :
306310 """
307311 Layer-wise train the DBN using RBMs.
308312
309313 Args:
310- x (np.ndarray): Training dataset.
314+ training_data (np.ndarray): Training dataset.
311315 """
312316 for idx , layer_size in enumerate (self .layers ):
313317 n_visible = self .input_size if idx == 0 else self .layers [idx - 1 ]
314318 n_hidden = layer_size
315319
316- rbm = RBM (n_visible , n_hidden , k = 5 , epochs = 300 )
317- x_input = self .generate_input_for_layer (idx , x )
320+ rbm = RBM (n_visible , n_hidden , cd_steps = 5 , epochs = 300 )
321+ x_input = self .generate_input_for_layer (idx , training_data )
318322 rbm .train (x_input )
319323 self .layer_params [idx ]["W" ] = rbm .weights
320324 self .layer_params [idx ]["hb" ] = rbm .hidden_bias
321325 self .layer_params [idx ]["vb" ] = rbm .visible_bias
322326 print (f"Finished training layer { idx + 1 } /{ len (self .layers )} " )
323327
324- def reconstruct (self , x : np .ndarray ) -> tuple [np .ndarray , np .ndarray , float ]:
328+ def reconstruct (
329+ self , input_data : np .ndarray
330+ ) -> tuple [np .ndarray , np .ndarray , float ]:
325331 """
326332 Reconstruct input through forward and backward Gibbs sampling.
327333
328334 Args:
329- x (np.ndarray): Input data to reconstruct.
335+ input_data (np.ndarray): Input data to reconstruct.
330336
331337 Returns:
332338 tuple: (encoded representation, reconstructed input, MSE error)
333339 """
334- h = x .copy ()
340+ h = input_data .copy ()
335341 for i in range (len (self .layer_params )):
336342 _ , h = self .sample_h (
337343 h , self .layer_params [i ]["W" ], self .layer_params [i ]["hb" ]
@@ -344,7 +350,7 @@ def reconstruct(self, x: np.ndarray) -> tuple[np.ndarray, np.ndarray, float]:
344350 )
345351 reconstructed = h
346352
347- error = np .mean ((x - reconstructed ) ** 2 )
353+ error = np .mean ((input_data - reconstructed ) ** 2 )
348354 print (f"Reconstruction error: { error :.6f} " )
349355
350356 return encoded , reconstructed , error
0 commit comments