Przewodniki
Samouczek: ewaluacja polityki uczenia ze wzmocnieniem
Praktyczny przewodnik do uruchomienia dla dwóch rodzajów pytań z uczenia ze wzmocnieniem: jak dobrze polityka, którą możesz uruchomić, radzi sobie w epizodach, oraz jak dobrze poradziłaby sobie polityka, której nie możesz wypróbować w świecie, oceniana na podstawie logu polityki, którą miałaby zastąpić. Oba przykłady działają lokalnie, bez poświadczeń dostawcy.
Dokumentacją każdego pola i ewaluatora jest Uczenie ze wzmocnieniem; terminy przypadek, ewaluator, metryka, przedział i bramka są w Podstawowych pojęciach. Ta strona to praktyczna droga przez nie.
Co Oloproof tutaj robi, a czego nie
Oloproof nie uruchamia Twojego środowiska, nie hostuje symulatora ani nie trenuje polityki. Twój system uruchamia każdy epizod we własnym środowisku i zapisuje go jako artefakt rl_episode/v1, a każda metryka epizodu jest odczytywana z tego rekordu. W drugiej części nic się w ogóle nie uruchamia: testowana polityka podaje tylko prawdopodobieństwo, jakie nadałaby każdej akcji.
Trzymaj te dwa pytania osobno:
| Pytanie | Co masz | Część |
|---|---|---|
| Jak dobrze ta polityka radzi sobie w epizodach? | Politykę i środowisko, które możesz uruchomić | 1 |
| Jak dobrze ta polityka poradziłaby sobie na produkcji? | Log decyzji podjętych przez starszą politykę, z prawdopodobieństwami, których użyła | 2 |
Wymagania wstępne
- Python 3.11 lub nowszy i zainstalowany Oloproof (pip install oloproof).
- Przykładowy projekt, dostarczany z pakietem. Skopiuj go do nowego katalogu:
oloproof init --example rl my-rlZawiera dwa projekty, episodes/ i off_policy/. Każde polecenie poniżej uruchamia się z wnętrza jednego z nich, a dowody są zapisywane w jego katalogu .oloproof/.
Część 1: polityka, którą możesz uruchomić
cd my-rl/episodesPliki
| Plik | Czym jest |
|---|---|
| env.py | Świat siatki, dwie polityki (wander i plan) oraz systemy, które je uruchamiają |
| data/tasks.jsonl | 24 siatki, każda jest zadaniem: ściany, zagrożenia, start i cel |
| oloproof.yaml | Zestaw dla polityki bazowej, wander |
| candidate.yaml | Ten sam zestaw dla polityki kandydującej, plan |
| release.yaml | Polityka wydań |
| comparison.yaml | Reguły, które kandydat musi spełnić względem punktu odniesienia |
Epizod przesuwa się o jedno pole na krok, aż osiągnie cel lub wyczerpie kroki. Każdy krok daje -1, a osiągnięcie celu daje +10, więc zwrot leży między -30 (30 kroków, bez celu) a 10. Wejście na zagrożenie kosztuje 1: to jest ograniczenie bezpieczeństwa. Jeden ruch na dziesięć ześlizguje się w losowym kierunku, niezależnie od wyboru polityki, więc ta sama polityka może radzić sobie różnie na tej samej siatce.
Zapisywanie epizodu
System sam uruchamia epizod i przekazuje rekord rejestratorowi przypadku:
@system(name="grid-agent", version="wander-v1", records=("rl_episode/v1",))
def run_baseline(task):
return _run(task, wander)
def _run(task, policy):
recorder = current_case()
record = episode(task, policy, seed=task["seed"] * 1000 + recorder.replicate)
recorder.rl_episode(record)
return {"success": record["success"], "steps": len(record["steps"])}Rekord wymienia nagrodę i koszt każdego kroku, to, czy epizod osiągnął cel (success), i to, czy zakończył go limit kroków (truncated).
Kilka epizodów na zadanie
oloproof.yaml deklaruje replicates: 5, więc każde zadanie uruchamia pięć epizodów. current_case().replicate mówi który (od 0 do 4), a środowisko ustawia na jego podstawie ziarno epizodu: ten sam epizod tego samego zadania zawsze odtwarza się tak samo, a drugi przebieg ponownie używa zapisanych epizodów zamiast uruchamiać je jeszcze raz.
Jednostką pozostaje zadanie. Każde kryterium to średnia, po 24 zadaniach, z własnej średniej każdego zadania po jego pięciu epizodach, więc przedział liczy 24 zadania, a nie 120 epizodów. Liczenie epizodów udawałoby, że pięć epizodów jednej siatki to pięć niezależnych siatek.
Ewaluatory:
evaluators:
- {type: episode_return, return_range: [-30, 10]}
- {type: episode_success}
- {type: episode_cost_within, budget: 0, criterion: hazard_free}
metrics:
- {id: solved_every_time, type: every_replicate, criterion: episode_success}return_range jest wymagane: przedział dla średniego zwrotu obowiązuje tylko w zakresie, w którym leży każdy zwrot, a zwrot spoza niego zatrzymuje przebieg. solved_every_time liczy zadanie tylko wtedy, gdy wszystkie pięć jego epizodów osiągnęło cel.
Uruchom politykę bazową
oloproof runGate: BLOCK (exit 3)
Decisions
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Rule ┃ Metric ┃ State ┃ Reasons ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ success-floor │ episode_success │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ safety-floor │ hazard_free │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
└───────────────┴─────────────────┴───────────────────────┴─────────────────────────────┘
Metrics
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Estimate ┃ Interval ┃ N ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ episode_return │ -10.51 │ [-16.44, -5.11] │ mean of 24 observed · 0 missing · 0 excluded │
│ episode_success │ 76.7% │ [55.4%, 89.6%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each · 14 flaky │
│ hazard_free │ 29.2% │ [10.3%, 54.0%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each · 9 flaky │
│ solved_every_time │ 37.5% │ [18.7%, 59.5%] │ 9 / 24 observed · 0 missing · 0 excluded · 5 replicates each · 14 flaky │
└───────────────────┴──────────┴─────────────────┴───────────────────────────────────────────────────────────────────────────┘
Cache: execution 0 hit/120 miss; judgment 0 hit/360 missCzytaj wiersz po wierszu:
- Błądząca polityka osiąga cel w 76.7% epizodów, ale przedział sięga od 55.4% do 89.6%, więc próg 75% nie jest ani osiągnięty, ani chybiony: INSUFFICIENT_EVIDENCE.
- Rozwiązuje tylko 9 z 24 zadań w każdym z pięciu epizodów. "Zwykle działa" i "działa za każdym razem" to różne twierdzenia, a agent, który będzie działał bez nadzoru, potrzebuje drugiego.
- 14 zadań jest niestabilnych: ich sukces zmieniał się między identycznymi epizodami. Jeden epizod na zadanie nie mógłby tego pokazać.
- Unika zagrożeń w 29.2% epizodów. Górna granica przedziału, 54.0%, jest powyżej progu 50%, więc nawet ten słaby wynik nie dowodzi jeszcze, że polityka jest niebezpieczna.
Spójrz na jedno zadanie:
oloproof inspect RUN_ID --case grid_03output: {
"success": false,
"steps": 30
}
judgments:
episode_return: score -30
episode_success: failed
hazard_free: passedPierwszy epizod grid_03 wyczerpał kroki: zwrot -30, bez celu, bez wejścia na zagrożenie.
Uruchom kandydata
oloproof run --config candidate.yaml│ success-floor │ episode_success │ PASS │ lower_bound_meets_minimum │
│ safety-floor │ hazard_free │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold ││ episode_return │ 0.42 │ [-5.78, 2.59] │ mean of 24 observed · 0 missing · 0 excluded │
│ episode_success │ 100.0% │ [79.9%, 100.0%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each │
│ hazard_free │ 57.5% │ [29.3%, 81.5%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each · 6 flaky │
│ solved_every_time │ 100.0% │ [85.7%, 100.0%] │ 24 / 24 observed · 0 missing · 0 excluded · 5 replicates each │Planista osiąga cel w każdym epizodzie, więc próg sukcesu jest zaliczony. Nadal wchodzi na zagrożenia: niektóre siatki nie mają bezpiecznej ścieżki, a śliska podłoga spycha go na inne. Jego wskaźnik bezpieczeństwa, 57.5%, jest powyżej progu, ale 24 zadania nie mogą jeszcze pokazać, że przekracza 50%.
Porównaj dwie polityki
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy comparison.yamlepisode_return: +10.92 [+0.21, +17.74] · 24 paired · 0 missing · 0 excluded
episode_success: +23.3 points [-3.2, +44.6] · 24 paired · 0 missing · 0 excluded
hazard_free: +28.3 points [-2.4, +54.7] · 24 paired · 0 missing · 0 excluded
solved_every_time: +62.5 points [+24.3, +87.7] · 24 paired · 0 missing · 0 excluded
Decisions
return-better episode_return superiority PASS difference_above_zero
safety-no-worse hazard_free non-inferiority, margin 5.0 points PASS lower_bound_above_margin
Gate: ALLOW (exit 0)Porównanie paruje każde zadanie z nim samym, a każda strona jest warta swojej średniej po pięciu epizodach. Różnica zwrotów jest we własnej jednostce zwrotu: planista zdobywa około 10.9 więcej na epizod, a co najmniej 0.21 więcej. Przy parowaniu na tych samych siatkach margines 5 punktów wystarcza, by pokazać, że planista nie jest mniej bezpieczny, choć żaden przebieg osobno nie mógł rozstrzygnąć własnego progu bezpieczeństwa.
Część 2: polityka oceniana na podstawie logu
cd ../off_policyDział wsparcia kieruje każde zgłoszenie do małego lub dużego modelu. Obecny router wybiera mały model w 70% przypadków, a duży w 30%, losowo. Napisałeś nową regułę: zgłoszenia techniczne oraz długie zgłoszenia rozliczeniowe trafiają do dużego modelu. Nie możesz jeszcze wypróbować jej na prawdziwych zgłoszeniach, ale log starego routera może odpowiedzieć za nią. To jest ewaluacja off-policy.
Log i jego pliki
| Plik | Czym jest |
|---|---|
| data/decisions.jsonl | 600 decyzji podjętych przez obecny router |
| make_log.py | Symulacja, która zapisała ten log, abyś mógł zobaczyć, jak powstał |
| router.py | Nowa reguła, testowany system |
| oloproof.yaml | Zestaw |
| release.yaml | Polityka wydań |
Każdy wiersz logu to jedna decyzja:
{"id":"t001","input":{"topic":"technical","length":"short"},"expected":{"action":"small","logging_probabilities":{"small":0.7,"large":0.3},"reward":1}}input to to, co widział router, action to, co wybrał, logging_probabilities to prawdopodobieństwo, jakie nadał każdemu modelowi w tamtej chwili, a reward to, czy zgłoszenie zostało rozwiązane. Nowa reguła odpowiada tylko własnymi prawdopodobieństwami:
@system(name="ticket-router", version="by-topic-v1")
def route_by_topic(ticket):
large = ticket["topic"] == "technical" or (
ticket["topic"] == "billing" and ticket["length"] == "long"
)
return {"probabilities": {"small": 0.0 if large else 1.0, "large": 1.0 if large else 0.0}}Ewaluatory:
evaluators:
- {type: off_policy_value, reward_range: [0, 1], propensity_floor: 0.3}
- {type: off_policy_improvement, reward_range: [0, 1], propensity_floor: 0.3}Każda zarejestrowana decyzja jest przeważana według tego, o ile bardziej lub mniej prawdopodobne było, że podejmie ją nowa reguła niż stary router: zgłoszenie, które stary router wysłał do dużego modelu z prawdopodobieństwem 0.3, a które nowa reguła zawsze by tam wysłała, liczy się 1 / 0.3 razy, a decyzja, której nowa reguła nigdy by nie podjęła, liczy się zero razy. propensity_floor to najmniejsze prawdopodobieństwo, jakie stary router nadał dowolnemu modelowi, który mógł wybrać. Ogranicza tę wagę, a bez ograniczenia żaden przedział nie obowiązuje.
Uruchom
oloproof run│ resolution-floor │ off_policy_value │ PASS │ lower_bound_meets_minimum │
│ better-than-current │ off_policy_improvement │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ │ │ │ about 2633 more cases would decide it, if the observed values hold (3233 in total) ││ off_policy_value │ 0.775 │ [0.632, 0.921] │ mean of 600 observed · 0 missing · 0 excluded │
│ off_policy_improvement │ 0.060 │ [-0.064, 0.188] │ mean of 600 observed · 0 missing · 0 excluded │Nowa reguła rozwiązałaby szacunkowo 77.5% zgłoszeń, a przy 95% ufności co najmniej 63.2%: próg 60% jest zaliczony. Czy przewyższa obecny router, nie jest rozstrzygnięte. Estymata jest o 6 punktów lepsza, ale przedział sięga od 6.4 punktu gorzej do 18.8 lepiej, a silnik podaje mniej więcej, ile więcej zarejestrowanych decyzji by to rozstrzygnęło.
Ta szerokość to cena uczenia się z cudzych wyborów. Tylko 30% zgłoszeń, które stary router akurat wysłał do dużego modelu, mówi cokolwiek o dużym modelu, a każde liczy się ponad trzy razy. Zawężają go dwie rzeczy: dłuższy log i stary router, który więcej eksploruje.
Rejestrowanie decyzji z własnego systemu
- Zapisuj prawdopodobieństwa w chwili, gdy router podejmuje decyzję, a nie później, i zapisuj prawdopodobieństwo każdej opcji, nie tylko wybranej. Oloproof ich nie szacuje: zgadnięte prawdopodobieństwo sprawiłoby, że przedział opierałby się na zgadywaniu.
- Umieszczaj w input tylko to, co router widział, gdy podejmował decyzję, nigdy wybranej opcji ani wyniku: testowana polityka nie może ich widzieć.
- Jeśli Twoja nowa polityka losuje swój wybór, zwracaj prawdopodobieństwa, z których losuje, a nie jedną wylosowaną opcję. Nazywaj każdą opcję dokładnie tak, jak robi to log: opcja zapisana inaczej liczy się jako taka, której stary router nigdy nie wybrał.
- Zachowaj trochę losowości w routingu produkcyjnym. Polityka, która nigdy nie wybiera danej opcji, nie zostawia w logu niczego, na podstawie czego można by ocenić regułę, która by ją wybierała.
- Odpowiedź opisuje zgłoszenia podobne do zarejestrowanych, w okresie, który obejmuje log. Zmiana w Twoim ruchu leży poza nią.
- Błędnie sformatowany wiersz (prawdopodobieństwa, które nie sumują się do 1, wybrana opcja z prawdopodobieństwem 0, nagroda spoza reward_range) zatrzymuje przebieg. Decyzja, którą nowa reguła podjęłaby z opcją, której stary router nadał mniej niż próg, albo której nagrody brakuje, liczy się jako brakująca i poszerza przedział.
Czego nie zbudowano
- Ewaluacji off-policy sekwencji decyzji, w której wybór na każdym kroku zmienia to, co następuje dalej. Obsługiwana jest tylko jedna decyzja na przypadek.
- Logu, którego router zmieniał swoje prawdopodobieństwa, ucząc się w trakcie objętego nim okresu.
- Hostowania ani resetowania środowisk, symulatorów czy robotów.
Dokąd dalej
- Uczenie ze wzmocnieniem: dokumentacja referencyjna tych ewaluatorów i rekordu epizodu.
- Pisanie zestawu: powtórzenia i inne pola zestawu.
- Porównywanie kandydata z punktem odniesienia: co odczytuje porównanie sparowane.
- Podstawowe pojęcia: dokąd sięga wynik, a dokąd nie.