머신러닝

sentence generator with GPT-2

content0474 2024. 10. 31. 15:14

이번에는 사전학습된 모델을 불러와서 아주 간단하게 문장 일부를 주면 뒷문장을 써주는 모델을 만들어보겠다.

 

전체코드(colab사용)

 
더보기

import torch
model = torch.hub.load('huggingface/pytorch-transformers', 'modelForCausalLM', 'gpt2').to('cuda')
tokenizer = torch.hub.load('huggingface/pytorch-transformers', 'tokenizer', 'gpt2')


input_text="Once upon a time"  #사용자가 제공하는 텍스트


tokenizer.pad_token = tokenizer.eos_token
encoded_input = tokenizer(input_text, return_tensors='pt', padding=True).to('cuda')

 

output = model.generate(
    encoded_input['input_ids'],
    attention_mask=encoded_input['attention_mask'],
    max_length=50,
    pad_token_id=tokenizer.eos_token_id,
    num_return_sequences=1
)

 

generated_text = tokenizer.decode(output[0], skip_special_tokens=True)
print(generated_text)

 

import torch
model = torch.hub.load('huggingface/pytorch-transformers', 'modelForCausalLM', 'gpt2').to('cuda')
tokenizer = torch.hub.load('huggingface/pytorch-transformers', 'tokenizer', 'gpt2')

더보기

사전학습된 모델과 토크나이저를 불러오는 코드

colab의 GPU를 활성화하고 모델과 데이터를 GPU로 전송시키기 위해 .to('cuda')를 써줬다.

(내 컴퓨터에는 cuda CPU버전밖에 설치가 안 되는 이슈로 colab 사용)

 

tokenizer.pad_token = tokenizer.eos_token

더보기

이건 토크나이저에서 패드토큰과 문장을 끝내는 토큰(end of sentence, eos) 을 같은걸로 설정하는 코드

입력하지 않으면 토큰 관련된 오류가 난다.

GPT-2는 패딩토큰이 설정되어 있지 않아서 임시로 eos토큰을 패딩토큰으로 가져다 쓴 것

나중에 attention mask를 설정해서 패딩과 문장이 끝나는 것을 구분할 수 있게 해주면 된다. 

 

 

encoded_input = tokenizer(input_text, return_tensors='pt', padding=True).to('cuda')

더보기

input_text(사용자가 입력한 내용)를 토큰화하고(tokenizer),

파이토치텐서(=pt)로 변환해서 패딩처리한 내용을 encoded_input에 저장했다.

 

이 때 encoded_input을 확인해보면 아래와 같이 input_id와 attenstion_mask를 키로 하는 딕셔너리임을 알 수 있다.

input_id: 문자열을 숫자로 변환하고 텐서로 처리

attention mask: 중요한 부분을 1로 그렇지 않은 부분을 0으로 표기한다.

attention mask는 어텐션 메커니즘이 특정 위치에 주의를 기울이도록 하는 필터

 

ex 문장길이를 6으로 설정해서 남은 부분에 패딩을 한다고 하면

text='I love you'

input_ids=[342, 46, 354, eos, pad, pad]

attention_mask=[1,1,1,1,0,0]

이 예시를 보면 attention mask에서 문장이 끝났을때는 1로 표시되지만 패딩은 0으로 표시되는 것을 알 수 있다.

-> attention mask를 설정해서 패딩과 문장이 끝나는 것을 구분

참고)tokenizer.encode(input_text, return_tensors='pt')

더보기

tokenizer() 가 아닌 tokenizer.encode() 를 쓰면 input_ids만 만들어진다.

이러면 나중에 attention_mask를 따로 정의해서 써야 하기 때문에 귀찮아진다.

 

output = model.generate(
    encoded_input['input_ids'],
    attention_mask=encoded_input['attention_mask'],
    max_length=50,
    pad_token_id=tokenizer.eos_token_id,
    num_return_sequences=1
)

더보기

결과를 우리가 불러온 모델에서 생성

참고로 output은 [tensor([ 342, 46, 354 , ...])]  이런식으로 'tensor형태로 저장된 시퀀스'가 들어있는 리스트이다.

 

(사실 많은것들이 리스트형태로 받고 또 리스트형태로 내놓는다..)

 

encoded_input['input_ids'], attention_mask=encoded_input['attention_mask']

