| """ |
| CNNbest Architecture |
| ==================== |
| The convolutional neural network architecture from the ASCAD paper |
| (Benadjila et al., 2020). |
| |
| Architecture: |
| Input(700,1) -> 5 Conv blocks [Conv1D -> SELU -> BatchNorm -> AvgPool] |
| -> Flatten -> 2 x Dense(4096, SELU) -> Dense(256, Softmax) |
| |
| Conv filters: [64, 128, 256, 512, 512], kernel_size=11, pool_size=2 |
| Optimizer: RMSprop(lr=1e-5) |
| """ |
|
|
| from typing import Dict, Any, List |
|
|
| import tensorflow as tf |
|
|
| from .base import BaseModel |
| from ..constants import CNN_DEFAULTS, NUM_CLASSES, WINDOW_SIZE |
|
|
|
|
| class CNNBest(BaseModel): |
| """ |
| CNNbest architecture for ASCAD side-channel attacks. |
| |
| This is the best-performing CNN architecture identified in the original |
| ASCAD paper. It uses 5 convolutional blocks with increasing filter counts, |
| SELU activation, batch normalization, and average pooling, followed by |
| two large fully connected layers. |
| """ |
|
|
| def __init__( |
| self, |
| input_length: int = WINDOW_SIZE, |
| num_classes: int = NUM_CLASSES, |
| conv_filters: List[int] = None, |
| kernel_size: int = CNN_DEFAULTS["kernel_size"], |
| pool_size: int = CNN_DEFAULTS["pool_size"], |
| fc_units: int = CNN_DEFAULTS["fc_units"], |
| num_fc_layers: int = CNN_DEFAULTS["num_fc_layers"], |
| ) -> None: |
| """ |
| Args: |
| input_length: Number of time samples in the input trace. |
| num_classes: Number of output classes. |
| conv_filters: List of filter counts for each conv block. |
| kernel_size: Kernel size for all conv layers. |
| pool_size: Pool size for average pooling. |
| fc_units: Number of units in each fully connected layer. |
| num_fc_layers: Number of fully connected layers. |
| """ |
| super().__init__(input_shape=(input_length, 1), num_classes=num_classes) |
| self.input_length = input_length |
| self.conv_filters = conv_filters or list(CNN_DEFAULTS["conv_filters"]) |
| self.kernel_size = kernel_size |
| self.pool_size = pool_size |
| self.fc_units = fc_units |
| self.num_fc_layers = num_fc_layers |
|
|
| def build(self) -> tf.keras.Model: |
| """Construct the CNNbest Keras model.""" |
| inputs = tf.keras.Input(shape=self.input_shape, name="trace_input") |
| x = inputs |
|
|
| |
| for i, filters in enumerate(self.conv_filters): |
| x = tf.keras.layers.Conv1D( |
| filters=filters, |
| kernel_size=self.kernel_size, |
| padding="same", |
| kernel_initializer="glorot_uniform", |
| name=f"conv_{i}", |
| )(x) |
| x = tf.keras.layers.Activation("selu", name=f"selu_{i}")(x) |
| x = tf.keras.layers.BatchNormalization(name=f"bn_{i}")(x) |
| x = tf.keras.layers.AveragePooling1D( |
| pool_size=self.pool_size, name=f"avgpool_{i}" |
| )(x) |
|
|
| x = tf.keras.layers.Flatten(name="flatten")(x) |
|
|
| |
| for i in range(self.num_fc_layers): |
| x = tf.keras.layers.Dense( |
| self.fc_units, |
| activation="selu", |
| kernel_initializer="glorot_uniform", |
| name=f"fc_{i}", |
| )(x) |
|
|
| outputs = tf.keras.layers.Dense( |
| self.num_classes, |
| activation="softmax", |
| name="predictions", |
| )(x) |
|
|
| self._model = tf.keras.Model(inputs=inputs, outputs=outputs, name="CNNbest") |
| return self._model |
|
|
| def get_config(self) -> Dict[str, Any]: |
| """Return architecture hyperparameters.""" |
| return { |
| "model_type": "cnn", |
| "architecture": "CNNbest", |
| "input_length": self.input_length, |
| "num_classes": self.num_classes, |
| "conv_filters": self.conv_filters, |
| "kernel_size": self.kernel_size, |
| "pool_size": self.pool_size, |
| "fc_units": self.fc_units, |
| "num_fc_layers": self.num_fc_layers, |
| "total_params": self.model.count_params(), |
| } |
|
|