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 · 174 lines markdown
Rendered Raw Blame History
1<!-- Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca -->2# 🏠 From Listing Images to Property Valuation34**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*56[![Paper](https://img.shields.io/badge/paper-PDF%20(66%20p.)-b31b1b?logo=latex&logoColor=white)](paper/main.pdf)7[![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](requirements.txt)8[![Data](https://img.shields.io/badge/data-1%2C200%20listings%20·%2014%2C245%20photos%20·%20roll%20link-4051b5)](data/README.md)9[![Reproducible](https://img.shields.io/badge/reproducible-Makefile%20pipeline%20%2B%20manifest-2e7d32)](Makefile)10[![Institution](https://img.shields.io/badge/UQO-D%C3%A9pt.%20des%20sciences%20administratives-16365c)](https://uqo.ca)11[![Author](https://img.shields.io/badge/author-Simon--Pierre%20Boucher-16365c)](mailto:simon-pierre.boucher@uqo.ca)1213> **TL;DR** — Can the photographs and text of a for-sale listing supply the building specification that the cost approach14> needs (materials, finish grade, condition, renovations), and what is a transparent cost indication built on it worth?15> On 1,200 Québec house listings matched within 15 m to their assessment-roll unit (14,245 photographs, three16> vision–language extractor tiers, nothing fitted on prices), visible exterior facts are read with 85–96 % agreement17> against broker fields, but sellers show a basement in 3 % of listings and a bathroom in 20 % within ten photographs18> (missing-not-at-random). Images and text move the cost indication mostly through condition-adjusted depreciation: the19> ratio to the asking price goes from 0.75 (roll only) to 0.95 (multimodal), MdAPE 30 → 28 %. The published unit-cost anchor20> matters more (−30 % under a Montréal hard-cost band); a calibration against assessor values transfers to a geographic21> hold-out and implies ≈ $270–306/sq ft, inside the 2026 Québec industry band.2223---2425## 📌 Status2627| Item | Value |28|---|---|29| Version | 1.0 (October 2026) |30| SSRN | not yet submitted |31| Journal | — (candidate: Journal of Real Estate Finance and Economics / Journal of Property Research) |32| Code & data | reproducible from `Makefile`; Vrai Prix databases not redistributed (see `data/README.md`, `docs/data_rights.md`) |33| 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`) |3435## 🎯 What this paper does3637The cost approach values a property as land plus the depreciated replacement cost of the improvements. Its inputs38(materials, grade, condition, renovations) are not in assessment rolls and are normally obtained by inspection. We39formalise an **extraction-to-valuation framework** with five evidence classes (administrative, visual, proxy, text-claimed,40latent), deterministic cross-image aggregation, text–image–record reconciliation, a fully sourced cost schedule and41uncertainty propagation:4243```44V_cost = L_roll(indexed) + (RCN(grade, storeys, add-ons) × (1+soft)(1+EI) + S) × (1 − D(effective age(condition, renovation)))45```4647and we evaluate every link on Vrai Prix data: agreement of visual attributes with broker fields and the roll, information48content of image vs text channels, cross-extractor reliability, confidence calibration, the MNAR structure of listing49photographs, and the alignment of the indication with assessor values, asking prices and recorded sales, with a geographic50hold-out. It is a cost-approach paper, not an image-enhanced hedonic model: no price enters the indication.5152## 🏆 Headline results5354| Object | Estimate |55|---|---|56| Roof covering / garage type / cladding agreement with broker fields (Sonnet 5.5, coverage 87–98 %) | 95.8 % / 94.1 % / 84.5 % |57| Storeys vs roll (±0.5 / exact), κ | 83.5 % / 70.8 %, κ 0.51 |58| Listings showing a basement / bathroom / kitchen within 10 photos | 3.1 % / 19.6 % / 67.8 % |59| Median cost indication B0 (roll only) → B3 (multimodal), anchor B | $407,545 → $514,919 |60| Median depreciation B0 → B3 | 0.62 → 0.40 |61| Ratio to asking price B0 → B3 (MdAPE) | 0.75 → 0.95 (30.1 % → 27.9 %) |62| Ratio to indexed assessor building value B0 → B3 | 0.68 → 1.12 |63| Effect of unit-cost anchor (Altus Montréal vs Québec 2026 guides) on median indication | −29.9 % |64| Implied standard hard cost from assessor calibration (hold-out transfers) | ≈ $272 (anchor B) – $306 (anchor A) per sq ft |65| Monte Carlo 10–90 % interval width (median) / coverage of asking price | 26 % / 28 % (nominal 80 %) |66| Recorded sales after listing (n = 146): B0 → B3 ratio to sale (MdAPE) | 0.70 → 0.84 (34 % → 29 %) |6768## 🗂 Repository layout6970```71multimodal-cost-approach-valuation/72├── CLAUDE.md                 # the author's specification (WP20)73├── configs/                  # data rules, ontology, prompts, models, cost schedule (every parameter sourced or flagged)74├── src/75│   ├── ingestion/            # build_frame.py (listings ↔ roll, rules R0–R10), sample.py (stratified, weights)76│   ├── extraction/           # client.py (Anthropic batches), textprep.py (price masking), aggregate.py (rules, B0–B4)77│   ├── feature_schema/       # schema.py (strict JSON schemas, versioned)78│   ├── cost_model/           # cost.py (RCN, depreciation, land, indices; pure functions)79│   ├── evaluation/           # metrics.py (κ, α, bootstrap), reference.py (broker-field normalisation)80│   └── visualization/        # figures.py (vector PDFs), tables.py (LaTeX fragments + numbers.tex macros)81├── scripts/                  # 03 smoke test · 04 batches · 05 aggregate · 06/07 experiments · 10 literature matrix · 11 manifest · 12 validation82├── tests/                    # pytest (masking, schema constraints, aggregation rules, metrics, cost identity)83├── data/                     # README; raw StatCan series; processed/ (not committed)84├── results/                  # tables/, figures/, raw/ (batch results, usage), manifest.json85├── docs/                     # implementation_audit, experiment_plan, results_validation, data_rights, limitations,86│                             # research_log, literature_matrix.csv, verification/, annotation_protocol, institutional_sources87└── paper/                    # main.tex + sections/ + appendix/ + tables/ + figures/ + references.bib → main.pdf88```8990## 🚀 Quick start9192```bash93python3 -m venv --system-site-packages .venv && .venv/bin/pip install -r requirements.txt94make test                       # unit tests95make frame sample               # needs the two Vrai Prix databases (paths in configs/data.yaml)96WP20_ENV_FILE=/path/to/.env make batches      # ANTHROPIC_API_KEY=…; ≈ US$110 at batch prices97make collect                    # once batches have ended98make aggregate experiments figures tables paper99```100101Runtime without API calls: frame + sample ≈ 1 min; experiments ≈ 4 min; figures + tables ≈ 30 s; LaTeX ≈ 1 min (Apple silicon laptop).102103## 🗃 Data104105Read-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),106transactions (769 k, aggregate use only), Immo-Ka listings (77,511 live houses/condos/lots, 67 connectors, 2026-08-12 →1072026-10-17) with photographs by URL, descriptions and broker fields. Frame: 39,278 house listings → 7,745 after rules108R1–R10 (`results/tables/frame_exclusions.csv`); sample 1,200 (cma × age × price quartile, seed 20261020). Photographs are109never stored; hosts whose robots.txt disallows retrieval (RE/MAX Québec) were excluded, not circumvented. Public sources:110StatCan 18-10-0289-01, Altus 2026 Cost Guide (via NerdWallet), Québec 2026 industry guides (`docs/institutional_sources.md`).111112## 🔬 Methodology1131141. Frame and stratified sample (`src/ingestion/`), design weights.1152. Per-image extraction with strict JSON schemas on three Claude 5.5 tiers via the Message Batches API; text extraction on116   masked text (`src/extraction/client.py`, `configs/prompts.yaml`, `configs/models.yaml`).1173. Deterministic aggregation (confidence-weighted votes, ordinal medians, means) and B0–B4 reconciliation (`aggregate.py`).1184. Cost model: two published unit-cost anchors, storey/attachment multipliers, add-ons, mark-ups, StatCan temporal index,119   condition-adjusted age–life depreciation, roll land value indexed (`src/cost_model/cost.py`, `configs/cost_schedule.yaml`).1205. Evaluation: agreement/κ/α, channel information content, image-budget truncation, MNAR room-presence analysis and121   omission simulation, confidence calibration, levels B0–B4 vs assessor and price benchmarks with municipality cluster122   bootstrap, one-at-a-time sensitivity, labelled scenarios, Monte Carlo, assessor-based calibration with Québec City hold-out,123   recorded-sales test (`scripts/06_*`, `scripts/07_*`).124125## 🖼 Figures & tables inventory126127| Figure | Content |128|---|---|129| `f02_sample_composition` | frame vs sample by CMA, age band, price quartile |130| `f03_scene_by_position` | scene of each photo by position in seller order (MNAR evidence) |131| `f04_scene_presence_vs_k` | share of listings showing kitchen/bathroom/basement vs image budget |132| `f05_agreement`, `f06_confusion` | coverage/agreement by attribute and tier; roof and storeys confusion matrices |133| `f07_cross_model` | Krippendorff α across tiers; condition score scatter |134| `f08_calibration` | stated confidence vs agreement; selective prediction |135| `f09_decomposition`, `f10_ratio_distributions` | L / RCN / depreciated improvements by level and age; alignment ratio violins |136| `f11_sensitivity`, `f12_grade_condition_effects` | tornado of assumptions; listing-level effect of grade and condition |137| `f13_monte_carlo`, `f14_subgroups`, `f15_recorded_sales`, `f16_image_count` | intervals; MdAPE heatmap age × price; sales; K-stability |138139Tables (generated, `results/tables/tex/`): frame exclusions, sample strata/descriptives, extraction runs, agreement, channels,140image count, cross-model, scene presence, error taxonomy, confidence calibration, levels summary/by tier, sensitivity,141calibration/hold-out, subgroups, scenarios, Monte Carlo OAT, market alignment, recorded sales, review load, schedule142grades/parameters, and `numbers.tex` (≈ 1,180 macros).143144## ⚠️ Limitations145146No human expert labels (protocol proposed, not run); latent defects unobservable; no cross-city cost levels; assessor values147are judgments; asking prices are not transactions (146-sale test only); frame excludes RE/MAX (robots.txt) and classifieds;148one model family; depreciation schedule assumed (too steep for old houses). Full list: `docs/limitations.md`.149150## 📖 Citation151152```bibtex153@techreport{boucher2026listing,154  author      = {Boucher, Simon-Pierre},155  title       = {From Listing Images to Property Valuation: A Multimodal AI Framework for Automated Building Characterization and the Cost Approach},156  institution = {Université du Québec en Outaouais, Département des sciences administratives},157  type        = {UQO Working Paper},158  number      = {20},159  year        = {2026},160  month       = {October}161}162```163164## 👤 Author & contact165166Simon-Pierre Boucher — Département des sciences administratives, Université du Québec en Outaouais — simon-pierre.boucher@uqo.ca167168## 📝 Notes169170- 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).171- 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.172- 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`).173- Secrets: the API key lives in an untracked `.env` outside this repository (`WP20_ENV_FILE`); `results/raw/api_usage.jsonl` records tokens only.174