더보기

위에서 확인한 바에 따르면

encoded_input={'input_ids':value1, 'attention_mask': value2} 형식의 딕셔너리였고, 여기서 키로 벨류를 호출하고 있는것

 

max_length=50

더보기

최대 50개까지의 토큰만 생성

 

pad_token_id=tokenizer.eos_token_id

더보기

GPT-2는 패딩처리가 없기 때문에 패딩토큰을 문장을 끝내는 토큰과 동일하게 처리해줌

참고로 위에서 tokenizer.pad_token = tokenizer.eos_token 이렇게 쓴 것은 토크나이저에서 두 토큰이 같다고 한 것이고, 모델에서 생성할 때는 명시적으로 pad_token_id를 지정해줘야 오류가 안생긴다.

num_return_sequences=1

더보기

생성할 시퀀스의 개수가 1개 -> 한 가지 문장이 출력됨

 

generated_text = tokenizer.decode(output[0], skip_special_tokens=True)

더보기

tokenizer.decode는 숫자를 사람이 읽을수있는 문장으로 바꿔주는 함수

output[0]은 생성한 시퀀스 리스트의 첫번째를 의미

리스트에 시퀀스가 하나만 있어도  output[0]을 해줘야 한다.

그냥 output만 입력하면 정수값을 받아야 하는데 리스트를 받았다는 오류가 난다.

결과물1

 

 

그렇다면, num_return_sequences를 여러 개 해서 문장을 여러 개 뽑아볼 수 있을까?

output = model.generate(
    encoded_input['input_ids'],
    attention_mask=encoded_input['attention_mask'],
    max_length=50,
    pad_token_id=tokenizer.eos_token_id,
    num_beams=3,              
    num_return_sequences=3      
)
num_beams=3
더보기
위의 코드 없이 바로 num_return_sequences=3 을 입력하면 아래와 같은 오류가 난다.
ValueError: Greedy methods without beam search do not support num_return_sequences different than 1 (got 3).
 
무슨 뜻이냐면, generate()는 디폴트로 greedy search 방식을 사용한다. 이는 다음 단어를 선택할 때 가장 가능성이 높은 단어 하나를 선택해 시퀀스를 이어나가는 방식이다. 가능성이 높은것 하나만 선택하므로 문장도 하나만 만들 수 있다.
 
beam search 방식은 여러 경로를 동시에 탐색하므로, 경로 수를 설정해주면 상위 n개의 경로를 추적해 n개의 시퀀스를 뽑아낸다. greedy search에서 beam search로 바꿔주려면 num_beams =개수  를 입력해주면 된다.
num_beams: 탐색할 경로 개수
num_return_sequences: 반환할 시퀀스의 개수
참고
num_beams> num_return_sequences
더보기
ex)
num_beams=10
num_return_sequences=3
10개의 경로를 탐색하지만, 그 중 가능성이 가장 높은 3개를 반환
-> 더 최적의 결과
 
한 편 num_beams< num_return_sequences 는 같은 경로에서 반복적으로 추출하는 결과가 나오므로 잘 사용되지 않음 

결과물2

 

이제 output[0]을 입력하면 시퀀스들의 첫번째가 반환되고, output[1]을 입력하면 두번째가 반환된다.

우리는 num_beams> num_return_sequences=3이므로 output[2] 까지 출력할 수 있다.

 

만약 output[0] 이런식으로 입력하지 않고 생성된 모든 시퀀스를 다 보고싶다면?

generated_texts = [tokenizer.decode(seq, skip_special_tokens=True) for seq in output]
for i, text in enumerate(generated_texts):
    print(f"Generated Sequence {i+1}: {text}")

이렇게 리스트 컴프리헨션을 사용하고 for in 문으로 반복해주면 된다.

결과물3

 

 


 

What's next?

더보기

AI 활용 강의 듣기(금,토,일)

딥러닝+감정분석 

Style Transfer Model

'머신러닝' 카테고리의 다른 글

openAI API를 활용한 챗봇 구현  (0) 2024.11.04
Sentiment Analysis with LSTM  (2) 2024.11.01
skin cancer HAM10000(2)  (0) 2024.10.30
skin cancer HAM10000  (0) 2024.10.29
wordcloud  (2) 2024.10.28