# 🏠 From Listing Images to Property Valuation **UQO Working Paper No. 20** — *A Multimodal AI Framework for Automated Building Characterization and the Cost Approach — Evidence from Québec single-family listings matched to the assessment roll* [![Paper](https://img.shields.io/badge/paper-PDF%20(66%20p.)-b31b1b?logo=latex&logoColor=white)](paper/main.pdf) [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](requirements.txt) [![Data](https://img.shields.io/badge/data-1%2C200%20listings%20·%2014%2C245%20photos%20·%20roll%20link-4051b5)](data/README.md) [![Reproducible](https://img.shields.io/badge/reproducible-Makefile%20pipeline%20%2B%20manifest-2e7d32)](Makefile) [![Institution](https://img.shields.io/badge/UQO-D%C3%A9pt.%20des%20sciences%20administratives-16365c)](https://uqo.ca) [![Author](https://img.shields.io/badge/author-Simon--Pierre%20Boucher-16365c)](mailto:simon-pierre.boucher@uqo.ca) > **TL;DR** — Can the photographs and text of a for-sale listing supply the building specification that the cost approach > needs (materials, finish grade, condition, renovations), and what is a transparent cost indication built on it worth? > On 1,200 Québec house listings matched within 15 m to their assessment-roll unit (14,245 photographs, three > vision–language extractor tiers, nothing fitted on prices), visible exterior facts are read with 85–96 % agreement > against broker fields, but sellers show a basement in 3 % of listings and a bathroom in 20 % within ten photographs > (missing-not-at-random). Images and text move the cost indication mostly through condition-adjusted depreciation: the > ratio to the asking price goes from 0.75 (roll only) to 0.95 (multimodal), MdAPE 30 → 28 %. The published unit-cost anchor > matters more (−30 % under a Montréal hard-cost band); a calibration against assessor values transfers to a geographic > hold-out and implies ≈ $270–306/sq ft, inside the 2026 Québec industry band. --- ## 📌 Status | Item | Value | |---|---| | Version | 1.0 (October 2026) | | SSRN | not yet submitted | | Journal | — (candidate: Journal of Real Estate Finance and Economics / Journal of Property Research) | | Code & data | reproducible from `Makefile`; Vrai Prix databases not redistributed (see `data/README.md`, `docs/data_rights.md`) | | Gates | 1 audit ✓ · 2 literature ✓ (104 verified refs) · 3 theory ✓ · 4 implementation ✓ (tests) · 5 experiments ✓ (14/16; 2 not feasible) · 6 manuscript ✓ · 7 integrity ✓ (`docs/results_validation.md`) | ## 🎯 What this paper does The cost approach values a property as land plus the depreciated replacement cost of the improvements. Its inputs (materials, grade, condition, renovations) are not in assessment rolls and are normally obtained by inspection. We formalise an **extraction-to-valuation framework** with five evidence classes (administrative, visual, proxy, text-claimed, latent), deterministic cross-image aggregation, text–image–record reconciliation, a fully sourced cost schedule and uncertainty propagation: ``` V_cost = L_roll(indexed) + (RCN(grade, storeys, add-ons) × (1+soft)(1+EI) + S) × (1 − D(effective age(condition, renovation))) ``` and we evaluate every link on Vrai Prix data: agreement of visual attributes with broker fields and the roll, information content of image vs text channels, cross-extractor reliability, confidence calibration, the MNAR structure of listing photographs, and the alignment of the indication with assessor values, asking prices and recorded sales, with a geographic hold-out. It is a cost-approach paper, not an image-enhanced hedonic model: no price enters the indication. ## 🏆 Headline results | Object | Estimate | |---|---| | Roof covering / garage type / cladding agreement with broker fields (Sonnet 5.5, coverage 87–98 %) | 95.8 % / 94.1 % / 84.5 % | | Storeys vs roll (±0.5 / exact), κ | 83.5 % / 70.8 %, κ 0.51 | | Listings showing a basement / bathroom / kitchen within 10 photos | 3.1 % / 19.6 % / 67.8 % | | Median cost indication B0 (roll only) → B3 (multimodal), anchor B | $407,545 → $514,919 | | Median depreciation B0 → B3 | 0.62 → 0.40 | | Ratio to asking price B0 → B3 (MdAPE) | 0.75 → 0.95 (30.1 % → 27.9 %) | | Ratio to indexed assessor building value B0 → B3 | 0.68 → 1.12 | | Effect of unit-cost anchor (Altus Montréal vs Québec 2026 guides) on median indication | −29.9 % | | Implied standard hard cost from assessor calibration (hold-out transfers) | ≈ $272 (anchor B) – $306 (anchor A) per sq ft | | Monte Carlo 10–90 % interval width (median) / coverage of asking price | 26 % / 28 % (nominal 80 %) | | Recorded sales after listing (n = 146): B0 → B3 ratio to sale (MdAPE) | 0.70 → 0.84 (34 % → 29 %) | ## 🗂 Repository layout ``` multimodal-cost-approach-valuation/ ├── CLAUDE.md # the author's specification (WP20) ├── configs/ # data rules, ontology, prompts, models, cost schedule (every parameter sourced or flagged) ├── src/ │ ├── ingestion/ # build_frame.py (listings ↔ roll, rules R0–R10), sample.py (stratified, weights) │ ├── extraction/ # client.py (Anthropic batches), textprep.py (price masking), aggregate.py (rules, B0–B4) │ ├── feature_schema/ # schema.py (strict JSON schemas, versioned) │ ├── cost_model/ # cost.py (RCN, depreciation, land, indices; pure functions) │ ├── evaluation/ # metrics.py (κ, α, bootstrap), reference.py (broker-field normalisation) │ └── visualization/ # figures.py (vector PDFs), tables.py (LaTeX fragments + numbers.tex macros) ├── scripts/ # 03 smoke test · 04 batches · 05 aggregate · 06/07 experiments · 10 literature matrix · 11 manifest · 12 validation ├── tests/ # pytest (masking, schema constraints, aggregation rules, metrics, cost identity) ├── data/ # README; raw StatCan series; processed/ (not committed) ├── results/ # tables/, figures/, raw/ (batch results, usage), manifest.json ├── docs/ # implementation_audit, experiment_plan, results_validation, data_rights, limitations, │ # research_log, literature_matrix.csv, verification/, annotation_protocol, institutional_sources └── paper/ # main.tex + sections/ + appendix/ + tables/ + figures/ + references.bib → main.pdf ``` ## 🚀 Quick start ```bash python3 -m venv --system-site-packages .venv && .venv/bin/pip install -r requirements.txt make test # unit tests make frame sample # needs the two Vrai Prix databases (paths in configs/data.yaml) WP20_ENV_FILE=/path/to/.env make batches # ANTHROPIC_API_KEY=…; ≈ US$110 at batch prices make collect # once batches have ended make aggregate experiments figures tables paper ``` Runtime without API calls: frame + sample ≈ 1 min; experiments ≈ 4 min; figures + tables ≈ 30 s; LaTeX ≈ 1 min (Apple silicon laptop). ## 🗃 Data Read-only copies (2026-10-17) of the Vrai Prix databases: assessment roll (MAMH, Données Québec CC-BY 4.0; 3.75 M units), transactions (769 k, aggregate use only), Immo-Ka listings (77,511 live houses/condos/lots, 67 connectors, 2026-08-12 → 2026-10-17) with photographs by URL, descriptions and broker fields. Frame: 39,278 house listings → 7,745 after rules R1–R10 (`results/tables/frame_exclusions.csv`); sample 1,200 (cma × age × price quartile, seed 20261020). Photographs are never stored; hosts whose robots.txt disallows retrieval (RE/MAX Québec) were excluded, not circumvented. Public sources: StatCan 18-10-0289-01, Altus 2026 Cost Guide (via NerdWallet), Québec 2026 industry guides (`docs/institutional_sources.md`). ## 🔬 Methodology 1. Frame and stratified sample (`src/ingestion/`), design weights. 2. Per-image extraction with strict JSON schemas on three Claude 5.5 tiers via the Message Batches API; text extraction on masked text (`src/extraction/client.py`, `configs/prompts.yaml`, `configs/models.yaml`). 3. Deterministic aggregation (confidence-weighted votes, ordinal medians, means) and B0–B4 reconciliation (`aggregate.py`). 4. Cost model: two published unit-cost anchors, storey/attachment multipliers, add-ons, mark-ups, StatCan temporal index, condition-adjusted age–life depreciation, roll land value indexed (`src/cost_model/cost.py`, `configs/cost_schedule.yaml`). 5. Evaluation: agreement/κ/α, channel information content, image-budget truncation, MNAR room-presence analysis and omission simulation, confidence calibration, levels B0–B4 vs assessor and price benchmarks with municipality cluster bootstrap, one-at-a-time sensitivity, labelled scenarios, Monte Carlo, assessor-based calibration with Québec City hold-out, recorded-sales test (`scripts/06_*`, `scripts/07_*`). ## 🖼 Figures & tables inventory | Figure | Content | |---|---| | `f02_sample_composition` | frame vs sample by CMA, age band, price quartile | | `f03_scene_by_position` | scene of each photo by position in seller order (MNAR evidence) | | `f04_scene_presence_vs_k` | share of listings showing kitchen/bathroom/basement vs image budget | | `f05_agreement`, `f06_confusion` | coverage/agreement by attribute and tier; roof and storeys confusion matrices | | `f07_cross_model` | Krippendorff α across tiers; condition score scatter | | `f08_calibration` | stated confidence vs agreement; selective prediction | | `f09_decomposition`, `f10_ratio_distributions` | L / RCN / depreciated improvements by level and age; alignment ratio violins | | `f11_sensitivity`, `f12_grade_condition_effects` | tornado of assumptions; listing-level effect of grade and condition | | `f13_monte_carlo`, `f14_subgroups`, `f15_recorded_sales`, `f16_image_count` | intervals; MdAPE heatmap age × price; sales; K-stability | Tables (generated, `results/tables/tex/`): frame exclusions, sample strata/descriptives, extraction runs, agreement, channels, image count, cross-model, scene presence, error taxonomy, confidence calibration, levels summary/by tier, sensitivity, calibration/hold-out, subgroups, scenarios, Monte Carlo OAT, market alignment, recorded sales, review load, schedule grades/parameters, and `numbers.tex` (≈ 1,180 macros). ## ⚠️ Limitations No human expert labels (protocol proposed, not run); latent defects unobservable; no cross-city cost levels; assessor values are judgments; asking prices are not transactions (146-sale test only); frame excludes RE/MAX (robots.txt) and classifieds; one model family; depreciation schedule assumed (too steep for old houses). Full list: `docs/limitations.md`. ## 📖 Citation ```bibtex @techreport{boucher2026listing, author = {Boucher, Simon-Pierre}, title = {From Listing Images to Property Valuation: A Multimodal AI Framework for Automated Building Characterization and the Cost Approach}, institution = {Université du Québec en Outaouais, Département des sciences administratives}, type = {UQO Working Paper}, number = {20}, year = {2026}, month = {October} } ``` ## 👤 Author & contact Simon-Pierre Boucher — Département des sciences administratives, Université du Québec en Outaouais — simon-pierre.boucher@uqo.ca ## 📝 Notes - Numbering: WP18 (housing leverage) and WP19 (housing liquidity premium) were created the same day in sibling folders; 20 is the next number (HARMONISATION.md §1). - Deviations from the series template: `configs/` + `src/` + `Makefile` instead of numbered `scripts/` only; `paper/appendix/appendix.tex` contains proofs, prompts, rules, protocol, sources and reproducibility; TikZ pipeline figure; `adjustbox` for wide tables. - Remote `origin = gitsrv:srv/git/wp20_uqo.git` created and pushed on 2026-10-20 (off-LAN, through the BHS64 WireGuard hub: `ssh -o ProxyJump=BHS64 simon-pierreboucher@10.67.0.2`). - Secrets: the API key lives in an untracked `.env` outside this repository (`WP20_ENV_FILE`); `results/raw/api_usage.jsonl` records tokens only.