How to generate text: using different decoding methods for language generation with Transformers

Introduction

최근 몇 년 동안 수백만 개의 웹 페이지에서 훈련된 대규모 트랜스포머(transformer) 기반 언어 모델의 등장으로, OpenAI의 ChatGPT와 Meta의 LLaMA를 포함한 개방형 언어 생성(Open-ended language generation)에 대한 관심이 증가하고 있습니다. 조건부 개방형 언어 생성(Conditioned open-ended language generation) 결과는 새로운 작업에 적용되거나 코드를 처리하거나 비텍스트 데이터를 입력으로 사용할 수 있을 만큼 인상적입니다. 개선된 트랜스포머(Transformer) 아키텍처와 방대한 비감독 학습 데이터(Unsupervised training data), 더 나은 디코딩 방법(Decoding methods)도 중요한 역할을 했습니다.

이 블로그 게시물은 다양한 디코딩 전략(Decoding strategies)에 대한 간략한 개요를 제공하며, 더 중요하게는 인기 있는 트랜스포머(transformer) 라이브러리를 사용하여 이를 매우 적은 노력으로 구현하는 방법을 보여줍니다!

다음 기능들은 모두 자동 회귀 언어 생성(Auto-regressive language generation)에 사용될 수 있습니다(여기에서는 복습입니다). 간단히 말해, 자동 회귀 언어 생성은 단어 시퀀스의 확률 분포가 조건부 다음 단어 분포(Conditional next word distributions)의 곱으로 분해될 수 있다는 가정에 기반을 두고 있습니다:

$$ P\left(w_{1: T} \mid W_0\right)=\prod_{t=1}^T P\left(w_t \mid w_{1: t-1}, W_0\right), \text { with } w_{1: 0}=\emptyset $$

와 $W_0$은 초기 문맥 단어 시퀀스(Initial context word sequence)입니다. 단어 시퀀스의 길이 $T$는 보통 동적으로 결정되며, 시간단계 $t=T$에서 EOS 토큰이 $P\left(w_t \mid w_{1: t-1}, W_0\right)$에서 생성될 때에 해당합니다.

현재 가장 주목받는 디코딩 방법들, 주로 탐욕 검색(Greedy search), 빔 검색(Beam search), 샘플링(Sampling)에 대한 투어를 제공할 것입니다.

빠르게 트랜스포머(Transformers)를 설치하고 모델을 로드해 봅시다. 데모용으로 GPT2 in PyTorch를 사용할 것이지만, TensorFlow와 JAX에서의 API는 1대1로 동일합니다.

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

torch_device = "cuda" if torch.cuda.is_available() else "cpu"

tokenizer = AutoTokenizer.from_pretrained("gpt2")

# add the EOS token as PAD token to avoid warnings
model = AutoModelForCausalLM.from_pretrained("gpt2", pad_token_id=tokenizer.eos_token_id).to(torch_device)

Greedy Search(탐욕 검색)

탐욕 검색은 가장 간단한 디코딩 방법(Decoding method)입니다. 각 시간단계 $t$에서 다음 단어로 가장 높은 확률을 가진 단어를 선택합니다: $w_t=\operatorname{argmax}w P\left(w \mid w{1: t-1}\right)$. 다음 스케치는 탐욕 검색을 보여줍니다.

Pasted image 20240513214757.png

"그(The)"라는 단어에서 시작하여, 알고리즘은 탐욕적으로 가장 높은 확률의 다음 단어 "좋은(nice)"을 선택하고, 이런 식으로 최종적으로 생성된 단어 시퀀스는 ("그(The)", "좋은(nice)", "여성(woman)")이며 전체 확률은 \(0.5 \times 0.4 = 0.2\)입니다.

이어서 우리는 ("나(I)","즐기다(enjoy)","걷기(walking)","와(with)","내(my)","귀여운(cute)","강아지(dog)")라는 문맥에서 GPT2를 사용하여 단어 시퀀스를 생성할 것입니다. 탐욕 검색(Greedy search)이 트랜스포머(Transformers)에서 어떻게 사용될 수 있는지 보겠습니다:

# encode context the generation is conditioned on
model_inputs = tokenizer('I enjoy walking with my cute dog', return_tensors='pt').to(torch_device)

# generate 40 new tokens
greedy_output = model.generate(**model_inputs, max_new_tokens=40)

print("Output:\\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
Output:
----------------------------------------------------------------------------------------------------
I enjoy walking with my cute dog, but I'm not sure if I'll ever be able to walk with my dog. I'm not sure if I'll ever be able to walk with my dog.

I'm not sure

좋아요! GPT2를 사용하여 첫 번째 짧은 텍스트를 생성했습니다 😊. 문맥에 따른 생성된 단어들은 합리적이지만, 모델이 빠르게 반복하기 시작합니다! 이것은 일반적으로 언어 생성에서 매우 흔한 문제이며, 탐욕 검색(Greedy search)과 빔 검색(Beam search)에서는 더욱 그렇습니다 - Vijayakumar et al., 2016Shao et al., 2017을 확인해보세요.

하지만 탐욕 검색의 주요 단점은 위 스케치에서 볼 수 있듯이 낮은 확률 단어 뒤에 숨겨진 높은 확률 단어를 놓친다는 것입니다: