Chuyển đến nội dung

Hướng dẫn

Hướng dẫn: đánh giá một chính sách học tăng cường

Một bài hướng dẫn có thể chạy được cho hai loại câu hỏi về học tăng cường: một chính sách (policy) mà bạn có thể chạy hoạt động tốt đến đâu qua các episode, và một chính sách mà bạn không thể thử ngoài thế giới thực lẽ ra đã hoạt động tốt đến đâu, được đánh giá từ log của chính sách mà nó sẽ thay thế. Cả hai ví dụ đều chạy cục bộ mà không cần thông tin xác thực của nhà cung cấp.

Tài liệu tham chiếu cho mọi trường và bộ đánh giá là Học tăng cường; các thuật ngữ trường hợp, bộ đánh giá, chỉ số, khoảng và cổng nằm ở Khái niệm cốt lõi. Trang này là lộ trình thực hành đi qua chúng.

Oloproof làm gì và không làm gì ở đây

Oloproof không chạy môi trường của bạn, không lưu trữ một trình mô phỏng và không huấn luyện một chính sách. Hệ thống của bạn chạy mỗi episode trong môi trường của chính nó và ghi lại episode đó dưới dạng một artifact rl_episode/v1, và mọi chỉ số episode đều được đọc ra từ bản ghi đó. Trong phần thứ hai, không có gì chạy cả: chính sách đang được kiểm thử chỉ nêu xác suất mà nó sẽ gán cho mỗi hành động.

Hãy giữ hai câu hỏi tách biệt:

Câu hỏiNhững gì bạn cóPhần
Chính sách này hoạt động tốt đến đâu qua các episode?Một chính sách và một môi trường mà bạn có thể chạy1
Chính sách này lẽ ra đã hoạt động tốt đến đâu trong môi trường production?Một log các quyết định mà một chính sách cũ hơn đã đưa ra, cùng với các xác suất mà nó đã dùng2

Điều kiện tiên quyết

  • Python 3.11 trở lên, và Oloproof đã được cài (pip install oloproof).
  • Dự án ví dụ, đi kèm với gói. Sao chép nó vào một thư mục mới:
oloproof init --example rl my-rl

Nó chứa hai dự án, episodes/ và off_policy/. Mọi lệnh bên dưới chạy từ bên trong một trong hai dự án đó, và bằng chứng được lưu trong thư mục .oloproof/ của dự án.

Phần 1: một chính sách bạn có thể chạy

cd my-rl/episodes

Các tệp

TệpNó là gì
env.pyMột thế giới lưới, hai chính sách (wander và plan) và các hệ thống chạy chúng
data/tasks.jsonl24 lưới, mỗi lưới là một tác vụ: tường, chướng ngại nguy hiểm, một điểm xuất phát và một đích
oloproof.yamlBộ kiểm thử cho chính sách đường cơ sở, wander
candidate.yamlCùng bộ kiểm thử đó cho chính sách ứng viên, plan
release.yamlChính sách phát hành
comparison.yamlCác quy tắc mà một ứng viên phải đáp ứng so với đường cơ sở

Một episode di chuyển một ô mỗi bước cho đến khi đến đích hoặc hết số bước. Mỗi bước nhận -1 và đến đích nhận +10, nên một tổng phần thưởng (return) nằm giữa -30 (30 bước, không đến đích) và 10. Bước vào một ô nguy hiểm tốn chi phí 1: đó là ràng buộc an toàn. Cứ mười bước di chuyển thì có một bước bị trượt theo một hướng ngẫu nhiên, bất kể chính sách đã chọn gì, nên cùng một chính sách có thể cho kết quả khác nhau trên cùng một lưới.

Ghi lại một episode

Hệ thống tự chạy episode và trao bản ghi cho bộ ghi của trường hợp:

@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"])}

Bản ghi liệt kê phần thưởng và chi phí của mỗi bước, liệu episode có đến đích hay không (success), và liệu giới hạn số bước có kết thúc nó hay không (truncated).

Nhiều episode cho mỗi tác vụ

oloproof.yaml khai báo replicates: 5, nên mọi tác vụ chạy năm episode. current_case().replicate cho biết đó là episode nào (0 đến 4), và môi trường lấy seed cho episode từ nó: cùng một episode của cùng một tác vụ luôn được phát lại theo cùng một cách, và lần chạy thứ hai tái sử dụng các episode đã lưu thay vì chạy lại chúng.

Tác vụ vẫn là đơn vị. Mỗi tiêu chí là trung bình, trên 24 tác vụ, của trung bình riêng của từng tác vụ trên năm episode của nó, nên khoảng tin cậy đếm 24 tác vụ chứ không phải 120 episode. Đếm theo episode sẽ là giả vờ rằng năm episode của một lưới là năm lưới độc lập.

