Berlin rent model (demo)

Please read this first. This model does not tell you what a flat in Berlin really costs. It was trained on a dataset whose author says the flats in it were generated by a pricing formula, not collected from real listings. The model learned that formula. Do not use its numbers to decide anything about a real rent.

In short

You describe a flat (district, size, rooms, floor, a few features). The model returns a monthly cold rent (Kaltmiete, rent without heating and service charges) and a range around it.

It is a small, simple model (a linear regression). It exists to show a complete, honest pipeline: data, training, a service, and a website that labels every number by where it came from. It is the demo model of the Berlin Housing AI project.

Version 0.1.0-research, trained 2026-10-30
What it predicts The monthly cold rent in euros, with an 80 % range
Newest data it saw 2026-04-29
Status Demo only. In the website, prediction is switched off by default.

How to use it

You need Python (tested with 3.12) and three packages: pip install huggingface_hub numpy pandas. There is nothing to train and no model file to trust: the model is a small JSON file, and inference.py (in this repository) reads it with NumPy and pandas.

import sys
from huggingface_hub import snapshot_download

path = snapshot_download("mfozdil/berlin-rent-demo")   # downloads the files, under 100 kB
sys.path.insert(0, path)

from inference import RentModel

model = RentModel(path)
status, result = model.predict({
    "bezirk": "Mitte",            # the official district name
    "ortsteil": "Moabit",         # optional: the neighborhood
    "property_type": "apartment",
    "rooms": 2,
    "area_m2": 65,
    "floor": 2,                   # optional
    "total_floors": 5,            # optional
    "has_balcony": True,          # optional
})

rent = result["estimate"]["monthly_cold_rent"]["value"]
low = result["estimate"]["interval"]["lower"]
high = result["estimate"]["interval"]["upper"]
print(f"Demo estimate: {rent:.0f} EUR per month (80 % range {low:.0f} to {high:.0f})")

Example output (the model is deterministic, so you get the same numbers):

Demo estimate: 1265 EUR per month (80 % range 924 to 1643)
  • predict returns two things: an HTTP-style status (200 is good, 422 means a field was invalid) and the answer. A 422 answer lists which fields to fix, so it never raises an error for bad input.
  • Required: bezirk, property_type, rooms, area_m2. Everything else is optional: ortsteil, floor, total_floors, building_era, energy_class, condition, has_lift, has_balcony, furnishing. Leave a field out and it is listed under result["inputs"]["missing"], and the range gets wider.
  • Every answer carries result["model"]["training_data"]["synthetic"] == True and a warning with the code TRAINING_DATA_UNVERIFIED. Keep that label if you show the number anywhere.
  • result["explanation"] says how much each factor moved the rent per m² compared with the average training flat.
  • The same model runs as a web service with a documented API in the GitHub project, and the project's website can show its answers under a "Demo model" label.

What it is good for, and what it is not

  • Good for: looking at how the pipeline works, reading the code, testing a website against a real model service.
  • Not for: pricing a real flat, judging a real rent, or any advice for tenants, landlords or buyers.

The data, in plain words

The model was trained on sergionefedov/berlin-real-estate-sales-and-rentals-2020-2026 from Kaggle (author: Sergey Nefedov, licence: Apache 2.0). Only the file rentals.csv was used (30,000 rows, January 2020 to April 2026).

What is real in that dataset, according to its author: the coordinates of the districts, the train stations and the interest-rate history. What is not real: the flats themselves. Their size, floor, condition and rent are generated from a pricing formula.

We checked and found the same signs: no missing values anywhere, almost no "round" rents like 1,000 or 1,200 (only 1.1 %), and a market level that moves in clean half-year steps. A real listing site looks messier than that.

Because of this, one result is expected: a simple linear model fits very well, since the data was made by a similar formula. A low error here shows that the model found the formula. It says nothing about real Berlin rents.

The data files are not included here. Their checksums are in the project's data/PROVENANCE.md.

How good is it?

The data was split by time, because a model must be judged on months it has not seen: it learned from 2020 to 2024, was checked on 2025 and tested on January to April 2026.

Average error in euros per month, checked on 2025:

Model Average error (€)
Just the typical €/m² of the training data 405
Linear regression (this model) 144
CatBoost (a tree-based model) 145
Small neural network 144

The three real models tie. That is what you would expect when the data comes from a simple formula. It does not prove that the fancier models cannot help on real data. On the 2026 months the linear model was on average 77 € too high, because the generated market stopped rising while the model's trend kept going. So the model is fixed at the last month of its data (April 2026) instead of looking forward.

The range. Together with the rent, the model gives a range that should contain the rent about 80 % of the time. On months it was not built from, it did: 78.9 % in 2025 and 76.4 % in 2026. That is a little under 80 %, because the market moved.

What you give it

District and neighborhood, living area, number of rooms (1 to 5), floor (0 to 8), floors in the building (3 to 8), building age (four bands), energy class, condition, furnished or not, lift, balcony.

If you leave something out, the model averages over how common each value is in its training data, and the range gets wider. Two inputs the service accepts but ignores: the property type (in the data it is only a label for the number of rooms) and the walk to the nearest station (in the data it does not mean that).

Limits

  • The training data is generated (see above). Nothing here is a statement about real rents.
  • Only 80 of Berlin's 97 neighborhoods are covered clearly. 15 are missing from the data, and two names ("Hohenschönhausen", "Treptow") are ambiguous.
  • Rooms are whole numbers from 1 to 5 in the data. Half rooms are rounded and bigger flats are capped, with a warning.
  • The data ends in April 2026. The service warns when a request is more than 180 days later.

Files in this repository

File What it holds
inference.py The code that reads the files and answers a request (NumPy and pandas)
linear_model.json The model: the numbers it learned. It is evaluated with plain NumPy, so there is no pickle file and nothing to execute.
residual_quantiles.json What the range is built from
feature_schema.json The list of inputs, the neighborhood centres and the allowed values
metadata.json Version, data fingerprint, how the data was split, all results and limits
experiments.csv Every experiment, including the ones that did not help

The numbers in linear_model.json match the training notebook's scikit-learn model to 1.3e-15 on 30,000 rows, and a test in the code repository keeps it that way.

Technical details

  • Target: log(rent per m²), multiplied back by the living area.
  • Split: train 2020-01 to 2024-12 (23,779 rows), validation 2025 (4,711), test 2026-01 to 04-29 (1,510). The shipped model is refit on all rows, so it has no untouched test set. The test numbers above belong to the models trained on 2020 to 2024.
  • Leaking columns (exact functions of the target) were removed: rent_per_m2_kalt_eur, warmmiete_eur_monthly, warmmiete_eur_monthly_usd.
  • Range method: split conformal prediction on the log scale, from the model's out-of-fold errors in the last 12 data months, level 0.8.
  • Difference CatBoost minus linear: +1.1 €, 95 % interval from −1.7 to +3.8. LightGBM was not tried.
  • Code, service and tests: github.com/cyberx801/berlin-housing-ai. Licence: MIT. The training data keeps its own licence (Apache 2.0).
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support