Python 52.4%
TeX 47.6%
1<!-- Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca -->2# 🏠 Hedonic House Pricing with Language-Model and Vision Features34**UQO Working Paper No. 14** — *Evidence from Quebec Real-Estate Listings*56[-b31b1b?logo=latex&logoColor=white)](paper/main.pdf)7[](requirements.txt)8[](data/raw/README.md)9[](scripts/)10[](https://uqo.ca)11[](mailto:simon-pierre.boucher@uqo.ca)1213> **TL;DR** — Much of a home's value is recorded only in the free text and the photographs of its listing. We use a14> large language model to turn each description into structured specifications and a vision–language model to turn the15> façade photo into objective visual attributes, both under strict JSON schemas. On 19,123 Quebec house listings the two16> AI layers lift the R² of an already rich log-linear hedonic model from 0.717 to 0.755; the vision layer is the largest17> out-of-sample gain of any feature block for every estimator (OLS, gradient boosting, neural nets); the best ensemble18> reaches out-of-sample R² 0.779 with a 14.0% median absolute error. Implicit prices are economically sensible (+2.3% per19> point of curb appeal, +3–4% for brick or stone) and survive municipality-clustered inference.2021---2223## 📌 Status2425| Item | Value |26|---|---|27| Version | 2.0 (July 2026) — revised draft after an internal review round (`scripts/13_revision_extras.py`) |28| SSRN | not yet submitted |29| Journal | Elsevier-format highlights prepared (`docs/highlights_elsevier.tex`); target to be decided |30| Code & data | reproducible from `scripts/` run from the repository root; raw crawls and LLM caches not committed |3132## 🎯 What this paper does3334Hedonic models are bounded by the characteristics an analyst can measure. We treat general-purpose AI models as a35**feature-engineering technology**: each listing description is parsed by an LLM into a fixed schema of property36specifications (renovations, finishes, systems, extras), and each façade photograph is parsed by a vision–language model37into objective attributes (cladding, storeys, garage, curb appeal). The outputs are ordinary econometric variables and38enter a log-linear hedonic regression with city fixed effects:3940```41ln P_i = α_city(i) + X_i'β + S_i'γ (LLM text layer) + V_i'δ (vision layer) + ε_i42```4344We then measure, under identical 5-fold splits, how much each layer adds for OLS, histogram gradient boosting, a45residual MLP with city/type embeddings, and a stacked blend.4647## 🏆 Headline results4849| Object | Estimate |50|---|---|51| Base hedonic OLS (structural + location), in-sample R² | 0.717 |52| + LLM text layer + vision layer | 0.755 |53| Out-of-sample R², OLS: base → +text → +text+vision (5-fold) | 0.52 → 0.62 → … (see `paper/tables/ablation.tex`) |54| Out-of-sample R², GBM: base → +text | 0.69 → 0.73 |55| Best ensemble (robust NN + GBM blend) | R²_OOS = 0.779, median absolute error 14.0% |56| Curb-appeal score (1–5) | +2.3% per point |57| Brick / stone façade | +3–4% |58| Inference | HC3 and municipality-clustered standard errors agree |5960## 🗂 Repository layout6162```63hedonic-llm-vision-features/64├── data/65│ ├── raw/ # two Apify crawls (Realtor.ca, DuProprio), 2026-07-03 — not committed66│ └── processed/ # LLM/vision caches, merged datasets, OOS predictions — not committed67├── scripts/ # run from the repository root, in order68│ ├── 00a_test_openai_extraction.py / 00b_test_vision_claude.py # API smoke tests69│ ├── 01_extract_specs_llm.py # description → JSON specs (parallel, resumable)70│ ├── 02_facade_vision.py # façade photo → visual attributes71│ ├── 03_merge_llm_features.py72│ ├── 04_hedonic_ols_base.py / 05_hedonic_ols_v4_text.py73│ ├── 06_hedonic_ml_cv.py / 07_torch_nn.py / 08_torch_nn_robust.py74│ ├── 09_v5_ml_text_vision.py / 10_final_blend.py75│ ├── 11_extras_v5_robustness.py / 12_ablation_extras.py / 13_revision_extras.py76│ └── 14_figures_v5.py / 15_make_tables.py / 16_make_tables_v5.py / 17_gen_regression_table.py77├── results/ # hedonic summaries (txt), ablation_extras.json, revision_extras.json78├── docs/ # METHODOLOGIE.md + methodologie.tex/pdf (French data & method note), Elsevier highlights79└── paper/ # main.tex + sections/ + tables/ + figures/ + references.bib → main.pdf80```8182## 🚀 Quick start8384```bash85python3 -m pip install -r requirements.txt86cp .env.example .env # OPENAI_API_KEY=…, ANTHROPIC_API_KEY=… (only steps 00–02 call the APIs)87# place the two Apify crawls in data/raw/ and build data/processed/maisons_avec_prix.json (docs/METHODOLOGIE.md §2–4), then:88WORKERS=24 python3 scripts/01_extract_specs_llm.py89python3 scripts/02_facade_vision.py90python3 scripts/03_merge_llm_features.py91python3 scripts/05_hedonic_ols_v4_text.py92python3 scripts/08_torch_nn_robust.py && python3 scripts/09_v5_ml_text_vision.py && python3 scripts/10_final_blend.py93python3 scripts/11_extras_v5_robustness.py && python3 scripts/12_ablation_extras.py && python3 scripts/13_revision_extras.py94python3 scripts/14_figures_v5.py && python3 scripts/16_make_tables_v5.py && python3 scripts/17_gen_regression_table.py95cd paper && make # latexmk → main.pdf96```9798LLM extraction ≈ 600 tokens in / 110 out per listing (24 threads); vision ≈ 2,470 tokens in per image (16 threads);99both are resumable. Econometrics and ML ≈ 20 minutes on an Apple-silicon laptop (PyTorch on MPS).100101## 🗃 Data10210327,302 Realtor.ca and 7,693 DuProprio listings crawled on 2026-07-03; houses (single-family and townhouse) with a valid104price retained, outliers removed by the rules in `docs/METHODOLOGIE.md` §3, giving 19,123 observations. Structural105variables (living area, lot, rooms, year built, type, city), text-derived specifications and façade attributes. See106`data/raw/README.md` and `data/processed/README.md`.107108## 🔬 Methodology1091101. **AI feature layers** with strict JSON schemas and retry/backoff — `scripts/01_…`, `scripts/02_…`.1112. **Log-linear hedonic OLS** with city fixed effects, HC3 and municipality-clustered SEs — `scripts/05_…`, `scripts/13_…`.1123. **Ablation by feature block × estimator** under identical folds (OLS, GBM, NN) — `scripts/09_…`, `scripts/12_…`.1134. **Stacked blend** of the robust NN and HistGradientBoosting — `scripts/10_final_blend.py`.1145. **Robustness** by segment, extreme city effects, calibration and importance plots — `scripts/11_…`, `scripts/14_…`.115116## 🖼 Figures & tables inventory117118| Figure | Content |119|---|---|120| `fig_ablation` | out-of-sample gain by feature block and estimator |121| `fig_calibration` | predicted vs actual, blend |122| `fig_importance` | feature importance |123| `fig_premia` | location premia |124| `fig_validation` | validation of AI-extracted attributes |125| `nn_diagram` (TikZ) | architecture of the robust NN |126127Tables (`paper/tables/*.tex`): summary stats, regression (A/B panels), models, ablation, robustness, AI summary.128129## ⚠️ Limitations130131- Asking prices from listings, single cross-section (July 2026).132- Realtor.ca listings lack coordinates; location enters through city fixed effects and postal codes.133- LLM/vision outputs are model-dependent; schemas and prompts are versioned in the scripts.134- API costs make the extraction layers the expensive part of replication.135136## 📝 Citation137138```bibtex139@techreport{boucher2026llmvisionhedonic,140 author = {Boucher, Simon-Pierre},141 title = {Hedonic House Pricing with Language-Model and Vision Features: Evidence from Quebec Real-Estate Listings},142 institution = {Universit\'e du Qu\'ebec en Outaouais, D\'epartement des sciences administratives},143 type = {Working Paper},144 number = {14},145 year = {2026},146 month = {July}147}148```149150## 👤 Author & contact151152**Simon-Pierre Boucher** — Département des sciences administratives,153Université du Québec en Outaouais (UQO), Gatineau, QC.154📧 [simon-pierre.boucher@uqo.ca](mailto:simon-pierre.boucher@uqo.ca)155156## 🗒 Notes157158- Numbering: this paper was first issued as "Working Paper No. 11" (July 2026); number 11 belongs to *Half a Million159 Prices, Twenty Models*. Renumbered **14** on 2026-10-17; cover date fixed to July 2026 (was `\today`).160- Harmonised 2026-10-17: flat scripts moved to `scripts/` (numbered), data to `data/`, logs removed, summaries to161 `results/`, methodology note to `docs/`; file paths inside the scripts now point to `data/…` and `results/…`162 (run them from the repository root). Bibliography switched from `plainnat` to `aer`.163- The script `13_revision_extras.py` was written for a Journal of Housing Economics revision round (clustered SEs,164 in-sample ablation, extreme city effects).165