Cracking Icebergs with Deep Learning: My Kaggle Statoil/C-CORE Challenge Project

Cracking Icebergs with Deep Learning: My Kaggle Statoil/C-CORE Challenge Project
An aerial view of a tanker ship navigating among icebergs β€” a dramatic reminder of why accurate detection is critical.

Distinguishing icebergs vs ships in Synthetic Aperture Radar (SAR) imagery matters for maritime safety and offshore operations. In this project I worked on the Kaggle Statoil/C-CORE Iceberg Classifier Challenge, building image models that convert raw satellite bands into reliable predictions.

πŸ‘‰ Full code & notebook: GitHub – Statoil Iceberg Classifier


Why this matters

  • Business impact: Reduces risk for vessels and offshore assets; helps allocate resources more efficiently.
  • Breadth of skills: Problem framing, exploratory analysis, modeling, evaluation, and reproducible delivery.
  • Transferability: Methods generalize to other imaging domains (quality control, medical, remote sensing).

What’s in the notebook

Data & representation

  • SAR JSON inputs (band_1, band_2, inc_angle).
  • Construction of three-channel tensors including a DIFF channel (band_1 - band_2) and per-image standardization.
  • Side feature: inc_angle used as a numeric input branch.
SAR visualization


Example visualization of SAR image bands, highlighting how raw radar data is transformed into usable inputs for modeling.


Models implemented

  1. Baseline two-input CNN (TensorFlow/Keras) β€” image branch + angle branch fused at the head.
  2. Transfer learning with ResNet50V2 (frozen backbone) β€” applied to DIFF-channel RGB, plus angle branch.

Both were trained with stratified splits, early stopping, and callbacks such as learning-rate reduction.

AUC & LogLoss


Training curves showing how AUC improves while LogLoss decreases β€” evidence of a well-tuned model.


Code snippets

Baseline two-input CNN:

inp_img = tf.keras.layers.Input(shape=(75, 75, 3))
x = augment(inp_img)
x = tf.keras.layers.Conv2D(32, 3, padding="same", activation="relu")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.Conv2D(64, 3, padding="same", activation="relu")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.MaxPooling2D()(x)
x = tf.keras.layers.GlobalAveragePooling2D()(x)
x = tf.keras.layers.Dropout(0.3)(x)

inp_ang = tf.keras.layers.Input(shape=(1,))
ang = tf.keras.layers.Normalization()(inp_ang)
ang = tf.keras.layers.Dense(16, activation="relu")(ang)

h = tf.keras.layers.Concatenate()([x, ang])
h = tf.keras.layers.Dense(128, activation="relu")(h)
h = tf.keras.layers.Dropout(0.3)(h)
out = tf.keras.layers.Dense(1, activation="sigmoid")(h)
model = tf.keras.Model([inp_img, inp_ang], out)

What I learned

  • Side-feature fusion (angle + image) can materially help performance in radar imagery.
  • DIFF channel emphasizes class-relevant texture/structure that plain bands may under-express.
  • Frozen transfer learning provides a strong baseline; careful regularization and calibration (label smoothing) matter.
  • Reproducibility: fixed seeds, stratified splits, and saved weights enable reliable iteration.

Explore the project