SPB Git forge

spb/wp20_uqo

Public
1commits 1branches 0releases
2.8 MBsize
maindefault branch
yesterdaylast push
TeX 59% Python 40.8%
12.1 KB

# 🏠 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 Python Data Reproducible Institution Author

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:

text
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

text
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.