File size: 8,996 Bytes
ee6e328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<!--Copyright 2020 The HuggingFace Team. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
-->

# κ³ μ • 길이 λͺ¨λΈμ˜ νŽ„ν”Œλ ‰μ„œν‹°(Perplexity)[[perplexity-of-fixedlength-models]]

[[open-in-colab]]

νŽ„ν”Œλ ‰μ„œν‹°(Perplexity, PPL)λŠ” κ°€μž₯ 일반적인 μ–Έμ–΄ λͺ¨λΈ ν‰κ°€μ§€ν‘œ 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€.
μžμ„Ένžˆ μ•Œμ•„λ³΄κΈ° 전에 이 ν‰κ°€μ§€ν‘œλŠ” 고전적인 μ–Έμ–΄ λͺ¨λΈ(μžκΈ°νšŒκ·€ λ˜λŠ” 인과적 μ–Έμ–΄ λͺ¨λΈμ΄λΌκ³ λ„ 함)μ—λ§Œ 적용되며 BERT와 같은 λ§ˆμŠ€ν‚Ήλœ μ–Έμ–΄ λͺ¨λΈμ—λŠ” 잘 μ μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ (BERTλŠ” [summary of the models](../en/model_summary) λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”).

νŽ„ν”Œλ ‰μ„œν‹°λŠ” μ‹œν€€μŠ€μ˜ 음의 둜그 μš°λ„(negative log-likelihood, NLL) κ°’μ˜ 평균에 μ§€μˆ˜(exponentiate)λ₯Ό μ·¨ν•œ κ°’μœΌλ‘œ μ •μ˜λ©λ‹ˆλ‹€.
ν† ν°ν™”λœ μ‹œν€€μŠ€ \\(X = (x_0, x_1, \dots, x_t)\\) κ°€ μžˆμ„ λ•Œ, \\(X\\) 의 νŽ„ν”Œλ ‰μ„œν‹°λŠ” μ•„λž˜ μˆ˜μ‹κ³Ό 같이 ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.

$$\text{PPL}(X) = \exp \left\{ {-\frac{1}{t}\sum_i^t \log p_\theta (x_i|x_{<i}) } \right\}$$

\\(\log p_\theta (x_i|x_{<i})\\) λŠ” λͺ¨λΈμ— i번째 μ΄μ „κΉŒμ§€ 토큰이 μ£Όμ–΄μ‘Œμ„ λ•Œ i번째 ν† ν°μ˜ 둜그 μš°λ„κ°’μž…λ‹ˆλ‹€.

μ§κ΄€μ μœΌλ‘œ λ§λ­‰μΉ˜μ—μ„œ μ§€μ •λœ 토큰 집합을 κ· μΌν•˜κ²Œ μ˜ˆμΈ‘ν•˜λŠ” λͺ¨λΈμ˜ λŠ₯λ ₯에 λŒ€ν•œ ν‰κ°€λ‘œ 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.
μ€‘μš”ν•œ 점은 토큰화 과정이 λͺ¨λΈμ˜ νŽ„ν”Œλ ‰μ„œν‹°μ— 직접적인 영ν–₯을 λ―ΈμΉ˜λ―€λ‘œ μ„œλ‘œ λ‹€λ₯Έ λͺ¨λΈμ„ 비ꡐ할 λ•Œ 항상 이λ₯Ό κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€.

