Moon CNN: Deep Learning Lunar Phase Recognition
A 2D Convolutional Neural Network designed to classify lunar phases from scratch. Features custom data augmentation pipelines, layer optimization, and experimental validation curves.
Interactive Lunar Phase Scanner Simulation
Dataset Construction & Pipeline
To build this model, I manually created a dataset of 250 lunar training images and a validation dataset of 50 images, distributed evenly across four celestial classes:
- New Moon: Minimal illumination, edge shadow boundary detection.
- Full Moon: Complete circular reflectance with crater contrast.
- First Quarter: Right-hand illuminated semicircle.
- Last Quarter: Left-hand illuminated semicircle.
Data Augmentation Strategy
Because astronomical datasets can be scarce, data augmentation was essential to prevent severe overfitting. The augmentation sequential layer applies:
- Resize: Downsampled from irregular camera dimensions to a uniform 140×140 grid.
- CenterCrop: Cropped to (128, 128) to eliminate peripheral background noise.
- RandomRotation (1.0) & RandomBrightness (0.2): Mimics variable telescope orientations and sensor exposure levels.
- RandomFlip (Horizontal): Enhances generalized edge detection across quarters.
- Rescale (1./127.5, offset=-1): Normalizes pixel tensors to the [-1, 1] range for numerical gradient stability.
CNN Architecture Implementation
The model is built using TensorFlow and Keras Sequential. Input tensors are processed in single-channel grayscale (since lunar morphology is captured primarily by luminance differentials), reducing parameter count and memory footprint:
import tensorflow as tf
model = tf.keras.Sequential([
tf.keras.Input(shape=(128, 128, 1)),
data_augmentation,
tf.keras.layers.Conv2D(32, kernel_size=3, padding='same', activation='relu'),
tf.keras.layers.MaxPool2D(),
tf.keras.layers.Conv2D(64, kernel_size=3, padding='same', activation='relu'),
tf.keras.layers.MaxPool2D(),
tf.keras.layers.Conv2D(128, kernel_size=3, padding='same', activation='relu'),
tf.keras.layers.MaxPool2D(),
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dense(units=128, activation="relu", kernel_initializer="he_normal"),
tf.keras.layers.Dropout(0.3),
tf.keras.layers.Dense(4, activation='softmax')
])
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
Architecture Highlight: GlobalAveragePooling2D
Replacing a dense Flatten layer (which generated over 4.9 Million parameters) with GlobalAveragePooling2D drastically curtailed model size, reduced memory bottlenecks, and suppressed overfitting on the validation set.
Validation Tests & Empirical Results
To understand the learning dynamics, three configuration runs were executed and compared:
Coarse Kernels & Overfitting
In the initial baseline run, a 5×5 kernel was too coarse for small crater edge features. The training loss converged, but validation loss exploded—a textbook case of the model memorizing training samples without generalization.
Global Pooling Parameter Reduction
Switching to 3×3 kernels and Global Average Pooling reduced millions of redundant weights. Training stability improved substantially, though validation variance remained high without data augmentation.
Augmentation Integration
Injecting stochastic rotation, brightness scaling, and center crops stabilized both training and validation losses. The validation loss curve showed sustained downward convergence and balanced multi-class recognition.