# `Edifice.Convolutional.DenseNet`
[🔗](https://github.com/blasphemetheus/edifice/blob/main/lib/edifice/convolutional/densenet.ex#L1)

DenseNet (Densely Connected Convolutional Network) implementation.

DenseNet connects each layer to every other layer in a feed-forward fashion.
Within a dense block, each layer receives the feature maps of all preceding
layers as input (via concatenation), encouraging feature reuse and reducing
parameter count compared to traditional CNNs.

## Architecture

```
Input [batch, H, W, C]
      |
+-----v----------+
| Stem            |  7x7 conv stride 2, BN, ReLU, 3x3 max pool
+--+--------------+
   |
+--v--------------+
| Dense Block 1    |  Each layer concatenates all previous feature maps
+--+--------------+
   |
+--v--------------+
| Transition 1     |  1x1 conv (compress) + 2x2 avg pool (downsample)
+--+--------------+
   |
+--v--------------+
| Dense Block 2    |
+--+--------------+
   |
   ... (repeat)
   |
+--v--------------+
| Final BN + ReLU  |
+--+--------------+
   |
+--v--------------+
| Global AvgPool   |
+--+--------------+
   |
+--v--------------+
| Dense            |  num_classes outputs
+-----------------+
```

## Configurations

| Model         | block_config       | growth_rate | Params |
|---------------|-------------------|-------------|--------|
| DenseNet-121  | [6, 12, 24, 16]  | 32          | ~8M    |
| DenseNet-169  | [6, 12, 32, 32]  | 32          | ~14M   |
| DenseNet-201  | [6, 12, 48, 32]  | 32          | ~20M   |
| DenseNet-264  | [6, 12, 64, 48]  | 32          | ~34M   |

## Usage

    # DenseNet-121 for CIFAR-10
    model = DenseNet.build(
      input_shape: {nil, 32, 32, 3},
      num_classes: 10,
      growth_rate: 32,
      block_config: [6, 12, 24, 16]
    )

    # Compact DenseNet for small datasets
    model = DenseNet.build(
      input_shape: {nil, 32, 32, 3},
      num_classes: 10,
      growth_rate: 12,
      block_config: [4, 8, 12, 8],
      compression: 0.5
    )

# `build_opt`

```elixir
@type build_opt() ::
  {:block_config, [pos_integer()]}
  | {:bn_size, pos_integer()}
  | {:compression, float()}
  | {:dropout, float()}
  | {:growth_rate, pos_integer()}
  | {:initial_channels, pos_integer()}
  | {:input_shape, tuple()}
  | {:num_classes, pos_integer() | nil}
```

Options for `build/1`.

# `build`

```elixir
@spec build([build_opt()]) :: Axon.t()
```

Build a DenseNet model.

## Options

  - `:input_shape` - Input shape as `{nil, height, width, channels}` (required)
  - `:num_classes` - Number of output classes (default: 10)
  - `:growth_rate` - Number of new feature maps per dense layer (default: 32)
  - `:block_config` - List of layer counts per dense block (default: [6, 12, 24, 16])
  - `:compression` - Compression factor for transitions, 0.0-1.0 (default: 0.5)
  - `:initial_channels` - Channels after stem conv (default: growth_rate * 2)
  - `:dropout` - Dropout rate in dense layers (default: 0.0)
  - `:bn_size` - Bottleneck width multiplier for BN-ReLU-1x1 layers (default: 4)

## Returns

  An Axon model outputting `[batch, num_classes]`.

# `dense_block`

```elixir
@spec dense_block(Axon.t(), pos_integer(), keyword()) :: {Axon.t(), pos_integer()}
```

Build a dense block where each layer receives all previous feature maps.

Within a dense block, layer `i` receives the concatenation of feature maps
from layers `0, 1, ..., i-1` as input. Each layer produces `growth_rate`
new feature maps.

## Parameters

  - `input` - Input Axon node `[batch, H, W, C]`
  - `num_layers` - Number of dense layers in this block

## Options

  - `:growth_rate` - New feature maps per layer (default: 32)
  - `:num_channels` - Current number of input channels (required for tracking)
  - `:dropout` - Dropout rate (default: 0.0)
  - `:bn_size` - Bottleneck width multiplier (default: 4)
  - `:name` - Layer name prefix (default: "dense_block")

## Returns

  Tuple of `{output_node, total_channels}` where `total_channels` is
  `num_channels + num_layers * growth_rate`.

# `output_size`

```elixir
@spec output_size(keyword()) :: pos_integer()
```

Get the output size (num_classes) for a DenseNet model.

# `transition_layer`

```elixir
@spec transition_layer(Axon.t(), pos_integer(), keyword()) :: Axon.t()
```

Build a transition layer between dense blocks.

Transitions reduce spatial dimensions (2x downsampling via average pooling)
and optionally compress the number of feature maps with a 1x1 convolution.

## Parameters

  - `input` - Input Axon node `[batch, H, W, C]`
  - `out_channels` - Number of output channels after compression

## Options

  - `:name` - Layer name prefix (default: "transition")

## Returns

  An Axon node with shape `[batch, H/2, W/2, out_channels]`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