μ΄λŠ” 데이터와 λͺ¨λΈ 예츑 κ°„μ˜ cross-entropy 값에 μ§€μˆ˜λ₯Ό μ·¨ν•œ 것과 λ™μΌν•©λ‹ˆλ‹€.
νŽ„ν”Œλ ‰μ„œν‹°μ™€ λ¬Έμžλ‹Ή λΉ„νŠΈ 수(BPC) 및 데이터 μ••μΆ•κ³Όμ˜ 관계에 λŒ€ν•΄ 더 직관적인 이해λ₯Ό μ›ν•˜μ‹ λ‹€λ©΄ λ‹€μŒ κΈ€
[fantastic blog post on The Gradient](https://thegradient.pub/understanding-evaluation-metrics-for-language-models/)을 ν™•μΈν•˜μ„Έμš”.

## κ³ μ • 길이 λͺ¨λΈμ˜ νŽ„ν”Œλ ‰μ„œν‹°(PPL) κ³„μ‚°ν•˜κΈ°[[calculating-ppl-with-fixedlength-models]]

λͺ¨λΈμ˜ μ»¨ν…μŠ€νŠΈ 크기가 μ •ν•΄μ Έμžˆμ§€ μ•Šλ‹€λ©΄,
μ•„λž˜μ™€ 같이 μ‹œν€€μŠ€λ₯Ό μžλ™ νšŒκ·€μ μœΌλ‘œ λΆ„ν•΄ν•˜κ³  각 λ‹¨κ³„μ—μ„œ μ„ ν–‰ ν•˜λŠ” 전체 μ‹œν€€μŠ€λ₯Ό 쑰건뢀 ν™•λ₯ μ— λ„£μ–΄ λͺ¨λΈμ˜ νŽ„ν”Œλ ‰μ„œν‹°λ₯Ό 계산할 κ²ƒμž…λ‹ˆλ‹€.

<img width="600" alt="Full decomposition of a sequence with unlimited context length" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/ppl_full.gif"/>

κ·ΈλŸ¬λ‚˜ λͺ¨λΈμ˜ κ·Όμ‚¬μΉ˜λ₯Ό ꡬ할 λ•ŒλŠ” 일반적으둜 λͺ¨λΈμ΄ μ²˜λ¦¬ν•  수 μžˆλŠ” 토큰 μˆ˜μ— μ œν•œμ΄ μžˆμŠ΅λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄, κ°€μž₯ 큰 λ²„μ „μ˜ [GPT-2](model_doc/gpt2)λŠ” ν† ν°μ˜ 길이가 1024둜 κ³ μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
λ”°λΌμ„œ \\(t\\) κ°€ 1024보닀 큰 κ²½μš°μ— \\(p_\theta(x_t|x_{<t})\\) 을 계산할 수 μ—†μŠ΅λ‹ˆλ‹€.

λŒ€μ‹  μ‹œν€€μŠ€λŠ” 일반적으둜 λͺ¨λΈμ˜ μ΅œλŒ€ μž…λ ₯ 크기와 λ™μΌν•œ κΈΈμ΄λŠ” κ°€μ§€λŠ” λΆ€λΆ„ μ‹œν€€μŠ€λ‘œ μͺΌκ°­λ‹ˆλ‹€.
λ§Œμ•½ λͺ¨λΈμ˜ μ΅œλŒ€ μž…λ ₯ 길이가 \\(k\\) 라면, 
토큰 \\(x_t\\) 의 μš°λ„ 값을 계산할 λ•Œ 이전 토큰을 λͺ¨λ‘ μ‚¬μš©ν•˜μ§€ μ•Šκ³ , \\(k-1\\) ν† ν°κΉŒμ§€ μ‚¬μš©ν•΄ λŒ€λž΅μ μΈ μš°λ„ 값을 μΆ”μ •ν•©λ‹ˆλ‹€. 

λͺ¨λΈμ˜ μ‹œν€€μŠ€μ— λŒ€ν•œ νŽ„ν”Œλ ‰μ„œν‹°λ₯Ό 계산할 λ•Œ,
μˆ˜μ›”ν•˜μ§€λ§Œ 차선책은 μ‹œν€€μŠ€λ₯Ό 청크둜 μͺΌκ°œκ³  λΆ„ν•΄λœ 각 λΆ€λΆ„μ˜ 둜그 μš°λ„ 값을 λ…λ¦½μ μœΌλ‘œ ν•©μ‚°ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

<img width="600" alt="Suboptimal PPL not taking advantage of full available context" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/ppl_chunked.gif"/>

이 방법은 각 λΆ€λΆ„μ˜ νŽ„ν”Œλ ‰μ„œν‹°λ₯Ό ν•œ 번의 ν¬μ›Œλ“œ 패슀둜 계산할 수 μžˆμ–΄ λΉ λ₯΄μ§€λ§Œ 일반적으둜 더 높은(더 λ‚˜μœ) PPL을 μ‚°μΆœν•©λ‹ˆλ‹€.
μ™œλƒν•˜λ©΄ λŒ€λΆ€λΆ„μ˜ 예츑 λ‹¨κ³„μ—μ„œ λͺ¨λΈμ˜ μ»¨ν…μŠ€νŠΈκ°€ 적기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

λŒ€μ‹ , κ³ μ • 길이 λͺ¨λΈμ˜ PPL은 μŠ¬λΌμ΄λ”© μœˆλ„μš° μ „λž΅μœΌλ‘œ 평가해야 ν•©λ‹ˆλ‹€.
이 μ „λž΅μ—λŠ” μ»¨ν…μŠ€νŠΈ μœˆλ„μš°μ„ 반볡적으둜 μŠ¬λΌμ΄λ”©ν•΄ λͺ¨λΈμ΄ 각 μ˜ˆμΈ‘μ„ μˆ˜ν–‰ν•  λ•Œ 더 λ§Žμ€ μ»¨ν…μŠ€νŠΈλ₯Ό 갖도둝 ν•˜λŠ” μž‘μ—…μ΄ ν¬ν•¨λ©λ‹ˆλ‹€.

<img width="600" alt="Sliding window PPL taking advantage of all available context" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/ppl_sliding.gif"/>

μ΄λŠ” μ‹œν€€μŠ€ ν™•λ₯ μ˜ μ‹€μ œ 뢄해에 더 κ°€κΉŒμš΄ κ·Όμ‚¬μΉ˜μ΄λ©° 일반적으둜 더 μœ λ¦¬ν•œ 점수λ₯Ό μ‚°μΆœν•©λ‹ˆλ‹€.
단점은 λ§λ­‰μΉ˜μ˜ 각 토큰에 λŒ€ν•΄ λ³„λ„μ˜ ν¬μ›Œλ“œ νŒ¨μŠ€κ°€ ν•„μš”ν•˜λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.
ν˜„μ‹€μ μœΌλ‘œ 쒋은 μ ˆμΆ©μ•ˆμ€ ν•œ λ²ˆμ— ν•œ 토큰씩 μŠ¬λΌμ΄λ”©ν•˜λŠ” 것이 μ•„λ‹ˆλΌ 더 큰 κ°„κ²©μœΌλ‘œ μ»¨ν…μŠ€νŠΈλ₯Ό μ΄λ™ν•˜λŠ” μŠ€νŠΈλΌμ΄λ“œκ°€ 적용된 μŠ¬λΌμ΄λ”© μœˆλ„μš°μ„ μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 
μ΄λ ‡κ²Œ ν•˜λ©΄ 계산을 훨씬 더 λΉ λ₯΄κ²Œ μ§„ν–‰ν•˜λ©΄μ„œλ„ λͺ¨λΈμ— 각 λ‹¨κ³„μ—μ„œ μ˜ˆμΈ‘μ„ μˆ˜ν–‰ν•  수 μžˆλŠ” κΈ΄ μ»¨ν…μŠ€νŠΈλ₯Ό μ œκ³΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

## 예제: πŸ€— Transformersμ—μ„œ GPT-2둜 νŽ„ν”Œλ ‰μ„œν‹°(perplexity) κ³„μ‚°ν•˜κΈ°[[example-calculating-perplexity-with-gpt2-in-transformers]]

이제 GPT-2둜 μœ„μ˜ 과정을 μ‹œμ—°ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

```python
from transformers import GPT2LMHeadModel, GPT2TokenizerFast

device = "cuda"
model_id = "gpt2-large"
model = GPT2LMHeadModel.from_pretrained(model_id).to(device)
tokenizer = GPT2TokenizerFast.from_pretrained(model_id)
```

WikiText-2 데이터 μ„ΈνŠΈλ₯Ό κ°€μ Έμ˜€κ³  λͺ‡ 가지 μŠ¬λΌμ΄λ”© μœˆλ„μš° μ „λž΅μ„ μ‚¬μš©ν•΄ νŽ„ν”Œλ ‰μ„œν‹°λ₯Ό κ³„μ‚°ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
이 데이터 μ„ΈνŠΈλŠ” 크기가 μž‘κ³  ν¬μ›Œλ“œ 패슀 ν•œ 번만 μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ— 전체 데이터 μ„ΈνŠΈλ₯Ό λ©”λͺ¨λ¦¬μ— κ°€μ Έμ˜€κ³  인코딩할 수 μžˆμŠ΅λ‹ˆλ‹€.

```python
from datasets import load_dataset

test = load_dataset("wikitext", "wikitext-2-raw-v1", split="test")
encodings = tokenizer("\n\n".join(test["text"]), return_tensors="pt")
```

πŸ€— Transformersλ₯Ό μ‚¬μš©ν•˜λ©΄ λͺ¨λΈμ˜ `labels`둜 `input_ids`λ₯Ό 전달해 각 토큰에 λŒ€ν•œ 평균 음의 μš°λ„ 값을 μ†μ‹€λ‘œ λ°˜ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
ν•˜μ§€λ§Œ μŠ¬λΌμ΄λ”© μœˆλ„μš° 방식을 μ‚¬μš©ν•˜λ©΄ 각 λ°˜λ³΅λ§ˆλ‹€ λͺ¨λΈμ— μ „λ‹¬ν•˜λŠ” 토큰이 κ²ΉμΉ©λ‹ˆλ‹€.
μ»¨ν…μŠ€νŠΈλ‘œ μ²˜λ¦¬ν•˜λŠ” 토큰에 λŒ€ν•œ 둜그 μš°λ„ 값이 손싀에 ν¬ν•¨λ˜λŠ” 것을 μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ΄λŸ¬ν•œ ν† ν°μ˜ `input_ids`λ₯Ό `-100`으둜 μ„€μ •ν•˜μ—¬ λ¬΄μ‹œν•  수 μžˆμŠ΅λ‹ˆλ‹€. 

λ‹€μŒμ€ μŠ€νŠΈλΌμ΄λ“œ(stride)λ₯Ό `512`둜 μ‚¬μš©ν•œ μ˜ˆμ‹œμž…λ‹ˆλ‹€. 
즉, λͺ¨λΈμ΄ ν•œ ν† ν°μ˜ 쑰건뢀 μš°λ„ 값을 계산할 λ•Œ μ»¨ν…μŠ€νŠΈμ— μ΅œμ†Œν•œ 512개의 토큰이 ν¬ν•¨λ˜μ–΄μžˆλ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€ (ν•΄λ‹Ή 토큰 μ•žμ— 512개의 토큰이 μžˆλŠ” 경우).

```python
import torch
from tqdm import tqdm

max_length = model.config.n_positions
stride = 512
seq_len = encodings.input_ids.size(1)

nlls = []
prev_end_loc = 0
for begin_loc in tqdm(range(0, seq_len, stride)):
    end_loc = min(begin_loc + max_length, seq_len)
    trg_len = end_loc - prev_end_loc  # λ§ˆμ§€λ§‰ λ£¨ν”„μ˜ μŠ€νŠΈλΌμ΄λ“œ κ°’κ³Ό λ‹€λ₯Ό 수 있음
    input_ids = encodings.input_ids[:, begin_loc:end_loc].to(device)
    target_ids = input_ids.clone()
    target_ids[:, :-trg_len] = -100

    with torch.no_grad():
        outputs = model(input_ids, labels=target_ids)

        # 손싀은 λͺ¨λ“  μœ νš¨ν•œ λ ˆμ΄λΈ”μ— λŒ€ν•œ 평균값을 κ΅¬ν•˜λŠ” ꡐ차 μ—”νŠΈλ‘œν”Ό(cross entropy)둜 κ³„μ‚°λ©λ‹ˆλ‹€.
        # λ‚˜μ΄λΈŒ λ² μ΄μ§€μ•ˆ λͺ¨λΈμ€ λ‚΄λΆ€μ μœΌλ‘œ λ ˆμ΄λΈ”μ„ μ™Όμͺ½μœΌλ‘œ 1κ°œμ”© λ°€κΈ° λ•Œλ¬Έμ—, (타켓 - 1)개 만큼의 λ ˆμ΄λΈ”μ— λŒ€ν•΄ 손싀을 κ³„μ‚°ν•©λ‹ˆλ‹€.
        neg_log_likelihood = outputs.loss

    nlls.append(neg_log_likelihood)

    prev_end_loc = end_loc
    if end_loc == seq_len:
        break

ppl = torch.exp(torch.stack(nlls).mean())
```

μŠ€νŠΈλΌμ΄λ“œλ₯Ό μ΅œλŒ€ μž…λ ₯ 길이와 λ™μΌν•˜κ²Œ μ„€μ •ν•˜λ©΄ μœ„μ—μ„œ μ„€λͺ…ν•œ 차선책인 λΉ„μŠ¬λΌμ΄λ”© μœˆλ„μš° μ „λž΅κ³Ό λ™μΌν•©λ‹ˆλ‹€.
일반적으둜 μŠ€νŠΈλΌμ΄λ“œκ°€ μž‘μ„μˆ˜λ‘ λͺ¨λΈμ΄ 각 μ˜ˆμΈ‘μ„ ν•  λ•Œ 더 λ§Žμ€ μ»¨ν…μŠ€νŠΈλ₯Ό λ³Ό 수 있게 λ˜μ–΄ νŽ„ν”Œλ ‰μ„œν‹° 값이 μ’‹μ•„μ§‘λ‹ˆλ‹€.

μœ„μ˜ 계산을 토큰이 κ²ΉμΉ˜μ§€ μ•Šλ„λ‘ `stride = 1024`둜 μ„€μ •ν•˜λ©΄ PPL은 `19.44`둜 GPT-2 λ…Όλ¬Έμ—μ„œ 보고된 `19.93`κ³Ό 거의 λ™μΌν•©λ‹ˆλ‹€.
`stride = 512`둜 μŠ¬λΌμ΄λ”© μœˆλ„μš° μ „λž΅μ„ μ‚¬μš©ν•˜λ©΄ PPL은 `16.45`둜 λ–¨μ–΄μ§‘λ‹ˆλ‹€. 
μ΄λŠ” 더 쒋은 점수일 뿐만 μ•„λ‹ˆλΌ μ‹œν€€μŠ€ ν™•λ₯ μ˜ μ‹€μ œ μžλ™ νšŒκ·€ 뢄해에 더 κ°€κΉŒμš΄ λ°©μ‹μœΌλ‘œ κ³„μ‚°λ©λ‹ˆλ‹€.