๐ KOBERT ๋?
KOBERT๋ Korean Bidirectional Encoder Representations from Transformers์ ์ฝ์๋ก, SKT Brain์์ ๊ณต๊ฐํ ์ผ์ข ์ ๊ธฐ๊ณ๋ฒ์ญ ๋ชจ๋ธ์ด๋ค.
KOBERT๋ 2018๋ Google ์์ ๋ฐํํ "BERT"์ ํ๊ตญ์ด ๋ฒ์ ๋ชจ๋ธ๋ก์ ํ ์คํธ ๋ถ๋ฅ, ๊ฐ์ฒด๋ช ์ธ์, ๊ฐ์ ๋ถ์, ๊ธฐ๊ณ ๋ฒ์ญ ๋ฑ์ ์์ฐ์ด ์ฒ๋ฆฌ(Natural Language Processing) ์์ ์์ ๋ฐ์ด๋ ์ฑ๋ฅ์ ๋ฐํํ ์ ์๋ค.
๐ KOBERT Finetuning ์ค์ต
1. ํ์ด์ฌ ๋ฉ์ธ ํจํค์ง ์ค์น
# ํตํฉ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ํ ipywidgets ํจํค์ง ์ค์น
!pip install ipywidgets
# GitHub ์ ์ฅ์์์ ์ต์ ๋ฒ์ ์ KOBERT๋ฅผ ๋ถ๋ฌ์ ์ค์น
!pip install git+https://git@github.com/SKTBrain/KoBERT.git@master
2. ํ์ด์ฌ ๊ธฐํ ํจํค์ง ์ค์น
import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import gluonnlp as nlp
import numpy as np
from tqdm.notebook import tqdm
torch
: ๋ฅ ๋ฌ๋ ํ๋ ์์ํฌ์ธ PyTorch ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์จ๋ค --> ์ ๊ฒฝ๋ง ๋ชจ๋ธ ๊ตฌ์ถ & ํ์ต์์ ์ฌ์ฉ
nn
: PyTorch์ nn ๋ชจ๋์ ๊ฐ์ ธ์จ๋ค --> .์ ๊ฒฝ๋ง ๋ชจ๋ธ ์ ์์ ์ฌ์ฉ
F
: PyTorch์ nn.functional ๋ชจ๋์ ๊ฐ์ ธ์จ๋ค --> ํ์ฑํ ํจ์ & ์์ค ํจ์ ์ ๊ณต
optim
: PyTorch์ ์ต์ ํ ์๊ณ ๋ฆฌ์ฆ์ ์ ๊ณตํ๋ optim ๋ชจ๋์ ๊ฐ์ ธ์จ๋ค --> ๊ฐ์ค์น ์ ๋ฐ์ดํธ๋ฅผ ์ํ ์ต์ ํ ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ
Dataset, DataLoader
: Dataset๊ณผ DataLoader ํด๋์ค๋ฅผ ๊ฐ์ ธ์จ๋ค --> PyTorch์ ๋ฐ์ดํฐ ๋ก๋ฉ ๋ฐ ์ ์ฒ๋ฆฌ
gluonnlp
: gluonnlp ํจํค์ง๋ฅผ ๊ฐ์ ธ์จ๋ค --> ์์ฐ์ด ์ฒ๋ฆฌ ์์
์ ์ํ ๋๊ตฌ ์ ๊ณต
numpy
: NumPy ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์จ๋ค --> ๋ค์ฐจ์ ๋ฐฐ์ด ๋ฐ ์ํ์ ์ฐ์ฐ ์ง์
tqdm
: tqdm ํจํค์ง๋ฅผ ๊ฐ์ ธ์จ๋ค --> ๋ฐ๋ณต ์์ ์ ์งํ ์ํฉ์ ํ์ํ๋ ํ๋ก๊ทธ๋ ์ค ๋ฐ ์ ๊ณต
3. Tokenizer ์ ์ด์ ์ ์ค์นํ KOBERT ๋ชจ๋ธ ํธ์ถ
from kobert import get_tokenizer
from kobert import get_pytorch_kobert_model
get_tokenizer
: KoBERT ํจํค์ง์์ get_tokenizer ํจ์๋ฅผ ๊ฐ์ ธ์จ๋ค --> KoBERT ๋ชจ๋ธ์ ๋ํ ํ ํฌ๋์ด์ (tokenizer)๋ฅผ ์ ๊ณต
- ํ ํฌ๋์ด์ (tokenizer)
: ํ ์คํธ๋ฅผ ์์ ๋จ์๋ก ๋ถํ ํ์ฌ ๋ชจ๋ธ์ ์ ๋ ฅํ๋ ์ ์ฒ๋ฆฌ ๊ณผ์ ( = ํ ํฐํ )์ ์ํํ๋ ํ๋ก๊ทธ๋จ
get_pytorch_kobert_model
: KoBERT ํจํค์ง์์ get_pytorch_kobert_model ํจ์๋ฅผ ๊ฐ์ ธ์จ๋ค --> PyTorch ํ์์ผ๋ก ๋ฏธ๋ฆฌ ํ๋ จ๋ KoBERT ๋ชจ๋ธ ํธ์ถ
4. Transformers ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ AdamW & ์ฝ์ฌ์ธ ์ค์ผ์ค๋ง ๊ธฐ๋ฒ ๋ถ๋ฌ์ค๊ธฐ
from transformers import AdamW
from transformers.optimization import get_cosine_schedule_with_warmup
Adamw
: transformers ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ AdamW ์ํฌํธ --> ๊ฐ์ค์น ๊ฐ์ (weight decay), ํ์ต๋ฅ ์ค์ผ์ค๋ง ๋ฑ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ฅ๋ฌ๋ ๋ชจ๋ธ ์ต์ ํ ์๊ณ ๋ฆฌ์ฆ
get_cosine_schedule_with_warmup
: transformers.optimization์์ ์ฝ์ฌ์ธ ์ค์ผ์ค๋ง๊ณผ ์๋ฐ์ ์ ๊ฐ์ ธ์จ๋ค --> ํ์ต๋ฅ ์ ์กฐ์ ํ๋ ๋ฐฉ๋ฒ ์ค ํ๋์ธ ์ฝ์ฌ์ธ ์ค์ผ์ค๋ง ์ ๊ณต
5. Pytorch ์์ ๋๋ฐ์ด์ค ์ค์ ํ๊ธฐ
## CPU
device = torch.device("cpu")
## GPU
device = torch.device("cuda:0")
๋๋ฐ์ด์ค๋ฅผ CPU๋ก ์ค์ ํ๋ ๊ฒฝ์ฐ
: torch.device("cpu")๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ์์คํ ์์ CPU๋ฅผ ์ฌ์ฉํ๋๋ก ์ง์ --> GPU๊ฐ ์๋ ํ๊ฒฝ์์ ๋ชจ๋ธ์ ์คํํ๊ฑฐ๋, GPU๋ฅผ ์ฌ์ฉํ์ง ์๊ณ CPU๋ง์ ์ฌ์ฉํ๊ณ ์ ํ ๋ ์ฌ์ฉ
๋๋ฐ์ด์ค๋ฅผ GPU๋ก ์ค์ ํ๋ ๊ฒฝ์ฐ
: torch.device("cuda:0")๋ฅผ ์ฌ์ฉํ์ฌ ์ฒซ ๋ฒ์งธ CUDA ๋๋ฐ์ด์ค(GPU)๋ฅผ ์ง์ --> ๋ชจ๋ธ์ ์ฐ์ฐ์ด ๋ณ๋ ฌํ๋์ด ๋ ๋น ๋ฅธ ์๋๋ก ์์ ์ํ
6. BERT ๋ชจ๋ธ๊ณผ Vocabulary ๊ฐ์ ธ์ค๊ธฐ
bertmodel, vocab = get_pytorch_kobert_model(cachedir=".cache")
get_pytorch_kobert_model
: ์ง์ ๋ ์บ์ ๋๋ ํ ๋ฆฌ(".cache")์ ์๋ KoBERT ๋ชจ๋ธ๊ณผ ์ดํ์ง ๊ฐ์ ธ์ค๊ธฐ --> get_pytorch_kobert_model ํจ์๊ฐ ์ดํ์ง๊ณผ KoBERT๋ฅผ PyTorch ๋ชจ๋ธ๋ก ๋ฐํ
bertmodel, vocab
: ์ฝ๋ ์คํ ํ bertmodel ๋ณ์์๋ ๋ก๋๋ KoBERT ๋ชจ๋ธ์ด, vocab ๋ณ์์๋ ๋ก๋๋ ์ดํ์ง์ด ์ ์ฅ --> ์ ์ฅ๋ ๋ชจ๋ธ๊ณผ ์ดํ์ง์ ์ฌ์ฉํ์ฌ ํ ์คํธ ๋ถ๋ฅ, ๊ฐ์ฒด๋ช ์ธ์, ๋ฌธ์ฅ ์์ฑ ๋ฑ ๋ค์ํ ์์ฐ์ด ์ฒ๋ฆฌ ์์ ์ํ
cachedir=".cache"
: ์บ์ ๋๋ ํ ๋ฆฌ๋ฅผ ์ค์ ํ์ฌ ๋ชจ๋ธ๊ณผ ์ดํ์ง์ ์บ์ --> ์ดํ ๋์ผํ ๋ชจ๋ธ๊ณผ ์ดํ์ง์ ๋ค์ ๋ก๋ํ ๋ ๋ค์ด๋ก๋ ์๊ฐ๊ณผ ๋ฆฌ์์ค ์ ์ฝ ๊ฐ๋ฅ
7. ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋ํ๊ธฐ
!wget -O .cache/ratings_train.txt http://skt-lsl-nlp-model.s3.amazonaws.com/KoBERT/datasets/nsmc/ratings_train.txt
!wget -O .cache/ratings_test.txt http://skt-lsl-nlp-model.s3.amazonaws.com/KoBERT/datasets/nsmc/ratings_test.txt
ํ์ต์ ์ํ ํ๋ จ์ฉ ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋
: ratings_train.txt ํ์ผ์ ๋ค์ด๋ก๋ํ์ฌ .cache ๋๋ ํ ๋ฆฌ์ ์ ์ฅ --> KoBERT ๋ชจ๋ธ ํ์ต์ ์ํ ํ๋ จ ๋ฐ์ดํฐ์ ( ๋ค์ด๋ฒ ์ํ ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ์ ) ์ ์ฅ
์ฑ๋ฅ ํ๊ฐ๋ฅผ ์ํ ํ ์คํธ์ฉ ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋
: ratings_test.txt ํ์ผ์ ๋ค์ด๋ก๋ํ์ฌ .cache ๋๋ ํ ๋ฆฌ์ ์ ์ฅ --> ํ์ต๋ ๋ชจ๋ธ์ ์์ธก ๊ฒฐ๊ณผ์ ์ค์ ํ๊ฐ ์ ์๋ฅผ ๋น๊ตํ์ฌ KoBERT ๋ชจ๋ธ์ ์ฑ๋ฅ ํ๊ฐ
8. ํ ์คํธ ๋ถ๋ฅ ์์ ์ ํ์ํ ๋ฐ์ดํฐ์ ๊ตฌ์ฑ
dataset_train = nlp.data.TSVDataset(".cache/ratings_train.txt", field_indices=[1,2], num_discard_samples=1)
dataset_test = nlp.data.TSVDataset(".cache/ratings_test.txt", field_indices=[1,2], num_discard_samples=1)
ํ๋ จ ๋ฐ์ดํฐ์ ๊ตฌ์ฑ
: nlp.data.TSVDataset์ ์ฌ์ฉํ์ฌ ํ๋ จ ๋ฐ์ดํฐ์ ๊ตฌ์ฑ
.cache/ratings_train.txt | ์์ ๋ค์ด๋ก๋ํ ๋ค์ด๋ฒ ์ํ ๋ฆฌ๋ทฐ ํ๋ จ ๋ฐ์ดํฐ์ ์ ๊ฒฝ๋ก |
field_indices=[1,2] | ๋ฐ์ดํฐ์ ์์ ์ฌ์ฉํ ํ๋ ์ธ๋ฑ์ค๋ฅผ ์ง์ ํ๋ ๊ฒ์ผ๋ก, 1๋ฒ ์ธ๋ฑ์ค์ 2๋ฒ ์ธ๋ฑ์ค์ ํด๋นํ๋ ํ๋ ์ฌ์ฉ |
num_discard_samples=1 | ๋ฐ์ดํฐ์ ์์ ๋ฒ๋ฆด ์ํ ์๋ฅผ ์ง์ . ์์ ๊ฒฝ์ฐ, ์ฒซ ๋ฒ์งธ ์ค์ ์๋ ํค๋๋ฅผ ๋ฒ๋ฆฌ๊ณ ๋ฐ์ดํฐ ์ฌ์ฉ |
ํ ์คํธ ๋ฐ์ดํฐ์ ๊ตฌ์ฑ
: nlp.data.TSVDataset์ ์ฌ์ฉํ์ฌ ํ ์คํธ ๋ฐ์ดํฐ์ ๊ตฌ์ฑ --> ํ๋ ์ธ๋ฑ์ค์ num_discard_samples๋ ํ๋ จ ๋ฐ์ดํฐ์ ๊ณผ ๋์ผํ๊ฒ ์ค์
.cache/ratings_test.txt | ๋ค์ด๋ก๋ํ ๋ค์ด๋ฒ ์ํ ๋ฆฌ๋ทฐ ํ ์คํธ ๋ฐ์ดํฐ์ ์ ๊ฒฝ๋ก |
9. BERT ๋ชจ๋ธ์ ์ฌ์ฉํ ํ ํฌ๋์ด์ ์ค์
tokenizer = get_tokenizer()
tok = nlp.data.BERTSPTokenizer(tokenizer, vocab, lower=False)
get_tokenizer() ํจ์
: KoBERT ๋ชจ๋ธ๊ณผ ํธํ๋๋ BERT ํ ํฌ๋์ด์ ๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์ --> BERT ํ ํฌ๋์ด์ ๋ ์ ๋ ฅ ํ ์คํธ๋ฅผ ํ ํฐ์ผ๋ก ๋ถํ ํ๋ ์ญํ ์ํ
nlp.data.BERTSPTokenizer๋ฅผ ์ฌ์ฉํ์ฌ BERT ํ ํฌ๋์ด์ ์ค์
: ํ ํฌ๋์ด์ ๋ tokenizer์ vocab์ ์ ๋ ฅ์ผ๋ก ๋ฐ์ --> tokenizer๋ ์์ ๊ฐ์ ธ์จ BERT ํ ํฌ๋์ด์ ๋ฅผ, vocab์ BERT ๋ชจ๋ธ์์ ์ฌ์ฉํ๋ ์ดํ์ง(Vocabulary)์ ์ ๋ฌ
lower=False
: ์๋ฌธ์ ๋ณํ์ ์ฌ์ฉํ์ง ์์์ ์๋ฏธ --> ๋์๋ฌธ์๋ฅผ ์ ์งํ๋ฉฐ ํ ํฐํ ์ํ
10. BERT ๋ชจ๋ธ์ ์ฌ์ฉ๋ ๋ฐ์ดํฐ์ ์์ฑ์ ์ํ BERTDataset ํด๋์ค ์ ์
class BERTDataset(Dataset):
def __init__(self, dataset, sent_idx, label_idx, bert_tokenizer, max_len,
pad, pair):
transform = nlp.data.BERTSentenceTransform(
bert_tokenizer, max_seq_length=max_len, pad=pad, pair=pair)
self.sentences = [transform([i[sent_idx]]) for i in dataset]
self.labels = [np.int32(i[label_idx]) for i in dataset]
def __getitem__(self, i):
return (self.sentences[i] + (self.labels[i], ))
def __len__(self):
return (len(self.labels))
BERTDataset ํด๋์ค
: torch.utils.data.Dataset์ ์์๋ฐ์ ๋ฐ์ดํฐ์ ์ ๊ตฌ์ฑ --> ์ฃผ์ด์ง ๋ฐ์ดํฐ์ ์ BERT ๋ชจ๋ธ์ ์ ํฉํ ํ์์ผ๋ก ๋ณํํ๋ ์ญํ ์ํ
__init__ ๋ฉ์๋
: ์ด๊ธฐํ ์์ ์ ์ํํ๋ ๋ฉ์๋ --> ์ฃผ์ด์ง ๋ฐ์ดํฐ์ , ๋ฌธ์ฅ ์ธ๋ฑ์ค(sent_idx), ๋ผ๋ฒจ ์ธ๋ฑ์ค(label_idx), BERT ํ ํฌ๋์ด์ (bert_tokenizer), ์ต๋ ๊ธธ์ด(max_len), ํจ๋ฉ(pad), ํ์ด(pair) ๋ฑ์ ์ธ์๋ก ๋ฐ์
nlp.data.BERTSentenceTransform
: ๋ฐ์ดํฐ์ ์ ๋ฌธ์ฅ์ BERT ์ ๋ ฅ ํ์์ผ๋ก ๋ณํ --> ๋ณํ๋ ๋ฌธ์ฅ๋ค์ self.sentences์, ๋ผ๋ฒจ์ ์ ์ ํํ๋ก ๋ณํ๋์ฌ self.labels์ ์ ์ฅ
__getitem__ ๋ฉ์๋
: ์ธ๋ฑ์ค i์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ ๋ฉ์๋ --> BERT ์ ๋ ฅ ๋ฌธ์ฅ๊ณผ ํด๋น ๋ผ๋ฒจ์ ํํ ํํ๋ก ๋ฐํ
__len__ ๋ฉ์๋
: ๋ฐ์ดํฐ์ ์ ๊ธธ์ด๋ฅผ ๋ฐํํ๋ ๋ฉ์๋ --> ๋ฐ์ดํฐ์ ์ ๊ธธ์ด๋ ๋ผ๋ฒจ์ ๊ฐ์์ ๋์ผ
11. BERT ๋ชจ๋ธ ํ์ต์ ์ฌ์ฉ๋๋ ํ๋ผ๋ฏธํฐ ์ค์
## Setting parameters
max_len = 64
batch_size = 64
warmup_ratio = 0.1
num_epochs = 5
max_grad_norm = 1
log_interval = 200
learning_rate = 5e-5
ํ๋ผ๋ฏธํฐ | ์ค๋ช |
max_len | ์ ๋ ฅ ๋ฌธ์ฅ์ ์ต๋ ๊ธธ์ด ์ค์ |
batch_size | ํ์ต 1ํ๋น ์ฌ์ฉ๋๋ ๋ฐ์ดํฐ ์ํ ๊ฐ์ ์ค์ |
warmup_ratio | ํ์ต ์ด๊ธฐ learning rate ์ฆ๊ฐ ๋น์จ ์ค์ |
num_epochs | ์ ์ฒด ๋ฐ์ดํฐ์ ์ ๋ํด ํ์ต ๋ฐ๋ณต ํ์ ์ค์ |
max_grad_norm | ๊ทธ๋๋์ธํธ(gradient)์ ์ต๋ ๋ ธ๋ฆ(norm)์ ์ ํ |
log_interval | ํ์ต ์ค ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ ๊ฐ๊ฒฉ ์กฐ์ |
learning_rate | ํ์ต ์๋๋ฅผ ์กฐ์ |
12. ๊ตฌ์ฑํ ๋ฐ์ดํฐ์ ์ BERTDataset ํด๋์ค์ ๊ฐ์ฒด๋ก ๋ณํ
data_train = BERTDataset(dataset_train, 0, 1, tok, max_len, True, False)
data_test = BERTDataset(dataset_test, 0, 1, tok, max_len, True, False)
ํ๋ จ ๋ฐ์ดํฐ์ ๋ณํ
: BERTDataset ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ํ๋ จ ๋ฐ์ดํฐ์ ์ ๋ณํ --> ๋ณํ๋ ๋ฌธ์ฅ๊ณผ ๋ผ๋ฒจ์ data_train์ ์ ์ฅ
ํ ์คํธ ๋ฐ์ดํฐ์ ๋ณํ
: BERTDataset ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ํ ์คํธ ๋ฐ์ดํฐ์ ๋ณํ --> ๋ณํ๋ ๋ฌธ์ฅ๊ณผ ๋ผ๋ฒจ์ data_test์ ์ ์ฅ
์ธ์ | ์๋ฏธ |
dataset_train | ํ๋ จ์ ์ฌ์ฉํ ๋ฐ์ดํฐ์ |
dataset_test |
ํ ์คํธ์ ์ฌ์ฉํ ๋ฐ์ดํฐ์ |
0 | ๋ฌธ์ฅ ์ธ๋ฑ์ค |
1 | ๋ผ๋ฒจ ์ธ๋ฑ์ค |
tok | ํ ํฌ๋์ด์ ๊ฐ์ฒด |
max_len | ์ต๋ ๋ฌธ์ฅ ๊ธธ์ด |
True | ํจ๋ฉ ์ฌ์ฉ ์ฌ๋ถ |
False | ํ์ด(Pair) ์ฌ์ฉ ์ฌ๋ถ |
13. ๋ฐ์ดํฐ์ ์ ๋ฏธ๋๋ฐฐ์น ํํ๋ก ๋ก๋ฉ
train_dataloader = torch.utils.data.DataLoader(data_train, batch_size=batch_size, num_workers=5)
test_dataloader = torch.utils.data.DataLoader(data_test, batch_size=batch_size, num_workers=5)
ํ๋ จ ๋ฐ์ดํฐ์ ๋ก๋ฉ
: torch.utils.data.DataLoader๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ จ ๋ฐ์ดํฐ์ ์ ๋ก๋ฉํ๋ ๋ฐ์ดํฐ๋ก๋(train_dataloader) ์์ฑ
ํ ์คํธ ๋ฐ์ดํฐ์ ๋ก๋ฉ
: torch.utils.data.DataLoader๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธ ๋ฐ์ดํฐ์ ์ ๋ก๋ฉํ๋ ๋ฐ์ดํฐ๋ก๋(test_dataloader) ์์ฑ
์ธ์ | ์ค๋ช |
batch_size | ํ ๋ฒ์ ๋ก๋ฉํ ๋ฏธ๋ ๋ฐฐ์น ํฌ๊ธฐ ์ง์ |
num_workers | ๋ฐ์ดํฐ ๋ก๋ฉ์ ์ฌ์ฉํ ํ๋ก์ธ์ค ์ ์ง์ |
data_train | ํ๋ จ ๋ฐ์ดํฐ์ |
data_test | ํ ์คํธ ๋ฐ์ดํฐ์ |
14. BERT ๋ชจ๋ธ ๊ธฐ๋ฐ ๋ถ๋ฅ๊ธฐ์ธ BERTClassifier ํด๋์ค ์ ์
class BERTClassifier(nn.Module):
def __init__(self,
bert,
hidden_size = 768,
num_classes=2,
dr_rate=None,
params=None):
super(BERTClassifier, self).__init__()
self.bert = bert
self.dr_rate = dr_rate
self.classifier = nn.Linear(hidden_size , num_classes)
if dr_rate:
self.dropout = nn.Dropout(p=dr_rate)
def gen_attention_mask(self, token_ids, valid_length):
attention_mask = torch.zeros_like(token_ids)
for i, v in enumerate(valid_length):
attention_mask[i][:v] = 1
return attention_mask.float()
def forward(self, token_ids, valid_length, segment_ids):
attention_mask = self.gen_attention_mask(token_ids, valid_length)
_, pooler = self.bert(input_ids = token_ids, token_type_ids = segment_ids.long(), attention_mask = attention_mask.float().to(token_ids.device))
if self.dr_rate:
out = self.dropout(pooler)
else:
out = pooler
return self.classifier(out)
BERTClassifier ํด๋์ค
: nn.Module์ ์์๋ฐ์ ๋ชจ๋ธ ์ ์ --> ์ฃผ์ด์ง BERT ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ ฅ ๋ฌธ์ฅ์ ๋ถ๋ฅํ๋ ์ญํ ์ํ
__init__ ๋ฉ์๋
: ์ด๊ธฐํ ์์ ์ ์ํํ๋ ๋ฉ์๋ --> ์ฃผ์ด์ง ์ธ์๋ค์ ๊ธฐ๋ฐ์ผ๋ก ๋ชจ๋ธ ๊ตฌ์กฐ ์ค์
์ธ์ | ์ค๋ช |
bert | BERT ๋ชจ๋ธ ๊ฐ์ฒด |
hidden_size | BERT ๋ชจ๋ธ์ ์๋ ์ํ ํฌ๊ธฐ ์ง์ |
num_classes | ๋ถ๋ฅํ ํด๋์ค ๊ฐ์ ์ง์ |
dr_rate | ๋๋กญ์์(dropout) ๋น์จ ์ง์ |
params | ์ถ๊ฐ์ ์ธ ํ๋ผ๋ฏธํฐ ์ค์ |
gen_attention_mask ๋ฉ์๋
: ์ดํ ์ ๋ง์คํฌ(attention mask)๋ฅผ ์์ฑํ๋ ๋ฉ์๋ --> ์ดํ ์ ๋ง์คํฌ๋ ์ ๋ ฅ ๋ฌธ์ฅ์ ํจ๋ฉ ๋ถ๋ถ์ ์ ์ธํ๊ณ ์ค์ ์ ๋ ฅ์ ๋ํ ์ดํ ์ ์ ์ํํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ฉฐ, ์ ๋ฉ์๋๋ ์ ๋ ฅ ํ ํฐ์ ๊ธธ์ด(`valid_length`)์ ๋ฐ๋ผ ์ดํ ์ ๋ง์คํฌ๋ฅผ ์์ฑํจ
forward ๋ฉ์๋
: ๋ชจ๋ธ์ ์์ ํ ์ฐ์ฐ์ ์ํํ๋ ๋ฉ์๋ --> ์ ๋ ฅ์ผ๋ก ์ฃผ์ด์ง ํ ํฐ(`token_ids`), ์ ํจ ๊ธธ์ด(`valid_length`), ์ธ๊ทธ๋จผํธ ํ ํฐ(`segment_ids`)์ ํ์ฉํ์ฌ BERT ๋ชจ๋ธ์ ํต๊ณผ์ํด
15. BERTClassifier ํด๋์ค์ ๊ฐ์ฒด ์์ฑ
model = BERTClassifier(bertmodel, dr_rate=0.5).to(device)
์์ | ์ค๋ช |
bertmodel | BERT ๋ชจ๋ธ ๊ฐ์ฒด |
dr_rate | ๋๋กญ์์ (dropout) ๋น์จ ์ค์ |
to(device) | ๋ชจ๋ธ์ ์ง์ ํ ์ฅ์น๋ก ์ด๋ |
16. ์ตํฐ๋ง์ด์ ์ ์ค์ผ์ค๋ฌ ์ค์
# Prepare optimizer and schedule (linear warmup and decay)
no_decay = ['bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
{'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01},
{'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
no_decay
: ๊ฐ์ค์น ๊ฐ์ (weight decay)๋ฅผ ์ ์ฉํ์ง ์์ ํ๋ผ๋ฏธํฐ๋ค์ ์ด๋ฆ์ ๋ํ๋ด๋ ๋ฆฌ์คํธ
optimizer_grouped_parameters
: ์ตํฐ๋ง์ด์ ๊ฐ ๊ฐฑ์ ํด์ผ ํ ํ๋ผ๋ฏธํฐ๋ค๊ณผ ํด๋น ํ๋ผ๋ฏธํฐ๋ค์ ๋ํ ๊ฐ์ค์น ๊ฐ์ ๊ฐ์ ์ค์ ํ๋ ๋ฆฌ์คํธ --> ๋ฆฌ์คํธ ๋ด์ ๊ฐ ๋์ ๋๋ฆฌ๋ ํ๋์ ํ๋ผ๋ฏธํฐ ๊ทธ๋ฃน์ ๋ํ๋
์์ | ์ค๋ช |
params | ๊ฐฑ์ ํด์ผ ํ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ํ๋ด๋ ๋ฆฌ์คํธ ์ง์ |
weight_decay | ํ๋ผ๋ฏธํฐ๋ค์ ๋ํ ๊ฐ์ค์น ๊ฐ์ ๊ฐ ์ค์ |
17. AdamW ์ตํฐ๋ง์ด์ ๊ฐ์ฒด ์์ฑ
optimizer = AdamW(optimizer_grouped_parameters, lr=learning_rate)
loss_fn = nn.CrossEntropyLoss()
loss_fn ํจ์
: ํฌ๋ก์ค ์ํธ๋กํผ ์์ค ํจ์(CrossEntropyLoss)๋ก, ๋ชจ๋ธ์ ์์ธก๊ฐ๊ณผ ์ค์ ๊ฐ ์ฌ์ด์ ์ฐจ์ด๋ฅผ ํตํด ์์ค ๊ณ์ฐ --> ์ฃผ๋ก ๋ค์ค ํด๋์ค ๋ถ๋ฅ ๋ฌธ์ ์์ ์ฌ์ฉ๋๋ฉฐ, ๋ชจ๋ธ์ ์ถ๋ ฅ๊ณผ ์ ๋ต ํด๋์ค ๊ฐ์ ์ฐจ์ด๋ฅผ ์ต์ํํ์ฌ ๋ชจ๋ธ ํ์ต์ ๊ธฐ์ฌํจ
optimizer_grouped_parameters
: optimizer_grouped_parameters๋ฅผ ์ฌ์ฉํ์ฌ AdamW ์ตํฐ๋ง์ด์ ๊ฐ์ฒด ์์ฑ
lr=learning_rate | ์ตํฐ๋ง์ด์ ๊ฐ ํ๋ผ๋ฏธํฐ๋ค์ ์ ๋ฐ์ดํธํ ๋ ์ฌ์ฉํ ํ์ต๋ฅ (learning rate) ์ง์ |
18. ํ์ต์ ํ์ํ ์ด ์คํ ์์ ์์ ์คํ ์ ๊ณ์ฐ
t_total = len(train_dataloader) * num_epochs
warmup_step = int(t_total * warmup_ratio)
t_total
: ์ ์ฒด ํ์ต ๋ฐ์ดํฐ์ ๋ฐฐ์น ์(`len(train_dataloader)`)์ ํ์ตํ ์ํฌํฌ ์(`num_epochs`)๋ฅผ ๊ณฑํ์ฌ ๊ณ์ฐ --> ์ด ๋ช ๋ฒ์ ์คํ ( = ๋ชจ๋ธ์ด ํ ๋ฒ์ ์์ ํ์ ์ญ์ ํ๋ฅผ ์๋ฃํ๋ ๋จ์ )์ ์ํํ ๊ฒ์ธ์ง๋ฅผ ๋ํ๋
warmup_step
: ์ด ์คํ ์(`t_total`)์ ์์ ๋น์จ(`warmup_ratio`)์ ๊ณฑํ์ฌ ๊ณ์ฐ --> ํ์ต ์ด๊ธฐ์ ํ์ต๋ฅ ์ ์ ์ง์ ์ผ๋ก ์ฆ๊ฐ์ํค๋ ์์ ๋จ๊ณ๋ฅผ ๋ํ๋ด๋ฉฐ, ์์ ๋จ๊ณ๋ ์ด๊ธฐ์๋ ํ์ต๋ฅ ์ ์๊ฒ ์ค์ ํ๊ณ ์ ์ฐจ์ ์ผ๋ก ์ฆ๊ฐ์์ผ ์์ ํ๋ ํ์ต์ ๋์
19. ์ฝ์ฌ์ธ ํจ์๋ฅผ ํตํด ์ค์ผ์ค๋ฌ(scheduler) ์์ฑ
scheduler = get_cosine_schedule_with_warmup(optimizer, num_warmup_steps=warmup_step, num_training_steps=t_total)
get_cosine_schedule_with_warmup ํจ์
: ์ฃผ์ด์ง ์ตํฐ๋ง์ด์ (optimizer)์ ๋ํด ์ฝ์ฌ์ธ ํจ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ต๋ฅ ์ ์กฐ์ ํ๋ ์ค์ผ์ค๋ฌ ์์ฑ
num_training_steps
: ์ด ์คํ ์๋ฅผ ๋ํ๋ด๋ฉฐ, ์์ ๊ณ์ฐํ 't_total' ๊ฐ์ ์ฌ์ฉํ์ฌ ์ค์ --> ์ ์ฒด ํ์ต ๋ฐ์ดํฐ์ ๋ํด ๋ช ๋ฒ์ ์คํ ์ ์ํํ ๊ฒ์ธ์ง๋ฅผ ์๋ฏธ
num_warmup_steps
: ์์ ์คํ ์๋ฅผ ๋ํ๋ด๋ฉฐ, ์์ ๊ณ์ฐํ 'warmup_step' ๊ฐ์ ์ฌ์ฉํ์ฌ ์ค์ --> ํ์ต ์ด๊ธฐ์ ํ์ต๋ฅ ์ ์ ์ง์ ์ผ๋ก ์ฆ๊ฐ์ํค๋ ์์ ๋จ๊ณ์ ์คํ ์๋ฅผ ์๋ฏธ
20. ์ ํ๋ ๊ณ์ฐ ํจ์ calc_accuracy ๊ตฌํ
def calc_accuracy(X,Y):
max_vals, max_indices = torch.max(X, 1)
train_acc = (max_indices == Y).sum().data.cpu().numpy()/max_indices.size()[0]
return train_acc
torch.max(X, 1)
: `X` ํ ์์ ๊ฐ ํ์์ ์ต๋๊ฐ๊ณผ ๊ทธ์ ํด๋นํ๋ ์ธ๋ฑ์ค ๋ฐํ
max_vals | ์ต๋๊ฐ ์ ์ฅ |
max_indices | ์ต๋๊ฐ์ ํด๋นํ๋ ์ธ๋ฑ์ค ์ ์ฅ |
(max_indices == Y).sum()
: ์์ธก๊ฐ๊ณผ ์ค์ ๊ฐ์ด ์ผ์นํ๋ ํ์ ๊ณ์ฐ --> sum() ์ True์ธ ์์์ ๊ฐ์ ๋ฐํ
.data.cpu().numpy()
: ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ CPU ์์์ ๋ํ์ด ๋ฐฐ์ด๋ก ๋ณํ
21. ์ฃผ์ด์ง ์ํฌํฌ ์๋งํผ ํ์ต ์งํ ํ ํ์ต ๋ฐ์ดํฐ์ ํ ์คํธ ๋ฐ์ดํฐ์ ๋ํ ์ ํ๋ ๊ณ์ฐ
for e in range(num_epochs):
train_acc = 0.0
test_acc = 0.0
model.train()
for batch_id, (token_ids, valid_length, segment_ids, label) in tqdm(enumerate(train_dataloader), total=len(train_dataloader)):
optimizer.zero_grad()
token_ids = token_ids.long().to(device)
segment_ids = segment_ids.long().to(device)
valid_length= valid_length
label = label.long().to(device)
out = model(token_ids, valid_length, segment_ids)
loss = loss_fn(out, label)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_grad_norm)
optimizer.step()
scheduler.step() # Update learning rate schedule
train_acc += calc_accuracy(out, label)
if batch_id % log_interval == 0:
print("epoch {} batch id {} loss {} train acc {}".format(e+1, batch_id+1, loss.data.cpu().numpy(), train_acc / (batch_id+1)))
print("epoch {} train acc {}".format(e+1, train_acc / (batch_id+1)))
model.eval()
for batch_id, (token_ids, valid_length, segment_ids, label) in tqdm(enumerate(test_dataloader), total=len(test_dataloader)):
token_ids = token_ids.long().to(device)
segment_ids = segment_ids.long().to(device)
valid_length= valid_length
label = label.long().to(device)
out = model(token_ids, valid_length, segment_ids)
test_acc += calc_accuracy(out, label)
print("epoch {} test acc {}".format(e+1, test_acc / (batch_id+1)))
train_acc
: ํ์ต ๋ฐ์ดํฐ์ ๋ํ ์ ํ๋๊ฐ ๋์ ๋๋ ๋ณ์
test_acc
: ํ ์คํธ ๋ฐ์ดํฐ์ ๋ํ ์ ํ๋๊ฐ ๋์ ๋๋ ๋ณ์
model.train()
: ๋ชจ๋ธ์ ํ์ต ๋ชจ๋๋ก ์ค์ --> ๋ชจ๋ธ ๋ด๋ถ์ ๋๋กญ์์(dropout)์ด๋ ๋ฐฐ์น ์ ๊ทํ(batch normalization)์ ๊ฐ์ ํ์ต ๋์ ํ์ฑํ
ํ์ต ๋ฐ์ดํฐ ์ํ ๋ฐ๋ณต๋ฌธ
: for batch_id, (token_ids, valid_length, segment_ids, label) in tqdm(enumerate(train_dataloader), total=len(train_dataloader)): --> ํ์ต ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์น ๋จ์๋ก ์ํํ๋ ๋ฐ๋ณต๋ฌธ์ผ๋ก, ๊ฐ ๋ฐฐ์น์ ๋ํด ๋ค์์ ์์ ์ํ
- optimizer.zero_grad()๋ฅผ ํตํด ์ตํฐ๋ง์ด์ ์ ๊ธฐ์ธ๊ธฐ ์ด๊ธฐํ
- ๋ฐ์ดํฐ๋ฅผ GPU๋ก ์ด๋์ํด
- ๋ชจ๋ธ์ ํตํด ์์ธก๊ฐ ๊ณ์ฐ
- ์์ค ํจ์๋ฅผ ํตํด ์์ค ๊ณ์ฐ
- ์ญ์ ํ๋ฅผ ์ํํ์ฌ ๊ธฐ์ธ๊ธฐ ๊ณ์ฐ
- torch.nn.utils.clip_grad_norm_(model.parameters(), max_grad_norm) ์ ํตํด ๊ธฐ์ธ๊ธฐ ํด๋ฆฌํ ์ํ
- ์ตํฐ๋ง์ด์ ๋ฅผ ํตํ ํ๋ผ๋ฏธํฐ ์ ๋ฐ์ดํธ
- ์ค์ผ์ค๋ฌ๋ฅผ ํตํ ํ์ต๋ฅ ์ค์ผ์ค ์ ๋ฐ์ดํธ
- train_acc += calc_accuracy(out, label) ์ ํตํด ์ ํ๋ ๊ณ์ฐ ๋ฐ ๊ณ์ฐ๊ฐ ๋์
- if batch_id % log_interval == 0: ์ ํตํด ์ผ์ ๊ฐ๊ฒฉ์ผ๋ก ํ์ฌ ์ํฌํฌ ๋ฐ ๋ฐฐ์น ID, ์์ค๊ณผ ํ๋ จ ์ ํ๋ ์ถ๋ ฅ
model.eval()
: ๋ชจ๋ธ์ ํ๊ฐ ๋ชจ๋๋ก ์ค์ --> ๋๋กญ์์์ด๋ ๋ฐฐ์น ์ ๊ทํ์ ๊ฐ์ ํ์ต ๋์์ ๋นํ์ฑํํ๊ณ , ๋ชจ๋ธ์ ์์ธก ๊ฒฐ๊ณผ๋ฅผ ์์ ์ ์ผ๋ก ํ๊ฐํ๊ธฐ ์ํด ์ฌ์ฉ
ํ ์คํธ ๋ฐ์ดํฐ ์ํ ๋ฐ๋ณต๋ฌธ
: for batch_id, (token_ids, valid_length, segment_ids, label) in tqdm(enumerate(test_dataloader), total=len(test_dataloader)): --> ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์น ๋จ์๋ก ์ํํ๋ ๋ฐ๋ณต๋ฌธ์ผ๋ก, ๊ฐ ๋ฐฐ์น์ ๋ํด ๋ค์์ ์์ ์ํ
- ๋ฐ์ดํฐ๋ฅผ GPU๋ก ์ด๋์ํด
- ๋ชจ๋ธ์ ํตํ ์์ธก๊ฐ ๊ณ์ฐ
- test_acc += calc_accuracy(out, label) ์ ํตํด ์ ํ๋ ๊ณ์ฐ ๋ฐ ๊ณ์ฐ๊ฐ ๋์
'โ๏ธ Kibwa Voice Phishing Prev Project > Natural Language Processing Model Study' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Natural Language Processing Model] KOBERT ์๋ฌ ๊ธฐ๋ก & ์ ๋ฆฌ (0) | 2023.07.01 |
---|