[normalize] - simplify and harden normalize(); remove duplicate in load_image_patches

image.py: replace repeat/reshape with keepdims=True; guard std < 1e-8 to
prevent NaN on constant patches (previously relied on call-site pre-filtering).
test_faces_sub_image.py: load_image_patches now calls normalize() directly
instead of duplicating the per-sample normalization logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 18:22:19 +02:00
co-authored by Claude Sonnet 4.6
parent 07fc7dc607
commit e0dcac5c82
2 changed files with 7 additions and 12 deletions
+4 -5
View File
@@ -1,11 +1,10 @@
from rbm.matrix import Mat, np from rbm.matrix import Mat, np
def normalize(image: Mat): def normalize(image: Mat):
image_shape = image.shape mean = np.mean(image, axis=1, keepdims=True)
mean_training_batch = np.reshape(np.repeat(np.mean(image, axis=1), image_shape[1], axis=0), image_shape) std = np.std(image, axis=1, keepdims=True)
var_training_batch = np.reshape(np.repeat(np.std(image, axis=1), image_shape[1], axis=0), image_shape) std = np.where(std < 1e-8, np.ones_like(std), std)
result = (image - mean_training_batch) / var_training_batch return (image - mean) / std
return result
class SubImage: class SubImage:
def __init__(self, sx: int, sy: int, px: int, py: int): def __init__(self, sx: int, sy: int, px: int, py: int):
+3 -7
View File
@@ -14,7 +14,7 @@ STRIDE = 16 # 50% overlap; set equal to PATCH for non-overlapping
GRAYSCALE = False # reassigned in __main__ when --grayscale is set GRAYSCALE = False # reassigned in __main__ when --grayscale is set
N_CH = 1 if GRAYSCALE else 3 N_CH = 1 if GRAYSCALE else 3
N_VIS = N_CH * PATCH * PATCH # 1024 grayscale / 3072 colour N_VIS = N_CH * PATCH * PATCH # 1024 grayscale / 3072 colour
N_HID = 128 N_HID = 64
N_IMAGES = 50 N_IMAGES = 50
@@ -24,7 +24,7 @@ class TestModel(Model):
self.unit1 = Entity( self.unit1 = Entity(
(N_VIS, N_HID), (N_VIS, N_HID),
EntityParams(do_gaussian_visible=True, do_gaussian_hidden=False), EntityParams(do_gaussian_visible=True, do_gaussian_hidden=False),
TrainingParams(learning_rate=0.001, momentum=0.9, num_epochs=3000, mini_batch_size=100) TrainingParams(learning_rate=0.001, momentum=0.9, num_epochs=3000, mini_batch_size=1000)
) )
def forward(self, x: Mat) -> Mat: def forward(self, x: Mat) -> Mat:
@@ -90,11 +90,7 @@ def load_image_patches(path: str):
img_nchw = np.array(_img_to_chw(img)[numpy.newaxis]) img_nchw = np.array(_img_to_chw(img)[numpy.newaxis])
patches_nchw = sub(img_nchw) patches_nchw = sub(img_nchw)
patches = patches_nchw.reshape(-1, N_VIS) patches = patches_nchw.reshape(-1, N_VIS)
return normalize(patches), nx_steps, ny_steps
mean = np.mean(patches, axis=1, keepdims=True)
std = np.std(patches, axis=1, keepdims=True)
std = np.where(std < 0.01, np.ones_like(std), std)
return (patches - mean) / std, nx_steps, ny_steps
def assemble_overlapping(patch_list, nx_steps: int, ny_steps: int) -> numpy.ndarray: def assemble_overlapping(patch_list, nx_steps: int, ny_steps: int) -> numpy.ndarray: