7 minute read


개요

Auto-regressive language model을 generation 할 때, 사용하는 디코딩 방법들을 정리합니다. 즉, 언어 모델이 다음 단어를 선택할 때 어떤 기준으로 선택하는가에 대한 방법들을 정리합니다.

예시로는 skt에서 공개한 kogpt2 모델을 사용하였습니다.

import torch
from transformers import PreTrainedTokenizerFast
from transformers import GPT2LMHeadModel

tokenizer = PreTrainedTokenizerFast.from_pretrained(
  "skt/kogpt2-base-v2",
   bos_token="</s>", 
  eos_token="</s>", 
  unk_token="<unk>",
   pad_token="<pad>", 
  mask_token="<mask>"
)
model = GPT2LMHeadModel.from_pretrained("skt/kogpt2-base-v2")

Decoding Methods

greedy search는 간단하게 다음 단어들 중에서 확률이 가장 높은 토큰을 하나 선택합니다.

그림처럼 dog, nice, car가 순서대로 [0.4, 0.5, 0.1] 이기 때문에 가장 높은 확률값을 가진 nice를 선택합니다.

그리고 woman, house, guy가 순서대로 [0.4, 0.3, 0.3]이기 때문에 가장 확률값이 높은 woman을 선택합니다. 여기서 woman을 선택할 때는 앞에서 선택한 nice의 확률값과는 독립적입니다. image

text = "근육이 커지기 위해서는"

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
greedy_output = model.generate(input_ids, max_length=100)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Output:
----------------------------------------------------------------------------------------------------
근육이 커지기 위해서는 무엇보다 규칙적인 생활습관이 중요하다.
특히, 아침식사는 단백질과 비타민, 무기질  영양소가 풍부한 음식을 골고루 섭취하는 것이 좋다.
또한 규칙적인 운동은 근육을 강화시켜주는 효과가 있다.
특히, 아침식사는 단백질과 비타민, 무기질  영양소가 풍부한 음식을 골고루 섭취하는 것이 좋다.
또한 규칙적인 운동은 근육을 강화시켜주는 효과가 있다.
특히, 아침식사는 단백질과 비타민, 무기질  영양소가 풍부한 음식을 골고루 섭취하는 것이 좋다.
또한 규칙적인 운동은 근육

Beam search는 각 타임스텝에서 가장 가능성이 높은 hypotheses의 개수를 유지하면서 최종적으로 그 중에서 가장 확률이 높은 hypotheses를 선택하는 것입니다. 답변을 조금 더 다양하게 할 수 있습니다.

사진은 num_beams=2 일때의 예시입니다.

위와 마찬가지로 dog, nice, car가 순서대로 [0.4, 0.5, 0.1] 이기 때문에 beam 개수 (여기서는 2개) 만큼 선택합니다. 그러면 (dog, 0.4), (nice, 0.5)가 선택됩니다. 위에서는 각 타임스텝의 확률 값 비교가 이전 타임스텝과는 독립적이었는데, 여기서는 누적 확률로 계산되기 때문에 서로 곱해져서 비교를 합니다.

(dog, and, 0.02), (dog, runs, 0.02), (dog, has, 0.36), (nice, woman, 0.2), (nice, house, 0.15), (nice, guy, 0.15) 중에서 누적 확률이 높은 hypotheses를 beam 개수만큼 선택합니다. 여기서는 “dog has” 와 “nice woman”이 선택됩니다. image

text = "근육이 커지기 위해서는"

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
greedy_output = model.generate(input_ids, num_beams=5, max_length=100)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Output:
----------------------------------------------------------------------------------------------------
근육이 커지기 위해서는 피부  콜라겐과 엘라스틴의 생성을 촉진시키는 것이 중요하다.
콜라겐과 엘라스틴의 생성을 촉진시키기 위해서는 피부  콜라겐과 엘라스틴의 생성을 촉진시키는 것이 중요하다.
피부  콜라겐과 엘라스틴의 생성을 촉진시키기 위해서는 피부  콜라겐과 엘라스틴의 생성을 촉진시키는 것이 중요하다.
피부  콜라겐과 엘라스틴의 생성을 촉진시키기 위해서는 피부  콜라겐과 엘라스틴의 생성을

하지만 여전히 반복되는 단어가 많은 것 같습니다. 이때 사용할 수있는 옵션으로는 no_repeat_ngram_size이 있습니다. 가장 일반적인 n-gram 페널티는 이미 나온 n-gram을 생성할 수 있는 다음 단어의 확률을 수동으로 0으로 설정하여 n-gram이 반복되지 않도록 하는 것입니다.

no_repeat_ngram_size=2 로 설정하면 이미 나왔던 2-gram이 다시 나오지 않습니다.

text = "근육이 커지기 위해서는"

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
greedy_output = model.generate(input_ids, num_beams=5, no_repeat_ngram_size=2, max_length=100)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Output:
----------------------------------------------------------------------------------------------------
근육이 커지기 위해서는 피부  콜라겐과 엘라스틴의 생성을 촉진시키는 것이 가장 중요하다.
콜라겐은 피부의 탄력을 유지하는  중요한 역할을 하기 때문에 피부 노화를 예방하고 탄력 있는 피부로 가꿔주는 것이 중요하다.
또한 피부 탄력이 떨어지기 쉬운 겨울철에는 보습과 영양을 동시에 챙길  있는 제품을 선택하는 것이 좋다.
겨울철에는 피부가 건조해지기 쉬우므로 충분한 수분을 섭취하는 것이 중요하다.

결과가 훨씬 잘 나온 것을 확인할 수 있습니다. no_repeat_ngram_size를 사용할 때 주의할 점은 설정한 n-gram이 반복해서 나오지 않기 때문에 예를 들어 “신한” “은행” 이 2-gram이라고 하면 “신한” “은행”은 한번 밖에 디코딩되지 않는 참사가 일어날 수 있습니다. 그래서 언어에 따라 어떻게 토크나이징 되는지 확인하는 것이 중요합니다. 한국어에서는 보통 no_repeat_ngram_size 를 4로 많이 사용하는 것 같습니다.

하나의 output이 아니라 여러 개의 output을 받고 싶을 때는 num_return_sequences 로 설정할 수 있습니다. 하지만 num_return_sequencesnum_beams보다는 클 수 없습니다!

image image

Greedy search도 beam search도 결과 확률 값이 높은 쪽을 따라 가는 것이기 때문에 뻔한 대답이 많이 나온다고 합니다.

ICLR 2020에서 발표한 The Curious Case of Neural Text Degeneration 논문에서는 같은 context를 주고 사람이 실제 이야기 한 것과 beam search 한 것을 비교했는데, 사람이 실제 이야기 한 것은 variance가 엄청컸다고 말하고 있습니다. 그래서 beam search가 기계번역이나 요약에서는 잘 작동하지만, dialog 또는 story generation 에서는 엄청 지루하고 뻔한 이야기만 한다고 말하고 있습니다.

그래서 beam search와 사람이 말하는 것 같은 dynamic 함의 간극을 강화학습으로 잘 해결했다고 생각합니다. 지금은 OpenAI가 RLHF (Reinforcement Learning from Human Feedback)로 좋은 사례를 보여줬지만, 이전에는 sampling을 사용해서 randomness를 주었습니다.

Sampling

image

sampling을 사용하려면 do_sample=True 로 사용할 수 있습니다. 같이 사용하는 옵션으로는 top_k와 top_p가 있습니다. top_k는 전체 토큰의 확률 값중에서 k를 sampling 하겠다는 것이고, top_p는 확률 값 p 이상인 것만 sampling 하겠다는 것입니다. 동시에 같이 사용할 수도 있습니다.
자세한 내용은 해당 포스트에서 확인해주세요.

image

또한 temperature 를 조절하면 randomness를 컨트롤 할 수 있습니다. temperature를 1 이하로 사용하면 높은 값은 더욱 더 높아지고, 낮은 값은 더욱 더 낮아지게 됩니다.

Top-K Sampling

image K를 6으로 한 예시 사진입니다. 전체 vocab에서 확률 값이 높은 6개만 sampling하여 사용합니다.

하지만 이렇게 개수로 sampling 하면 왼쪽 사진에서는 “man” 과 “people”의 차이가 작음에도 순위가 밀려 선택이 되지 않고, 오른쪽 사진에서는 “down”, “a” 같이 값이 낮음에도 선택이 되는 경우가 발생합니다. 이를 dynamic하게 sampling 하기 위해 Top-p 또는 nucleus sampling이 나오게 됩니다.

text = "근육이 커지기 위해서는"
torch.manual_seed(0)

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
greedy_output = model.generate(input_ids, no_repeat_ngram_size=2, do_sample=True, top_k=6, max_length=100)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Output:
----------------------------------------------------------------------------------------------------
근육이 커지기 위해서는 무엇보다도 식습관 개선과 더불어 영양소가 풍부해야 한다.
식욕이 왕성하면 체내의 수분이 부족한 상태에서 영양분이 부족해지기 때문이다.
따라서 균형 잡힌 영양소를 섭취하는 것이 중요하며, 영양이 풍부한 음식을 먹도록 해야 한다.
이런 영양소 섭취는 신진대사를 활성화시켜 신진대사량을 높여주고 신진 대사량을 증가시켜 준다.
또한 신진대사가 원활해지면 혈중 콜레스테롤이 감소되고, 혈압을 낮추는  도움이 된다.
따라서, 체내 수분이 풍부한 음식과 과일을 섭취

Top-p (nucleus) sampling

