Seminar paper · Web scraping · Deep learning

Predicting rent from kitchen and bathroom photos

Can two photos predict the rent of an apartment? I built a web scraper that collected 6,631 Berlin listings with images from ImmobilienScout24 and trained a two-branch CNN on one kitchen and one bathroom photo per apartment. With images alone, it cut the error of a simple baseline by 22%, while structured data remained the stronger predictor.

Solo project Seminar "Data Retrieval & Analytics" Python · Keras

01 / Overview

Motivation

Rent prediction is a classic machine learning task, usually based on structured variables such as living space, number of rooms, and location. But anyone browsing listings judges an apartment by its photos too.

This project asks how much a single kitchen photo and a single bathroom photo reveal about the rent per square meter, with no address, no room count, nothing else, and how this compares to models using structured data.

ImmobilienScout24 Berlin Rent per m²

02 / Web Scraping

Building the dataset

No suitable dataset with apartment photos existed, so I wrote a scraper in Python (requests and BeautifulSoup). Starting from the newest Berlin listing, it steps backwards through all listings and stores the structured data, text fields, and photos of each one.

  • Structured data (rent, area, rooms, district, …) read from a hidden data tag on each listing page
  • Text fields such as condition, energy class, and description
  • Photos downloaded as JPEG, floor plans excluded

Built to run repeatedly

Robust, polite scraping

  • Incremental: each run saves its results with a timestamp; listings already collected are skipped, so later runs only add new ones.
  • Rate limiting: random pauses of 1–9 seconds between image downloads and up to 21 seconds between listings keep the load on the website low.
  • Navigation: the scraper follows the site's own "next listing" links instead of guessing URLs.

03 / Image Selection

Finding the kitchen and the bathroom

Listings contain many photos, labeled by the landlord. To find the right ones, I searched the photo captions for German and English keywords and kept the first match per category.

  • Kitchen: kitchen, Küche, Koch …
  • Bathroom: Bad, WC, Toilette, Dusche, Klo …
Some photos were mislabeled on the website, for example outdoor shots captioned as bathrooms. A manual review removed about 4% of the selected images.

Data funnel

From 6,631 listings to 3,150 apartments

1
6,631 listings scraped

All Berlin rental listings collected by the scraper

2
Kitchen and bathroom photos

3,578 kitchen and 4,013 bathroom photos found; 153 and 149 removed in manual review

3
3,194 listings with both

Only apartments with a valid kitchen and bathroom photo

4
3,150 apartments

After cleaning the structured data and removing outliers

04 / Preprocessing

Cleaning the data

The raw data has 37 columns, many of which need cleaning before modeling.

Structured data

Target, location, categories

  • Target: base rent divided by living area; listings above 50 EUR/m² removed as outliers
  • Location: district names normalized and addresses geocoded to coordinates with the TomTom API
  • Categories: district, condition, and energy efficiency one-hot encoded; missing values imputed

Images

Ready for the CNN

  • Resized to 220 × 220 pixels with three color channels
  • Stored as NumPy arrays, one per photo category
  • 80/20 split into training and test apartments (630 test apartments)

05 / Modeling

A two-branch CNN

The CNN, built with the Keras functional API, has two identical branches: one processes the kitchen photo, the other the bathroom photo. Their outputs are concatenated and passed to a small shared head that predicts rent per square meter.

Keras / TensorFlow Adam MSE loss 100 epochs

Architecture per branch

  • Input: 220 × 220 × 3 image
  • 3 convolution blocks: 32, 64, and 128 filters (6 × 6, ReLU), each followed by 3 × 3 max pooling
  • Dense layers: 500 → 250 → 100 → 25 → 10 units, with dropout (0.25)
  • Head: both branches concatenated → Dense(10) → one linear output

06 / Results

Model comparison

Five models were compared with a baseline that always predicts the mean rent per m² of the training set.

  • Images carry signal: using only the two photos, the CNN (Model 1) reduces MAE by 22% and MSE by 31% compared with the baseline.
  • Structured data is stronger: XGBoost on structured data (Model 4) performs best, with 58% lower MAE and 74% lower MSE than the baseline.
  • Combining does not help: adding the image model to the structured-data models makes them worse, because the CNN overfits the training set.
With images alone, over 55% of predictions lie within ±3 EUR/m² of the true rent, and only 29 of 630 test apartments are off by more than 10 EUR/m².

Error on 630 test apartments (EUR/m²)

Model Input MAE MSE
BaselineMean of training set4.51034.227
Model 1CNN: kitchen + bathroom photo3.52923.548
Model 2Neural network: structured data2.32711.111
Model 3Model 1 + Model 2 combined2.65015.035
Model 4XGBoost: structured data1.8818.909
Model 5XGBoost + Model 1 predictions2.11811.275

Lower is better. Highlighted: best model.

07 / Discussion

Possible applications

With more data and a stronger image model, photo-based rent estimates could be useful along the real estate chain:

  • Tenants could check whether a listed rent is plausible.
  • Landlords and agents could test how photo selection affects listing prices.
  • Platforms could recommend which photos to show.

Limitations and next steps

What I would do differently

  • Overfitting: the CNN fits the training set almost perfectly (training MSE below 1 after 30 epochs) while the test MSE stays around 22. A pretrained backbone, data augmentation, and early stopping would be the first fixes.
  • One photo per room: only the first kitchen and bathroom photo were used; more photos could add information.
  • Label noise: landlord captions are not always correct.
  • Berlin only: other cities would need retraining because of different price levels.