Skip to content
UH.
Cross-City Building Age Classification banner
CompletedFeatured project

Cross-City Building Age Classification

Predict the majority construction-age class of buildings inside 30 m Landsat pixels from decades of spectral reflectance, trained on Madrid and transferred to Amsterdam. Class-conditional CORAL lifts zero-shot macro-F1 from 0.36 to 0.65 with no Amsterdam labels.

Hack4Dev Data Science Hackathon. Domain adaptation under a real city shift: feature engineering from satellite time series, class-balanced Random Forests, CORAL alignment, few-shot transfer, and a spatial adjacency audit that stress-tested whether our own evaluation was inflating few-shot gains.

The task was to estimate which of four construction-age groups dominates a 30 m satellite patch, using roughly forty years of Landsat spectral reflectance. Models were trained primarily on Madrid and had to generalise to Amsterdam (a city never seen in training), creating a classic domain-adaptation problem scored on macro-F1.

We engineered about 108 per-pixel features: multi-period band statistics, spectral indices, the size and timing of major spectral change, and neighbourhood feature averages. Stage 1 was a class-balanced Random Forest (500 trees). For zero-shot transfer we used class-conditional CORAL, aligning each Madrid age group to the covariance of Amsterdam pixels the model assigned to that group over two rounds, with no Amsterdam labels. With a few local labels we added budget-scaled feature whitening, a small local forest, probability blending with Stage 1, and inverse-distance neighbour smoothing.

Madrid in-city 5x5 CV macro-F1 reached 0.664 ± 0.004. Raw Amsterdam zero-shot scored 0.358; pooled CORAL 0.578; class-conditional CORAL 0.651, roughly 0.36 to 0.65 macro-F1 with zero Amsterdam labels. Few-shot rose from 0.662 ± 0.003 at 5 labels/class to 0.737 ± 0.005 at 200.

We also audited spatial adjacency in the few-shot protocol. Under the organisers' random split, roughly 80% of support pixels had an immediate map neighbour in the query set; a spatial-block re-run showed that about half the few-shot lift at higher budgets was proximity rather than labels. The zero-shot result uses no labels, has no adjacency issue, and remains the strongest transferable number. Treating the few-shot curve as an upper bound was a deliberate evaluation stress test, not a footnote.

Project goal

Build a Madrid to Amsterdam building-age transfer pipeline that works with few or zero Amsterdam labels, and honestly measure how much of any few-shot gain is real skill versus spatial proximity in the evaluation design.

Screenshots

Feature-space scatter comparing Madrid and Amsterdam building-age classes, from the Cross-City Building Age Classification project by Ubayd HattasCross-city feature space

Key features

  • 108 engineered features from Landsat time series (spectral stats, indices, change timing, neighbourhood averages)
  • Class-balanced Random Forest (500 trees) as Stage-1 Madrid model
  • Pooled and class-conditional CORAL for label-free domain alignment
  • Budget-scaled feature whitening, local few-shot modelling, and probability blending
  • Inverse-distance spatial neighbour smoothing (prediction-only)
  • Madrid 5×5 CV ceiling: 0.664 ± 0.004 macro-F1
  • Zero-shot Amsterdam: 0.358 → 0.651 macro-F1 via class-conditional CORAL
  • Spatial adjacency audit showing ~half of higher-budget few-shot lift was proximity

Project details

Event

Hack4Dev Data Science Hackathon

Type

Domain Adaptation / Remote Sensing

Zero-shot

0.36 → 0.65 macro-F1

Madrid CV

0.664 ± 0.004

Features

108 engineered

Few-shot (200)

0.737 ± 0.005

Technologies

PythonMachine Learningscikit-learnPandasNumPyRandom ForestDomain AdaptationFeature EngineeringRemote SensingSpatial AnalysisCross-ValidationStatistics