Các bộ đánh giá:

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 là bắt buộc: khoảng tin cậy trên một tổng phần thưởng trung bình chỉ đúng bên trong một khoảng giá trị mà mọi tổng phần thưởng đều nằm trong đó, và một tổng phần thưởng nằm ngoài khoảng đó sẽ dừng lần chạy. solved_every_time chỉ tính một tác vụ khi cả năm episode của nó đều đến đích.

Chạy đường cơ sở

oloproof run
Gate: 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 miss

Hãy đọc từng hàng:

  • Chính sách đi lang thang đến đích ở 76.7% số episode, nhưng khoảng tin cậy chạy từ 55.4% đến 89.6%, nên ngưỡng sàn 75% không được vượt qua mà cũng không bị trượt: INSUFFICIENT_EVIDENCE.
  • Nó chỉ giải được 9 trên 24 tác vụ ở mọi episode trong năm episode của mình. "Thường hoạt động" và "hoạt động mọi lần" là hai khẳng định khác nhau, và một agent sẽ chạy mà không có người giám sát cần khẳng định thứ hai.
  • 14 tác vụ là không ổn định (flaky): kết quả thành công của chúng thay đổi giữa các episode giống hệt nhau. Một episode cho mỗi tác vụ không thể cho thấy điều đó.
  • Nó tránh được các ô nguy hiểm ở 29.2% số episode. Đỉnh của khoảng tin cậy, 54.0%, cao hơn ngưỡng sàn 50%, nên ngay cả thành tích kém này cũng chưa chứng minh được rằng chính sách không an toàn.

Xem một tác vụ:

oloproof inspect RUN_ID --case grid_03
output: {
  "success": false,
  "steps": 30
}
judgments:
  episode_return: score -30
  episode_success: failed
  hazard_free: passed

Episode đầu tiên của grid_03 đã hết số bước: tổng phần thưởng -30, không đến đích, không bước vào ô nguy hiểm nào.

Chạy ứng viên

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            │

Bộ lập kế hoạch đến đích ở mọi episode, nên ngưỡng sàn về thành công đạt. Nó vẫn bước vào các ô nguy hiểm: một số lưới không có đường đi an toàn, và mặt sàn trơn đẩy nó vào những ô khác. Tỷ lệ an toàn 57.5% của nó cao hơn ngưỡng sàn, nhưng 24 tác vụ chưa thể cho thấy nó cao hơn 50%.

So sánh hai chính sách

oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy comparison.yaml
episode_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)

Phép so sánh ghép cặp mỗi tác vụ với chính nó, mỗi phía có giá trị bằng trung bình của nó trên năm episode. Chênh lệch tổng phần thưởng theo đơn vị riêng của tổng phần thưởng: bộ lập kế hoạch nhận thêm khoảng 10.9 mỗi episode, và ít nhất 0.21. Được ghép cặp trên cùng các lưới, một biên độ 5 điểm là đủ để cho thấy bộ lập kế hoạch không kém an toàn hơn, mặc dù không lần chạy nào tự nó có thể quyết định ngưỡng sàn an toàn của chính mình.

Phần 2: một chính sách được đánh giá từ một log

cd ../off_policy

Một bộ phận hỗ trợ định tuyến mỗi ticket tới một mô hình nhỏ hoặc một mô hình lớn. Bộ định tuyến hiện tại chọn mô hình nhỏ 70% thời gian và mô hình lớn 30%, một cách ngẫu nhiên. Bạn đã viết một quy tắc mới: ticket kỹ thuật, và ticket thanh toán dài, được gửi tới mô hình lớn. Bạn chưa thể thử nó trên ticket thật, nhưng log của bộ định tuyến cũ có thể trả lời thay cho nó. Đó là đánh giá ngoài chính sách (off-policy evaluation).

Log và các tệp của nó

TệpNó là gì
data/decisions.jsonl600 quyết định mà bộ định tuyến hiện tại đã đưa ra
make_log.pyPhép mô phỏng đã viết ra log đó, để bạn có thể thấy nó được tạo như thế nào
router.pyQuy tắc mới, hệ thống đang được kiểm thử
oloproof.yamlBộ kiểm thử
release.yamlChính sách phát hành

Mỗi dòng của log là một quyết định:

{"id":"t001","input":{"topic":"technical","length":"short"},"expected":{"action":"small","logging_probabilities":{"small":0.7,"large":0.3},"reward":1}}

