Spaces:
Sleeping
Sleeping
| """ | |
| Custom CNN model architecture for emotion recognition. | |
| Optimized for 48x48 grayscale images. | |
| """ | |
| import tensorflow as tf | |
| from tensorflow.keras.models import Sequential, Model | |
| from tensorflow.keras.layers import ( | |
| Conv2D, MaxPooling2D, Dense, Dropout, Flatten, | |
| BatchNormalization, Input, GlobalAveragePooling2D | |
| ) | |
| from tensorflow.keras.regularizers import l2 | |
| import sys | |
| from pathlib import Path | |
| sys.path.append(str(Path(__file__).parent.parent.parent)) | |
| from src.config import IMAGE_SIZE, NUM_CLASSES, NUM_CHANNELS | |
| def build_custom_cnn( | |
| input_shape: tuple = (*IMAGE_SIZE, NUM_CHANNELS), | |
| num_classes: int = NUM_CLASSES, | |
| dropout_rate: float = 0.25, | |
| dense_dropout: float = 0.5, | |
| l2_reg: float = 0.01 | |
| ) -> Model: | |
| """ | |
| Build a custom CNN architecture for emotion recognition. | |
| Architecture: | |
| - 4 Convolutional blocks with increasing filters (64 -> 128 -> 256 -> 512) | |
| - Each block: Conv2D -> BatchNorm -> ReLU -> MaxPool -> Dropout | |
| - Dense layers for classification | |
| Args: | |
| input_shape: Input image shape (height, width, channels) | |
| num_classes: Number of emotion classes | |
| dropout_rate: Dropout rate for conv blocks | |
| dense_dropout: Dropout rate for dense layers | |
| l2_reg: L2 regularization factor | |
| Returns: | |
| Compiled Keras model | |
| """ | |
| model = Sequential([ | |
| # Input layer | |
| Input(shape=input_shape), | |
| # Block 1: 64 filters | |
| Conv2D(64, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| Conv2D(64, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| MaxPooling2D(pool_size=(2, 2)), | |
| Dropout(dropout_rate), | |
| # Block 2: 128 filters | |
| Conv2D(128, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| Conv2D(128, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| MaxPooling2D(pool_size=(2, 2)), | |
| Dropout(dropout_rate), | |
| # Block 3: 256 filters | |
| Conv2D(256, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| Conv2D(256, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| MaxPooling2D(pool_size=(2, 2)), | |
| Dropout(dropout_rate), | |
| # Block 4: 512 filters | |
| Conv2D(512, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| Conv2D(512, (3, 3), padding='same', activation='relu', | |
| kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| MaxPooling2D(pool_size=(2, 2)), | |
| Dropout(dropout_rate), | |
| # Classification head | |
| Flatten(), | |
| Dense(512, activation='relu', kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| Dropout(dense_dropout), | |
| Dense(256, activation='relu', kernel_regularizer=l2(l2_reg)), | |
| BatchNormalization(), | |
| Dropout(dense_dropout), | |
| Dense(num_classes, activation='softmax') | |
| ], name='custom_emotion_cnn') | |
| return model | |
| def build_custom_cnn_v2( | |
| input_shape: tuple = (*IMAGE_SIZE, NUM_CHANNELS), | |
| num_classes: int = NUM_CLASSES | |
| ) -> Model: | |
| """ | |
| Alternative CNN architecture with residual-like connections. | |
| Args: | |
| input_shape: Input image shape | |
| num_classes: Number of emotion classes | |
| Returns: | |
| Keras model | |
| """ | |
| inputs = Input(shape=input_shape) | |
| # Initial convolution | |
| x = Conv2D(32, (3, 3), padding='same', activation='relu')(inputs) | |
| x = BatchNormalization()(x) | |
| # Block 1 | |
| x = Conv2D(64, (3, 3), padding='same', activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = Conv2D(64, (3, 3), padding='same', activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = MaxPooling2D(pool_size=(2, 2))(x) | |
| x = Dropout(0.25)(x) | |
| # Block 2 | |
| x = Conv2D(128, (3, 3), padding='same', activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = Conv2D(128, (3, 3), padding='same', activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = MaxPooling2D(pool_size=(2, 2))(x) | |
| x = Dropout(0.25)(x) | |
| # Block 3 | |
| x = Conv2D(256, (3, 3), padding='same', activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = Conv2D(256, (3, 3), padding='same', activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = MaxPooling2D(pool_size=(2, 2))(x) | |
| x = Dropout(0.25)(x) | |
| # Global pooling and classification | |
| x = GlobalAveragePooling2D()(x) | |
| x = Dense(256, activation='relu')(x) | |
| x = BatchNormalization()(x) | |
| x = Dropout(0.5)(x) | |
| outputs = Dense(num_classes, activation='softmax')(x) | |
| model = Model(inputs=inputs, outputs=outputs, name='custom_emotion_cnn_v2') | |
| return model | |
| def get_model_config() -> dict: | |
| """ | |
| Get the default model configuration. | |
| Returns: | |
| Dictionary with model configuration | |
| """ | |
| return { | |
| "name": "Custom CNN", | |
| "input_shape": (*IMAGE_SIZE, NUM_CHANNELS), | |
| "num_classes": NUM_CLASSES, | |
| "expected_accuracy": "60-68%", | |
| "training_time": "~30 minutes (GPU)", | |
| "parameters": "~5M" | |
| } | |
| if __name__ == "__main__": | |
| # Build and display model summary | |
| model = build_custom_cnn() | |
| model.summary() | |
| print("\nModel configuration:") | |
| config = get_model_config() | |
| for key, value in config.items(): | |
| print(f" {key}: {value}") | |