"""
Lecture 12 -- CNNs: Motivation & Architecture
Reproduces the numeric claims from the lecture: the MLP parameter
explosion on raw images, the conv-layer parameter-count formula, the
1x1 convolution channel-reduction example, and a landmark-architecture
comparison table.
"""

# ---- 1. Why not a dense network on images? ----
H, W, D = 224, 224, 3
flat_inputs = H * W * D
hidden_units = 1000
mlp_weights = flat_inputs * hidden_units

print(f"Flattened 224x224x3 image  -> {flat_inputs:,} inputs")
print(f"Dense layer to {hidden_units} units -> {mlp_weights:,} weights "
      f"(~{mlp_weights/1e6:.1f} million)\n")

# ---- 2. Generic conv-layer parameter count ----
def conv_params(K, D_in, D_k):
    """K x K filter, D_in input channels, D_k output filters (+1 bias each)."""
    return (K * K * D_in * D_k) + D_k

# e.g. 15 filters of 3x3 over a 3-channel input (Lecture 13's example)
p = conv_params(K=3, D_in=3, D_k=15)
print(f"3x3 conv, 3 input channels, 15 filters -> {p:,} parameters "
      f"(compare to the {mlp_weights:,} MLP weights above)\n")

# ---- 3. 1x1 convolution as a channel-depth bottleneck (GoogLeNet trick) ----
in_h, in_w, in_channels = 56, 56, 64
num_1x1_filters = 5
out_shape = (in_h, in_w, num_1x1_filters)
reduction_factor = in_channels / num_1x1_filters
print(f"1x1 conv: {in_h}x{in_w}x{in_channels} -> {out_shape[0]}x{out_shape[1]}x{out_shape[2]}"
      f"  ({reduction_factor:.1f}x channel reduction)\n")

# ---- 4. Landmark architecture comparison ----
architectures = [
    {"name": "AlexNet",   "year": 2012, "params_m": 62.3, "top5_err": 15.4},
    {"name": "VGG16",     "year": 2014, "params_m": 140.0, "top5_err": 7.3},
    {"name": "GoogLeNet", "year": 2015, "params_m": 5.0,  "top5_err": 6.7},
    {"name": "ResNet-152","year": 2015, "params_m": 60.0, "top5_err": 3.57},
]
print(f"{'Model':<12}{'Year':>6}{'Params (M)':>13}{'Top-5 err %':>14}")
for a in architectures:
    print(f"{a['name']:<12}{a['year']:>6}{a['params_m']:>13.1f}{a['top5_err']:>14.2f}")

fewer_params_than_alexnet = architectures[0]["params_m"] / architectures[2]["params_m"]
print(f"\nGoogLeNet uses {fewer_params_than_alexnet:.1f}x fewer parameters than "
      f"AlexNet, yet has lower Top-5 error -- accuracy and parameter count "
      f"are not the same axis.")
