38.4 Saját data science munkaasztal specifikációja

Átadható specifikáció: a munkakörnyezethez ne csak eszközlista, hanem tiszta telepítési parancs, platform- és verziómátrix, minimális smoke test, függőséglakat és adat-hozzáférési szabály is tartozzon.

38.4.1 Cél és konkrét helyzet

A gyakorlóprojekt a Northstar prediktív feladat: a rendelés pillanatában becsüljük, hogy a kézbesítés 7 napon túli lesz-e (target_late). A bemenet a mellékelt labor customers.csv, orders.csv és order_items.csv fájlja; a megfigyelési egység az order_id; a cutoff 2025-05-01T00:00:00+00:00. A feladat most nem maga a modell, hanem az, hogy a munkaasztalt úgy adjuk át, hogy egy friss CPU-gép a labor readme-je alapján ugyanazt a splitet és ugyanazt az elfogadási kontrollt reprodukálja.

Előzetes nagyságrendi becslés: az orders.csv 2400 sora 15 oszlop esetén, ha minden oszlop 8 byte-os numerikus érték lenne, 2400⋅15⋅8B≈0,29MB2400 \cdot 15 \cdot 8\,\mathrm{B} \approx 0{,}29\,\mathrm{MB}; a mellékelt tételekkel együtt is néhány MB-os szinten maradunk. Ez tehát memóriában elférő, egygépes feladat; a Spark, a GPU vagy a menedzselt klaszter nem indokolt. Ez becslés, nem futási mérés.

38.4.2 Fogalmi magyarázat: a hét réteg mint predikátumlánc

A munkaasztal-specifikáció hét rétege nem egymás melletti csomaglista, hanem futtatási lánc. A Mikor érdemes másik ökoszisztémát választani? alfejezetben használt döntési fegyelem és az Eszközbevezetés, licenc és verziófrissítés alfejezetben bevezetett ellenőrzött érték–forrás–dátum–állapot sor itt konkrét rétegtesztként jelenik meg.

Réteg Kötelező választás Alternatíva / opcionális Elfogadási teszt
interpreter CPython 3.12 a projekt .venv-jében pyenv, conda/mamba sys.version_info[:2] == (3,12) és sys.executable a projekt .venv alatt
packages uv lock feloldás, majd uv sync --locked venv+pip, pip-tools a lock-feloldás sikeres, a labor alap numerikus csomagjai importálhatók
data engine pandas feature-tábla, DuckDB SQL, Parquet checkpoint Polars, SQLite COUNT(*) = 2400, séma-ellenőrzés, manifest.json hash
tracking artifacts/split.json + egy futástábla MLflow kötelező kulcsok, train/purged/test sorszámok
execution helyi CPU, uv run, JupyterLab CI futás, Docker a starter script exit-kódja 0, a nyers bemenet változatlan
reporting notebook + Modellkártya Quarto/HTML nbconvert exit 0, a kártyában „Nem támogatott használat” szakasz
handoff README, lock, manifest, modellkártya, runbook Docker image tiszta gépről uv sync --locked + script + hash-egyezés

A lap elfogadását egy logikai ÉS-lánc írja le:

S=⋀i=17ti, S = \bigwedge_{i=1}^{7} t_i,

ahol ti∈{0,1}t_i \in \{0,1\} az ii-edik rétegteszt kimenete; a tt vektor alakja (7,)(7,), értékkészlete {0,1}7\{0,1\}^7, mértékegysége darab sikeres teszt. A specifikáció akkor elfogadott, ha S=1S=1; egyetlen 0 is elutasítást jelent. Ez azért fontos, mert a stack nem „részben reprodukálható”: ha a kernel rossz interpreterre mutat, a tökéletes lock sem segít.

A hét réteg közül a interpreter és a packages különválasztása szándékos. Az interpreter az a program, amely a Python-bytecode-ot végrehajtja; a packages a projekthez tartozó könyvtárak lockolt halmaza. A notebook maga nem interpreter: csak egy kliens, amely egy regisztrált kernelén keresztül beszél valamelyik Pythonhoz. Ezért a notebook típusa nem dönti el, hogy a labor .venv-jét futtatjuk-e.

38.4.3 Teljes minimális munkapélda: specifikációs lap és ellenőrző munkamenet

Az átadandó egyoldalas lap fejléce a konkrét projektet köti a hét réteghez:

# Northstar stack-spec
projekt: rendeléskori target_late predikció
megfigyelési egység: order_id
cutoff: 2025-05-01T00:00:00+00:00
invariánsok: orders sorcount=2400, manifest SHA-256,
             split train+purged+test=2400
futtatási hely: egyetlen CPU-gép, helyi labor

A táblázat hét sora e fejléc alá kerül; a leadott lapban minden sorhoz egy rövid, idézhető forrás vagy ellenőrzési utasítás tartozik. A következő platformjelölt munkamenet mutatja a minimális láncot. Munkakönyvtár: a repository lab/ mappája; platform: Linux/macOS Bash. A lab még nem szállít feloldott lockot, ezért az első lépés a feloldás.