input là những gì bộ định tuyến đã thấy, action là thứ nó đã chọn, logging_probabilities là xác suất mà nó đã gán cho mọi mô hình tại thời điểm đó, và reward cho biết ticket có được giải quyết hay không. Quy tắc mới chỉ trả lời bằng các xác suất của chính nó:

@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}}

Các bộ đánh giá:

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}

Mỗi quyết định đã ghi log được gán lại trọng số theo mức độ quy tắc mới có khả năng đưa ra nó cao hơn, hoặc thấp hơn, so với bộ định tuyến cũ: một ticket mà bộ định tuyến cũ gửi tới mô hình lớn với xác suất 0.3, mà quy tắc mới sẽ luôn gửi tới đó, được tính 1 / 0.3 lần, và một quyết định mà quy tắc mới sẽ không bao giờ đưa ra được tính bằng không. propensity_floor là xác suất nhỏ nhất mà bộ định tuyến cũ đã gán cho bất kỳ mô hình nào mà nó có thể chọn. Nó giới hạn trọng số đó, và không có giới hạn thì không khoảng tin cậy nào đúng.

Chạy nó

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 │

Quy tắc mới sẽ giải quyết ước tính 77.5% số ticket và, với độ tin cậy 95%, ít nhất 63.2%: ngưỡng sàn 60% đạt. Việc nó có tốt hơn bộ định tuyến hiện tại hay không thì chưa được quyết định. Ước lượng tốt hơn 6 điểm, nhưng khoảng tin cậy chạy từ kém hơn 6.4 điểm đến tốt hơn 18.8 điểm, và engine cho biết đại khái cần thêm bao nhiêu quyết định được ghi log để quyết định nó.

Độ rộng đó là cái giá của việc học từ những lựa chọn của người khác. Chỉ 30% số ticket mà bộ định tuyến cũ tình cờ gửi tới mô hình lớn mới nói lên được điều gì đó về mô hình lớn, và mỗi ticket đó được tính hơn ba lần. Có hai điều thu hẹp nó: một log dài hơn, và một bộ định tuyến cũ khám phá nhiều hơn.

Ghi log quyết định từ hệ thống của chính bạn

  • Hãy ghi lại các xác suất khi bộ định tuyến quyết định, không phải sau đó, và ghi lại xác suất của mọi lựa chọn, không chỉ của lựa chọn đã được chọn. Oloproof không ước lượng chúng: một xác suất được đoán sẽ làm khoảng tin cậy dựa trên phỏng đoán đó.
  • Chỉ đặt vào input những gì bộ định tuyến đã thấy khi nó quyết định, không bao giờ đặt lựa chọn đã được chọn hay kết quả: chính sách đang được kiểm thử không được thấy chúng.
  • Nếu chính sách mới của bạn lấy mẫu lựa chọn của nó, hãy trả về các xác suất mà nó lấy mẫu từ đó, không phải một lựa chọn mà nó đã rút ra. Hãy đặt tên mỗi lựa chọn đúng như log đặt tên: một lựa chọn viết khác đi được tính là một lựa chọn mà bộ định tuyến cũ chưa bao giờ chọn.
  • Hãy giữ một phần ngẫu nhiên trong việc định tuyến ở môi trường production. Một chính sách không bao giờ chọn một lựa chọn nào đó sẽ không để lại gì trong log để đánh giá một quy tắc sẽ chọn nó.
  • Câu trả lời mô tả các ticket giống những ticket đã được ghi log, trong khoảng thời gian mà log bao phủ. Một thay đổi trong lưu lượng của bạn nằm ngoài phạm vi đó.
  • Một dòng sai định dạng (các xác suất không cộng lại bằng 1, một lựa chọn đã được chọn có xác suất 0, một phần thưởng nằm ngoài reward_range) sẽ dừng lần chạy. Một quyết định mà quy tắc mới sẽ đưa ra bằng một lựa chọn mà bộ định tuyến cũ đã gán xác suất thấp hơn ngưỡng sàn, hoặc có phần thưởng bị thiếu, được tính là bị thiếu và làm rộng khoảng tin cậy.

Những gì chưa được xây dựng

  • Đánh giá ngoài chính sách trên một chuỗi quyết định, trong đó lựa chọn ở mỗi bước thay đổi những gì xảy ra tiếp theo. Chỉ hỗ trợ một quyết định cho mỗi trường hợp.
  • Một log mà bộ định tuyến của nó đã thay đổi các xác suất khi nó học trong khoảng thời gian đó.
  • Lưu trữ hoặc đặt lại môi trường, trình mô phỏng hay robot.

Tiếp theo