Overview
The keras_darknet19.py module implements the Darknet-19 convolutional neural network architecture in Keras. Darknet-19 serves as the feature extraction backbone for YOLO v2, providing efficient and effective feature representations for object detection.
Architecture Components
DarknetConv2D
Wrapper for Conv2D with Darknet-specific configuration.
DarknetConv2D(*args, **kwargs)
Features
kernel_regularizer
regularizer
default:"l2(5e-4)"
L2 regularization with weight decay of 0.0005 applied to all convolution kernels.
Padding mode for convolutions. Always uses ‘same’ padding to preserve spatial dimensions.
@functools.wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
"""Wrapper to set Darknet weight regularizer for Convolution2D."""
darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
darknet_conv_kwargs.update(kwargs)
return _DarknetConv2D(*args, **darknet_conv_kwargs)
Usage
from keras_darknet19 import DarknetConv2D
# Create a convolution layer with Darknet defaults
layer = DarknetConv2D(64, (3, 3), activation='relu')
DarknetConv2D_BN_Leaky
Convolution block with Batch Normalization and Leaky ReLU activation.
DarknetConv2D_BN_Leaky(*args, **kwargs)
Architecture
This function composes three layers in sequence:
- DarknetConv2D: Convolution without bias (since BN follows)
- BatchNormalization: Normalizes activations
- LeakyReLU: Leaky ReLU with alpha=0.1
Bias is disabled since Batch Normalization includes bias parameters.
Negative slope coefficient for Leaky ReLU activation.
def DarknetConv2D_BN_Leaky(*args, **kwargs):
"""Darknet Convolution2D followed by BatchNormalization and LeakyReLU."""
no_bias_kwargs = {'use_bias': False}
no_bias_kwargs.update(kwargs)
return compose(
DarknetConv2D(*args, **no_bias_kwargs),
BatchNormalization(),
LeakyReLU(alpha=0.1))
Usage
from keras_darknet19 import DarknetConv2D_BN_Leaky
# Create a conv-bn-leaky block
layer = DarknetConv2D_BN_Leaky(128, (3, 3))
Building Blocks
bottleneck_block()
Bottleneck block with three convolutions: 3x3, 1x1, 3x3.
bottleneck_block(outer_filters, bottleneck_filters)
Parameters
Number of filters for the 3x3 convolutions (first and last).
Number of filters for the 1x1 convolution (middle layer).
Returns
Composed function that applies the bottleneck block to an input tensor.
Architecture
Input
|
v
[Conv 3x3, outer_filters] + BN + LeakyReLU
|
v
[Conv 1x1, bottleneck_filters] + BN + LeakyReLU
|
v
[Conv 3x3, outer_filters] + BN + LeakyReLU
|
v
Output
def bottleneck_block(outer_filters, bottleneck_filters):
"""Bottleneck block of 3x3, 1x1, 3x3 convolutions."""
return compose(
DarknetConv2D_BN_Leaky(outer_filters, (3, 3)),
DarknetConv2D_BN_Leaky(bottleneck_filters, (1, 1)),
DarknetConv2D_BN_Leaky(outer_filters, (3, 3)))
bottleneck_x2_block()
Extended bottleneck block with five convolutions: 3x3, 1x1, 3x3, 1x1, 3x3.
bottleneck_x2_block(outer_filters, bottleneck_filters)
Parameters
Number of filters for the 3x3 convolutions.
Number of filters for the 1x1 convolutions.
Returns
Composed function that applies the extended bottleneck block.
Architecture
Input
|
v
[bottleneck_block(outer_filters, bottleneck_filters)]
|
v
[Conv 1x1, bottleneck_filters] + BN + LeakyReLU
|
v
[Conv 3x3, outer_filters] + BN + LeakyReLU
|
v
Output
def bottleneck_x2_block(outer_filters, bottleneck_filters):
"""Bottleneck block of 3x3, 1x1, 3x3, 1x1, 3x3 convolutions."""
return compose(
bottleneck_block(outer_filters, bottleneck_filters),
DarknetConv2D_BN_Leaky(bottleneck_filters, (1, 1)),
DarknetConv2D_BN_Leaky(outer_filters, (3, 3)))
Main Architecture
darknet_body()
Generates the first 18 convolutional layers of Darknet-19.
Returns
Composed function that builds the Darknet-19 feature extraction layers.
Architecture
The Darknet-19 body consists of:
- Conv1: 32 filters, 3x3, followed by MaxPooling
- Conv2: 64 filters, 3x3, followed by MaxPooling
- Conv3-5: Bottleneck block (128/64), followed by MaxPooling
- Conv6-8: Bottleneck block (256/128), followed by MaxPooling
- Conv9-13: Extended bottleneck block (512/256), followed by MaxPooling
- Conv14-18: Extended bottleneck block (1024/512)
def darknet_body():
"""Generate first 18 conv layers of Darknet-19."""
return compose(
DarknetConv2D_BN_Leaky(32, (3, 3)),
MaxPooling2D(),
DarknetConv2D_BN_Leaky(64, (3, 3)),
MaxPooling2D(),
bottleneck_block(128, 64),
MaxPooling2D(),
bottleneck_block(256, 128),
MaxPooling2D(),
bottleneck_x2_block(512, 256),
MaxPooling2D(),
bottleneck_x2_block(1024, 512))
Feature Map Progression
For input size 416x416:
Input: 416 x 416 x 3
Conv1 + Pool: 208 x 208 x 32
Conv2 + Pool: 104 x 104 x 64
Conv3-5 + Pool: 52 x 52 x 128
Conv6-8 + Pool: 26 x 26 x 256
Conv9-13 + Pool: 13 x 13 x 512
Conv14-18: 13 x 13 x 1024
darknet19()
Generates complete Darknet-19 model for ImageNet classification.
Parameters
Input tensor for the model. Typically shape (224, 224, 3) or (416, 416, 3).
Returns
Complete Darknet-19 model with 1000-class ImageNet classification head.
def darknet19(inputs):
"""Generate Darknet-19 model for Imagenet classification."""
body = darknet_body()(inputs)
logits = DarknetConv2D(1000, (1, 1), activation='softmax')(body)
return Model(inputs, logits)
Classification Head
The final layer uses:
- 1x1 convolution to reduce spatial features to class predictions
- 1000 filters (one per ImageNet class)
- Softmax activation for probability distribution
Usage Examples
from keras.layers import Input
from keras.models import Model
from keras_darknet19 import darknet_body
# Create feature extractor
inputs = Input(shape=(416, 416, 3))
features = darknet_body()(inputs)
feature_extractor = Model(inputs, features)
print(feature_extractor.summary())
# Output shape: (None, 13, 13, 1024)
ImageNet Classification
from keras.layers import Input
from keras_darknet19 import darknet19
# Create full classification model
inputs = Input(shape=(224, 224, 3))
model = darknet19(inputs)
# Compile for training
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
print(model.summary())
Custom Architecture
from keras.layers import Input, GlobalAveragePooling2D, Dense
from keras.models import Model
from keras_darknet19 import darknet_body
# Custom classifier using Darknet-19 features
inputs = Input(shape=(416, 416, 3))
features = darknet_body()(inputs)
# Add custom head
x = GlobalAveragePooling2D()(features)
x = Dense(256, activation='relu')(x)
outputs = Dense(10, activation='softmax')(x) # 10 custom classes
model = Model(inputs, outputs)
Using Building Blocks
from keras.layers import Input
from keras.models import Model
from keras_darknet19 import DarknetConv2D_BN_Leaky, bottleneck_block
from ..utils import compose
# Build custom architecture
inputs = Input(shape=(256, 256, 3))
x = compose(
DarknetConv2D_BN_Leaky(64, (3, 3)),
DarknetConv2D_BN_Leaky(128, (3, 3)),
bottleneck_block(256, 128),
DarknetConv2D_BN_Leaky(512, (3, 3))
)(inputs)
model = Model(inputs, x)
Design Principles
Regularization
All convolutional layers use L2 regularization with weight decay of 0.0005 to prevent overfitting:
kernel_regularizer=l2(5e-4)
Batch Normalization
Batch normalization is applied after every convolution (before activation) to:
- Stabilize training
- Reduce internal covariate shift
- Allow higher learning rates
- Provide regularization effect
Leaky ReLU
Leaky ReLU with alpha=0.1 is used instead of standard ReLU to:
- Prevent dying neurons
- Allow gradients to flow for negative values
- Improve gradient flow during backpropagation
Bottleneck Design
Bottleneck blocks use 1x1 convolutions to:
- Reduce computational cost
- Decrease number of parameters
- Increase network depth without excessive computation
- Create multi-scale feature representations
Model Statistics
Darknet-19 Parameters
- Total layers: 19 convolutional layers + 5 max pooling
- Parameters: ~20.8 million (for ImageNet version)
- Feature extractor: 18 layers (without classification head)
- Output features: 1024 channels at 13x13 spatial resolution (for 416x416 input)
Computational Efficiency
Darknet-19 is designed to be efficient:
- Fewer parameters than VGG-16 or ResNet-50
- Faster inference time
- Suitable for resource-constrained devices (Raspberry Pi)
- Good balance between accuracy and speed
Pre-trained Weights
Darknet-19 can be initialized with pre-trained ImageNet weights for transfer learning. The YOLO-Pi project uses pre-trained weights converted from the original Darknet framework.
# Load pre-trained weights (example)
model = darknet19(inputs)
model.load_weights('darknet19_weights.h5')
Integration with YOLO
In YOLO v2, darknet_body() serves as the base feature extractor:
from keras_yolo import yolo_body
from keras_darknet19 import darknet_body
# YOLO v2 uses darknet_body for features
inputs = Input(shape=(416, 416, 3))
model = yolo_body(inputs, num_anchors=5, num_classes=20)
# Internally uses: darknet = Model(inputs, darknet_body()(inputs))
The 13th layer output (layer 43 in the full model) is also used in YOLO’s passthrough layer for multi-scale detection.