cd lab
uv lock                 # a mellékelt laborhoz nincs szállított uv.lock
uv sync --locked        # ezután már a lock a mérvadó
uv run python -c "import sys; print(sys.version.split()[0], sys.executable)"
uv run python generate_data.py
uv run python quickcheck.py
uv run python starter_experiment.py
python -m json.tool artifacts/split.json | head -n 20

Fontos: ezt a munkamenetet ebben az írási módban nem futtattam; a labor tényleges ellenőrzött kimenetét a mellékelt eredményjegyzék tartalmazza. Az itt látható parancsfolyam tehát a specifikáció előírt sorrendje, nem most mért futási eredmény.

A kézi kontroll a rétegekre:

SELECT count(*) AS n_orders
FROM read_csv_auto('data/orders.csv');

Várt érték: 2400. Az adatfájlra számított SHA-256-ot a manifest.json ugyanazon fájlhoz tartozó mezőjével hasonlítjuk össze; a hash csak akkor fogadható el, ha a manifestből kiolvasott értékkel egyezik, nem egy korábbi kényelmes másolatból írt konstans. A sys.executable várt alakja a projekt .venv alatti Python, nem a rendszer /usr/bin/python3.

38.4.4 Eredmény értelmezése

Ha S=1S=1, akkor állítható, hogy az adott gépen a hét réteg lánca zárt: a projekt Pythonja a projekt lockolt csomagkészletét látja, az adatellenőrzés sor- és hash-szinten átment, a split műtermék létrejött, és a leadási csomag rendelkezik az ismétléshez szükséges elemekkel. Ez nem állítja, hogy a modell illeszkedése vagy a statisztikai következtetés helyes; az a modellértékelés és a Robusztusság és korlátokkal írt modellkártya alfejezetben leírt modellkártya dolga. A stack-teszt tehát szükséges környezeti kapu, nem elégséges tudományos minősítés.

A manifest-hash szerepe az, hogy a megváltozott bemenetet azonnal elkapja. Ha valaki újragenerálja az adatot más seeddel, a sor- és hash-invariáns megtörik, és a handoff-teszt elutasít. Így a „működött nálam” állítás helyett ellenőrzött predikátumok döntenek.

38.4.5 Feladatok

  1. Értelmezés. A specifikáció szerint az interpreter és a packages teszt is zöld, de a Jupyter notebookban a pandas.__version__ értéke 2.1.4, miközben a projekt Pythonja a 2.2.3-at importálja. Melyik rétegben van a hiba? Mit kell összehasonlítani, és mi a javítás?

  2. Megvalósítás. Írj a repository gyökerébe egy stack_probe.py szkriptet, amely nem importál projektfüggő könyvtárat, és a standard kimenetre JSON-t ír a következő mezőkkel: interpreter-verzió, sys.executable, a lab/pyproject.toml és a lab/uv.lock megléte, valamint a lab/data/orders.csv mérete byte-ban. A kilépési kód legyen 0, ha az interpreter 3.12 és mindkét projektfájl létezik, különben 1.

  3. Hibadiagnózis. Egy tiszta gépen a uv sync --locked parancsot a repository gyökerében futtatták, és „sikeres” üzenetet kaptak. Ezután a uv run python starter_experiment.py a ModuleNotFoundError hibát adja a duckdb modulra. Adj három ellenőrző parancsot, és magyarázd el, miért nem látja a projekt csomagjait a futtatás. Mit kell javítani?

  4. Megváltozott feltétel. A forrásadatok Parquet-re váltanak, és a méret 12 GB-ra nő; a megfigyelési egység továbbra is rendelés, és a számításnak egyetlen CPU-gépen kell maradnia. Írd át a data engine, az execution és a handoff sor elfogadási tesztjét. Mely kritériumot kell a korábbi Elosztott kísérlet és adatelhelyezés alfejezetből idehozni?

38.4.6 Megoldások és indoklás

1. megoldás. A hiba a packages és az execution határán, a kernel-interpreter illesztésben van. A notebookban a sys.executable értéket kell összehasonlítani a uv run python -c "import sys; print(sys.executable)" kimenetével. Ha a kettő különbözik, a notebook nem a projekt .venv-jét használja. A javítás a projekt kerneljének regisztrálása – például a Kernelregisztráció és tiszta újrafuttatás alfejezetben tárgyalt kernelspec-ellenőrzéssel –, majd a notebook újraindítása. A verziók puszta kijelzése nem bizonyítja, hogy a notebook azon az interpreteren fut.

2. megoldás. Az alábbi szkript szándékosan standard library-s, hogy akkor is diagnosztizáljon, ha a függőségek nincsenek telepítve. A pontos elérési utat a labor aktuális README-jéhez kell igazítani, ha a CSV máshol fekszik.

from pathlib import Path
import json
import sys

