일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- python 문법
- 정규분포 MLE
- scatter
- Comparisons
- Array operations
- Python 특징
- Numpy
- 가능도
- 표집분포
- VSCode
- groupby
- Python 유래
- 부스트캠프 AI테크
- type hints
- seaborn
- ndarray
- pivot table
- linalg
- 딥러닝
- Python
- subplot
- 최대가능도 추정법
- unstack
- Numpy data I/O
- Operation function
- 카테고리분포 MLE
- dtype
- namedtuple
- boolean & fancy index
- BOXPLOT
- Today
- Total
또르르's 개발 Story
[20-2] HuggingFace's Transformers - GPT-2 본문
HuggingFace는 Transformer에 기반한 다양한 모델을 제공합니다.
Huggingface's의 다양한 모델과 사용법들은 아래 링크에 존재합니다.
https://huggingface.co/transformers/index.html
Transformers — transformers 4.3.0 documentation
Blenderbot (from Facebook) released with the paper Recipes for building an open-domain chatbot by Stephen Roller, Emily Dinan, Naman Goyal, Da Ju, Mary Williamson, Yinhan Liu, Jing Xu, Myle Ott, Kurt Shuster, Eric M. Smith, Y-Lan Boureau, Jason Weston. Ble
huggingface.co
https://github.com/huggingface/transformers
huggingface/transformers
🤗Transformers: State-of-the-art Natural Language Processing for Pytorch and TensorFlow 2.0. - huggingface/transformers
github.com
Hugging Face – On a mission to solve NLP, one commit at a time.
Text Classification • Updated Dec 11, 2020 • 507k
huggingface.co
1️⃣ 설정
transformers를 install합니다.
!pip install transformers
필요한 모듈을 import 합니다.
from transformers import *
from torch import nn
from tqdm import tqdm
import torch
2️⃣ GPT-2 불러오기
Pre-train된 GPT-2의 config, tokenizer, model을 각각 불러올 수 있습니다.
gpt_name = 'gpt2'
from_pretrained 함수는 대량 데이터로 훈련된 모델을 가지고 옵니다.
만약 from_pretrained로 불러오지 않으면 구조는 똑같지만, 훈련이 되지않은 모델을 가지고 옵니다.
주의할점은 model과 tokenizer가 같은 모델을 불러와야합니다. (small, base, large와 같이 model과 tokenizer는 같은 데이터로 학습되어 있기 때문에)
config = GPT2Config.from_pretrained(gpt_name)
tokenizer = GPT2Tokenizer.from_pretrained(gpt_name)
model = GPT2Model.from_pretrained(gpt_name)
config는 아래와 같은 구조를 가집니다.
>>> config
GPT2Config {
"activation_function": "gelu_new",
"architectures": [
"GPT2LMHeadModel"
],
"attn_pdrop": 0.1,
"bos_token_id": 50256,
"embd_pdrop": 0.1,
"eos_token_id": 50256,
"gradient_checkpointing": false,
"initializer_range": 0.02,
"layer_norm_epsilon": 1e-05,
"model_type": "gpt2",
"n_ctx": 1024, # x embedding size
"n_embd": 768, # hidden size
"n_head": 12,
"n_inner": null,
"n_layer": 12,
"n_positions": 1024,
"resid_pdrop": 0.1,
"summary_activation": null,
"summary_first_dropout": 0.1,
"summary_proj_to_labels": true,
"summary_type": "cls_index",
"summary_use_proj": true,
"task_specific_params": {
"text-generation": {
"do_sample": true,
"max_length": 50
}
},
"transformers_version": "4.3.2",
"use_cache": true,
"vocab_size": 50257
}
tokenizer는 vocab_size와 speical_tokens를 보여줍니다.
>>> tokenizer
# vocab_size=50257
# 'bos_token' : <SOS> token
# 'eos_token' : <EOS> toekn
# no <PAD> token : GPT2는 단방향이기 때문에 그 다음 token을 보지 않거나, 무시해버림
PreTrainedTokenizer(name_or_path='gpt2', vocab_size=50257, model_max_len=1024, is_fast=False, padding_side='right', special_tokens={'bos_token': AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True), 'eos_token': AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True), 'unk_token': AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True)})
model은 각 Attention들과 in_feature와 out_feature을 보여주며, 최종 fine-tuning이 어떻게 나오는지 알려줍니다.
>>> model
GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0): Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(dropout): Dropout(p=0.1, inplace=False)
)
)
...
(11): Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
3️⃣ Tokenizer 사용
예시 문장은 아래와 같습니다.
sentence = "I want to go home."
1) token -> ids
예시 문장을 tokenizer 하면 다음과 같은 구성을 가지게 됩니다.
- input_ids : (pre-trained 된) vocab 내에 정의된 index => 자동으로 앞에 101 '[CLS]', 뒤에 102 '[SEP]'를 붙여줌
- token_type_ids : 문장을 index, 한 문장이기 때문에 다 0
- attention_mask : padding(0으로 된 값)만 가려주는 값
output = tokenizer(sentence)
>>> output
{'input_ids': [40, 765, 284, 467, 1363, 13], 'attention_mask': [1, 1, 1, 1, 1, 1]}
혹은 직접 tokenize 함수를 호출할 수도 있습니다.
tokenized = tokenizer.tokenize(sentence)
>>> tokenized
['I', 'Ġwant', 'Ġto', 'Ġgo', 'Ġhome', '.'] # Ġ는 띄어쓰기를 character한 것
또한 다음과 vocabulary를 확인할 수 있습니다.
vocab = tokenizer.get_vocab()
>>> print(len(vocab))
50257
여기서 [endoftext] token은 vocab의 가장 마지막 token입니다.
>>> vocab['<|endoftext|>'] # 가장 마지막에 있는 token (end token)
50256
tokenizer의 convert_tokens_to_ids를 사용하면 token_ids를 쉽게 출력할 수 있습니다.
token_ids = tokenizer.convert_tokens_to_ids(tokenized)
>>> print(token_ids)
[40, 765, 284, 467, 1363, 13]
tokenizer의 encode를 사용하면 token_ids를 출력합니다.
token_ids = tokenizer.encode(sentence)
>>> print(token_ids)
[40, 765, 284, 467, 1363, 13]
2) ids -> token
반대로 원문으로 되돌릴 수도 있습니다.
sentence = tokenizer.convert_tokens_to_string(tokenized)
>>> print(sentence)
I want to go home.
마찬가지로 convert_ids_to_tokens 함수를 사용하면 ids를 token으로 되돌릴 수 있습니다.
tokens = tokenizer.convert_ids_to_tokens(token_ids)
>>> print(tokens)
['I', 'Ġwant', 'Ġto', 'Ġgo', 'Ġhome', '.']
convert_tokens_to_string 함수를 사용하면 string 형태로 나오게됩니다.
sentence = tokenizer.convert_tokens_to_string(tokens)
>>> print(sentence)
I want to go home.
4️⃣ 데이터 전처리
Sample data를 GPT-2에 넣을 수 있는 형태로 전처리합니다.
data = [
"I want to go home.",
"My dog's name is Max.",
"Natural Language Processing is my favorite research field.",
"Welcome. How can I help you?",
"Shoot for the moon. Even if you miss, you'll land among the stars."
]
padding을 수행합니다.
max_len = 0
batch = []
for sent in tqdm(data):
token_ids = tokenizer.encode(sent)
max_len = max(max_len, len(token_ids))
batch.append(token_ids)
tokenizer를 수행해 token을 id로 변환해줍니다.
pad_id = 0
for i, token_ids in enumerate(tqdm(batch)):
if len(token_ids) < max_len:
batch[i] = token_ids + [pad_id] * (max_len-len(token_ids))
batch = torch.LongTensor(batch)
>>> print(batch)
tensor([[ 40, 765, 284, 467, 1363, 13, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0],
[ 3666, 3290, 338, 1438, 318, 5436, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0],
[35364, 15417, 28403, 318, 616, 4004, 2267, 2214, 13, 0,
0, 0, 0, 0, 0, 0, 0, 0],
[14618, 13, 1374, 460, 314, 1037, 345, 30, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0],
[ 2484, 1025, 329, 262, 8824, 13, 3412, 611, 345, 2051,
11, 345, 1183, 1956, 1871, 262, 5788, 13]])
>>> print(batch.shape)
torch.Size([5, 18])
batch 크기만큼의 mask를 만들어줍니다.
batch_mask = (batch != pad_id).float()
>>> print(batch_mask)
tensor([[1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0.],
[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0.],
[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0.,
0., 0.],
[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0.],
[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1.]])
>>> print(batch.shape)
torch.Size([5, 18])
5️⃣ GPT-2 사용
GPT-2 model에다가 batch와 attention_mask를 넣어줍니다.
outputs = model(input_ids=batch, attention_mask=batch_mask)
여기서 자세히 보면 0번째 index에 'last_hidden_state'가 존재하는 것을 알 수 있습니다.
따라서 hidden state의 마지막 layer 출력값을 가져옵니다.
# B: batch size, L: max length, d_h: hidden size
last_hidden_states = outputs[0] # (B, L, d_h)
>>> print(last_hidden_states.shape)
torch.Size([5, 18, 768])
다음과 같이 fully connected layer를 하나 사용하여 다음 단어를 예측할 수 있습니다.
lm_linear = nn.Linear(config.hidden_size, config.vocab_size)
# V: vocab size
lm_output = lm_linear(last_hidden_states) # (B, L, V)
>>> print(lm_output)
tensor([[[-0.7913, 3.6519, 3.5032, ..., 0.6125, -0.1065, -0.1929],
[-1.4443, 6.7288, 6.9894, ..., 1.5242, 0.7410, 0.7647],
[-4.0697, 12.1660, 12.2526, ..., 0.4085, 2.7958, -0.4065],
...,
[-3.7830, 11.3455, 12.0138, ..., 2.5964, 0.9142, 1.3958],
[-3.7818, 11.3579, 12.0331, ..., 2.5883, 0.9291, 1.3894],
[-3.7783, 11.3682, 12.0460, ..., 2.5851, 0.9382, 1.3849]],
[[-0.6966, 3.2385, 3.1239, ..., 0.5808, -0.0614, -0.2034],
[-1.9884, 9.2645, 8.7747, ..., 1.5402, 1.4220, -0.1500],
[-2.3617, 9.1607, 9.4420, ..., 1.3571, 2.8291, -0.3722],
...,
[-3.8542, 11.5922, 12.0352, ..., 2.5888, 1.0430, 1.2891],
[-3.8524, 11.6009, 12.0449, ..., 2.5863, 1.0460, 1.2877],
[-3.8581, 11.6095, 12.0552, ..., 2.5826, 1.0502, 1.2887]],
[[-0.7175, 2.7248, 2.6493, ..., 0.2418, 0.0711, -0.1356],
[-2.2305, 7.6880, 7.5684, ..., -0.0646, 1.0513, -0.1847],
[-2.2820, 7.6720, 8.2406, ..., 0.2072, 2.1117, 0.1297],
...,
[-3.7283, 11.3132, 11.9637, ..., 2.9279, 1.1766, 1.7487],
[-3.7396, 11.3168, 11.9674, ..., 2.9194, 1.1816, 1.7538],
[-3.7550, 11.3327, 11.9841, ..., 2.9043, 1.1911, 1.7489]],
[[-0.9455, 2.9493, 2.7908, ..., -0.0831, 0.1867, -0.2864],
[-2.5054, 9.1022, 9.3702, ..., 2.7348, 0.2493, 1.3105],
[-2.8008, 10.2121, 11.0764, ..., 1.9745, 1.9565, 0.6839],
...,
[-3.0636, 9.6677, 10.1732, ..., 2.8035, 0.5857, 1.3267],
[-3.0689, 9.6734, 10.1781, ..., 2.7997, 0.5890, 1.3274],
[-3.0759, 9.6843, 10.1862, ..., 2.7965, 0.5934, 1.3289]],
[[-0.5438, 3.2860, 3.1016, ..., 0.7102, -0.2854, -0.2231],
[-1.7813, 8.1649, 8.4487, ..., 2.0985, 0.9220, 0.6952],
[-1.7021, 7.9246, 7.5861, ..., 1.8540, 0.9416, 0.5683],
...,
[-2.9117, 10.7740, 11.3179, ..., 1.7744, 1.6347, -0.3579],
[-1.8014, 9.8287, 9.7994, ..., 1.9349, 1.1544, 0.2743],
[-4.6167, 11.6725, 12.0909, ..., 2.3268, 1.1195, 1.3845]]],
grad_fn=<AddBackward0>)
>>> print(lm_output.shape)
torch.Size([5, 18, 50257])
6️⃣ GPT-2 다양한 모델
GPT-2의 다양한 head를 추가한 모델을 제공합니다.
https://huggingface.co/transformers/model_doc/gpt2.html
OpenAI GPT2 — transformers 4.3.0 documentation
past_key_values (tuple(tuple(torch.FloatTensor)), optional, returned when use_cache=True is passed or when config.use_cache=True) – Tuple of tuple(torch.FloatTensor) of length config.n_layers, with each tuple having 2 tensors of shape (batch_size, num_he
huggingface.co
- GPT2LMHeadModel : Language Modeling 수행
lm_model = GPT2LMHeadModel.from_pretrained(gpt_name)
GPT2LMHeadModel은 마지막 Linear에서 vocab size만큼의 vector를 출력합니다. (단어를 맞춰야 하기 때문)
>>> lm_model
GPT2LMHeadModel(
(transformer): GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0): Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(dropout): Dropout(p=0.1, inplace=False)
)
)
...
(11): Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
(lm_head): Linear(in_features=768, out_features=50257, bias=False) # vocab size만큼 출력
)
GPT2LMHeadModel은 input_ids와 labels를 함께 줄 경우 자동으로 cross entropy loss까지 계산해줍니다.
labels가 주어지지 않을 경우엔 기존과 동일하게 결과만 주어집니다.
outputs = lm_model(input_ids=batch, attention_mask=batch_mask, labels=batch)
>>> outputs
CausalLMOutputWithCrossAttentions([('loss', # loss 출력
tensor(6.2792, grad_fn=<NllLossBackward>)),
('logits',
tensor([[[ -39.3084, -39.0100, -41.8374, ..., -46.9337, -44.9073,
-39.5149],
[ -68.6073, -68.2685, -74.1494, ..., -76.0179, -78.3470,
-72.2143],
[-137.8778, -137.4907, -143.7934, ..., -147.5330, -148.3120,
-140.3080],
...,
[-128.7599, -127.7347, -129.9467, ..., -140.4377, -140.0011,
-125.0607],
[-128.9034, -127.8914, -130.1397, ..., -140.5851, -140.1500,
-125.2252],
[-128.9673, -127.9750, -130.2378, ..., -140.6508, -140.2140,
-125.3020]],
...
loss는 outputs의 0번째에 존재합니다.
loss = outputs[0]
>>> print(loss)
tensor(6.2792, grad_fn=<NllLossBackward>)
logits는 outputs의 1번쨰에 존재합니다.
logits = outputs[1]
>>> print(logits.shape)
torch.Size([5, 18, 50257])
7️⃣ Special token 추가
경우에 따라선 별도의 special token을 추가하고 싶을 수 있습니다.
huggingface에는 special token을 추가할 수 있습니다.
GPT-2의 vocab은 50257 크기입니다.
>>> print(vocab)
{'!': 0, '"': 1, '#': 2, '$': 3, '%': 4, '&': 5, "'": 6, '(': 7, ')': 8, '*': 9, '+': 10, ',': 11, '-': 12, '.': 13, '/': 14, '0': 15, '1': 16, '2': 17, '3': 18, '4': 19, '5': 20, '6': 21, '7': 22, '8': 23, '9': 24, ':': 25, ';': 26, '<': 27, ..
>>> print(len(vocab))
50257
special token을 추가하기 위해 token 정의를 해줍니다.
special_tokens = {
'bos_token': '[BOS]',
'eos_token': '[EOS]',
'pad_token': '[PAD]',
'additional_special_tokens': ['[SP1]', '[SP2]']
}
tokenizer의 add_special_tokens 함수를 사용하면 tokenizer의 vocab에 token이 추가되는 것을 알 수 있습니다.
num_new_tokens = tokenizer.add_special_tokens(special_tokens)
>>> print(num_new_tokens)
5
vocab을 확인하면 50257개에서 5개가 추가된 50262개라는 것을 알 수 있습니다.
vocab = tokenizer.get_vocab()
>>> print(len(vocab)) # 5개가 증가한 것을 알 수 있다. (마지막에 추가됨)
50262
Special token을 추가했다면 거기에 맞게 모델의 embedding layer의 input size도 바꿔주어야 합니다.
>>> model.resize_token_embeddings(len(vocab)) # 바뀐 vocab size를 model 알려주서 input size를 변경해야함
Embedding(50262, 768)
model을 확인하면 embedding layer의 input size가 바뀐 것을 알 수 있습니다.
>>> model
GPT2Model(
(wte): Embedding(50262, 768) # input size가 변경
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0): Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(dropout): Dropout(p=0.1, inplace=False)
)
)
...
'부스트캠프 AI 테크 U stage > 실습' 카테고리의 다른 글
[22-2] PageRank using Networkx (0) | 2021.02.23 |
---|---|
[21-1] Graph using networkx (0) | 2021.02.22 |
[20-3] KoELECTRA (수정) (0) | 2021.02.20 |
[20-1] HuggingFace's Transformers - BERT (0) | 2021.02.20 |
[19-3] Masked Multi-head Attention Using PyTorch (0) | 2021.02.19 |