[PapEx] Human Eval Dataset & pass@k metric
Tags: paperexplained
Published: 2024-05-14 17:51:59

Nếu các bạn đang làm trong lĩnh vực AI/Machine Learning, chắc hẳn các bạn sẽ biết đến các metric thông dụng để đánh giá độ chính xác của mô hình như accuracy, precision, recall,... Tuy nhiên, khi đánh giá cho bài toán Code Generation thì các metrics trên  không phải là tối ưu và chúng ta cần chọn 1 metric khác để đánh giá. Không phải BLEU, ROUGE mà là pass@k metric.

Nguồn: https://arxiv.org/pdf/2107.03374

Trước tiên, pass@k hiểu đơn giản là xem xét trong những đoạn sample codes/problem mà con LLM generate ra, có bao nhiêu đoạn là pass test cases. Mục tiêu là xác định xem xác suất mà "ít nhất 1 trong `k` đoạn codes" là đúng. 

Lấy 1 ví dụ đơn giản như này, chúng ta đã có trained LLM, và chúng ta cần đánh giá con LLM này trên 10 problems (tất nhiên tập dataset sẽ có input-text, test-cases). Trước hết bạn cho con LLM này generate ra mỗi 8 sample codes/problem (tổng cộng là 10x8=80 đoạn codes), sau đó xác định xem xác suất có ít nhất 1 đoạn code đúng trong 8 đoạn code được sinh ra.

Mới đầu có 1 ông (Kulal et al. cho model generate `k` sample codes/problem, nhưng phương pháp này bị high variance, hiểu đơn giản là không hiệu quả lắm). Về sau, người ta cải tiến hơn đó là thay vì generate `k` samples, người ta sẽ cho model generate ra `n` samples/problem (`n>=k`, trong paper người ta lấy `n=200`, `k<=100`) và tính số lượng correct sample `c` mà pass tất cả các unit tests.

Tôi sẽ chứng minh công thức pass@k metric ở đoạn này
```
Objective: probability mà ít nhất 1 đoạn code đúng trong k đoạn codes (given `n` là số lượng đoạn code sinh ra, `c` là số lượng đoạn code đúng)
-> Nghịch đảo bài toán: Tính xác suất trong `k` đoạn code thì tất cả đều sai

C(k,n-c) / C(k,n)

trong đó 
+ C(k,n-c): tổng số k đoạn code được chọn trong (n-c) incorrect sample codes
+ C(k,n): tổng số k đoạn code được chọn trong n đoạn code

Suy ra để có ít nhất 1 đoạn code là đúng trong k đoạn code:

pass@k = 1 - C(k,n-c) / C(k,n)
```

Còn có 1 cách giải thích khác và công thức khác của pass@k (trong phần Appendix của paper)
```
pass@k = 1 - (1 - pass@1)^k 

Trong đó: 
+ pass@1 = 1 - C(1,n-c) / C(1,n) = 1 - (n-c)/n = c/n
+ 1-pass@1: xác suất mà đoạn code được chọn là sai
+ k: có tất cả k samples -> xác suất để tất cả k samples đều sai (không pass test cases) là (1-pass@1)^k 
-> nghịch đảo lấy 1 trừ là ra
```

Về HumanEval Dataset:
+ Có tất cả 164 problems
+ Trung bình 7.7 test cases / problem
+ Khái niệm "Sandbox": để executing generated programs (Tức là khi model generate ra những đoạn code, thì cần phải chạy xem có pass test cases không -> chạy ở "Sandbox")

Implementation:
Về flow của việc evaluatation: Bạn đã có model và cần đánh giá task code generation trên tập HumanEval. Trước tiên bạn phải cho model generate ra `n x 164` đoạn codes (n là số lượng đoạn code sinh ra mỗi problem, và có tất cả 164 problems) sau đó mới evaluate code ở "Sandbox". Hiểu đơn giản là sẽ có 2 phases: Generate code -> Evaluate Code

Tại sao lại làm như vậy, nếu bạn có suy nghĩ giống tôi và lúc implement tôi cũng thắc mắc như thế này: Tại sao không vừa generate vừa evaluate, tức là mỗi problem mà model generate ra, thì sẽ evaluate luôn thay vì generate tất cả 200x164=32800 đoạn code sau đó mới evaluate, làm vậy thì lâu bỏ m* ra :))). Tại thư viện người ta viết vậy chứ giờ sửa thế nào được. 

Bạn đọc có thể tham khảo cách implement trên README.md ở link sau: https://github.com/openai/human-eval
Bạn có thể sử dụng GPU để generate code nhanh hơn (Kiểu như 2 con GPU chạy song song sẽ sinh code nhanh hơn mỗi con CPU)

Tôi thấy cách implement cũng khá đơn giản và tôi sẽ gửi lại các bạn phần mà tôi evaluate mô hình CodeT5, CodeT5+ của nhà Salesforce bằng tập HumanEval với metric pass@k ở dưới phần comment.

Vậy thôi