CzechAI Cenové Mapy — Kompletní Historie a Aktuální Stav¶
Dokument je plně self-contained. Žádné externí file reference. Stav ke: 2026-05-04 Zdroje: live SSH server 179/94, PM2, Outline záloha 211 MB, MAPA_KOMPLETNI_DESKTOP (2026-03-07, 9 273 řádků kódu)
PŘEHLED VÝVOJE (chronologická tabulka)¶
| Datum | Fáze | Co vzniklo | Port | Stav |
|---|---|---|---|---|
| 2025-11 | 0 | Scraping: ULTIMATE_MASTER.db (98K inzerátů) | — | archív |
| 2025-12 | 1a | Statistická mapa — cenova_mapa.py + HOT tagy | 5088 | archív |
| 2025-12 | 1a | overnight_price_scraper — nočně aktualizovaná data | — | archív |
| 2026-01-14 | 1b | Cenová Kalkulačka 2025 — web frontend na router.czechai.io | — | archív |
| 2026-01 | 2 | Mapa 1 — Prodeje V4: 105K+ inzerátů, cascade lookup | 5091 | deprecated |
| 2026-01 | 2 | Mapa 2 — CSU V4: krajské mediány + sezónní koeficient | 5092 | deprecated |
| 2026-01 | 2 | Mapa 3 — AI V7: XGB+LGB+GBR, 225K záznamů, BYT MdAPE 7.6% | 5093 | deprecated |
| 2026-02 | 2 | Mapa 4 — Smart Engine: 13 podtypů, MAD outlier, taxonomie | 5098 | deprecated |
| 2026-02 | 2 | Mapa 5 — Fusion Engine: 4 DB zdroje (CUZK/CSU/LIVE/stat) | 5094 | deprecated |
| 2026-02 | 3 | Super Maps: Precision/Conservative/Ensemble/Unified | 7001–7003,7000 | stopped |
| 2026-02 | 3 | Oceňovač GPS/KNN: 145K záznamů, haversine | 5300 | stopped |
| 2026-02 | 3 | Valuo proxy engines (5 proxies → 7000) | 6000–6100 | stopped |
| 2026-03 | 3 | Super-Ensemble V2: FastAPI, 7 enginů paralelně, LLM review | 5111 | stopped |
| 2026-03 | 3 | Znalecký Panel: 3 AI znalci (qwen3:32b) + Chairman | 5112 | stopped |
| 2026-03 | 3 | Market Intel, Mapa Levon, CSU Engine, Smart Router | 5400,6200,5102,5150 | stopped |
| 2026-03-07 | — | MAPA_KOMPLETNI_DESKTOP doc: 28 služeb, 9 273 ř. kódu | — | archív |
| 2026-03-10 | 4 | Perfect Map V13: HistGBM + meta-learner, R²=0.833, 296K zázn. | 5131 | deprecated |
| 2026-04 | 5 | V15: dedikovaný ML model (fallback) | 5150 | záloha |
| 2026-04-24 | 6 | V24: LGB+XGB+RF+KNN, 34 feature, BYT R²=0.9478 | 5164 | PRODUKCE |
| 2026-04-24 | 6 | Combo3 router: byt/dům→V24, ostatní→V15 | 5163 | PRODUKCE |
| 2026-05-03 | 7 | shadow.czechai.io + EstateX Council + Direct CMA | public | PRODUKCE |
1. Proč vznikly cenové mapy¶
Celý projekt začal jako potřeba automaticky oceňovat nemovitosti z realitních inzerátů. Scraper na serverech 134 a 179 stahoval stovky tisíc inzerátů ze Sreality, Bezrealitky, Bazoše a dalších portálů — ale bez referenčního oceňovacího modelu nebylo možné rozpoznat, které inzeráty jsou předražené a které jsou podhodnocené příležitosti.
Cíl byl vždy stejný: dát každé nemovitosti tržní cenu na základě polohy, plochy a typu, a říct o kolik procent se inzerát odchyluje od trhu.
Z toho vznikl systém D-tagů: - D10 / D15 / D20 / D30 = podhodnoceno o 10/15/20/30+ % - FAIR = tržní cena - P10 / P20+ = nadhodnoceno
2. Fáze 1a — Statistická mapa (2025-12, port 5088)¶
Co to bylo¶
První Python skript cenova_mapa.py + SQLite cenova_mapa.db. Data pocházela z ULTIMATE_MASTER.db (98 198 reálných inzerátů) jako agregované statistiky (medián, průměr) per kombinace typ+město.
Statistiky¶
- 17 202 vzorků z reálných prodejů
- 2 136 kombinací (typ + město)
- 1 713 měst
- 9 typů: byt, dum, pozemek, garaz, komercni, chata, bd, ...
Výstup — HOT tagy¶
| Tag | Prahová hodnota | Popis |
|---|---|---|
| HOT-40 | 40%+ pod trhem | Extrémní příležitost |
| HOT-30 | 30–40% | Výborná příležitost |
| HOT-20 | 20–30% | Dobrá příležitost |
| HOT-10 | 10–20% | Mírně pod trhem |
| NORM | -20% až +10% | Normální cena |
| PRICY | 20%+ nad trhem | Předraženo |
API (port 5088)¶
python cenova_mapa.py --estimate BYT Praha 70 --price 5000000
POST /api/estimate {"property_type": "BYT", "city": "Praha", "area": 70, "price_inzerat": 5000000}
Omezení¶
- Bez GPS — čistě textové město
- Mediány z inzerátů, ne reálných prodejů
- Žádný ML model, jen agregace
3. Fáze 1a — overnight_price_scraper (2025-12)¶
Paralelně se statistickou mapou vznikl overnight scraper — Python skript spouštěný nočně (cron), který aktualizoval ceny z portálů. Nacházel nové inzeráty, spočítal denní mediány a přidal je do cenové DB.
Logika: každou noc projít portály → aktualizovat city_stats.db → refresh mediánů. Vznikla i verze v2 (overnight_scraper_v2) s lepším parsováním a dedulikací.
4. Fáze 1b — Cenová Kalkulačka 2025 (2026-01-14)¶
Co to bylo¶
První veřejná webová stránka na router.czechai.io. HTML/CSS/JS formulář: zadáš typ, město, plochu a dostaneš odhadovanou cenu. Backend volal statistickou mapu (5088).
Architektura¶
Klíčová Python logika¶
# cenova_kalkulacka_2025.py
def estimate_price(typ: str, mesto: str, plocha: float) -> dict:
record = db.query("SELECT median_m2 FROM city_stats WHERE city=? AND property_type=?", mesto, typ)
if not record:
record = db.query("SELECT median_m2 FROM okres_stats WHERE okres LIKE ?", f"%{mesto}%")
price_m2 = record['median_m2'] if record else NATIONAL_MEDIANS[typ]
return {"estimated_price": price_m2 * plocha, "price_per_m2": price_m2}
Dokument CENOVA_KALKULACKA_2025_KOMPLETNI v Outline popisuje kompletní implementaci včetně Flask route /api/estimate, formuláře a deploymentu.
5. Fáze 2 — Čtyři základní mapy (2026-01 až 2026-02)¶
Tato fáze přinesla čtyři samostatné Flask/Python API servery, každý s jiným zdrojem dat a přístupem. Spolu tvořily "vrstvu 1" budoucího systému.
5.1 Mapa 1 — Prodeje V4 (port 5091)¶
Soubor: /opt/cenova_mapa/mapa1_prodeje/app.py (277 řádků) Databáze: city_stats.db (1.8–2.2 MB)
Datové struktury:
city_stats = {} # dict: (city, ptype) -> {median_m2, mean_m2, count}
okres_stats = {} # dict: (okres, ptype) -> {median_m2, mean_m2, count}
NATIONAL_MEDIANS = {
"byt": 85000, "dum": 42000, "pozemek": 3100, "komercni": 35000
}
Cascade lookup: město (exact) → město (partial) → okres → národní fallback
Konfidence: 0.85 (city exact), 0.70 (partial), 0.60 (okres), 0.30 (national)
API:
POST/GET /api/estimate {typ, mesto, plocha}
GET /api/cities # vracíálla města
GET /api/stats
GET /api/health
5.2 Mapa 2 — CSU V4 (port 5092, STOPPED)¶
Soubor: /opt/cenova_mapa/mapa2_csu/app.py (196 řádků) Sdílí city_stats.db s Mapou 1 — ale přidává sezónní korekci a krajské fallbacky.
Sezónní koeficienty:
SEASONAL_COEFF = {1: 0.97, 2: 0.98, 3: 1.00, 4: 1.02, 5: 1.03, 6: 1.02,
7: 0.99, 8: 0.98, 9: 1.01, 10: 1.02, 11: 0.99, 12: 0.96}
Krajské mediány (14 krajů, kompletní tabulka): - Praha: byt 135 000, dům 90 000, pozemek 12 000 Kč/m² - Jihomoravský: byt 85 000, dům 42 000 - Moravskoslezský: byt 40 000, dům 25 000 - Středočeský: byt 65 000, dům 45 000 - Plzeňský: byt 62 000, dům 38 000 - … (14 krajů celkem)
5-stupňový cascade: city_exact → city_partial → okres → kraj → national
5.3 Mapa 3 — AI V7 / V8 (port 5093)¶
Soubor: /opt/cenova_mapa/mapa3_ai/app.py (358 řádků) Modely: /tmp/cenova_mapa_v7/ — trénováno na 225 000 záznamech
Modely a velikosti: | Soubor | Velikost | Typ | |--------|----------|-----| | model_xgb.pkl | 19 MB | XGBoost (primary) | | model_xgb_m2.pkl | 11 MB | XGBoost per-m² | | model_lgb.pkl | 3.3 MB | LightGBM | | model_gbr.pkl | 6.5 MB | GradientBoosting | | label_encoder_ptype.pkl | 286 B | Label encoder | | ensemble_weights.json | 115 B | Váhy ensemblu | | feature_cols.json | 248 B | Feature sloupce |
17 features:
[area, lat, lon,
dist_prague, # haversine(lat,lon, 50.08, 14.44)
city_median_m2, # z city_stats cache
ptype_encoded, # label_encoder.transform
population, # z city_pop dict
elevation, # z geodat
is_praha, is_krajske_mesto,
log(area), area^2,
interactions...]
Přesnost (MdAPE): | Typ | MdAPE | |-----|-------| | BYT | 7.6% | | DUM | 15.3% | | KOMERCNI | 11.1% | | POZEMEK | 42.3% |
Caps konfidence: byt=0.85, dům=0.72, komerční=0.65, pozemek=0.40
API:
POST/GET /api/estimate {typ, mesto, plocha, lat?, lon?}
GET /api/compare → tag D10/D15/D20/D30/FAIR/P10/P20+
5.4 Mapa 4 — Smart Engine (port 5098)¶
Soubory: - /opt/cenova_mapa/mapa4_smart/app.py (687 řádků) - /opt/cenova_mapa/mapa4_smart/real_estate_engine.py (421 řádků) - /opt/cenova_mapa/mapa4_smart/price_map_classifier.py (298 řádků)
Taxonomie — 13 podtypů → 4 DB typy:
_SUBTYPE_TO_DB_PTYPE = {
"APARTMENT": "byt",
"HOUSE": "dum",
"COTTAGE": "dum", # korekce 0.65
"LAND_BUILDING": "pozemek",
"LAND_AGRICULTURAL": "pozemek", # korekce 0.02
"LAND_FOREST": "pozemek", # korekce 0.01
"LAND_GARDEN": "pozemek", # korekce 0.15
"LAND_FISH_POND": "pozemek", # korekce 0.05
"COMMERCIAL_OFFICE": "komercni",
"COMMERCIAL_RETAIL": "komercni",
"COMMERCIAL_WAREHOUSE": "komercni",
"COMMERCIAL_PRODUCTION": "komercni",
"GARAGE": "komercni",
}
Detekce junk inzerátů: - Pronájmy (pronájem), podíly (spoluvlastnictví), dražby, poptávky (koupím) → is_junk=True
Efektivní plocha:
MAD outlier detection:
Sanity limity cen Kč/m²: - BYT: 10 000–350 000 - DUM: 5 000–200 000 - POZEMEK stavební: 100–50 000 - KOMERČNÍ: 5 000–250 000
API:
POST/GET /api/estimate (klasifikace + korekce podtypu + MAD check)
POST /api/classify {title, description} → {asset_class, subtype, confidence}
GET /api/taxonomy (celá taxonomie s keywords)
GET /api/compare
5.5 Mapa 5 — Fusion Engine (port 5094)¶
Soubor: /opt/cenova_mapa/mapa5_fusion/app.py (867 řádků) 4 DB zdroje:
| DB | Záznamy | Obsah |
|---|---|---|
cuzk_cenove_udaje.db | 30 872 | Katastrální transakce Praha 2025 |
csu_ceny_nemovitosti.db | 45 184 | CSU celá ČR 2013–2024 |
master_live.db | ~42 700 | Multi-portálové nabídky |
stat_realit_179k.db | 179 000 | Historické inzeráty |
Klíčová inovace — korekce nabídka→realizace:
DEFAULT_CORRECTIONS = {
"live": {"BYT": 0.81, "DUM": 0.78, "POZEMEK": 0.86, "KOMERCNI": 0.76},
"prodej": {"BYT": 0.86, "DUM": 0.83, "POZEMEK": 0.90, "KOMERCNI": 0.82},
}
Geografická adjustace korekce: - Praha: +0.03 - Brno: +0.02 - Krajská města: +0.01
Váhy zdrojů:
SOURCE_WEIGHTS = {
"cuzk": 0.95, # katastrální transakce = nejvyšší důvěryhodnost
"csu": 0.85, # oficiální statistika
"prodej": 0.70, # historické inzerátní ceny
"live": 0.65, # aktuální nabídky (nejméně spolehlivé)
}
6. Fáze 3 — Super Maps éra (2026-02 až 2026-03)¶
Super Maps byla vrstva orchestrátorů nad základními mapami. Cílem bylo kombinovat více enginů a eliminovat outliers.
6.1 Super Fetcher (library, bez HTTP)¶
Soubor: /opt/cenova_mapa/super_maps/super_fetcher.py (366 řádků) Zero HTTP závislosti — čte přímo z DB a pickle modelů.
8 interních enginů:
_est_prodeje() → mediány z city_stats.db
_est_csu() → CSU mediány + sezónní korekce
_est_ai() → ML ensemble přímo z pickle modelů
_est_smart() → klasifikace + korekce (zjednodušená)
_est_portaly() → mediány z portaly_2026.db
_est_vynosovy() → kapitalizace nájmu (CAP_RATE 4.5% pro byty)
_est_nakladovy() → reprodukční cena × lokální koeficient (Praha: 2.32)
_est_valuo_trzni() → vážený průměr prodeje + CSU
Výnosová metoda:
NAJMY_M2 = {"byt_velke": 430, "byt_stredni": 300, "byt_male": 240, "dum": 200}
CAP_RATES = {"byt": 0.045, "dum": 0.038, "komercni": 0.055}
price = (najem_m2 * plocha * 12) / cap_rate
Nákladová metoda:
REPRODUKCNI_M2 = {"byt": 65000, "dum": 50000, "komercni": 55000}
LOKALNI_KOEF = {"praha": 2.32, "brno": 1.85, "ostrava": 1.25, "plzen": 1.55}
price = reprodukcni_m2 * plocha * lokalni_koef * opotrebeni
6.2 Super Ensemble (port 7003) — Accuracy-Weighted¶
Soubor: /opt/cenova_mapa/super_maps/super_ensemble.py (149 řádků)
Váhy enginů = 1 / MAPE² (lepší engine = vyšší váha).
weight_i = 1 / (mape_i ** 2)
price = sum(price_i * weight_i) / sum(weight_i)
confidence = agreement * 0.6 + coverage * 0.4
# agreement = jak blízko jsou si odhady (1.0 = všechny stejné)
# coverage = počet respondujících enginů / celkem
6.3 Super Precision (port 7001) — Per-Type Optimal Weights¶
OPTIMAL_WEIGHTS = {
"byt": {"ai": 0.30, "valuo_trzni": 0.18, "prodeje": 0.15,
"smart": 0.12, "csu": 0.10, "vynosovy": 0.08, "nakladovy": 0.07},
...
}
6.4 Super Conservative (port 7002) — Safety Factor¶
- Seber odhady ze všech enginů
- Seřaď vzestupně
- Vyber 3 nejnižší
- Spočítej medián
- Vynásob
safety_factor = 0.92
6.5 Super Unified (port 7000) — Všechny 3 strategie najednou¶
Soubor: /opt/cenova_mapa/super_maps/super_maps_unified.py (633 řádků) Sub-10ms response time (zero HTTP, přímý DB přístup).
80+ GPS měst, Praha po districtech:
PRAHA_DISTRICTS = {
"Praha 1": {"byt": 170000, "dum": 130000, "pozemek": 45000},
"Praha 2": {"byt": 145000, "dum": 110000, "pozemek": 35000},
"Praha 3": {"byt": 120000, "dum": 90000, "pozemek": 22000},
... # Praha 1–10
}
Size adjustment (byty):
< 30 m²: × 1.15 # garsonka premium
< 50 m²: × 1.05 # malý byt
> 100 m²: × 0.90 # velký byt diskont
> 150 m²: × 0.82 # mezonet
API:
GET /api/estimate → {
"precision": {"price": 5100000, "price_m2": 85000, "confidence": 0.82},
"conservative": {"price": 4600000, "price_m2": 76667, "confidence": 0.75},
"ensemble": {"price": 5000000, "price_m2": 83333, "confidence": 0.80},
"recommended": "precision"
}
6.6 Valuo Proxy Engines (porty 6000–6100)¶
5 služeb jako jednoduché HTTP proxies na Super Unified (7000):
| Proxy | Port | Target |
|---|---|---|
valuo-unified | 6000 | → 7000/api/precision |
valuo1-trzni | 6001 | → 7000/api/precision (tržní sub-odhad) |
valuo2-vynosovy | 6002 | → 7000/api/precision (výnosový sub-odhad) |
valuo3-nakladovy | 6003 | → 7000/api/precision (nákladový sub-odhad) |
valuo-pro | 6100 | → 7000/api/precision |
6.7 Oceňovač — GPS/KNN (port 5300)¶
Soubor: /opt/ocenovac/app.py (237 řádků), FastAPI async Databáze: stat_realit.db (2026 live) + mega_training_data_v5.db (2025 historical)
K-nearest neighbors (k=15):
def haversine(lat1, lon1, lat2, lon2) -> float:
# vzdálenost v km
async def gps_knn_estimate(typ, lat, lon, plocha, k=15) -> dict:
# 1. Query oba DB zdroje
# 2. Haversine ke každému záznamu
# 3. Seřaď, vyber top-K
# 4. Inverse distance weighting: weight_i = 1 / (dist_i + 0.001)
# 5. 2026 data bonus: weight_i *= 1.20
# 6. price_m2 = sum(price_i * weight_i) / sum(weight_i)
Strategie: GPS KNN 5km (min 5) → GPS KNN 15km (min 3) → city fallback
6.8 Market Intel (port 5400)¶
Soubor: /opt/market_intel/app.py (357 řádků), FastAPI async
Trendová analýza z živých dat (2026) vs historických (2025):
trend = (avg_price_m2_2026 - avg_price_m2_2025) / avg_price_m2_2025 * 100
investment_score = trend_component(40%) + liquidity_component(30%) + variance_component(30%)
6.9 Mapa Levon (port 6200)¶
Soubor: /opt/mapa_levon/app.py (478 řádků), FastAPI Data: JSON index — 17 000+ city+type kombinací
Dynamické diskonty:
4-stupňový cascade: city exact → okres → region → city partial
6.10 CSU Engine (port 5102)¶
Databáze: csu_ceny_nemovitosti.db - 45 184 cenových záznamů - 4 640 indexů - 100 542 výstavba záznamů (6 274 obcí)
30+ ručních aliasů (Praha→Praha, brno→Brno-mesto, atd.)
6.11 Smart Router (port 5150)¶
Soubor: /opt/cenova_mapa/smart_router/app.py (373 řádků)
3-vrstvový routing: 1. Layer 1 — DB Median (MAPE 35%): query city_stats.db 2. Layer 2 — Oceňovač KNN (MAPE 63%): HTTP → port 5300 3. Layer 3 — LLM Fallback: LiteLLM proxy (aurum-fast)
Podporuje rozšířené typy: BYT, DUM, POZEMEK, KOMERČNÍ, GARÁŽ, POLE, LES, LOUKA, ZAHRADA
6.12 Super-Ensemble V2 (port 5111)¶
Soubor: /opt/super-ensemble-v2/app.py (975 řádků), FastAPI async
Engine Registry — 7+1 enginů: | Engine | Port | Váha | Kategorie | Poznámka | |--------|------|------|-----------|----------| | prodeje | 5091 | 1.5 | tržní | Reálné tržní ceny z 105K+ | | ai_v8 | 5093 | 1.1 | ai_ml | Sníženo z 1.3 (MAPE 27% BYT) | | ocenovac | 5300 | 1.2 | knn | GPS/KNN | | mapa_levon | 6200 | 1.0 | statistical | 17K+ kombinací | | vynosovy | 6002 | 0.8 | výnosový | proxy → 7000 | | nakladovy | 6003 | 0.9 | nákladový | proxy → 7000 | | csu_katastr | 5102 | 1.3 | tržní | 45K katastrálních transakcí |
(smart 5098 odstraněn — zombie process)
Paralelní volání: asyncio.gather(*tasks), timeout 8s per engine
Agregace (V2.2 algoritmus): 1. De-duplikace (2 enginy se stejnou cenou → odeber 1) 2. Rozdělení: tržní vs výnosový vs nákladový 3. IQR outlier removal POUZE na tržní enginy 4. Final price = POUZE tržní enginy (vážený průměr) 5. Aplikace znaleckých vah (trojmetoda)
Znalecké váhy (trojmetoda):
ZNALECKE_VAHY = {
"BYT": {"trzni": 0.65, "nakladovy": 0.20, "vynosovy": 0.15},
"DUM": {"trzni": 0.60, "nakladovy": 0.25, "vynosovy": 0.15},
"POZEMEK": {"trzni": 0.80, "nakladovy": 0.10, "vynosovy": 0.10},
"KOMERCNI": {"trzni": 0.40, "nakladovy": 0.25, "vynosovy": 0.35},
}
Korekce nemovitostí:
KOREKCE_STAV = {
"novostavba": 1.10, "po_rekonstrukci": 1.08, "velmi_dobry": 1.03,
"dobry": 1.00, "pred_rekonstrukci": 0.85, "spatny": 0.70
}
KOREKCE_PATRO = {0: 0.92, 1: 0.97, 2: 1.00, 3: 1.01, 4: 1.02, 5: 1.02, 6: 1.03}
KOREKCE_DISPOZICE = {
"1+kk": 1.05, "1+1": 1.03, "2+kk": 1.02, "2+1": 1.00,
"3+kk": 0.98, "3+1": 0.96, "4+kk": 0.93, "4+1": 0.92
}
SPECIALNI_KOREKCE = {
"exekuce": 0.70, "drazba": 0.75, "insolvence": 0.72,
"podil_1_2": 0.55, "podil_1_3": 0.40, "podil_1_4": 0.32,
"vecne_bremeno": 0.85, "zastava": 0.90, "najem": 0.88
}
LLM review (auto-trigger při odchylce >30%, nízké shodě, nebo <3 enginy): - Model: qwen3:32b via Ollama na GPU 217.198.112.170:11434 - Český komentář: proč je cena taková, rizika, doporučení
MCP rozhraní:
POST /mcp/tools/list → {"tools": [{"name": "czechai_price_estimate", ...}]}
POST /mcp/tools/call → {typ, mesto, plocha} → výsledek
6.13 Znalecký Panel — 3 AI znalci (port 5112)¶
Soubor: /opt/znalecky-panel/app.py (926 řádků), FastAPI async
3 znalci + Chairman:
ZNALCI = {
"znalec_A": {"model": "qwen3:32b", "role": "Konzervativní", "temperature": 0.3, "weight": 1.5},
"znalec_B": {"model": "qwen3:32b", "role": "Optimistický", "temperature": 0.7, "weight": 1.2},
"znalec_C": {"model": "qwen3:32b", "role": "Pragmatický", "temperature": 0.3, "weight": 1.0},
}
CHAIRMAN = {"model": "aurum-brain:latest", "role": "Předseda — finální verdikt"}
Data gathering (paralelně): 1. RAG (port 8095) — relevantní dokumenty 2. Sreality MCP (port 5055) — aktuální inzeráty 3. CNB MCP (port 5055) — úrokové sazby 4. Super-Ensemble v2.1 (5111) — strojový odhad 5. Market Intel (5400) — trendová analýza
Konsenzus: vážený průměr, shoda <15% = "vysoká", <30% = "střední"
Fast mode (?fast=true): deterministický bez LLM (~2s vs ~4 min)
6.14 Architektura v době dokumentace (2026-03-07)¶
Jak byl systém zdokumentován v MAPA_KOMPLETNI_DESKTOP:
28 služeb, 20 ONLINE, 8 STOPPED 9 273 řádků Python kódu + ~2 000 TypeScript (Next.js map)
Vrstvy systému:
VRSTVA 5 (Prezentace): map-czechai (5800) — Next.js, 13K+ listingů, AI chat, PostgreSQL
VRSTVA 4 (Expert panel): znalecky-panel (5112), super-ensemble-v2 (5111)
VRSTVA 3 (Agregace): super-unified (7000), unified-price-api (5099), smart-router (5150)
VRSTVA 2 (Pokročilé): mapa5-fusion (5094), mapa4-smart (5098), mapa3-ai (5093)
ocenovac (5300), mapa-levon (6200), csu-engine (5102)
VRSTVA 1 (Základní): mapa1-prodeje (5091), mapa2-csu (5092), market-intel (5400)
VALUO PROXIES: 6001,6002,6003,6100,6000 → 7000
Datový tok:
Scraper (server 134) → MASTER_LIVE.db (42.7K) → city_stats.db (mediány)
↓
ML training: 225K záznamů → XGB+LGB+GBR V7 modely
↓
User request → Super-Ensemble v2 (5111) → 7 enginů paralelně
→ IQR outlier removal → znalecké váhy → korekce
→ optional: LLM review / Znalecký panel
7. Fáze 4 — Perfect Map V13 (2026-03-10, port 5131)¶
Motivace¶
Super-Ensemble v2 byl komplexní, ale závisel na HTTP volání 7 enginů. Cílem V13 bylo vytvořit jednu self-contained ML mapu s lepší přesností.
Architektura¶
PM2: perfect-map-v13 (ID 157) Cesta: /opt/cenova_mapa/perfect_map_v13/03_perfect_map_api.py
Data: - 296 291 čistých záznamů z 6 databází (čistění, deduplication, outlier removal) - 20 564 geo-lokalit - Hlavní DB: city_stats.db + CUZK + CSU + LIVE + stat_realit + portaly
ML model — HistGradientBoosting + Meta-learner:
Vrstva 1: HistGradientBoostingRegressor (sklearn)
→ 5-fold cross-validation → OOF předpovědi
Vrstva 2: Meta-learner (LinearRegression)
→ blenduje OOF výstupy
Výsledky: R² = 0.833, MAE = 15 687 Kč
5 geo-úrovní (cascade):
D-tagy: - D30 (30%+ pod trhem), D20, D15, D10, FAIR, P20+ (20%+ nad trhem)
API:
GET /api/estimate?typ=byt&mesto=Praha&plocha=60
GET /api/compare?typ=byt&cena=5500000&plocha=55&mesto=Praha
GET /api/taxonomy # typy, D-tagy, velká města
GET /api/cities # top města s mediány
GET /api/stats
GET /api/health
8. Fáze 5 — V15 (2026-04, port 5150)¶
V15 nahradil Smart Router na portu 5150. Šlo o dedikovaný ML model s lepší architekturou než V7, ale ještě bez dispozice jako feature.
PM2: cenova-mapa-v15-shadow-combo3 (port 5165 v produkci na serveru 94)
V15 se stal záložní (fallback) vrstvou pro typy pozemek a komerční v Combo3 routeru.
9. Fáze 6 — V24 (2026-04-24, port 5164)¶
Největší skok v přesnosti¶
Tréninkový skript: /opt/cenova_mapa/retrain_v24.py Experiment dir: /opt/cenova_mapa/experiments/combo3_2026-04-24/
Nové features — klíčová změna: dispozice¶
V24 přidal dispozice (1+kk, 2+1, 3+kk, …) jako feature, která předchozím modelům chyběla.
34 features pro BYT:
features_byt = [
'plocha', 'lat', 'lon',
'dispozice_num', # ← NOVÉ: normalizovaná dispozice na číslo
'dist_prague',
'city_median_m2', 'city_count',
'okres_median_m2', 'kraj_median_m2',
'ptype_encoded',
'log_plocha', 'plocha_sq',
'is_praha', 'is_brno', 'is_ostrava',
'is_krajske_mesto',
'population', 'elevation',
... # celkem 34
]
Ensemble — 4 modely:
models = {
"lgb": LGBMRegressor(learning_rate=0.05, num_leaves=127, n_estimators=800),
"xgb": XGBRegressor(learning_rate=0.05, max_depth=6, n_estimators=800),
"rf": RandomForestRegressor(n_estimators=200, max_features=0.6),
"knn": KNeighborsRegressor(n_neighbors=15, weights='distance'),
}
# 5-fold OOF → stacking meta-learner
Váhy ensemblu:
{
"BYT": {"lgb": 0.3363, "xgb": 0.3359, "rf": 0.3278},
"DUM": {"lgb": 0.3373, "xgb": 0.3356, "rf": 0.3271}
}
Výsledky — dramatické zlepšení: | Typ | V13 R² | V24 R² | V24 median APE | |-----|--------|--------|----------------| | BYT | 0.833 | 0.9478 | 7.13% | | DUM | — | 0.9178 | — |
Log-target pro DUM: DUM model trénuje na log(price) → lepší chování pro velký rozptyl cen domů.
10. Fáze 6 — Combo3 Router (port 5163)¶
Soubor: /opt/cenova_mapa/combo3_router_5163.py PM2: cenova-mapa-combo3-shadow (ID 171)
Logika routování¶
def route_request(typ):
if typ in ['byt', 'dum']:
return forward_to_v24(5164) # V24 primary
else:
return forward_to_v15(5165) # V15 fallback pro pozemek/komerční
Normalizace parametrů¶
def normalize_params(params):
# Typ aliasy: type→typ, area→plocha, city→mesto
# Město aliasy: type→typ atd.
# Dispozice: "2+1" → číslo 2.5
Timeout: 8s, fallback transparent k clientovi.
11. Fáze 7 — shadow.czechai.io — Produkce (2026-05-03)¶
Infrastruktura¶
DNS shadow.czechai.io → 46.224.121.179 (server 179, SSL terminator)
Nginx 179: proxy_pass → http://94.130.223.40
Nginx 94: HTTP+HTTPS → PM2 procesy (5163/5166)
PM2 na serveru 94 (94.130.223.40): | PM2 name | Port | Popis | |----------|------|-------| | cenova-mapa-combo3-shadow | 5163 | Combo3 router (PRIMARY endpoint) | | cenova-mapa-v24-shadow-combo3 | 5164 | V24 model (přímý) | | cenova-mapa-v15-shadow-combo3 | 5165 | V15 model (fallback) | | shadow-url-estimator | 5166 | Bridge: URL/adresa → odhad/council/adjudicate |
Hlavní service: /opt/czechai/shadow_url_estimator.py
Veřejné API endpointy (shadow.czechai.io)¶
| Endpoint | Metoda | Popis |
|---|---|---|
/v1/cenova/estimate | GET/POST | Univerzální odhad z adresy nebo URL inzerátu |
/v1/cenova/from-address | GET | Odhad z adresy |
/v1/cenova/from-url | GET | Odhad z URL inzerátu |
/v1/cenova/adjudicate | GET/POST | Arbiter → APPROVE/CONFIRM/REVIEW/NEGATE |
/v1/cenova/council | GET/POST | Valuation Council (produkce + CMA) |
/v1/cenova/admin/stats | GET | Admin statistiky (token) |
/v1/cenova/admin/events | GET | Admin eventy (token) |
Veřejné stránky: - /cenovy-odhad — veřejný formulář - /widget/cenova-widget.js — embeddable widget - /cenova-admin — admin panel - /cenova-api — API dokumentace - /v1/cenova/openapi.json — OpenAPI spec - /health — health check
Výstup odhadu (co API vrací)¶
{
"cena": 5200000,
"cena_m2": 86667,
"interval": {"low": 4800000, "high": 5600000},
"confidence": 0.87,
"explanation": "Na základě 234 srovnatelných nemovitostí...",
"comparable_properties": [...],
"spatial_neighbor_correction": 1.02,
"hedonic_avm_layer": {...},
"investment_metrics": {...},
"arbitration_verdict": "CONFIRM"
}
EstateX Direct CMA Agent¶
Soubor: /opt/czechai/estatex_cma_agent.py MCP: mcp/estatex-cma-mcp/server.mjs DB: /opt/reality-pipeline/data/MASTER_LIVE.db
Nezávislý agent bez V24 — vážený medián podle vzdálenosti/podobnosti/zdroje. - Deduplikuje cílovou nemovitost podle URL/GPS/plochy/ceny - Městský filtr: zamezuje míchání GPS z jiných měst do Prahy
Benchmark (100 vzorků): | Agent | Median APE | Byty APE | |-------|-----------|---------| | Direct CMA | 24.68% | 14.23% | | V24 | 7.13% | 10.83% |
→ V24 je primární model; Direct CMA = nezávislá kontrolní vrstva
EstateX Valuation Council¶
CLI: scripts/estatex_valuation_council.mjs MCP: mcp/estatex-council-mcp/server.mjs Produkční endpoint: /v1/cenova/council
Logika verdiktu:
produkce NEGATE → NEGATE (bez diskuse)
produkce REVIEW → REVIEW
CMA vs produkce ≥ 25% → REVIEW
CMA vs produkce ≥ 15% → CONFIRM (s výhradou)
nízká confidence → CONFIRM
rozdíl < 15% + dobrá CMA confidence → blend 78% produkce + 22% CMA → APPROVE
EstateX Opportunity Brain¶
Soubor: scripts/estatex_opportunity_brain.py Model: /opt/czechai/estatex_opportunity_brain.joblib (ExtraTrees) Výsledky tréninku: 93 885 řádků, MAE ~16 758 Kč/m², MAPE ~47.82%, R² log ~0.8186, 3 331 segmentů
Výstup: fairMarketPriceCzk, maxReasonableBuyPriceCzk, targetOfferCzk
12. Kompletní portová mapa (všechny verze)¶
| Port | Služba | Fáze | Stav |
|---|---|---|---|
| 5088 | Stat. mapa (portaly 2026 fallback) | 1a | archív |
| 5091 | Mapa 1 — Prodeje V4 | 2 | deprecated |
| 5092 | Mapa 2 — CSU V4 | 2 | deprecated |
| 5093 | Mapa 3 — AI V7/V8 | 2 | deprecated |
| 5094 | Mapa 5 — Fusion Engine | 2 | deprecated |
| 5097 | Mapa 5 — Timeseries | 2 | stopped |
| 5098 | Mapa 4 — Smart Engine | 2 | deprecated |
| 5099 | Unified Price API (Levon+AI+Smart) | 3 | stopped |
| 5100 | Cenova Ultimate (simple median) | 3 | stopped |
| 5102 | CSU Engine | 3 | stopped |
| 5111 | Super-Ensemble V2 (7 enginů) | 3 | stopped |
| 5112 | Znalecký Panel (3 AI znalci) | 3 | stopped |
| 5131 | Perfect Map V13 | 4 | deprecated |
| 5150 | Smart Router / V15 model | 3/5 | záloha |
| 5163 | Combo3 Router (PRODUKCE) | 6 | ✅ LIVE |
| 5164 | V24 direct | 6 | ✅ LIVE |
| 5165 | V15 direct (fallback) | 5/6 | ✅ LIVE |
| 5166 | Bridge (URL/adresa → estimate) | 7 | ✅ LIVE |
| 5300 | Oceňovač GPS/KNN | 3 | stopped |
| 5400 | Market Intel | 3 | stopped |
| 5800 | Map-CzechAI (Next.js) | 3 | stopped |
| 6000–6003 | Valuo proxies → 7000 | 3 | stopped |
| 6100 | Valuo-pro → 7000 | 3 | stopped |
| 6200 | Mapa Levon (17K kombinací) | 3 | stopped |
| 7000 | Super Unified (3 strategie) | 3 | stopped |
| 7001 | Super Precision | 3 | stopped |
| 7002 | Super Conservative | 3 | stopped |
| 7003 | Super Ensemble (MAPE-weighted) | 3 | stopped |
13. Datové zdroje (přehled všech DB)¶
| Soubor | Záznamy | Obsah | Používá |
|---|---|---|---|
ULTIMATE_MASTER.db | 98 198 | Nejstarší master DB | archív |
city_stats.db | 105K+ listings | Mediány per město+typ | Mapa 1, 2, Super Fetcher |
cuzk_cenove_udaje.db | 30 872 | Katastr Praha 2025 | Fusion, CSU engine |
csu_ceny_nemovitosti.db | 45 184 | CSU celá ČR 2013–2024 | Fusion, CSU engine |
stat_realit_179k.db | 179 000 | Historické inzeráty | Fusion, Oceňovač |
MASTER_LIVE.db | 42 700–246 000 | Multi-portálové live | Fusion, CMA agent |
mega_training_data_v5.db | historická | Training data 2025 | Oceňovač |
cenova_estimates.sqlite3 | live log | Produkční odhady | shadow.czechai.io |
map_czechai (PostgreSQL) | 13 456 | Listings pro mapu | Map-CzechAI |
14. ML modely — evoluce přesnosti¶
| Model | Trénováno | Záznamy | Metrika | BYT |
|---|---|---|---|---|
| AI V7 (XGB+LGB+GBR) | ~2026-01 | 225 000 | MdAPE | 7.6% |
| AI V8 | ~2026-02 | — | — | ~27%* |
| Perfect Map V13 | 2026-03-10 | 296 291 | R² | 0.833 |
| V24 (LGB+XGB+RF+KNN) | 2026-04-24 | — | R² | 0.9478 |
*V8 regresoval na MAPE 27% pro BYT — proto byla jeho váha v Super-Ensemble snížena z 1.3 na 1.1
15. Pravidla pro produkci (platí vždy)¶
🔴 ABSOLUTNÍ PRAVIDLA:
1. Port 5163 (Combo3) = JEDINÝ správný port pro cenovou mapu
ZAKÁZÁNO: 5131 (V13), 5150 (V15), 5154 (starý V24), 5111 (Super-Ensemble)
2. API volání V24:
- plocha → posílat jako area i plocha
- typ → posílat jako type i typ
- město → posílat jako city i mesto
- dispozice (2+kk / 3+1) → normalizovat na číslo
3. Sreality URL nikdy neupravovat — přesně jak jsou v DB
4. AML systém nesahat bez explicitního souhlasu
16. Klíčové soubory v repository¶
scripts/shadow_url_estimator.py # Bridge: URL/adresa → odhad
scripts/estatex_cma_agent.py # Direct CMA (bez V24)
scripts/estatex_valuation_agent.mjs # CLI agent
scripts/estatex_valuation_council.mjs # Council CLI
scripts/estatex_opportunity_brain.py # Opportunity Brain (ExtraTrees)
scripts/evaluate_cma_vs_v24.py # Benchmark script
scripts/cenova_e2e_test.mjs # E2E testy
mcp/estatex-valuation-mcp/server.mjs # MCP pro valuation
mcp/estatex-opportunity-mcp/server.mjs # MCP pro opportunity
mcp/estatex-cma-mcp/server.mjs # MCP pro CMA
mcp/estatex-council-mcp/server.mjs # MCP pro council
tmp/nginx/shadow.czechai.io # Nginx config template
server.mjs / package.json # Main server
docs/ESTATEX_DIRECT_CMA_AGENT.md
docs/ESTATEX_VALUATION_COUNCIL.md
docs/CMA_VS_V24_BENCHMARK_100.md
17. Benchmark — všechny mapy najednou (2026-03-02)¶
Testovací nemovitost: BYT Praha 75 m² | Server: 46.224.121.179
Výsledky (seřazeno dle pořadí v architektuře)¶
| # | Mapa | Port | Odhad (CZK) | CZK/m² | Confidence | Čas |
|---|---|---|---|---|---|---|
| 1 | Super-Ensemble v2.1 | 5111 | 10 228 890 | 136 385 | 0.95 | 0.2s |
| 2 | Znalecký Panel (3 AI) | 5112 | 10 595 059 | 141 267 | 0.79 | 53s |
| 3 | Prodeje V4 | 5091 | 12 417 339 | 165 565 | 0.90 | <50ms |
| 4 | AI V8 (4 ML) | 5093 | 11 117 448 | 148 233 | 1.00 | <50ms |
| 5 | Oceňovač GPS/KNN | 5300 | 12 376 206 | 165 016 | 0.90 | 880ms |
| 6 | Mapa Levon | 6200 | 10 790 539 | 143 874 | 0.88 | <100ms |
| 7 | Market Intel | 5400 | (kontext) | med 184k | — | <200ms |
| 8 | Valuo Pro (3-way) | 6100 | 11 951 689 | 159 355 | 0.70 | <100ms |
| 9 | Valuo Výnosový | 6002 | 4 760 000 | 63 466 | 0.70 | <50ms |
| 10 | Valuo Nákladový | 6003 | 9 450 000 | 126 000 | 0.70 | <50ms |
Rozpětí tržních enginů (bez výnosového/nákladového): - Min: 10 228 890 CZK (SE v2.1) - Max: 12 417 339 CZK (Prodeje V4) - Spread: 21 % — akceptovatelné pro BYT v Praze
Detail klíčových enginů¶
Super-Ensemble v2.1 (5111):
Cena: 10 228 890 CZK (136 385 CZK/m²)
Shoda enginů: 0.928
Confidence: 0.95
Enginy: 6/6 OK
Tržní: 11 730 600 CZK
Výnosový: 4 760 000 CZK
Nákladový: 9 450 000 CZK
Review needed: false
Znalecký Panel (5112) — 3 AI znalci:
Znalec A (konzervativní, váha 1.5): 8 500 000 CZK (conf 0.70)
Znalec B (optimistický, váha 1.2): 13 000 000 CZK (conf 0.88)
Znalec C (pragmatický, váha 1.0): 10 228 890 CZK (conf 0.93)
────────────────────────────────────────────────────────────
Konsenzus: 10 595 059 CZK (141 267/m²)
Shoda znalců: 0.785
Data sources: 4/5 OK (RAG, Sreality, ČNB, SE)
Čas: 53.2s
Market Intel (5400) — Praha centrum, 3km radius:
Záznamy: 4 133 (1 031 live + 3 102 historické)
Trend: stabilní (+3.3%)
Likvidita: vysoká
Investment score: 82/100
Medián m²: 184 286 CZK
P25–P75: 158 469 – 216 687 CZK/m²
Přesnost podle typu nemovitosti (SE v2.1)¶
| Typ | Shoda enginů | Confidence | Review needed |
|---|---|---|---|
| BYT | 0.928 | 0.95 | NE |
| DUM | 0.920 | 0.94 | NE |
| POZEMEK | 0.634 | 0.74 | ANO |
| KOMERCNI | 0.536 | 0.68 | ANO |
AI V8 benchmark přesnost (MdAPE)¶
| Typ | MdAPE |
|---|---|
| BYT | 7.78% |
| DUM | 18.19% |
| KOMERCNI | 18.96% |
| POZEMEK | 41.11% |
Review mechanismus (automatický)¶
Dotaz → SE v2.1 (0.2s)
│
├── Shoda enginů > 0.70?
│ └── ANO → výsledek (review_needed: false)
│
└── NE (odchylka >30% mezi tržními enginy)
├── review_needed: true
└── auto_review=true?
├── NE → jen flag, caller rozhodne
└── ANO → Znalecký Panel (+53s)
├── <15% → "SE a Panel se shodují"
├── 15–25% → "Mírná neshoda – ruční kontrola"
└── >25% → "Velká neshoda – expertní revize"
Vrstvy architektury (2026-03-02, schéma)¶
DATABÁZE:
city_stats.db (8 357 kombinací)
stat_realit.db (39K live 2026)
mega_training_data_v5.db (103K historické 2025)
mapa_levon_index.json (17K+ kombinací)
ML modely (XGB, LGB, GB, CatBoost)
│
LAYER 1 (jednotlivé enginy):
5091 Prodeje V4 | 5093 AI V8 (4 ML)
5300 Oceňovač KNN | 6200 Mapa Levon
6002 Výnosový | 6003 Nákladový
│
LAYER 2 (agregátory):
5111 SUPER-ENSEMBLE v2.1
6 enginů + IQR + vážený medián + 3 přístupy
+ Market Intel + LLM + AUTO REVIEW → 5112
5112 ZNALECKÝ PANEL v1.0
3 AI znalci (qwen3:32b) + RAG + MCP + ČNB
+ Chairman konsenzus (~53s)
5400 MARKET INTEL
Trendy, likvidita, investiční skóre, P25–P75
Klíčové DB (stav 2026-03-02)¶
| Databáze | Záznamy | Použití |
|---|---|---|
city_stats.db | 8 357 kombinací | Prodeje V4, AI V8 |
stat_realit.db | 39K live 2026 | Oceňovač, Market Intel, Valuo |
mega_training_data_v5.db | 103K hist. 2025 | Oceňovač, Market Intel |
mapa_levon_index.json | 17K+ kombinací | Mapa Levon |
| ML modely (.pkl) | 4 modely × typ | AI V8 |
Dokument generován 2026-05-04 · Claude Code (claude-sonnet-4-6) Zdroje: SSH server 179, PM2, /opt/outline/outline_backup_20260328.sql, MAPA_KOMPLETNI_DESKTOP (2026-03-07), SERVICE_MAP benchmark (2026-03-02)
18. Vědecká sekce — ETL GIS Pipeline (2026-04-15)¶
Tato sekce zachycuje kompletní ETL pipeline, která předcházela tréninku V23 a V24. Pipeline transformovala 138 056 surových zákazníků z databáze na GIS-obohacená tréninková data se 12 novými prostorovými features.
18.1 Proč GIS enrichment?¶
Předchozí modely (V7, V8, V13) používaly jako prostorové features pouze: - GPS souřadnice (gps_lat, gps_lon) — přímé - Vzdálenosti k velkým městům (d_praha, d_brno, ...) — vypočítané haversine - Mediány per město (city_median_m2) — z city_stats.db
Výzkum ukázal, že tyto features nestačí pro přesné ocenění v různých lokalitách. Nemovitost 500 metrů od dálnice vs 10 km se liší o 5–15 % v ceně — a model to neviděl. Proto vznikla GIS ETL pipeline.
18.2 Pipeline — 8 kroků¶
01_enrich_transport.py → vzdálenost k silnicím, dálnici, MHD, počet zastávek
02_enrich_water.py → vzdálenost k vodě/řece, zaplavovací zóna
03_enrich_landuse.py → landuse typ, zelené plochy, POI score, vzdálenost ke škole
04_enrich_ruian.py → PSČ medián (poštovní kódy → ceny)
05_merge_features.py → merge všech 4 parquet souborů → enriched_v23.parquet
06_test_1000.py → validační skript (1000 náhodných bodů)
07_train_v23_enriched.py → trénink V23 na obohacených datech
08_deploy_v23.sh → deploy (V23 nikdy nasazen — viz sekce 22)
etl_06_add_dispozice.py → klíčové: přidání dispozice → enriched_v24.parquet
18.3 ETL 01 — Transport features (2026-04-15 12:44)¶
Skript: /opt/cenova_mapa/etl/01_enrich_transport.py Čas běhu: 25 sekund Výstup: transport_features.parquet (138 056 řádků)
Vypočítané features:
distance_to_motorway — vzdálenost k nejbližší dálnici (km)
distance_to_primary_road — vzdálenost k hlavní silnici (km)
distance_to_mhd — vzdálenost k nejbližší zastávce MHD (km)
mhd_stops_500m — počet MHD zastávek v okruhu 500 m
Logy — sanity checks (PASSED ✅):
2026-04-15 12:44:28 === SANITY CHECKS ===
distance_to_motorway: min=0.00, max=68.77, mean=10.74 km
distance_to_mhd: min=0.00, max=12.46, mean=0.40 km
mhd_stops_500m: min=0, max=55, mean=4.7
Praha centrum (5936 bodů): MHD=0.163km, zastávky=13.7
Sanity checks PASSED ✅
Uloženo: /opt/cenova_mapa/etl/output/transport_features.parquet (138056 řádků)
Hotovo za 25s
Interpretace dat: - Průměrná vzdálenost k dálnici 10.74 km odpovídá hustotě silniční sítě ČR - Praha centrum: MHD zastávka průměrně 163 metrů, 13.7 zastávek v dosahu = hustá MHD síť - Max 55 zastávek v 500m = Florenc/hlavní uzly - Max vzdálenost MHD 12.46 km = vzdálené vesnice bez MHD
Technická implementace:
# Haversine kdtree pro rychlé hledání nejbližší zastávky
from scipy.spatial import cKDTree
import numpy as np
# GeoJSON datové sady (OpenStreetMap):
# - motorways: dálnice D1, D2, D3...
# - primary roads: silnice 1. třídy
# - MHD zastávky: Praha, Brno, Ostrava, regionální
tree_mhd = cKDTree(mhd_coords_radians) # KD-Tree na sférických souřadnicích
dists, _ = tree_mhd.query(coords_radians, k=51) # 51 = 1 cílový + 50 sousedů
distance_to_mhd = dists[:, 0] * 6371 # km
mhd_stops_500m = np.sum(dists[:, 1:] * 6371 < 0.5, axis=1)
18.4 ETL 02 — Water features (2026-04-15 12:45)¶
Skript: /opt/cenova_mapa/etl/02_enrich_water.py Čas běhu: 32 sekund Výstup: water_features.parquet (138 056 řádků)
Vypočítané features:
distance_to_water — vzdálenost k nejbližší vodní ploše (rybník, jezero, nádrž) (km)
distance_to_river — vzdálenost k nejbližší řece (km)
in_flood_zone — boolean: leží v zaplavovací zóně Q100?
Logy — sanity checks (PASSED ✅):
2026-04-15 12:45:07 === SANITY CHECKS ===
distance_to_water: min=0.000, max=8.65, mean=0.25 km
distance_to_river: min=0.001, max=26.17, mean=2.75 km
in_flood_zone: 11189 bodů v záplavové zóně (8.1%)
Sanity checks PASSED ✅
Uloženo: /opt/cenova_mapa/etl/output/water_features.parquet (138056 řádků)
Hotovo za 32s
Interpretace dat: - 8.1 % nemovitostí v záplavové zóně — odpovídá reálnému podílu v říčních nivách - Průměrná vzdálenost k vodě 250 metrů = ČR je vodnatá země - Max vzdálenost k řece 26.17 km = suchá plateau
Datový zdroj: - Záplavové zóny: ČHMÚ data (Q100 = povodeň 1× za 100 let) - Vodní plochy a řeky: OpenStreetMap waterway + natural=water
Vliv na cenu nemovitosti: - Blízkost k vodě (< 300 m) → prémium +3–8 % - In_flood_zone=1 → diskont -5–12 % (pojistné riziko, hypotéky)
18.5 ETL 03 — Landuse features (2026-04-15 12:46–12:48)¶
Skript: /opt/cenova_mapa/etl/03_enrich_landuse.py Čas běhu: 172 sekund (nejdelší ETL krok) Výstup: landuse_features.parquet (138 056 řádků)
Vypočítané features:
landuse_type — typ využití území (0-7, kódováno)
green_area_500m — podíl zelených ploch v okruhu 500 m (0.0–1.0)
poi_score_500m — skóre dostupnosti bodů zájmu (0–100)
school_distance — vzdálenost k nejbližší škole (km)
Kódování landuse_type:
0 = residential (obytná zóna) — 103 848 bodů (75.2%)
1 = mixed (smíšené využití) — implicit fallback
2 = commercial (komerční) — 348 bodů
3 = industrial (průmyslové) — 1 323 bodů
4 = agricultural (zemědělské) — 126 bodů
5 = park/green (park/zeleň) — 5 983 bodů (4.3%)
6 = forest (les) — 4 417 bodů (3.2%)
7 = water (voda) — 53 bodů
Logy — sanity checks (PASSED ✅):
2026-04-15 12:48:00 === SANITY CHECKS ===
landuse_type dist: {1: 103848, 0: 21958, 5: 5983, 6: 4417, 3: 1323, 2: 348, 4: 126, 7: 53}
green_area_500m: mean=0.224, max=1.000
poi_score_500m: mean=8.6, max=100.0
school_distance: mean=3.38km
Sanity checks PASSED ✅
Hotovo za 172s
POI score metodika:
POI_WEIGHTS = {
"restaurant": 3,
"cafe": 2,
"supermarket": 5,
"pharmacy": 4,
"doctor": 3,
"bank": 2,
"public_transport": 6,
"park": 2,
"cinema": 1,
"gym": 1,
}
# score = min(100, sum(count_i * weight_i) pro POI v okruhu 500m)
Interpretace: - green_area_500m mean=0.224 → průměrně 22.4 % zeleně v dosahu 500 m - poi_score max=100 = centrum Prahy/Brna (přes 100 bodů, cappováno) - school_distance mean=3.38 km → průměrně 3+ km ke škole (include rural areas)
18.6 ETL 04 — RUIAN / PSČ features (2026-04-15 12:48)¶
Skript: /opt/cenova_mapa/etl/04_enrich_ruian.py Čas běhu: 1 sekunda (fallback mode) Výstup: ruian_features.parquet (138 056 řádků)
Cíl: Přidat psc_median — medián ceny Kč/m² pro poštovní kód (PSČ)
Fallback mode — co se stalo:
2026-04-15 12:48:01 Training data: 138056 GPS bodů
2026-04-15 12:48:01 FALLBACK MODE: RUIAN CSV není dostupné, použiji PSČ z city_stats
2026-04-15 12:48:02 Unikátních PSČ nalezeno: 1
2026-04-15 12:48:02 PSČ mediánů: 1, global median: 47875 Kč/m²
2026-04-15 12:48:02 psc_median: min=47875, max=47875, mean=47875 Kč/m²
2026-04-15 12:48:02 psc_code sample: ['00000', '00000', '00000', '00000', '00000']
Problém: RUIAN CSV (Registr územní identifikace, adres a nemovitostí) nebyl v době ETL dostupný na serveru. Skript přešel do fallback módu a přiřadil všem nemovitostem globální medián 47 875 Kč/m².
Důsledek: Feature psc_median v V23/V24 modelu má nulovou informační hodnotu (konstantní). V budoucí verzi modelu by měla být nahrazena skutečnými PSČ mediány z RUIAN.
18.7 ETL 05 — Merge features (2026-04-15 12:48)¶
Skript: /opt/cenova_mapa/etl/05_merge_features.py Čas běhu: 7 sekund Výstup: enriched_v23.parquet (140 396 řádků, 35 sloupců)
Merge na klíčovém sloupci id — spojení všech 4 parquet souborů.
Logy — výsledky merge:
2026-04-15 12:48:02 Načítám base training data...
2026-04-15 12:48:03 Base training: 138056 záznamů
2026-04-15 12:48:03 Načítám enriched features...
2026-04-15 12:48:03 transport: 138056, water: 138056, landuse: 138056, ruian: 138056
2026-04-15 12:48:03 Merguju na id...
2026-04-15 12:48:03 Merged: 140396 záznamů
2026-04-15 12:48:05 Počítám V15 vzdálenostní features...
2026-04-15 12:48:09 Počítám mic/mac features...
=== SANITY CHECKS ===
Rozměr: (140396, 35)
Null hodnoty: Series([], dtype: int64) ← žádné null hodnoty!
property_type distribuce:
dum 54802 (39.0 %)
byt 47063 (33.5 %)
pozemek 32291 (23.0 %)
komercni 6240 (4.4 %)
price_m2: min=6, max=8500000, mean=62666
V23 nové features stats:
distance_to_motorway: min=0.00, max=68.77, mean=10.748
distance_to_mhd: min=0.00, max=12.46, mean=0.399
in_flood_zone: min=0.00, max=1.00, mean=0.081
landuse_type: min=0.00, max=7.00, mean=1.205
poi_score_500m: min=0.00, max=100.00, mean=8.570
Hotovo za 7s
Poznámka: Výsledek 140 396 řádků (vs 138 056 vstupu) — 2 340 nových záznamů přibylo při merge. Důvod: base data obsahovala záznamy s jinými ID než ETL výstupy, Pandas left join přidal NaN řádky, které pak byly doplněny mediány.
35 sloupců v enriched_v23.parquet:
id, property_type, city, area, price, price_m2, gps_lat, gps_lon,
log_area, d_praha, d_brno, d_ostrava, d_plzen, d_liberec, d_olomouc,
d_cb, d_hk, d_usti, d_pardubice, d_zlin, d_kv, d_jihlava,
d_min, mic_key, mac_key,
distance_to_motorway, distance_to_primary_road, distance_to_mhd,
mhd_stops_500m, distance_to_water, distance_to_river, in_flood_zone,
landuse_type, green_area_500m, poi_score_500m, school_distance, psc_median
19. ETL 06 — Klíčová inovace: dispozice feature (2026-04-24)¶
19.1 Co je dispozice a proč je to průlom¶
dispozice je typ bytu z pohledu počtu místností: 1+kk, 2+1, 3+kk, 4+1, 5+kk atd. Pro českou realitní trh je to fundamentální proměnná — cena za m² 2+kk bytu je výrazně jiná než stejně velký 1+kk nebo 3+1.
Příklad — Praha 60 m²: | Dispozice | Typická cena Kč/m² | Poznámka | |-----------|-------------------|----------| | 1+kk (jednobytový) | 120 000–140 000 | Garsonka premium | | 2+kk | 105 000–120 000 | Standardní | | 2+1 | 98 000–112 000 | Starší panelák | | 3+kk | 92 000–108 000 | Větší byt diskont | | 3+1 | 88 000–105 000 | Starší standard | | 4+kk | 82 000–98 000 | Velký byt |
Bez dispozice musel model hádat z pouhé plochy — a při 60 m² neví, jestli jde o prostorný 1+kk nebo stísněný 2+1. Chyba ±10 %.
19.2 Skript etl_06_add_dispozice.py¶
Soubor: /opt/cenova_mapa/etl_06_add_dispozice.py Datum spuštění: 2026-04-24 (před tréninkem V24)
#!/usr/bin/env python3
"""ETL 06 — Přidá dispozice (rooms) do enriched_v23.parquet → enriched_v24.parquet
Zdroj: mega_training_data_v6.db → dispozice (int: 1-6, 0=unknown)
BYT: 95.8% fill → median = 2
DUM/pozemek/komercni: dispozice = 0 (neaplikovatelné)
Výstup: /opt/cenova_mapa/etl/output/enriched_v24.parquet
"""
IN_FILE = '/opt/cenova_mapa/etl/output/enriched_v23.parquet'
OUT_FILE = '/opt/cenova_mapa/etl/output/enriched_v24.parquet'
DB_PATH = '/opt/cenova_mapa/MASTER_MAP_DB/training_data/mega_training_data_v6.db'
df = pd.read_parquet(IN_FILE) # 140396 × 35
disp = pd.read_sql('SELECT id, dispozice FROM training_data', conn)
disp = disp.drop_duplicates('id') # safety
df = df.merge(disp[['id', 'dispozice']], on='id', how='left')
df['dispozice'] = pd.to_numeric(df['dispozice'], errors='coerce').fillna(0).astype(np.int8)
df['dispozice'] = df['dispozice'].clip(0, 6) # max 6+kk
# Sanity checks
assert df.isnull().sum().sum() == 0
assert df.shape[1] == 36 # 35+1 nová feature
assert len(df) == 140396 # počet řádků se nezměnil
df.to_parquet(OUT_FILE, index=False)
# Výstup: enriched_v24.parquet (140396 rows, 36 cols)
19.3 Fill rate dispozice per property_type¶
BYT: 95.8 % (44 946 / 46 916 bytů má dispozici)
DUM: 0.0 % (dispozice = 0, neaplikovatelné)
POZEMEK: 0.0 % (dispozice = 0, neaplikovatelné)
KOMERCNI: 0.0 % (dispozice = 0, neaplikovatelné)
Distribuce BYT dispozice:
0 (unknown): 2 117 (4.2 %) → imputováno mediánem 2
1 (1+kk/1+1): 8 403 (16.7 %)
2 (2+kk/2+1): 19 862 (39.5 %) ← nejčastější
3 (3+kk/3+1): 12 044 (23.9 %)
4 (4+kk/4+1): 5 391 (10.7 %)
5 (5+kk/5+1): 2 184 (4.3 %)
6 (6+ pokojů): 835 (1.7 %)
Mediánová imputace: Pro BYT s dispozice=0 (4.2 %) se při inferenci použije medián = 2 (2+kk/2+1).
Proč integer a ne string? Aby model mohl zachytit monotónní vztah: 1 < 2 < 3 < 4 < 5 < 6. Řetězce "2+kk" a "2+1" jsou sloučeny — rozlišení kk vs. +1 zůstává schováno ve ploše (2+1 typicky větší).
20. V15 iterace — kompletní vědecká evoluce (7 verzí)¶
V15 nebyla jedna verze — byl to výzkumný projekt s 7 iteracemi (b–h), kde každá experimentovala s jinou architekturou. Každá verze zůstala na serveru jako .py skript.
20.1 Kontext — proč iterovat od V15b?¶
V15a (nenazvaná) byla přímý nástupce Smart Routeru na portu 5150. Zbývá neznámá — v adresáři existuje jen train_v15_perfect.py bez b/c/d prefixu. Iterace b–h jsou systematické experimenty.
20.2 V15b — Direct price_m2 + micro-region KNN¶
Soubor: /opt/cenova_mapa/train_v15b.py Klíčová změna: Přímá predikce price_m2 (ne celkové ceny), micro-region KNN jako feature
Architektura:
Datový zdroj: mega_training_data_v6.db
Outlier removal: log-normální 2.5σ + micro-region relative (cena/median_micro < 0.3 nebo > 3.0)
Micro-region: (lat*100).round() + "_" + (lon*100).round() = ~1km čtverce
Target: price_m2 (přímo, ne log)
4-modelový ensemble:
XGB: n_estimators=1200, max_depth=8, lr=0.02, subsample=0.85,
colsample=0.75, reg_alpha=0.3, reg_lambda=1.5
LGB: n_estimators=1200, max_depth=8, lr=0.02, subsample=0.85,
colsample=0.75, reg_alpha=0.3, reg_lambda=1.5
CAT: iterations=1200, depth=8, lr=0.02
KNN: n_neighbors=10, weights=distance
Váhy: XGB=0.35, LGB=0.30, CAT=0.20, KNN=0.15
Tréninková data per typ:
Výsledky V15b (z v15_results.json): | Typ | MAPE | Median APE | <5% | <10% | |-----|------|-----------|-----|------| | BYT | 9.84% | 5.53% | 46.7% | 69.3% | | DUM | 16.19% | 7.23% | 39.5% | 59.1% | | POZEMEK | 46.72% | 10.69% | 30.5% | 48.4% | | KOMERCNI | 12.99% | 4.49% | 53.1% | 69.5% |
Hodnocení: BYT přijatelné (median 5.5%), DUM slabé (16%), pozemek katastrofa (47%). Identifikuje se problém: KNN overfituje na blízkých bodech, pozemky mají extrémní rozptyl.
20.3 V15c — POZEMEK + DUM focus¶
Soubor: /opt/cenova_mapa/train_v15c.py Klíčová změna: Specializace na pozemky a domy
Co se změnilo:
# Nová taxonomie pozemků z land_subtype_code:
land_map = {
2: "stavebni", # stavební parcela — nejcennější
5: "zahrada", # zahrada
7: "zemedelsky", # orná půda
10: "les", # les — nejlevnější
13: "ostatni",
14: "ostatni"
}
# Jemnější GPS grid: 0.005 stupně = ~500 metrů (V15b mělo 0.01 = ~1km)
df["micro"] = (df["gps_lat"]/0.005).round().astype(str) + "_" + (df["gps_lon"]/0.005).round().astype(str)
# Pro pozemky: vyšší váha KNN (location is everything for land)
weights_pozemek = {"xgb": 0.20, "lgb": 0.20, "cat": 0.10, "knn": 0.50}
Výsledek: Pozemky stále problematické — root cause není v taxonomii ale v základní architektuře.
20.4 V15d — Stacked Meta-Ensemble + 3 datové zdroje¶
Soubor: /opt/cenova_mapa/train_v15d.py Klíčová změna: Přidány 2 nové datové zdroje, stacking meta-learner
Datové zdroje (poprvé 3 zdroje dohromady):
# Source 1: Training DB (138K)
db1 = sqlite3.connect("/opt/cenova_mapa/mega_training_data_v6.db")
df1 = pd.read_sql("SELECT ... FROM training_data WHERE ...", db1)
df1["source"] = "training"
# Source 2: stat_realit (183K historické inzeráty)
db2 = sqlite3.connect("/opt/cenova_mapa/mapa5_fusion/data/stat_realit_179k.db")
df2 = pd.read_sql("SELECT ... FROM stat_realit WHERE ...", db2)
df2["source"] = "stat"
# Source 3: CUZK katastr (31K reálných transakcí Praha)
db3 = sqlite3.connect("/opt/cenova_mapa/cuzk_cenove_udaje.db")
# Vyžaduje S-JTSK → WGS84 konverzi (Křovák → GPS):
gps = [sjtsk_to_wgs84(r["gps_x"], r["gps_y"]) for _, r in df3.iterrows()]
Konverze S-JTSK → WGS84: Klíčová matematická část — CUZK ukládá souřadnice ve starém českém systému Křovák. V15d implementoval korektní Molodensky transformaci (dx=570.8, dy=85.7, dz=462.8 metrů posun referenčního elipsoidu z Bessel 1841 na WGS84).
Meta-feature: V13 estimated_price jako vstupu do V15d (kde je dostupný). Model "naučí se opravit" odhad V13.
Výsledek: Zlepšení díky CUZK datům, ale meta-feature z V13 příliš noise.
20.5 V15e — POZEMEK specialist¶
Soubor: /opt/cenova_mapa/train_v15e.py Klíčová změna: Oddělený model pouze pro pozemky
# Pouze pozemky, oba zdroje:
df = pd.read_sql("SELECT * FROM training_data WHERE property_type='pozemek' ...", db)
df2 = pd.read_sql("SELECT ... FROM stat_realit WHERE property_type='pozemek' ...", db2)
Hypotéza: Stavební parcely v Praze se chovají úplně jinak než orná půda na Vysočině. Samostatný model pro stavební vs venkovské.
Výsledek: Zlepšení MdAPE pro pozemky z ~47% na ~35% — ale stále katastrofální vs BYT (5.5%).
20.6 V15f — Residual Modeling (klíčový architektonický skok)¶
Soubor: /opt/cenova_mapa/train_v15f.py Klíčová změna: Dvoustupňová architektura — KNN dá baseline, GBM opraví residuál
Architektura:
Step 1 (Baseline): KNN predikuje price_m2 z GPS + micro_median
→ dobrý odhad "jaká je průměrná cena v lokalitě"
Step 2 (Correction): GBM predikuje RESIDUÁL (actual - KNN_pred) ze všech features
→ model se učí "jak moc se tato nemovitost liší od lokálního průměru"
Final = KNN_pred + GBM_residual_pred
Klíčová myšlenka: KNN ví, kde jste (lokalita). GBM ví, co je nemovitost (plocha, typ, dispozice). Oddělení těchto dvou znalostí eliminuje interference.
Winsorization:
# Clip extreme price_m2 per micro-region to 3x IQR
# Prevents outliers from corrupting GBM training
q25, q75 = micro_prices.quantile([0.25, 0.75])
iqr = q75 - q25
winsorized_price = price.clip(q25 - 3*iqr, q75 + 3*iqr)
3 datové zdroje: training_138K + stat_realit_183K + CUZK_31K (S-JTSK konverze zdědena z V15d).
Hodnocení V15f: Nejlepší V15 varianta — stala se základem produkčního V15 modelu (5165).
20.7 V15g — Confidence-Based Routing + Anti-Overfitting¶
Soubor: /opt/cenova_mapa/train_v15g.py Klíčová změna: KNN overfitting fix + confidence routing
Identifikovaný problém: KNN overfituje, když testovací bod leží blízko tréninkovému. Pokud inzerát je 50 metrů od tréninku, KNN "vidí" skoro sám sebe a vrátí exaktní cenu — ale to není generalizace.
Řešení:
# Leave-One-Out style: při KNN query vyřadit záznamy se stejnou URL nebo cenou
# V produkci: vyřadit záznamy v okruhu 50 metrů od dotazu (anti-leakage)
# Confidence routing:
if knn_distance < 0.3: # < 300m od blízkého souseda
weight_knn = 0.70 # Věřím KNN
weight_gbm = 0.30
elif knn_distance < 1.0: # < 1km
weight_knn = 0.40
weight_gbm = 0.60
else: # > 1km (sparse area)
weight_knn = 0.10
weight_gbm = 0.90 # GBM dominuje
# TRUE geographic hold-out: 80% micro-regionů train, 20% micro-regionů test
# (nikoliv náhodné 80/20 — aby test byl na skutečně neviditelných lokalitách)
Trimmed MAPE: Reportoval trimmed MAPE (95. percentil) pro eliminaci datových chyb.
Výsledek: Realističtější MAPE, ale v produkci složitější inference (need to compute KNN distance per query).
20.8 V15h — Spatial Smoothing (No GPS in GBM)¶
Soubor: /opt/cenova_mapa/train_v15h.py Klíčová změna: GBM nevidí GPS souřadnice — jen prostorové agregace
Motivace: GPS souřadnice v GBM způsobují přeučení na konkrétní lokality v train setu. GBM si "zapamatuje" adresu místo toho, aby se naučil obecné principy.
Nové features místo GPS:
# Spatial radius features (agregace z tréninku):
avg_price_5km = mean(train_prices kde vzdálenost < 5km)
avg_price_10km = mean(train_prices kde vzdálenost < 10km)
avg_price_20km = mean(train_prices kde vzdálenost < 20km)
density_5km = count(train_points kde vzdálenost < 5km) # urbanizace
# Hierarchical fallback:
price_micro = median per micro-region (500m grid)
price_macro = median per macro-region (5km grid)
price_city = median per city
price_national = global median
# BallTree pro rychlou radius query
from sklearn.neighbors import BallTree
tree = BallTree(coords_radians, metric='haversine')
Výsledek: Lepší generalizace na neviditelné lokality, ale malý regression na známých lokalitách. Geographic hold-out test byl férový — model fungoval lépe než V15g na neznámých oblastech.
20.9 Shrnutí V15 iterací¶
| Verze | Klíčová idea | Výsledek BYT | Nasazena? |
|---|---|---|---|
| V15b | Direct price_m2, micro-KNN | MAPE 9.84% | NE |
| V15c | Pozemek subtypes, 500m grid | — | NE |
| V15d | 3 datové zdroje, CUZK, meta-features | — | NE |
| V15e | Pozemek specialist | — | NE |
| V15f | Residual modeling (KNN+GBM) | nejlepší V15 | ANO (port 5165) |
| V15g | Confidence routing, LOO exclusion | realistická MAPE | NE |
| V15h | Spatial smoothing, no GPS in GBM | lepší generalizace | NE |
Proč V15f do produkce: Nejlepší balance přesnosti a inference rychlosti. Residual modeling je elegantní — KNN řeší lokalitu, GBM řeší vlastnosti nemovitosti.
21. V16 ULTIMATE — proč nebyl nasazen¶
Soubor: /opt/cenova_mapa/train_v16_ultimate.py
21.1 Architektura (3-layer confidence-weighted)¶
Layer 1: Spatial baseline
→ radius-based weighted average z tréninku
→ "jaká je průměrná cena v okruhu X km?"
→ adaptivní radius: Praha 2km, krajská města 5km, vesnice 15km
Layer 2: GBM correction
→ učí se RESIDUÁL od spatial baseline (jako V15f)
→ features: plocha, vzdálenosti k městům, typ, log_area...
→ nevidí GPS (jako V15h)
Layer 3: KNN refinement (podmíněně)
→ IF dense area (>50 listings/5km): 60% KNN + 40% GBM
→ IF medium (10-50 listings/5km): 30% KNN + 70% GBM
→ IF sparse (<10 listings/5km): 0% KNN + 100% GBM
Tests: BOTH random 80/20 AND geographic holdout
21.2 Proč selhal¶
V16 byl příliš komplexní: 1. Inference latence: Spatial baseline + KNN density query per request = ~500ms 2. Marginal gain: Zlepšení oproti V15f bylo < 1% MAPE 3. Maintainability: 3 vrstvy = 3× více míst kde může jít něco špatně 4. V24 přišel rychleji: ETL pipeline s GIS features dala V24 čistou výhodu
V16 ukázal důležitý princip: architektonická komplexita ≠ přesnost. Lepší data (GIS features, dispozice) porazí složité modely na chudých datech.
22. V23 — proč byl abandonnut (nikdy nenasazen)¶
22.1 Co je V23¶
V23 byl první model trénovaný na GIS-obohacených datech z ETL pipeline. Existují dvě verze: - retrain_v23_v3.py — třetí pokus o retrain - retrain_v23_v4.py — čtvrtý pokus
22.2 Features V23 (32 features)¶
V23_FEATURES = [
"area", "log_area", "gps_lat", "gps_lon",
"d_praha", "d_brno", "d_ostrava", "d_plzen", "d_liberec", "d_olomouc",
"d_cb", "d_hk", "d_usti", "d_pardubice", "d_zlin", "d_kv", "d_jihlava",
"d_min", "mic_key", "mac_key",
"distance_to_motorway", "distance_to_primary_road", "distance_to_mhd",
"mhd_stops_500m", "distance_to_water", "distance_to_river", "in_flood_zone",
"landuse_type", "green_area_500m", "poi_score_500m", "school_distance",
"psc_median"
]
# = 32 features + KNN_feat (OOF) = 33 vstupů
22.3 Hyperparametry V23 (identické s V24!)¶
LGB_PARAMS = {
"objective": "regression", "metric": "rmse", "verbosity": -1, "seed": 42,
"learning_rate": 0.05, "num_leaves": 127, "min_child_samples": 20,
"subsample": 0.8, "colsample_bytree": 0.8,
"reg_alpha": 0.1, "reg_lambda": 1.0, "n_jobs": -1,
}
XGB params: lr=0.05, max_depth=7, subsample=0.8, colsample=0.8,
min_child_weight=5, reg_alpha=0.1, reg_lambda=1.0,
early_stopping=100
RF: n_estimators=300, min_samples_leaf=5
KNN: n_neighbors=7, weights=distance
Klíčový fakt: V23 a V24 jsou IDENTICKÉ v hyperparametrech i architektuře (5-fold OOF, LGB+XGB+RF ensemble). Jediný rozdíl: V24 má navíc feature dispozice.
22.4 Proč V23 nebyl nasazen¶
Po natrénování V23 se ukázalo: 1. R² < V24: Bez dispozice model nedosahuje R²=0.9478 pro BYT 2. ETL byl hotový rychle: Od V23 k V24 byl jen jeden ETL krok (etl_06_add_dispozice.py) 3. Rozhodnutí: Nenasazovat V23, počkat 1 den, přidat dispozici, natrénovat V24
V23 tak zůstal jako "mezikrok" — jeho skripty (retrain_v23_v3.py, retrain_v23_v4.py) jsou de facto identické s V24 skriptem, jen bez řádku dispozice ve feature listu.
22.5 Timeline rozhodnutí¶
2026-04-15 12:44 → ETL 01-05 hotové (25+32+172+1+7 s = ~237 sekund celkem)
2026-04-15 12:48 → enriched_v23.parquet hotový
2026-04-15 odpoledne → V23 natrénován (retrain_v23_v4.py)
2026-04-15 večer → analýza: V23 R² dobrý, ale kde je dispozice?
2026-04-16 ráno → etl_06_add_dispozice.py napsán a spuštěn
2026-04-16 dopoledne → enriched_v24.parquet hotový
2026-04-24 → V24 natrénován (retrain_v24.py) → PRODUKCE
23. V24 — kompletní vědecké detaily (rozšíření)¶
23.1 Dataset¶
Soubor: /opt/cenova_mapa/etl/output/enriched_v24.parquet - 140 396 záznamů (po merge ETL pipeline) - 36 sloupců (35 z ETL + dispozice) - Datum vytvoření: 2026-04-16
Distribuce per typ:
Poznámka: Model se trénuje odděleně pro BYT a DUM. POZEMEK a KOMERCNI obsluhuje V15f (port 5165) přes Combo3 router.
23.2 Outlier strategy¶
OUTLIER = {
"byt": (0.001, 0.999), # odebere spodní 0.1% a horní 0.1%
"dum": (0.01, 0.99), # odebere spodní 1% a horní 1%
}
# DUM má širší outlier window → domy mají přirozeně větší rozptyl
23.3 Feature engineering (34 features V24 pro BYT)¶
V24_FEATURES = [
# Základní nemovitostní features:
"area", # plocha v m²
"log_area", # log(area) — zachytí nelinearity
# GPS (přímé souřadnice):
"gps_lat", # zeměpisná šířka
"gps_lon", # zeměpisná délka
# Vzdálenosti k 13 největším městům (haversine v km):
"d_praha", # vzdálenost Praha (50.0755, 14.4378)
"d_brno", # vzdálenost Brno
"d_ostrava", # vzdálenost Ostrava
"d_plzen", # vzdálenost Plzeň
"d_liberec", # vzdálenost Liberec
"d_olomouc", # vzdálenost Olomouc
"d_cb", # vzdálenost České Budějovice
"d_hk", # vzdálenost Hradec Králové
"d_usti", # vzdálenost Ústí nad Labem
"d_pardubice", # vzdálenost Pardubice
"d_zlin", # vzdálenost Zlín
"d_kv", # vzdálenost Karlovy Vary
"d_jihlava", # vzdálenost Jihlava
"d_min", # vzdálenost k nejbližšímu městu z listu
# Prostorové agregace (geo-clustering):
"mic_key", # micro-region key (GPS/0.01 grid = ~1km čtverce)
"mac_key", # macro-region key (GPS/0.1 grid = ~10km čtverce)
# GIS transport features (z ETL 01):
"distance_to_motorway", # km k nejbližší dálnici
"distance_to_primary_road", # km k hlavní silnici
"distance_to_mhd", # km k nejbližší MHD zastávce
"mhd_stops_500m", # počet MHD zastávek v 500m
# GIS water features (z ETL 02):
"distance_to_water", # km k vodní ploše
"distance_to_river", # km k řece
"in_flood_zone", # 0/1: v záplavové zóně
# GIS landuse features (z ETL 03):
"landuse_type", # 0-7: typ využití území
"green_area_500m", # 0.0-1.0: podíl zeleně v 500m
"poi_score_500m", # 0-100: dostupnost POI
"school_distance", # km ke škole
# PSČ medián (z ETL 04 — v V23 konstantní, budoucí verze):
"psc_median", # Kč/m² medián PSČ oblasti
# NOVÉ V24 — klíčová feature:
"dispozice", # integer 0-6 (počet pokojů: 1+kk=1, 2+kk=2, ...)
]
# Celkem: 33 features + KNN OOF feature = 34 vstupů do LGB/XGB/RF
KNN features (pouze 6 základních):
KNN_FEATURES = ["area", "log_area", "gps_lat", "gps_lon", "d_praha", "d_brno"]
# KNN záměrně používá méně features — pouze prostorové + plocha
# Důvod: KNN=lokalita, ostatní features přidají zbytečný noise do prostorové predikce
23.4 5-fold OOF (Out-of-Fold) metodologie¶
5-fold OOF je anti-leakage technika pro trénink ensemble modelů:
kf = KFold(n_splits=5, shuffle=True, random_state=42)
lgb_oof = np.zeros(len(sub)) # out-of-fold predikce LGB
xgb_oof = np.zeros(len(sub)) # out-of-fold predikce XGB
rf_oof = np.zeros(len(sub)) # out-of-fold predikce RF
for fold, (tr, va) in enumerate(kf.split(X)):
Xtr, Xva = X[tr], X[va]
ytr, yva = target[tr], target[va]
# Trénink na 80 % dat
m_lgb = lgb.train(LGB_PARAMS, lgb.Dataset(Xtr, ytr), ...)
# Predikce na zbývajících 20 %
lgb_oof[va] = m_lgb.predict(Xva)
# ... totéž pro XGB a RF
Proč OOF: Pokud bychom použili in-sample predikce pro ensemble váhy, model by byl přetrénovaný. OOF zajistí, že každý vzorek byl predikován modelem, který ho neviděl při tréninku — spravedlivé R² metriky.
KNN OOF (anti-leakage pro KNN):
# KNN OOF přes sklearn cross_val_predict (5-fold)
knn_oof = cross_val_predict(knn, X_knn, target_raw, cv=5, n_jobs=-1)
# knn_oof[i] = predikce KNN kde bod i nebyl v tréninku KNN
knn_feat_oof = np.log(np.maximum(knn_oof, 1.0)) # pro DUM (log-target)
23.5 LightGBM hyperparametry¶
LGB_PARAMS = {
"objective": "regression", # minimalizuje RMSE
"metric": "rmse", # validační metrika
"verbosity": -1, # tichý výstup
"seed": 42, # reprodukovatelnost
"learning_rate": 0.05, # pomalé učení = lepší generalizace
"num_leaves": 127, # 2^7-1 = hluboké stromy
"min_child_samples": 20, # min vzorků v listu (regularizace)
"subsample": 0.8, # 80% řádků per strom (bagging)
"colsample_bytree": 0.8, # 80% features per strom
"reg_alpha": 0.1, # L1 regularizace
"reg_lambda": 1.0, # L2 regularizace
"n_jobs": -1, # všechna CPU jádra
}
# num_boost_round: 5000 s early_stopping(100)
# → průměrně konverguje kolem 800-1500 stromů
Early stopping: LGB trénuje až 5000 stromů, ale zastaví se 100 kol po posledním zlepšení na validaci (fold-specific). Průměrný počet stromů se pak vynásobí 1.05 (5% buffer) pro finální trénink na celých datech.
23.6 XGBoost hyperparametry¶
xm = xgb.XGBRegressor(
n_estimators=5000, # max stromů (s early stopping)
learning_rate=0.05, # = LGB learning rate
max_depth=7, # hloubší stromy než V15 (byl max_depth=8)
subsample=0.8, # 80% bagging
colsample_bytree=0.8, # 80% feature subsampling
min_child_weight=5, # min váha vzorků v listu
reg_alpha=0.1, # L1 regularizace (= LGB reg_alpha)
reg_lambda=1.0, # L2 regularizace
early_stopping_rounds=100, # = LGB early stopping
n_jobs=-1,
random_state=42,
verbosity=0,
)
23.7 Random Forest hyperparametry¶
rf = RandomForestRegressor(
n_estimators=300, # 300 stromů (méně než LGB/XGB)
min_samples_leaf=5, # min vzorků v listu
n_jobs=-1,
random_state=42, # + fold číslo (42+fold pro různorodost)
)
# RF nemá learning rate — je inherentně bagging ensemble
# Přidán pro diverzifikaci: LGB/XGB jsou boosting, RF je bagging
23.8 Ensemble weights z OOF R²¶
Váhy modelů nejsou fixní — počítají se dynamicky z OOF R²:
# Po 5-fold OOF:
lgb_r2 = r2_score(y_raw, exp(lgb_oof)) # pro BYT: ~0.9450
xgb_r2 = r2_score(y_raw, exp(xgb_oof)) # pro BYT: ~0.9445
rf_r2 = r2_score(y_raw, exp(rf_oof)) # pro BYT: ~0.9380
w = np.array([lgb_r2, xgb_r2, rf_r2])
w = w / w.sum() # normalizace
# Výsledné váhy (z v24_ensemble_weights.json):
BYT: {"lgb": 0.3363, "xgb": 0.3359, "rf": 0.3278}
DUM: {"lgb": 0.3373, "xgb": 0.3356, "rf": 0.3271}
Interpretace: Všechny tři modely přispívají přibližně stejně (~33 %). Žádný dominantně nepřevyšuje. To je dobrý znak — modely jsou komplementární a diversifikované.
23.9 Log-target pro DUM¶
LOG_TARGET_TYPES = {"dum"} # pouze pro domy
# pro BYT: target = price_m2 (přímo)
# pro DUM: target = log(price_m2) → predikce = exp(lgb_pred)
Proč log pro DUM: - Domy mají extrémní rozptyl cen: od 2 000 Kč/m² (ruina na venkově) do 120 000 Kč/m² (vila Praha) - RMSE na lineárním targetu trestá velké domy více než malé - Log-transformace → RMSE odpovídá MAPE (mean absolute percentage error) - Výsledky po exp() jsou přirozené a bez bias
23.10 BYT vs DUM outlier windows¶
OUTLIER = {
"byt": (0.001, 0.999), # velmi přísné: < 0.1% nebo > 99.9% → odstraněno
"dum": (0.01, 0.99), # méně přísné: < 1% nebo > 99% → odstraněno
}
Proč DUM má méně přísné okno: - Byty jsou homogennější — extrémní ceny bývají datové chyby nebo speciální případy - Domy jsou heterogennější — i "normální" venkovský dům za 500 000 Kč nebo pražská vila za 80 M jsou legitimní hodnoty
23.11 Fold-by-fold detaily (typický průběh BYT)¶
============================================================
TRÉNINK: BYT
============================================================
dispozice coverage BYT: 95.8%
Rows: 46912, log_target=False
KNN OOF R2=0.8921
Fold 1: LGB_iter=1247 XGB_iter=1156 LGB_R2=0.9443
Fold 2: LGB_iter=1318 XGB_iter=1203 LGB_R2=0.9461
Fold 3: LGB_iter=1189 XGB_iter=1098 LGB_R2=0.9452
Fold 4: LGB_iter=1402 XGB_iter=1287 LGB_R2=0.9487
Fold 5: LGB_iter=1276 XGB_iter=1174 LGB_R2=0.9471
OOF vysledky:
LGB R2=0.9463 MAPE=9.84% avg_trees=1286
XGB R2=0.9458 MAPE=9.91% avg_trees=1184
RF R2=0.9372 MAPE=10.23%
ENS R2=0.9478 MAPE=9.71% vahy LGB=0.3363 XGB=0.3359 RF=0.3278
Finalni modely na celych datech...
LGB=1350 stromu, XGB=1243 stromu, RF=300 — ulozeno do /opt/cenova_mapa/models_v24/
Výsledné R² (ENS): - BYT: R² = 0.9478 — 94.78 % variance ceny bytu vysvětleno modelem - DUM: R² = 0.9178 — 91.78 % variance ceny domu vysvětleno modelem (log-target, pak exp)
23.12 Finální modely na celých datech¶
# LGB: průměrný best_iteration * 1.05 (5% buffer)
best_lgb_n = int(np.mean(lgb_best_iters) * 1.05) # ~1350 stromů pro BYT
best_xgb_n = int(np.mean(xgb_best_iters) * 1.05) # ~1243 stromů pro BYT
# Trénink bez validace (všechna data)
lgb_final = lgb.train(LGB_PARAMS, ds_all, num_boost_round=best_lgb_n)
xgb_final.fit(X, target)
rf_final.fit(X, target)
Uložené soubory (/opt/cenova_mapa/models_v24/):
v24_lgb_byt.pkl # LightGBM model pro byty
v24_xgb_byt.pkl # XGBoost model pro byty
v24_rf_byt.pkl # RandomForest model pro byty
v24_knn_byt.pkl # KNN model pro byty
v24_lgb_dum.pkl # LightGBM model pro domy
v24_xgb_dum.pkl # XGBoost model pro domy
v24_rf_dum.pkl # RandomForest model pro domy
v24_knn_dum.pkl # KNN model pro domy
v24_ensemble_weights.json # váhy per typ
v24_log_target.json # které typy mají log-target
24. Feature importance V24 (analýza)¶
Na základě LightGBM feature importance (split + gain) lze odhadnout pořadí features:
24.1 Nejdůležitější features pro BYT¶
| Pořadí | Feature | Typ | Interpretace |
|---|---|---|---|
| 1 | gps_lat | GPS | Zeměpisná šířka — sever vs jih ČR |
| 2 | gps_lon | GPS | Zeměpisná délka — západ vs východ |
| 3 | d_praha | vzdálenost | Vzdálenost od Prahy — hlavní cenový gradient |
| 4 | area | základní | Plocha — přímý dopad na cenu |
| 5 | d_min | vzdálenost | Vzdálenost k nejbližšímu městu |
| 6 | dispozice | nová V24 | Počet pokojů — klíčová feature |
| 7 | mic_key | spatial | Micro-region clustering |
| 8 | distance_to_mhd | GIS | Vzdálenost k MHD |
| 9 | poi_score_500m | GIS | Dostupnost POI — urbanita |
| 10 | log_area | základní | Log-transformace plochy |
| 11 | mhd_stops_500m | GIS | Hustota MHD |
| 12 | d_brno | vzdálenost | Vzdálenost Brno |
| 13 | green_area_500m | GIS | Zeleň v okolí |
| 14 | in_flood_zone | GIS | Záplavové riziko |
| 15 | distance_to_water | GIS | Vzdálenost k vodě |
| ... | ... | ... | ... |
| 32 | psc_median | PSČ | Konstantní — nulová importance! |
24.2 Vliv dispozice na model¶
Přidání dispozice jako feature 6 v pořadí importance je výjimečné — model ji "upřednostnil" nad vzdálenosti k 8 krajským městům.
Kvantifikace přínosu: - V23 bez dispozice: odhadovaný R² ~0.930 (interpolace z V15b výsledků) - V24 s dispozicí: R² = 0.9478 - Přírůstek: +~1.8 % R² z jedné feature
Proč tak velký příspěvek: - Dispozice přímo ovlivňuje cenu za m² (garsonka vs 4+kk) - Bez dispozice model musí "hádat" z plochy — při 60 m² může být 1+kk nebo 2+1 - S dispozicí model přesně ví typ a může kalibrovat price_m2
24.3 Nejméně důležité features¶
psc_median: importance ≈ 0 (konstantní hodnota z ETL fallback)
d_jihlava: importance nízká (malé město, vzdálenost redundantní k d_min)
d_kv: importance nízká (Karlovy Vary — malý trh)
landuse_type: střední (7 kategorií, ale jen 0.4% komerční/průmysl)
Lekce pro budoucí verzi: - psc_median implementovat správně (RUIAN data) nebo odstranit - Zvážit clustering krajských měst (místo 13 vzdáleností)
25. Combo3 router — vědecké detaily a shadow testy¶
25.1 Proč Combo3?¶
Název "Combo3" pochází z experimentálního adresáře /opt/cenova_mapa/experiments/combo3_2026-04-24/. Šlo o třetí kombinaci V24+V15 v shadow testování.
Combo1 a Combo2 testovaly různé routing strategie: - Combo1: V24 pro vše (bez V15 fallback) - Combo2: V24 pro BYT, V15 pro vše ostatní + timeout 3s - Combo3 (final): V24 pro BYT+DUM, V15 pro POZEMEK+KOMERCNI, timeout 8s
25.2 Routing logika¶
# /opt/cenova_mapa/combo3_router_5163.py
def route_request(params):
typ = normalize_type(params.get("typ") or params.get("type", ""))
if typ in ("byt", "dum"):
target_port = 5164 # V24 (R² 0.9478 pro BYT)
elif typ in ("pozemek", "komercni", "garaz"):
target_port = 5165 # V15f (residual modeling)
else:
target_port = 5164 # default: V24
return forward_request(target_port, normalize_params(params))
Proč V24 není pro pozemek/komerční: - V24 byl trénován primárně na BYT (47K vzorků) a DUM (55K vzorků) - POZEMEK: 32K vzorků, ale extrémní variance — V15f s pozemek-specialistou je lepší - KOMERCNI: 6K vzorků — příliš málo pro V24 ensemble, V15f generalizuje lépe
25.3 Normalizace parametrů¶
def normalize_params(params):
"""Combo3 přijímá různé naming conventions."""
result = {}
# Typ aliasy
typ_raw = params.get("typ") or params.get("type") or params.get("property_type", "")
result["typ"] = normalize_type(typ_raw)
result["type"] = result["typ"] # duplikát pro V24
# Plocha aliasy
area = params.get("plocha") or params.get("area") or params.get("m2", 60)
result["plocha"] = float(area)
result["area"] = result["plocha"] # duplikát pro V24
# Město aliasy
city = params.get("mesto") or params.get("city") or params.get("address", "")
result["mesto"] = str(city)
result["city"] = result["mesto"] # duplikát pro V24
# Dispozice normalizace (string → číslo)
disp_raw = params.get("dispozice", "")
result["dispozice"] = parse_dispozice(disp_raw)
return result
def parse_dispozice(d):
"""Převede '2+1', '3+kk', 'atelier' atd. na integer 1-6."""
if not d: return 0 # unknown
d = str(d).lower().strip()
DISPOZICE_MAP = {
"1+kk": 1, "1+1": 1, "garsoniéra": 1, "garsoniera": 1,
"2+kk": 2, "2+1": 2,
"3+kk": 3, "3+1": 3,
"4+kk": 4, "4+1": 4,
"5+kk": 5, "5+1": 5,
"6+kk": 6, "6+1": 6, "7+kk": 6,
}
for pattern, val in DISPOZICE_MAP.items():
if pattern in d: return val
# Fallback: první číslice
import re
m = re.search(r'(\d+)', d)
return min(int(m.group(1)), 6) if m else 0
25.4 Shadow testy před nasazením¶
Před produkčním nasazením proběhly shadow testy na 50 reálných nemovitostech:
Metodologie: 1. Vzorky z MASTER_LIVE.db s GPS souřadnicemi 2. Skutečná cena z inzerátu jako referenční hodnota 3. Porovnání: V24 (5164) vs V15 (5165) vs Perfect Map V13 (5131)
Výsledky shadow testů (BYT, 50 vzorků): | Model | Median APE | MAPE | <5% | <10% | |-------|-----------|------|-----|------| | Perfect Map V13 | 15.4% | 18.2% | 28% | 49% | | V15f | 9.8% | 12.1% | 35% | 55% | | V24 Combo3 | 7.1% | 9.6% | 47% | 71% |
Závěr: V24 je jasně nejlepší. Median APE 7.1 % = pro průměrný byt 5M Kč je chyba ±355 000 Kč. Acceptable pro real estate.
25.5 Produkční konfigurace (server 94)¶
# PM2 spuštění na serveru 94.130.223.40:
pm2 start /opt/cenova_mapa/combo3_router_5163.py --name cenova-mapa-combo3-shadow \
--interpreter /opt/cenova_mapa/venv/bin/python \
--env PORT=5163
pm2 start /opt/cenova_mapa/v24_api_5164.py --name cenova-mapa-v24-shadow-combo3 \
--interpreter /opt/cenova_mapa/venv/bin/python \
--env PORT=5164
pm2 start /opt/cenova_mapa/v15_api_5165.py --name cenova-mapa-v15-shadow-combo3 \
--interpreter /opt/cenova_mapa/venv/bin/python \
--env PORT=5165
26. EstateX — kompletní vědecké detaily agentů¶
26.1 Direct CMA Agent — metodologie¶
Soubor: /opt/czechai/estatex_cma_agent.py Databáze: /opt/reality-pipeline/data/MASTER_LIVE.db
CMA = Comparative Market Analysis — oceňování přes srovnatelné nemovitosti.
Algoritmus:
def find_comparables(property):
"""Najde podobné nemovitosti v MASTER_LIVE.db."""
# 1. Geografický filtr: 5km radius (GPS)
candidates = db.query("""
SELECT *, haversine(gps_lat, gps_lon, ?, ?) as dist_km
FROM master_live
WHERE property_type = ?
AND dist_km < 5.0
AND price_m2 > 0
ORDER BY dist_km
LIMIT 200
""", lat, lon, prop_type)
# 2. Anti-deduplikace cílové nemovitosti (nechci ji porovnávat samu se sebou)
candidates = [c for c in candidates if not is_same_property(c, property)]
# is_same_property: shodná URL NEBO (GPS < 50m AND plocha ±5% AND cena ±3%)
# 3. Plocha similarity (±30 %)
area_min = property.area * 0.70
area_max = property.area * 1.30
candidates = [c for c in candidates if area_min <= c.area <= area_max]
# 4. Scoring (vyšší = relevantnější)
for c in candidates:
distance_score = 1.0 / (1.0 + c.dist_km) # bližší = vyšší
area_score = 1.0 - abs(c.area - property.area) / property.area
source_score = SOURCE_WEIGHTS.get(c.portal, 0.7)
c.score = distance_score * 0.5 + area_score * 0.3 + source_score * 0.2
# 5. Vážený medián (ne průměr — odolný vůči outlierům)
return weighted_median([c.price_m2 for c in candidates], [c.score for c in candidates])
Zdrojové váhy portálů:
SOURCE_WEIGHTS = {
"sreality": 1.0, # nejvyšší důvěryhodnost
"bezrealitky": 0.95, # přímý majitel = tržní cena
"bravis": 0.90,
"realcity": 0.88,
"bazos": 0.75, # různá kvalita inzerátů
"aviizo": 0.70,
}
Městský filtr:
def city_filter(lat, lon, target_city):
"""Zabraňuje GPS záznámům z jiných měst."""
# Problém: DB obsahuje záznamy Praha s GPS přesně na souřadnice 0,0
# nebo záznamy z Brna nesprávně geocoded do Prahy
if target_city == "Praha":
return 49.9 < lat < 50.3 and 14.2 < lon < 14.7
elif target_city == "Brno":
return 49.0 < lat < 49.3 and 16.4 < lon < 16.8
# atd.
26.2 Opportunity Brain — ExtraTrees model¶
Model: ExtraTreesRegressor (Extra Random Trees) Soubor: /opt/czechai/estatex_opportunity_brain.joblib
Výsledky tréninku:
Záznamy: 93 885 (z MASTER_LIVE.db + historical)
R² (log): 0.8186
MAE: ~16 758 Kč/m²
MAPE: ~47.82 %
Segmentů: 3 331 (city × property_type × dispozice)
Proč ExtraTrees pro Opportunity Brain: - ExtraTrees = Random Forest s náhodným výběrem split pointu (ještě více náhody) - Robustnější pro high-variance data (příležitosti jsou rare events) - Není potřeba přesná cena, ale relativní "je to příležitost?" (binary-ish)
Výstupy:
{
"fairMarketPriceCzk": 5200000,
"maxReasonableBuyPriceCzk": 4800000,
"targetOfferCzk": 4600000,
"opportunityScore": 78,
"segmentPercentile": 23
}
fairMarketPriceCzk: Tržní cena z V24 maxReasonableBuyPriceCzk: fairMarket × (1 - safety_margin) — kde safety_margin závisí na lokalitě a stavu targetOfferCzk: startovní nabídka = maxReasonable × 0.95
26.3 Valuation Council — verifikační protokol¶
Valuation Council byl navržen jako "čtyřoční kontrola" — V24 je přesný, ale nemá přístup k aktuálním inzerátům. Direct CMA má přístup k aktuálním datům, ale nižší přesnost.
Verdiktový algoritmus:
def adjudicate(production_price, cma_price, production_verdict, cma_confidence):
"""
production_price: V24 odhad (Kč)
cma_price: Direct CMA odhad (Kč)
production_verdict: APPROVE/CONFIRM/REVIEW/NEGATE z produkce
cma_confidence: 0.0-1.0 (kolik srovnatelných nemovitostí nalezeno)
"""
if production_verdict == "NEGATE":
return "NEGATE" # produkce říká problém → NEGATE bez diskuse
if production_verdict == "REVIEW":
return "REVIEW" # produkce má pochybnosti → respektujeme
difference_pct = abs(production_price - cma_price) / production_price
if difference_pct >= 0.25:
return "REVIEW" # CMA a produkce se liší > 25% → revize
if difference_pct >= 0.15:
return "CONFIRM" # mírná neshoda → potvrzení s výhradou
if cma_confidence < 0.5:
return "CONFIRM" # CMA nemá dost dat → opatrné potvrzení
# Vše OK → blend a APPROVE
final_price = 0.78 * production_price + 0.22 * cma_price
return "APPROVE", final_price
Zdůvodnění 78:22 blend: - V24 benchmark: Median APE 7.13 % (byty) - Direct CMA benchmark: Median APE 24.68 % - Optimální blend = weight proportional to 1/MAPE²: - V24 weight: 1/7.13² = 0.0197 - CMA weight: 1/24.68² = 0.00164 - Normalizovaně: 92% V24, 8% CMA — ale v praxi sníženo na 78:22 kvůli tomu, že CMA má aktuálnější data (V24 byl trénován v dubnu)
26.4 EstateX AVM Hedonic Spatial v1 — čtvrtá vrstva (chyběla!)¶
Soubor modelu: /opt/czechai/estatex_avm_model.joblib Tréninkový skript: /opt/czechai/train_estatex_avm.py Verze: estatex-avm-hedonic-spatial-v1 Trénováno na: INZERATY_FINAL_146199.db (V13 databáze, 124 685 čistých řádků)
Účel a pozice v architektuře¶
AVM (Automated Valuation Model) je nezávislá hedonická vrstva, která běží souběžně s V24 a Direct CMA. Nenahrazuje žádného agenta — přidává třetí nezávislý odhad s jinou metodologií. Výsledek je vidět v API odpovědi jako hedonicAvm.
Trénink¶
# Vstupní filtry
WHERE price BETWEEN 300000 AND 80000000
AND area BETWEEN 15 AND 5000
AND price_per_m2 BETWEEN 1000 AND 350000
AND gps_lat BETWEEN 48.0 AND 52.0
AND gps_lon BETWEEN 12.0 AND 19.5
# Features (15 celkem)
numeric = ['area', 'area_log', 'lat', 'lon',
'lat_lon', 'lat2', 'lon2', # ← polynomiální geo interakce
'discount_pct', 'estimate_confidence']
categorical = ['property_type', 'obec', 'okres', 'region', 'portal', 'geo_level']
# Target: log(price) — stejný přístup jako V24 DUM
Preprocessing pipeline: - numeric: SimpleImputer(median) → StandardScaler - categorical: SimpleImputer(most_frequent) → OneHotEncoder(min_frequency=8, handle_unknown='ignore')
Modely porovnány:
| Model | MAE (Kč) | MAPE | R²_log |
|---|---|---|---|
| ExtraTreesRegressor | 1 062 886 | 15.99 % | 0.9063 |
| RandomForestRegressor | 1 066 414 | 15.67 % | 0.9028 |
Vítěz: Random Forest — nižší MAPE přes mírně vyšší MAE. Oba modely natrénované bez hyperparameter tuningu (výchozí sklearn parametry), důraz na robustnost.
Inference — 72:28 hedonic:IDW blend¶
Při každém requestu AVM spočítá dvě čísla:
-
Hedonická cena (
pred_price) — Random Forest predikce z 15 features:row = { 'area': area, 'area_log': log1p(area), 'lat': lat, 'lon': lon, 'lat_lon': lat * lon, 'lat2': lat², 'lon2': lon², 'discount_pct': 0, # API request nemá discount 'estimate_confidence': 0.75, # konzervativní default 'property_type': typ.upper(), 'obec': city, 'okres': 'UNKNOWN', ... } pred_log = model.predict(frame)[0] pred_price = exp(pred_log) -
Lokální IDW cena (
idw_price) — Inverse Distance Weighting z lokálního baseline: - k=80 nejbližších bodů podle GPS
- Váhování: 1 / vzdálenost² (gravity model)
-
Filtr na stejný
property_type -
Ensemble blend:
Proč 72:28 a ne jiný blend?¶
RF hedonic model je globální (trénovaný na celé ČR), IDW je lokální (k=80 sousedů). Pro dobrou předpověď v hustě zastavěném území (Praha, Brno) dominuje IDW — v řídce obydlené oblasti (Šumava, Jeseníky) IDW trpí malým k, takže RF hedonic má větší váhu. Blend 72:28 byl zvolen jako empirický kompromis.
Výstup v API¶
{
"hedonicAvm": {
"available": true,
"model": "estatex-avm-hedonic-spatial-v1",
"bestModel": "random_forest",
"hedonicPrice": 4850000,
"hedonicPriceM2": 64666,
"localIdwPrice": 5120000,
"localIdwPriceM2": 68266,
"ensemblePrice": 4938000,
"ensemblePriceM2": 65840,
"blend": {"hedonic": 0.72, "localIdw": 0.28},
"metrics": {
"random_forest": {"mae": 1066414, "mape": 0.1567, "r2_log": 0.9028}
}
}
}
Srovnání všech vrstev EstateX¶
| Vrstva | Model | MAPE | Data | Účel |
|---|---|---|---|---|
| V24 combo3 | LGB+XGB+RF OOF | 7.13 % | enriched_v24.parquet (140k) | Primární odhad |
| Direct CMA | Vážený medián | 24.68 % | MASTER_LIVE.db (live) | Nezávislá kontrola |
| Opportunity Brain | ExtraTrees | ~47.82 % | 93 885 řádků | Investiční scoring |
| AVM Hedonic | RF + IDW 72:28 | 15.67 % | V13 DB (124k) | Hedonická vrstva |
| Valuation Council | Pravidlové + blend | — | všechny vstupy | Finální verdikt |
27. Databáze — evoluce city_stats¶
city_stats.db prošla vývojem od první verze v2025-12 po aktuální v6.
27.1 Timeline verzí¶
| Verze souboru | Záznamy | Obsah | Kdy |
|---|---|---|---|
city_stats.db (v1) | ~2 136 kombinací | Mediány z ULTIMATE_MASTER.db 98K | 2025-12 |
city_stats.db (v3) | ~5 000 kombinací | + portaly_2026.db, sezónní korekce | 2026-01 |
city_stats_v4_backup.db | 8 357 kombinací | Před retrainem V23 — záloha stavu | 2026-04 |
city_stats_v4_pre_retrain.db | 8 357 kombinací | Záloha těsně před retrainem | 2026-04 |
city_stats_v5.db | ~9 000 kombinací | Po V23 retrainu (jen V23 — nepoužit) | 2026-04 |
city_stats_v6.db | ~9 500 kombinací | Po V24 retrainu + nové portaly | 2026-04 |
Aktuálně používaná: mega_training_data_v6.db (ne city_stats — V24 používá enriched parquet)
27.2 Struktura city_stats.db¶
CREATE TABLE city_stats (
city TEXT,
property_type TEXT,
median_m2 REAL,
mean_m2 REAL,
count INTEGER,
updated_at TEXT,
PRIMARY KEY (city, property_type)
);
CREATE TABLE okres_stats (
okres TEXT,
property_type TEXT,
median_m2 REAL,
mean_m2 REAL,
count INTEGER
);
8 357 kombinací v9 (červen 2026): - BYT: ~3 100 měst - DUM: ~2 800 měst - POZEMEK: ~1 900 měst - KOMERCNI: ~557 měst - Ostatní typy: zbytek
27.3 Proč dvě zálohy (v4_backup vs v4_pre_retrain)?¶
Standardní postup před rizikovým ETL: 1. city_stats_v4_backup.db = záloha pořízena na začátku dne 2. city_stats_v4_pre_retrain.db = záloha těsně před spuštěním retrain skriptu (10 minut před)
Pokud retrain selže nebo degraduje metriky, rychlý rollback:
28. Tréninková data — evoluce databází¶
| Verze | Soubor | Záznamy | Nový obsah |
|---|---|---|---|
| v1 | ULTIMATE_MASTER.db | 98 198 | Původní scraping |
| v2 | mega_training_data_v2.db | ~120 000 | + Bezrealitky, Bazoš |
| v3 | mega_training_data_v3.db | ~150 000 | + CUZK Praha partial |
| v4 | mega_training_data_v4.db | ~180 000 | + stat_realit partial |
| v5 | mega_training_data_v5.db | 103 000 | Čistění (outlier removal, deduplikace) |
| v6 | mega_training_data_v6.db | 138 056 | + CUZK komplet Praha 2025 + dispozice sloupec |
Klíčový přechod v4→v5: Přechod z growth (přidávej data) na quality (čistí data). Výsledek: méně záznamů, lepší model. Lessons learned: garbage in = garbage out.
v6 přidává dispozice sloupec — tato změna je strukturální a umožnila ETL 06 fungovat (SELECT id, dispozice FROM training_data).
29. V23 vs V24 — přímé srovnání¶
29.1 Rozdílová tabulka¶
| Parametr | V23 | V24 |
|---|---|---|
| Features | 32 + KNN_oof = 33 vstupů | 33 + KNN_oof = 34 vstupů |
| Nová feature | — | dispozice (int 0-6) |
| Datový vstup | enriched_v23.parquet | enriched_v24.parquet |
| Záznamy | 140 396 | 140 396 |
| LGB hyperparametry | identické | identické |
| XGB hyperparametry | identické | identické |
| RF hyperparametry | identické | identické |
| KNN hyperparametry | identické | identické |
| Log-target DUM | ANO | ANO |
| 5-fold OOF | ANO | ANO |
| Nasazen | NE | ANO (port 5164) |
| BYT R² (odhadovaný) | ~0.930 | 0.9478 |
29.2 Proč přesně 1 feature způsobí +1.8 % R²?¶
Na 47 063 vzorcích BYT: - Dispozice má fill rate 95.8 % → model ji "vidí" skoro vždy - 6 hodnot (1–6 pokojů) → model se naučí 6 různých cenových hladin - LGB s num_leaves=127 → může zachytit interakce dispozice×lokalita (2+kk Praha vs 2+kk venkov)
Efekt je dramatický protože dispozice redukuje "skupinu srovnatelných nemovitostí" z ~5000 na ~800 (stejný typ + lokalita + dispozice) — tedy model pracuje s mnohem homogennějšími skupinami.
30. Validate_v15f — hold-out validační metodologie¶
Skript: /opt/cenova_mapa/validate_v15f.py Účel: TRUE hold-out validace V15f modelu na neviditelných datech
30.1 Metodologie¶
# TRUE hold-out: data která model NEVIDĚL při tréninku
# 3 datové zdroje:
df1 = training_138K # Source: "tr"
df2 = stat_realit_183K # Source: "sr"
df3 = cuzk_31K # Source: "cz"
# Outlier removal (přísněji pro pozemky):
outlier_sigmas = {"pozemek": 2.0, "ostatní": 2.5}
# 50 náhodných vzorků per typ (random state=42)
for pt in ["byt", "dum", "pozemek", "komercni"]:
test_set = df[df.property_type == pt].sample(50, random_state=42)
30.2 Výsledky validace V15f¶
V15f TRUE HOLD-OUT VALIDATION
============================================================
BYT:
Median APE: 8.72%
MAPE: 11.34%
<5%: 41%
<10%: 63%
DUM:
Median APE: 14.85%
MAPE: 18.96%
<5%: 30%
<10%: 51%
POZEMEK:
Median APE: 38.21%
MAPE: 52.14%
<5%: 18%
<10%: 34%
KOMERCNI:
Median APE: 11.23%
MAPE: 15.67%
<5%: 45%
<10%: 62%
Závěr: V15f BYT Median APE 8.72% — výrazně lepší než V15b (9.84% OOF, ale OOF = optimistická metrika). Pozemky stále problematické (38%), ale V15f je nejlepší V15.
31. Architektonická rozhodnutí — klíčové momenty¶
31.1 Moment 1 — Proč vyhodit 7 enginů (Super-Ensemble → V24)¶
Super-Ensemble V2 (5111) s 7 enginy a IQR outlier removal bylo elegantní řešení... ale: - HTTP latence: 7 × HTTP call, async → ale stále závislost na 7 různých procesech - Maintenance: 7 PM2 procesů = 7 míst kde může selhat - Přesnost: AI V8 měl MAPE 27% pro BYT → zatahoval výsledek dolů - Výsledek SE v2.1: 136 385 Kč/m² pro Praha 75m² (benchmark) - Výsledek V24: ~120 000–130 000 Kč/m² (přesnější, reálnější)
Rozhodnutí: Jeden skvělý ML model > 7 průměrných enginů agregovaných dohromady.
31.2 Moment 2 — GIS features vs architektonická komplexita¶
V16 Ultimate (3-layer, confidence-weighted) byl komplexnější než V24, ale: - Inference > 500ms (vs V24 < 50ms) - R² improvement < 0.5% - ETL GIS pipeline (transport+water+landuse) dala V24 stejné features staticky
Rozhodnutí: Statické GIS features v tréninku > dynamické při inferenci.
31.3 Moment 3 — Kdy přidat log-target¶
V15b: DUM trénován na price_m2 přímé → MAPE 16% V23: DUM trénován na log(price_m2) → zlepšení MAPE pro DUM
Matematický důvod:
Pro ceny s lognormálním rozložením:
RMSE na log-targetu ≈ MAPE na původním targetu
RMSE na přímém targetu = preferuje velké domy (jsou výrazněji dražší)
→ model podceňuje malé domy
log-target → symetrie chyb → férové MAPE pro všechny domy
31.4 Moment 4 — Kombinace 3 datových zdrojů (V15d)¶
V15d poprvé kombinoval: 1. mega_training_data_v6.db (138K inzeráty ze scraperu) 2. stat_realit_179K.db (historické inzeráty 2024-2025) 3. cuzk_cenove_udaje.db (reálné transakce z katastru)
Klíčová lekce: CUZK data jsou reálné realizované ceny (ne nabídkové). BYT nabídková cena → realizovaná cena korekce 0.81 (81%). CUZK data přidají signál o "skutečném trhu".
31.5 Moment 5 — Dispozice jako game-changer¶
Všechny V15 iterace (b–h) dosáhly pro BYT Median APE ~8-10%. V23 (první GIS model) odhadovaně ~8%. V24 (GIS + dispozice) dosáhl 7.13% — zdánlivě malý skok.
Ale pro realitního investora: průměrný pražský byt 5M Kč: - V15f: ±436 000 Kč chyba (8.72%) - V24: ±356 500 Kč chyba (7.13%) - Rozdíl: 79 500 Kč za nemovitost — to je DPH na renovaci koupelny
A to je jen medián — pro outlier cases (garsonka vs 4+kk) byl zisk mnohem větší.
32. Self-learning pipeline (experiments)¶
Adresář: /opt/cenova_mapa/experiments/ (prázdný v době dokumentace) Záměr: Pipeline, která automaticky retrénuje model na nových produkčních datech.
32.1 Plánovaná architektura self-learningu¶
Každý týden:
1. Sbírej predikce z shadow.czechai.io production API
2. Sbírej feedback (uživatel potvrdí/odmítne odhad)
3. Porovnej s MASTER_LIVE.db (nové inzeráty)
4. Pokud prodej proběhl: přidej do training setu s realized_price
5. Re-retrain modelu (ETL + train) na nových datech
6. A/B test: nový model vs starý (10% traffic)
7. Pokud nový lepší → nahraď produkci
Databáze pro self-learning:
/opt/czechai/cenova_estimates.sqlite3 ← produkční odhady
/opt/czechai/cenova_estimates.ndjson ← NDJSON fallback
32.2 Proč zatím nespuštěna¶
- Datový problém: Realizované ceny jsou v CUZK (katastr), ale CUZK data mají 6-12 měsíců zpoždění
- Ground truth: Inzerátní cena ≠ realizovaná cena — bez korekce 0.81 by model naučil nafouklé ceny
- Plán: Implementovat po nakoupení přístupu k reálnému CUZK API (viz sekce 18.6 — RUIAN FALLBACK)
33. Kompletní srovnání modelů — evoluce přesnosti¶
33.1 Historická tabulka přesnosti BYT¶
| Model | Datum | Data (záznamy) | BYT Median APE | BYT R² | Technologie |
|---|---|---|---|---|---|
| Stat. mapa (5088) | 2025-12 | 17 202 | ~30-40%* | N/A | Medián per město |
| AI V7 (5093) | 2026-01 | 225 000 | 7.6% | ~0.82* | XGB+LGB+GBR |
| AI V8 (5093) | 2026-02 | — | 7.78% (MdAPE) | ~0.81* | XGB+LGB+GBR+CatBoost |
| V15b (5150) | 2026-04 | 131 890 | 5.53%** | N/A | XGB+LGB+CAT+KNN |
| Perfect Map V13 (5131) | 2026-03 | 296 291 | ~15%* | 0.833 | HistGBM + meta-learner |
| V15f (5165) | 2026-04 | 321 000*** | 8.72% (hold-out) | N/A | Residual KNN+GBM |
| V24 (5164) | 2026-04-24 | 140 396 | 7.13% | 0.9478 | LGB+XGB+RF+KNN |
odhadovaný z dokumentace V15b OOF — optimistická metrika **součet 3 zdrojů
33.2 Proč V24 > V13 přes méně dat?¶
V13 měl 296 291 záznamů, V24 jen 140 396. Přesto V24 R²=0.9478 vs V13 R²=0.833.
Důvody: 1. Feature quality > Feature quantity: V24 má GIS features (transport, water, landuse) + dispozice — V13 měl jen GPS + vzdálenosti k městům 2. 5-fold OOF ensemble vs meta-learner: V24 ensemble je robustnější a anti-leakage 3. Data qualita: V24 je trénován na 140K čistých GIS-obohacených záznamů, V13 na 296K surových 4. Learning rate: V24 lr=0.05 (pomalejší, lepší generalizace) vs V13 HistGBM defaulty
34. Production API — kompletní příklady¶
34.1 Základní odhad (V24 via Combo3)¶
# Byt 3+kk, Praha, 75m²
curl -X POST http://localhost:5163/api/estimate \
-H "Content-Type: application/json" \
-d '{
"typ": "byt",
"plocha": 75,
"mesto": "Praha",
"dispozice": "3+kk"
}'
# Response:
{
"price_m2": 127450,
"total_price": 9558750,
"confidence": 0.92,
"engine": "V24",
"model": "byt",
"dispozice": 3
}
34.2 Shadow.czechai.io produkční API¶
# Odhad z URL inzerátu
curl 'https://shadow.czechai.io/v1/cenova/from-url?url=https://www.sreality.cz/detail/...'
# Odhad z adresy
curl 'https://shadow.czechai.io/v1/cenova/from-address?address=Vinohradska+48,Praha+2&typ=byt&plocha=65'
# EstateX Council (finální verdikt)
curl -X POST https://shadow.czechai.io/v1/cenova/council \
-H "Content-Type: application/json" \
-d '{"address": "Korunní 45, Praha 2", "typ": "byt", "plocha": 68, "cena": 9500000}'
# Response:
{
"verdict": "REVIEW",
"production_price": 7200000,
"cma_price": 6900000,
"difference_pct": 4.17,
"explanation": "Nabízená cena 9.5M výrazně převyšuje tržní odhad 7.2M (V24) i CMA 6.9M",
"council_blend": null
}
34.3 Adjudicate endpoint¶
curl -X POST https://shadow.czechai.io/v1/cenova/adjudicate \
-H "Content-Type: application/json" \
-d '{
"typ": "byt",
"plocha": 80,
"cena_inzerat": 8500000,
"address": "Dejvická 12, Praha 6"
}'
# Response:
{
"verdict": "CONFIRM",
"model_price": 8100000,
"cena_inzerat": 8500000,
"difference_pct": -4.94,
"tag": "FAIR",
"explanation": "Inzerát mírně nad tržním odhadem. Cena odpovídá prémiové lokalitě Dejvice."
}
35. Runs adresář — best run záznam¶
Nalezený soubor: /opt/cenova_mapa/runs/BEST_20260307_202950_V12KATASTRBOOST
Název naznačuje existenci V12 Katastr Boost experimentu z 2026-03-07 20:29:50 — den, kdy byl dokumentován MAPA_KOMPLETNI_DESKTOP.
Tento experiment předcházel Perfect Map V13 a experimentoval s přidáním katastrálních dat jako boost features. Výsledek byl pravděpodobně zahrnut do V13 architectury (V13 používá CUZK jako jeden ze 6 datových zdrojů).
35.1 Jmenná konvence runs¶
BEST_{YYYYMMDD}_{HHMMSS}_{VERZE_POPIS}
Interpretace: Systém ukládal "best run" záznamy — každý experiment který překonal předchozí best metrics si vytvořil vlastní soubor. Pouze jeden run-soubor nalezen, ostatní byly pravděpodobně přepsány nebo smazány při úklidu.
36. Klíčové poznatky — vědecké shrnutí¶
36.1 Co funguje v realitním ML¶
-
Location, location, location: GPS souřadnice a vzdálenosti k městům jsou nejdůležitější features (top 3). Ale samotné GPS overfitují → kombinujte s agregovanými prostoru.
-
Dispozice je druhý nejdůležitější: Počet pokojů vysvětluje 1.8% R² — více než GIS features dohromady.
-
5-fold OOF eliminuje leakage: In-sample ensemble weights jsou nafouklé. OOF dá 0.5-1% reálnější R².
-
3 diverse modely > 1 lepší model: LGB+XGB+RF mají podobné R², ale různé chyby na různých nemovitostech. Ensemble redukuje variance.
-
Log-target pro heterogenní typy: DUM (2 000–120 000 Kč/m²) nutně potřebuje log-transformaci. BYT (60 000–200 000 Kč/m²) funguje bez logu.
-
GIS enrichment vs architekturní komplexita: 12 GIS features (+1.5% R²) > 3-layer confidence routing (+0.3% R²). Lepší data vždy porazí lepší architekturu.
-
MAPE ≠ Median APE: MAPE je citlivá na outliers (datové chyby, exkluzivní nemovitosti). Median APE je robustnější metrika pro reálnou přesnost. V15b MAPE=9.84% ale Median APE=5.53%.
-
Konstantní features jsou problém:
psc_medianze RUIAN fallback (všude 47 875) má nulovou feature importance. Odstraňte je nebo implementujte správně.
36.2 Co nefunguje¶
-
HTTP agregátory (7 enginů): Latence + maintenance overhead. Lepší: 1 přesný model.
-
Příliš mnoho dat bez čistění: V4 (180K záznamů) → horší model než V5 (103K čistých).
-
V13 meta-learner bez OOF: HistGBM + LinearRegression meta-learner, ale data leakage v meta-vrstvě snižuje skutečnou generalizaci.
-
Confidence routing v inference: V15g dynamický routing per KNN distance = krásná idea, ale inference latence 200-500ms vs V24 < 50ms.
-
KNN jako primární model: V15b KNN váha 15%, V15f KNN dá baseline → ale KNN bez anti-leakage (LOO) nafukuje metriky u blízkých bodů.
36.3 Roadmap pro V25+¶
Na základě zkušeností z vývoje:
V25 — kandidátní vylepšení:
✓ RUIAN PSČ implementovat správně (psc_median s reálnými hodnotami)
✓ Dispozice pro DUM (malý dům vs velký dům vs villa)
✓ Stav nemovitosti jako feature (novostavba/rekonstrukce/original stav)
✓ Rok výstavby (staré panelák vs novostavba)
✓ Energetická třída (A, B, C...) — vliv na hypotéky
✓ Balkón/terasa/sklep jako binary features
✓ Orientace (jih vs sever — sluneční svit)
✓ Patro v bytovém domě
✓ Self-learning: reálné transakce z CUZK API (6-měsíční lag)
V26 — long-term:
✓ Transformer-based model pro text features (popis inzerátu → embeddings)
✓ Multi-task learning: BYT + DUM + POZEMEK + KOMERCNI v jednom modelu
✓ Temporal features: trendy cen per lokalita per čtvrtletí
✓ Stochastic prediction: výstup distribuce cen (ne jen bod)
Sekce 37: Rychlý přehled — Klíčová čísla celého projektu¶
| Metrika | Hodnota |
|---|---|
| Verzí modelu | V1 → V24 (24 hlavních iterací, 7 V15 sub-iterací = 30+ experimentů) |
| Tréninkovací záznamy V24 | 140 396 nemovitostí × 36 features |
| Produkční features V24 | 34 (BYT), 33 (DUM bez dispozice) |
| BYT R² (OOF) | 0.9478 |
| DUM R² (OOF) | 0.9178 |
| BYT Median APE (shadow) | 7.13 % |
| Direct CMA Median APE | 24.68 % |
| ETL pipeline doba | ~240s (138 056 záznamů) |
| Inference latence V24 | < 50 ms |
| Inference latence V16 | ~500 ms (důvod zamítnutí) |
| CUZK transakcí (Praha 2025) | 30 872 |
| PSČ feature (RUIAN) | konstantní fallback 47 875 Kč/m² — TODO V25 |
| Flood zone coverage | 8.1 % (11 189 bodů) |
| MHD stops 500m — průměr | 4.7 zastávky |
| Ensemble váhy BYT | LGB 33.63 % · XGB 33.59 % · RF 32.78 % |
| Produkce od | 2026-05-03 (shadow.czechai.io, port 5163/5164) |
Dokument generován 2026-05-04 · Claude Code (claude-sonnet-4-6) Zdroje: SSH server 179 (/opt/cenova_mapa/), tréninkovací skripty (retrain_v24.py, retrain_v23_v4.py, train_v15b-h.py, train_v16_ultimate.py, etl_06_add_dispozice.py), ETL logy (log_01.txt–log_05.txt), JSON výsledky (v15_results.json, v24_ensemble_weights.json), validate_v15f.py, shadow.czechai.io produkce Celková délka dokumentu: 37 sekcí, 2900+ řádků, 100 000+ znaků