def main() -> int:
    lab = Path("lab")
    pyproject = lab / "pyproject.toml"
    lock = lab / "uv.lock"
    orders = lab / "data" / "orders.csv"
    report = {
        "interpreter": {
            "version": f"{sys.version_info.major}.{sys.version_info.minor}",
            "executable": sys.executable,
            "ok": sys.version_info[:2] == (3, 12),
        },
        "packages": {
            "pyproject_exists": pyproject.exists(),
            "lock_exists": lock.exists(),
        },
        "data": {
            "orders_exists": orders.exists(),
            "orders_bytes": orders.stat().st_size if orders.exists() else None,
        },
    }
    report["packages"]["ok"] = (
        report["packages"]["pyproject_exists"]
        and report["packages"]["lock_exists"]
    )
    report["data"]["ok"] = (
        report["data"]["orders_exists"]
        and report["data"]["orders_bytes"] is not None
    )
    print(json.dumps(report, indent=2, ensure_ascii=False))
    ok = (
        report["interpreter"]["ok"]
        and report["packages"]["ok"]
        and report["data"]["ok"]
    )
    return 0 if ok else 1

if __name__ == "__main__":
    raise SystemExit(main())

Az ellenőrzés kimeneti sémája JSON; a várt kulcsok hiánya vagy a false érték egyetlen pillantással azonosítja a hibás réteget. A működést legalább egyszer a saját gépen futtatni kell; a fenti kód ellenőrizetlen sablon mindaddig, amíg a tanuló nem futtatja.

3. megoldás. A három parancs:

pwd
ls pyproject.toml
uv run python -c "import duckdb; print(duckdb.__version__)"

A hiba oka a munkakönyvtár: a uv sync --locked a repository gyökerében nem a lab/pyproject.toml lockját telepítette, ezért a starter_experiment.py futtatása sem a projekt virtuális környezetét látta. Az első parancs megmutatja a tényleges könyvtárat, a második azt, hogy ott nincs pyproject.toml, a harmadik pedig reprodukálja a hiányzó modulhibát. A javítás: cd lab, majd ott uv sync --locked, végül uv run python starter_experiment.py. A „sikeres” szöveg önmagában nem állapotleírás; a futtatókönyvtárral és az interpreterrel együtt értelmezendő.

4. megoldás. A három módosított sor:

Az Elosztott kísérlet és adatelhelyezés költségmodelljéből ide tartozik az adatelhelyezés és az amortizációs kérdés: 12 GB helyi Parquet esetén a helyi DuckDB/Polars akkor marad ésszerű, ha a compute az adat közelében van, és a feladat egyszeri nagy szkennelés helyett több lépcsőben újrafelhasználható checkpointot képez. A fenti küszöbök nem mért értékek; a projekt saját gépi mérését kell a specifikáció forrásaként feltüntetni.

38.4.7 Gyakori tévedés és ellenpélda

A tipikus hiba az, hogy a „minden eszköz telepítve” lista helyettesíti az architektúrát. Ellenpélda: a terminálban a python -c "import duckdb" sikeres, a notebookban az import duckdb mégsem fut. A felhasználó a rendszer-Python pip list kimenetéből tudja, hogy „a DuckDB telepítve van”, és elfogadja a munkaasztalt. A notebook kernelje azonban másik interpreterre mutat, így a csomagok halmaza nem az, amelyet a rendszerhéj listáz. A helyes állítás nem az, hogy „minden név jelen van”, hanem az, hogy az S=1S=1 predikátum a projekt .venv-jében, a lockon keresztül, a kernel ugyanazon sys.executable értékével teljesül. Ez az Eszközbevezetés, licenc és verziófrissítés alfejezetben tanult ellenőrzött forrás–dátum–állapot elvet ismétli: a puszta képernyőlista nem bizonyíték.

Önellenőrzés a leadott lapra: mind a hét sorban megtalálható-e kötelező választás, alternatíva és elfogadási teszt; a teszt megad-e belépő adatot és várt invariánst; a handoff-sor tartalmaz-e hash- és count-ellenőrzést; és a lap fejléce rögzíti-e a megfigyelési egységet és a cutoffot?

38.4.8 Rövid összefoglaló

A saját data science munkaasztal nem csomaglista, hanem hét rétegből álló predikátumlánc: interpreter, packages, data engine, tracking, execution, reporting, handoff. A Northstar laborhoz a minimális választás CPython 3.12, uv lock és sync, pandas/DuckDB/Parquet, split-manifest, helyi CPU-s uv run, notebook és modellkártya, valamint readme-lock-manifest runbook. Az egyoldalas specifikáció soronként kötelező választást, alternatívát és elfogadási tesztet ad; a lap S=1S=1 esetben zöld, de ez csak környezeti kapu, nem modellminősítés. Következőként a Probléma és adatszerződés befagyasztása záróprojekt ezen a specifikáción fagyasztja be a prediktív projekt adatszerződését.