image

top-p sampling은 확률값이 높은 순서대로 내림차순 정렬을 한 뒤 누적 확률값이 p 이하인 단어들 가운데 하나를 다음 단어로 선택하는 기법입니다.

text = "근육이 커지기 위해서는"
torch.manual_seed(0)

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
greedy_output = model.generate(input_ids, no_repeat_ngram_size=2, do_sample=True, top_p=0.75, max_length=100)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Output:
----------------------------------------------------------------------------------------------------
근육이 커지기 위해서는 무엇보다도 중요한 것이 근육이다.
근육은 근육으로 인해 생긴 여러 가지 문제를 해결해주는 역할을 하는데,  중에서도 척추근육 중요하다는 것이 가장  특징이다.
척추는 우리 몸에서 가장 대표적인 척추다.
 때문에 평소에는 몸을 움직이는  있어 가장 중요한 역할을 한다.
그래서 체중의 절반 이상을 차지하는 근육이 제대로 움직일  있도록 도와주는 근육과 인대가 가장 중요하다.
근육을 많이 사용하지 않으면 체중을 지탱하기 어려워 척추가  약해져 요통이나 허리디스크, 디스크가 발생하기 쉽다.

top-k와 top-p를 동시에 사용할 수도 있습니다.

text = "근육이 커지기 위해서는"
torch.manual_seed(0)

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
beam_outputs = model.generate(input_ids, no_repeat_ngram_size=2, do_sample=True, top_k=50, top_p=0.75,  num_return_sequences=3, max_length=100)

print("Output:\n" + 100 * '-')
for i, beam_output in enumerate(beam_outputs):
  print("{}: {}".format(i, tokenizer.decode(beam_output, skip_special_tokens=True)))
Output:
----------------------------------------------------------------------------------------------------
0: 근육이 커지기 위해서는 무엇보다도 생활패턴을 바꿔야 한다.
특히 여름철에는 시원한 그늘이 아닌, 시원하고 시원한 바닷가로 가서 수영을 하거나 수영복이나 스노우보드를 신고 운동을 하는 것이 좋다.
또한 자외선을 피하기 위해 외출할 때에는 자외선 차단제와 함께 자외선 차단에 신경써야 하며, 평소보다 자외선 노출량을 늘려주도록 해야 한다.
여름철 가장 주의해야  것은 자외선으로부터 피부를 보호하기 위해 보습이 필요한 피부이므로 피부 보호에 소홀해서는  된다.
자외선에 노출된
1: 근육이 커지기 위해서는  가지가 필요하다.
첫째, 성장호르몬이 분비되면 성장 호르몬이 제대로 분비되기 위해서는 성장 속도를 조절하는 것이 중요하다.
 번째는 성장기 어린이는 성장 과정에서 성장 호르몬의 분비가 원활하게 이루어지지 못해 성장 기분이 좋지 않아 성장장애를 겪는다.
성장호르몬의 분비량과 수치가 일정 수준에 도달하면 성장 장애로 인한 성장 지연이나 통증, 성장 부진으로 인해 정신 장애나 우울증 등의 증세가 나타날  있다.
 번째는 호르몬 분비량이 너무 적거나 과도하게 분비되어 성장이 둔화되고
2: 근육이 커지기 위해서는 무엇보다 식단 관리를  해야 한다.
특히나 다이어트 식단을 짜는 것만큼 중요한 것이 바로 규칙적인 식사이다.
아침에는 하루의 식사가 끝나고 나서 아침과 저녁으로 운동을 하고, 점심에는 간단한 스트레칭을 하면서 운동을 하면 아침 식사를 끝낼  있다.
운동을 통해 부족한 근육을 보강하고 체지방을 분해하는 효과를   있기 때문이다.
또한 운동은 하루 30 이상 하는 것이 좋다.
운동은 일주일에   정도 하는 것도 도움이 된다.
운동을 하면서 근육에 쌓인 노폐물을

토큰이 반복되어 비슷한 내용이 디코딩 되는 문제를 해결하기 위한 디코딩 기법입니다. image

언어모델이 예측한 확률 분포 중에서 top-k개의 prediction을 고릅니다. 여기서 model confidence는 언어모델이 예측한 확률 분포 값이고, degeneration penalty는 예측한 토큰의 representations과 이전 context 들과의 representations을 cosine similarity 계산하고 가장 큰 값을 취합니다.

α=0 으로 하면 greedy search와 동일하며, 보통 k=4, α=0.6을 많이 사용합니다.

text = "근육이 커지기 위해서는"
torch.manual_seed(0)

# encode context the generation is conditioned on
input_ids = tokenizer.encode(text, return_tensors="pt")

# generate text until the output length (which includes the context length) reaches 100
greedy_output = model.generate(input_ids, penalty_alpha=0.6, top_k=4)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))

Leave a comment