1. BERT, KoBERT๋?
๊ตฌ๊ธ์์ 2018๋ ์ ๊ณต๊ฐํ BERT๋ ๋ฑ์ฅ๊ณผ ๋์์ ์๋ง์ NLP ํ์คํฌ์์ ์ต๊ณ ์ฑ๋ฅ์ ๋ณด์ฌ์ฃผ๋ฉด์ NLP์ ํ ํ์ ๊ทธ์ ๋ชจ๋ธ๋ก ํ๊ฐ๋ฐ๊ณ ์๋ค. ์๋ฐฉํฅ์ฑ์ ์งํฅํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.(B: bidirection) BERT ๋ชจ๋ธ์ ๋ฌธ๋งฅ ํน์ฑ์ ํ์ฉํ๊ณ ์๊ณ , ๋์ฉ๋ ๋ง๋ญ์น๋ก ์ฌ์ ํ์ต์ด ์ด๋ฏธ ์งํ๋์ด ์ธ์ด์ ๋ํ ์ดํด๋๋ ๋๋ค. ํ์ง๋ง BERT๋ ํ๊ตญ์ด์ ๋ํด์ ์์ด๋ณด๋ค ์ ํ๋๊ฐ ๋จ์ด์ง๋ค๊ณ ํ๋ค.
์ค๋ ๊ธฐ์ ํด๋ณผ KoBERT ๋ชจ๋ธ์ SKTBrain์์ ๊ณต๊ฐํ๋๋ฐ, ํ๊ตญ์ด ์ํค 5๋ฐฑ๋ง ๋ฌธ์ฅ๊ณผ ํ๊ตญ์ด ๋ด์ค 2์ฒ๋ง ๋ฌธ์ฅ์ ํ์ตํ ๋ชจ๋ธ์ด๋ค. ์์ ์ ์ฌ์ฉ ๋ชฉ์ ์ ๋ฐ๋ผ ํ์ธํ๋์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ output layer๋ง์ ์ถ๊ฐ๋ก ๋ฌ์์ฃผ๋ฉด ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํด๋ผ ์ ์๋ค. ๋ง์ BERT ๋ชจ๋ธ ์ค์์๋ KoBERT๋ฅผ ์ฌ์ฉํ ์ด์ ๋ "ํ๊ตญ์ด"์ ๋ํด ๋ง์ ์ฌ์ ํ์ต์ด ์ด๋ฃจ์ด์ ธ ์๊ณ , ๊ฐ์ ์ ๋ถ์ํ ๋, ๊ธ์ ๊ณผ ๋ถ์ ๋ง์ผ๋ก ๋ถ๋ฅํ๋ ๊ฒ์ด ์๋ ๋ค์ค ๋ถ๋ฅ๊ฐ ๊ฐ๋ฅํ ๊ฒ์ด ๊ฐ์ ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ด ํฌ์คํ ์์ ํ๋ ์์ ์ ๋ฐ๋ก ํ์ธ ํ๋(Fine-tuning)์ ํด๋นํ๋ค. ๋ค๋ฅธ ์์ ์ ๋ํด์ ํ๋ผ๋ฏธํฐ ์ฌ์กฐ์ ์ ์ํ ์ถ๊ฐ ํ๋ จ ๊ณผ์ ์ ๊ฑฐ์น๋ ๊ฒ์ ๋งํ๋ค. ์์๋ฅผ ๋ค์ด๋ณด๋ฉด, ์ฐ๋ฆฌ๊ฐ ํ๊ณ ์ถ์ ์์ ์ด ์ฐ์ธ์ฆ ๊ฒฝํฅ ๋ฌธํ ๋ถ๋ฅ๋ผ๊ณ ํ์์ ๋, ์ด๋ฏธ ์ํคํผ๋์ ๋ฑ์ผ๋ก ์ฌ์ ํ์ต๋ BERT ์์ ๋ถ๋ฅ๋ฅผ ์ํ ์ ๊ฒฝ๋ง์ ํ ์ธต ์ถ๊ฐํ๋ ๊ฒ์ด๋ค. ์ด๋ฏธ BERT๊ฐ ์ธ์ด ๋ชจ๋ธ ์ฌ์ ํ์ต ๊ณผ์ ์์ ์ป์ ์ง์์ ํ์ฉํ ์ ์์ผ๋ฏ๋ก ์ฐ์ธ์ฆ ๊ฒฝํฅ ๋ฌธํ ๋ถ๋ฅ์์ ๋ณด๋ค ๋ ์ข์ ์ฑ๋ฅ์ ์ป์ ์ ์๋ค.
2. ์ด๋ ค์ ๋ ์
KoBERT ๋ชจ๋ธ์ ์ ๊ณตํ๋ ๊ธฐ์กด ์๋ฒ๊ฐ ์์ ํ ๋ซํ๊ณ hugingface hub๋ก ์ด์ ๋์๋ค.
ํ์ง๋ง ์ฌ์ ํ github์๋ ๋๊ฐ์ง์ method๋ฅผ ์ ๊ณตํ๊ณ ์๊ณ
๋ฐ๋ ์๋ฒ๋ก์ ์์ ์ฝ๋๋ฅผ ์ ๊ณตํด์ฃผ๊ณ ์์ง ์์
์ฝ๋๋ฅผ ์ดํดํ๊ณ ์์ฑํ๋๋ฐ ์ค๋ ์๊ฐ์ด ๊ฑธ๋ ธ๋ค.
ํด๋น ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํด ๋ฐํ๋ issue์ ๋ฌด๋ ค 36๋ถ ์ ์ ๊ฐ๋ฐ์๋ถ๊ป์ comment๋ฅผ ๋จ๊ฒจ์ฃผ์ จ์๋ค. ๋ด๋ด it ๊ธฐ์ ์ ๋ค๋ฃจ์ด๋ ๊ฝค๋ ์ค๋ซ๋์ ๊ฒ์ฆ๋ฐ๊ณ ์๋น์ค๊ฐ ๋ง์ด ๋๊ณ ์๋ ์์ ์ ์ธ ์ธ์ด, ํ๋ ์์ํฌ, ๋ชจ๋ธ๋ค๋ง ์ฌ์ฉํ๋ค๊ฐ ๊ฐ์๊ธฐ ์ ๊ธฐ์ ์ ์ ์ ์ ์์๋ ๊ฒ ๊ฐ์ ์กฐ๊ธ ์ ๊ธฐํ๋ค.
3. ๋ฐ์ดํฐ์ ์ค๋ช , ์ฝ๋ฉ ํ๊ฒฝ
๋ค์ด๋ฒ ๋ฆฌ๋ทฐ 2์ค ๋ถ๋ฅ ์์ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑํ์๊ณ ๋ฐ์ดํฐ๋ AiHub์ ๊ฐ์ ๋ถ๋ฅ๋ฅผ ์ํ ๋ํ ์์ฑ ๋ฐ์ดํฐ์ ์ ์ด์ฉํ๋ค. ํด๋น ๋ฐ์ดํฐ์ ์์๋ ๊ฐ์ ์ 7๊ฐ์ง ๊ฐ์ (happiness-ํ๋ณต, angry-๋ถ๋ ธ, disgust-ํ์ค, fear-๊ณตํฌ, neutral-์ค๋ฆฝ, sadness-์ฌํ, surprise-๋๋) 7๊ฐ์ง๋ก ๋ถ๋ฅํด์ฃผ๊ณ ์๋ค. colab ํ๊ฒฝ์์ ์ฝ๋ฉํ๊ณ ํ ์คํธํด๋ณด์๋ค.
4. ์ค์ต ์ค๋ช , ์ฝ๋
1. Colab ํ๊ฒฝ์ ์ค์ ํ๊ธฐ
!pip install gluonnlp pandas tqdm
!pip install mxnet
!pip install sentencepiece==0.1.91
!pip install transformers==4.8.2
!pip install torch
ํ์ฌ ์๊ตฌํ๋ ์ฌ์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
KoBERT ๊ฐ ์๊ตฌํ๋ ์ต์ ๋ฒ์ ์ ๋ณด๋ ์ด ๊ณต์ ์คํ์์ค ๋งํฌ#์์ ํ์ธํ ์ ์๋ค.
2. github์์ KoBERT ํ์ผ์ ๋ก๋ ๋ฐ KoBERT๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ
!pip install 'git+https://github.com/SKTBrain/KoBERT.git#egg=kobert_tokenizer&subdirectory=kobert_hf'
https://github.com/SKTBrain/KoBERT/tree/master/kobert_hf ์ kobert_tokenizer ํด๋๋ฅผ ๋ค์ด๋ฐ๋ ์ฝ๋์ด๋ค.
!pip install git+https://git@github.com/SKTBrain/KoBERT.git@master
https://github.com/SKTBrain/KoBERT ์ ํ์ผ๋ค์ ๋ค์ด๋ฐ๋ ์ฝ๋์ด๋ค.
from kobert.pytorch_kobert import get_kobert_model
from kobert_tokenizer import KoBERTTokenizer
tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1')
bertmodel, vocab = get_kobert_model('skt/kobert-base-v1',tokenizer.vocab_file)
BERT๋ ์ด๋ฏธ ๋๊ตฐ๊ฐ๊ฐ ํ์ตํด๋ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ค(pre-trained model)๋ ๊ฒ์ ๋ปํ๋ค. ๋ฐ๋ผ์ ์ฌ์ฉํ๋ model๊ณผ tokenizer๋ ํญ์ mapping ๊ด๊ณ์ฌ์ผ ํ๋ค. ์๋ฅผ ๋ค์ด์ U ํ์ด ๊ฐ๋ฐํ BERT๋ฅผ ์ฌ์ฉํ๋๋ฐ, Vํ์ด ๊ฐ๋ฐํ BERT์ tokenizer๋ฅผ ์ฌ์ฉํ๋ฉด model์ ํ ์คํธ๋ฅผ ์ดํดํ ์ ์๋ค. Uํ์ BERT์ ํ ํฌ๋์ด์ ๋ '์ฐ๋ฆฌ'๋ผ๋ ๋จ์ด๋ฅผ 23๋ฒ์ผ๋ก int encodingํ๋ ๋ฐ๋ฉด์, V๋ผ๋ BERT์ tokenizer๋ '์ฐ๋ฆฌ'๋ผ๋ ๋จ์ด๋ฅผ 103๋ฒ์ผ๋ก int encodingํด ๋จ์ด์ mapping ๋๋ ์ ๋ณด ์์ฒด๊ฐ ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ๋ถ๋ถ์ ๋ค์์ ๊ฐ๋จํ ์งํํด๋ณธ ์ค์ต์์ ๋ ์์ธํ ๋ค๋ค๋ณผ ๊ฒ์ด๋ค.
ํ ์ค ํ ์ค ์์ธํ ์ค๋ช ํด๋ณด๋ฉด,
1: https://github.com/SKTBrain/KoBERT ์ kobert ํด๋์ pytorch_kobert.py ํ์ผ์์ get_kobert_model ๋ฉ์๋๋ฅผ ๋ถ๋ฌ์ค๋ ์ฝ๋์ด๋ค.
2: https://github.com/SKTBrain/KoBERT/tree/master/kobert_hf ์ kobert_tokenizer ํด๋์ kobert_tokenizer.py ํ์ผ์์ KoBERTTokenizer ํด๋์ค๋ฅผ ๋ถ๋ฌ์ค๋ ์ฝ๋์ด๋ค.
3, 4: ๋ถ๋ฌ์จ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ํด๋์ค๋ฅผ ๊ฐ์ ธ์ ๊ฐ๊ฐ tokenizer์, model, vocabulary๋ฅผ ๋ถ๋ฌ์๋ค.
์ด๋ ๊ฒ ํด๋น ์ค์ต์ ํ์ํ KoBERT๋ชจ๋ธ์ ๋ถ๋ฌ์ค๋ ์ฝ๋ ์ค๋ช ์ ๋๋ฌ๋ค.
์๋์ ๋ฐฉ๋ฒ์ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก KoBERT ๋ชจ๋ธ์ ๋ถ๋ฌ์ค๋ ์ฝ๋์ด๋ค.
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained("skt/kobert-base-v1")
model = AutoModel.from_pretrained("skt/kobert-base-v1")
github์ ํตํ ๋ค์ด๋ก๋ ์์ด ์ฌ์ฉํ ์ ์๋ค. ํ์ง๋ง ํด๋น ์ฝ๋๋ก๋ vocab์ ์ ๊ทผํ๊ธฐ ์ด๋ ต๋ค. ๊ฐ์์ ์ํฉ์ ๋ง๊ฒ ์ฐ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
huggingface ์ฌ์ดํธ์ skt/kobert-base-v1์ผ๋ก ์ฌ๋ผ์์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
3. ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ถ๋ฌ์ค๊ธฐ
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 import tqdm, tqdm_notebook
import pandas as pd
#transformers
from transformers import AdamW
from transformers.optimization import get_cosine_schedule_with_warmup
from transformers import BertModel
#GPU ์ฌ์ฉ ์
device = torch.device("cuda:0")
์ฌ์ ํ์ต๋ BERT๋ฅผ ์ฌ์ฉํ ๋๋ transformers๋ผ๋ ํจํค์ง๋ฅผ ์์ฃผ ์ฌ์ฉํ๋ค.
๋ํ, ํ์ต์๊ฐ์ ์ค์ด๊ธฐ ์ํด, GPU๋ฅผ ์ฌ์ฉํ๋ค.
4. ๋ฐ์ดํฐ์ ๋ถ๋ฌ์ค๊ธฐ
์ผ์ชฝ ์๋จ์ ํด๋ ๋ฒํผ์ ๋๋ฅด๊ณ ์ ๋ก๋ ๋ฒํผ์ ๋๋ฌ์ ๋ก์ปฌ์ ์๋ ํ์ผ์ ์ ๋ก๋ํ ์ ์๋ค. ์ค๋ ์ค์ต์ ํ์ํ '๊ฐ์ ๋ถ๋ฅ๋ฐ์ดํฐ์ .csv' ํ์ผ์ ์ ๋ก๋ํ๋ฉด ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ํ์ผ์ด ์๊ธด๋ค.
data = pd.read_csv('๊ฐ์ ๋ถ๋ฅ๋ฐ์ดํฐ์
.csv', encoding='cp949')
ํ์ผ ์ด๋ฆ์ ์ง์ ํด pandas library๋ก ์ด์ด์ฃผ์๋ค.
ํด๋น ๋ฐ์ดํฐ์ ์ ์์ ๊ฐ์ด column ๋ช ์ด ์ง์ ๋์ด ์๋ค.
data.loc[(data['์ํฉ'] == "fear"), '์ํฉ'] = 0 #๊ณตํฌ => 0
data.loc[(data['์ํฉ'] == "surprise"), '์ํฉ'] = 1 #๋๋ => 1
data.loc[(data['์ํฉ'] == "angry"), '์ํฉ'] = 2 #๋ถ๋
ธ => 2
data.loc[(data['์ํฉ'] == "sadness"), '์ํฉ'] = 3 #์ฌํ => 3
data.loc[(data['์ํฉ'] == "neutral"), '์ํฉ'] = 4 #์ค๋ฆฝ => 4
data.loc[(data['์ํฉ'] == "happiness"), '์ํฉ'] = 5 #ํ๋ณต => 5
data.loc[(data['์ํฉ'] == "disgust"), '์ํฉ'] = 6 #ํ์ค => 6
data_list = []
for ques, label in zip(data['๋ฐํ๋ฌธ'], data['์ํฉ']) :
data = []
data.append(ques)
data.append(str(label))
data_list.append(data)
7๊ฐ์ ๊ฐ์ class๋ฅผ 0~6 ์ซ์์ ๋์์์ผ data_list์ ๋ด์์ค๋ค.
5. ์ ๋ ฅ ๋ฐ์ดํฐ์ ์ ํ ํฐํํ๊ธฐ
class BERTDataset(Dataset):
def __init__(self, dataset, sent_idx, label_idx, bert_tokenizer,vocab, max_len,
pad, pair):
transform = nlp.data.BERTSentenceTransform(
bert_tokenizer, max_seq_length=max_len,vocab=vocab, 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))
๊ฐ ๋ฐ์ดํฐ๊ฐ BERT ๋ชจ๋ธ์ ์ ๋ ฅ์ผ๋ก ๋ค์ด๊ฐ ์ ์๋๋ก tokenization, int encoding, padding ๋ฑ์ ํด์ฃผ๋ ์ฝ๋์ด๋ค.
<tokenization ๋ถ๊ฐ ์ค๋ช >
"tokenization" ์ด ๋ด์ฉ์ ๋ํด์ ์กฐ๊ธ ์ ์์ธํ ์ ์ด๋ณด๋ ค๊ณ ํ๋ค.
์ฐธ๊ณ ๋ก ์ด ๋ถ๋ถ์ ๋ค์ค ๋ถ๋ฅ๋ฅผ ์ํ ์ฝ๋์ ์ฐ๊ฒฐ๋์ง ์๋๋ค. ๊ทธ์ ์ดํด๋ฅผ ๋๊ธฐ ์ํด ๋ถ๊ฐ ์ค๋ช ๋ ๋ถ๋ถ์ด๋ค !!
์ฐธ๊ณ ๋ก, ํ์ฌ ์ฌ์ฉํ ๋ชจ๋ธ์ KoBERT ์ด์ง๋ง, KcBERT์์ vocab file์ ๋ณด๊ธฐ ์ฝ๊ฒ text ํ์ผ๋ก ๋ณด์ฌ์ฃผ๊ณ ์์ด, ๋จผ์ KcBERT๋ก ํ ํฐํ, ์ ์ ์ธ์ฝ๋ฉ, padding์ ๋ณด๊ณ KoBERT์์๋ ๋ค์ ํ๋ฒ ์คํํด๋ณด๊ณ ์ ํ๋ค.
KcBERT ๋ชจ๋ธ์์์ ํ ํฐํ์ ์ ์ ์ธ์ฝ๋ฉ
KcBERT ๋ชจ๋ธ์ ๋ง๋ค๊ธฐ ์ํ ๊ธฐ์กด ํ๋ จ ๋ฐ์ดํฐ๋ก๋ถํฐ ๋ง๋ค์ด์ง vocabulary๋ ์์ ๊ฐ์ ํํ๋ฅผ ๋๋ค.
์ฌ์ง์ ๋๋ฅด๋ฉด KcBERT ๊ณต์ repo๋ก ์ฐ๊ฒฐ๋๋ค.
์ด vocab๊ฐ ์ด๋ป๊ฒ ๋์๋๋์ง ์ดํด๋ณด๊ธฐ ์ํด ์์ฃผ ๊ฐ๋จํ ์ฝ๋๋ฅผ ์ง์ ์์ฑํด๋ณด์๋ค.
!pip install --no-cache-dir transformers sentencepiece
from transformers import AutoTokenizer, AutoModelForMaskedLM
# kcbert์ tokenizer์ ๋ชจ๋ธ์ ๋ถ๋ฌ์ด.
kcbert_tokenizer = AutoTokenizer.from_pretrained("beomi/kcbert-base")
kcbert = AutoModelForMaskedLM.from_pretrained("beomi/kcbert-base")
result = kcbert_tokenizer.tokenize("๋๋ ๋ด๋
๋์ ๋ ํฌํํ ์ ์์ด?")
print(result)
print(kcbert_tokenizer.vocab['๋์ '])
print([kcbert_tokenizer.encode(token) for token in result])
์๋ก์ด colab ํ์ผ์ ์ด์ด ๋จ 4์ค์ ์ฝ๋๋ก model๊ณผ tokenizer๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋ค.
์๋ก์ด ๋ฌธ์ฅ์ ๋ง๋ค์ด ๋จผ์ tokenize ํด๋ณด์๋๋, ๋์ ์ด๋ผ๋ ๋จ์ด๋ ๋ชจ๋ธ์์ '๋์ '์ผ๋ก ๋์๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ชจ๋ธ์์ '๋์ '์ผ๋ก ์ ์๋์ด ์๋ ๊ฒ์ ํ์ธํ ํ ํด๋น ๋จ์ด๋ฅผ vocabulary ํ์ผ์์ 9311๋ก ๋์๋๋ค.
vocab์ indexing์ 0๋ถํฐ ์์ํ๋๋ฐ github ํ๊ฒฝ์์ line of code๋ 1๋ถํฐ ์์ํ๋ ๊ฒ ๋๋ฌธ์ 9312์์ 1์ด ๋บ ๊ฐ์ด ๋์ค๋ ๊ฒ์ด๋ค.
์ฆ, ์์์ vocab ํ์ผ๊ณผ ์ ํํ ๋์๋๋ ๊ฒ์ ์ง์ ๋์ผ๋ก ํ์ธํด๋ณผ ์ ์์๋ค.
๋ ๋์๊ฐ ํ ํฐํ๋ ๋ฌธ์ฅ์ ์ ์ ์ธ์ฝ๋ฉํด ํ์ธํด๋ณด๋ ์๊ณผ ๋ค์ 2, 3์ padding์ด๋ margin์ผ๋ก ํ๋จํ ์ ์๊ณ , ์ญ์๋ 9311๋ก ์ธ์ฝ๋ฉ๋ ๊ฐ์ด ์ ์ฐ์ด๊ณ ์๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
KoBERT ๋ชจ๋ธ์์์ ํ ํฐํ์ ์ ์ ์ธ์ฝ๋ฉ
๊ณผ์ฐ ๊ทธ๋ ๋ค๋ฉด ์ค๋ ๋ค๋ฃจ๊ณ ์๋ KoBERT ๋ชจ๋ธ์์๋ ์ด๋ป๊ฒ ํ ํฐ์ผ๋ก ๋๋์ด์ค๊น?
!pip install --no-cache-dir transformers sentencepiece
from transformers import AutoModel, AutoTokenizer
kobert_tokenizer = AutoTokenizer.from_pretrained("skt/kobert-base-v1", use_fast=False)
kobert = AutoModel.from_pretrained("skt/kobert-base-v1")
result = kobert_tokenizer.tokenize("๋๋ ๋ด๋
๋์ ๋ ํฌํํ ์ ์์ด?")
print(result)
kobert_vocab = kobert_tokenizer.get_vocab()
print(kobert_vocab.get('โ๋์ '))
print([kobert_tokenizer.encode(token) for token in result])
kobert ์์๋ ์์๋๋ก ๊ฐ์ ๋ฌธ์ฅ์ด ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ํ ํฐํ๋์๋ค.
๋์ ์ด๋ผ๋ ๋จ์ด๊ฐ 'โ๋์ ' ์ด๋ ํ ํฐ์ผ๋ก ๋์๋์๊ณ 1654 ์ ์ ๊ฐ์ ๊ฐ์ง๋ค.
๋ฌธ์ฅ์ ๋ชจ๋ ์ธ์ฝ๋ฉํ์ ๋์๋ ๊ฐ์ ๊ฐ์ผ๋ก ์ ์ธ์ฝ๋ฉ๋๊ณ ์์์ ํ์ธํด๋ณด์๋ค.
์ด ์คํ์ผ๋ก bert ๋ชจ๋ธ์ด ๋ฌธ์ฅ์ ์ด๋ป๊ฒ ๋๋์ด ํ์ตํ ์ ์๋์ง ์ดํดํ ์ ์์๋ค.
๋ํ, ์์์ ์ค๋ช ํ๋ ๋ถ๋ถ ์ค ์ฌ์ฉํ๋ model๊ณผ tokenizer๋ ํญ์ mapping ๊ด๊ณ์ฌ์ผ ํ๋ค๋ ์ฌ์ค์ด ์ด์ ๋ ๋๋ฌด๋ ๋น์ฐํ๊ฒ ๋๊ปด์ง๋ค.
๋ง์ง๋ง์ผ๋ก KoBERT๊ฐ ์๋ ๋ ๋ค๋ฅธ ๋ชจ๋ธ์ธ KcBERT๋ฅผ ๋ถ๋ฌ์ ์ง์ ์ฝ๋ฉํด๋ณด๋ ๊ณผ์ ์์ ๋ค๋ฅธ ๋ชจ๋ธ์ ์ ์ฉํด๋ณด๋ ์ํ์ฐฉ์ค๋ ๋ฏธ๋ฆฌ ๊ฒฝํํด๋ณด์๋ค.
์! ๋ค์ ๋ค์ค ๋ถ๋ฅ ๋ชจ๋ธ์ ๊ตฌํํ๊ธฐ ์ํ ์ฝ๋๋ก ๋์๊ฐ๋ณด์!
# 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
parameter์ ๊ฒฝ์ฐ, ์์ ์ฝ๋์ ์๋ ๊ฐ๋ค์ ๋์ผํ๊ฒ ์ค์ ํด์ฃผ์๋ค.
#train & test ๋ฐ์ดํฐ๋ก ๋๋๊ธฐ
from sklearn.model_selection import train_test_split
dataset_train, dataset_test = train_test_split(data_list, test_size=0.2, shuffle=True, random_state=34)
์ฌ์ดํท๋ฐ์์ ์ ๊ณตํด์ฃผ๋ train_test_split ๋ฉ์๋๋ฅผ ํ์ฉํด ๊ธฐ์กด data_list๋ฅผ train ๋ฐ์ดํฐ์ ๊ณผ test ๋ฐ์ดํฐ์ ์ผ๋ก ๋๋๋ค. 5:1 ๋น์จ๋ก ๋๋์๋ค.
tok=tokenizer.tokenize
data_train = BERTDataset(dataset_train, 0, 1, tok, vocab, max_len, True, False)
data_test = BERTDataset(dataset_test,0, 1, tok, vocab, max_len, True, False)
์์์ ๊ตฌํํ BERTDataset ํด๋์ค๋ฅผ ํ์ฉํด tokenization, int encoding, padding ์ ์งํํ์๋ค.
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 ํ์์ dataset์ ๋ง๋ค์ด์ฃผ๋ฉด์, ์ ๋ ฅ ๋ฐ์ดํฐ์ ์ ์ฒ๋ฆฌ๊ฐ ๋ชจ๋ ๋๋ฌ๋ค.
6. KoBERT ๋ชจ๋ธ ๊ตฌํํ๊ธฐ
class BERTClassifier(nn.Module):
def __init__(self,
bert,
hidden_size = 768,
num_classes=7, ##ํด๋์ค ์ ์กฐ์ ##
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),return_dict=False)
if self.dr_rate:
out = self.dropout(pooler)
return self.classifier(out)
#BERT ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ
model = BERTClassifier(bertmodel, dr_rate=0.5).to(device)
#optimizer์ schedule ์ค์
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}
]
optimizer = AdamW(optimizer_grouped_parameters, lr=learning_rate)
loss_fn = nn.CrossEntropyLoss() # ๋ค์ค๋ถ๋ฅ๋ฅผ ์ํ ๋ํ์ ์ธ loss func
t_total = len(train_dataloader) * num_epochs
warmup_step = int(t_total * warmup_ratio)
scheduler = get_cosine_schedule_with_warmup(optimizer, num_warmup_steps=warmup_step, num_training_steps=t_total)
#์ ํ๋ ์ธก์ ์ ์ํ ํจ์ ์ ์
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
train_dataloader
์์ ์ฝ๋์ ๋์ผํ๊ฒ ์ฌ์ฉํ์๋ค.
7. train
train_history=[]
test_history=[]
loss_history=[]
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 enumerate(tqdm_notebook(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)
#print(label.shape,out.shape)
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)))
train_history.append(train_acc / (batch_id+1))
loss_history.append(loss.data.cpu().numpy())
print("epoch {} train acc {}".format(e+1, train_acc / (batch_id+1)))
#train_history.append(train_acc / (batch_id+1))
model.eval()
for batch_id, (token_ids, valid_length, segment_ids, label) in enumerate(tqdm_notebook(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)))
test_history.append(test_acc / (batch_id+1))
KoBERT ๋ชจ๋ธ์ ํ์ต์ํค๋ ์ฝ๋์ด๋ค. epoch์ ์์ ๋ฐ๋ผ ํ์ต์๊ฐ์ด ๋ง์ด ๋ฌ๋ผ์ง๋๋ฐ epoch๋ฅผ 5๋ก ์ง์ ํ๋ฉด 30๋ถ ์ ๋ ๊ฑธ๋ฆฐ๋ค. ์์ ์ด ๋ชจ๋ธ์์ ํ์ต์ํฌ ์ ์๋๋ก ์ ๋ ฅ ๋ฐ์ดํฐ์ ์ ์ฒ๋ฆฌํ๊ณ , ํ๋ผ๋ฏธํฐ๋ฅผ ๋ชจ๋ ์ง์ ํ์์ผ๋ฏ๋ก ์์ ์ฝ๋์ ๋์ผํ๊ฒ ์งํํ์๋ค.
train dataset์ ๋ํด์๋ 0.979, test dataset์ ๋ํด์๋ 0.918์ ์ ํ๋๋ฅผ ๊ธฐ๋กํ๋ค.
์ด๋ ๊ฒ ๋์ ์ ํ๋๋ฅผ ๊ธฐ๋กํ๋ ์ด์ ๋ ๋ฐ๋ก ๋ฐ์ดํฐ์ ์ ์๋ค.
์งํํ๊ณ ์๋ ํ๋ก์ ํธ์์ ํด๋น ํ๋ก์ ํธ์ ์ฑ๊ฒฉ์ ๋ง๊ฒ 3๋ง 7์ฒ ์ฌ๊ฐ ์ ๋์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ์ฌ๋ถ๋ฅํ๊ณ ์๋๋ฐ, ์ฌ๋์ด ์๊ฐํ๊ธฐ์๋ ๋ถ๋ช ํ ๊ฐ์ ๋ด์ฉ์ผ๋ก ์ธ์ง๋๋ ๋ฌธ์ฅ์ด์ง๋ง ์ฌ์ํ ๋จ์ด๋ฅผ ํ ๋๊ฐ ์์ ์, ์ด๋จ ๋๋ ์ค์ํ ๋จ์ด๋ฅผ ์ ๊ฑฐํด์, ์งง๊ฒ ์๋ผ์, ๊ธธ๊ฒ ๋์ฌ๋จ๋ ค์, ๋ฌธ์ฅ ๋ถํธ๋ฅผ ๋ค๋ฅด๊ฒ, ๊ฐํ์ฌ๋ฅผ ์ถ๊ฐํด์ ๋ฑ๋ฑ ์ค๋ณต์ธ ๋ฏ ์ค๋ณต ์๋ ๋ฐ์ดํฐ๋ก ํ์ต์์ผฐ๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ ๋์ ์ ํ๋๊ฐ ๋์จ ๊ฒ์ด๋ผ ํ๋จ๋๋ค.
8. ์ง์ ๋ง๋ ์๋ก์ด ๋ฌธ์ฅ์ผ๋ก ํ ์คํธ
์ด์ ์ง์ ๋ฌธ์ฅ์ ๋ง๋ค์ด ํ์ต๋ ๋ชจ๋ธ์ด ๋ค์ค ๋ถ๋ฅ๋ฅผ ์ ํด๋ด๋์ง ์์๋ณด๋ ค๊ณ ํ๋ค.
def predict(predict_sentence):
data = [predict_sentence, '0']
dataset_another = [data]
another_test = BERTDataset(dataset_another, 0, 1, tok, vocab, max_len, True, False)
test_dataloader = torch.utils.data.DataLoader(another_test, batch_size=batch_size, num_workers=5)
model.eval()
for batch_id, (token_ids, valid_length, segment_ids, label) in enumerate(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_eval=[]
for i in out:
logits=i
logits = logits.detach().cpu().numpy()
if np.argmax(logits) == 0:
test_eval.append("๊ณตํฌ๊ฐ")
elif np.argmax(logits) == 1:
test_eval.append("๋๋์ด")
elif np.argmax(logits) == 2:
test_eval.append("๋ถ๋
ธ๊ฐ")
elif np.argmax(logits) == 3:
test_eval.append("์ฌํ์ด")
elif np.argmax(logits) == 4:
test_eval.append("์ค๋ฆฝ์ด")
elif np.argmax(logits) == 5:
test_eval.append("ํ๋ณต์ด")
elif np.argmax(logits) == 6:
test_eval.append("ํ์ค๊ฐ")
print(">> ์
๋ ฅํ์ ๋ด์ฉ์์ " + test_eval[0] + " ๋๊ปด์ง๋๋ค.")
ํ์ต๋ ๋ชจ๋ธ์ ํ์ฉํ์ฌ ๋ค์ค ๋ถ๋ฅ๋ ํด๋์ค๋ฅผ ์ถ๋ ฅํด์ฃผ๋ predict ํจ์๋ฅผ ๊ตฌํํ ๊ฒ์ด๋ค.
#์ง๋ฌธ ๋ฌดํ๋ฐ๋ณตํ๊ธฐ! 0 ์
๋ ฅ์ ์ข
๋ฃ
end = 1
while end == 1 :
sentence = input("ํ๊ณ ์ถ์ ๋ง์ ์
๋ ฅํด์ฃผ์ธ์ : ")
if sentence == "0" :
break
predict(sentence)
print("\n")
5. ์ด ๋ชจ๋ธ์ด ์ด๋ป๊ฒ ํ์ฉ๋ ๊น?
์ด ๊ธฐ์ ์ ํ์ฌ ๊ธ์ด์ด๊ฐ ์งํํ๊ณ ์๋ ์บก์คํค ํ๋ก์ ํธ์์ ๊ฐ์ฅ ์ฃผ์ถ์ด ๋๋ ๊ธฐ์ ์ด๋ค.
์กฐ๊ธ ๋ ์์ธํ ๋งํ๋ฉด, ์ฌ์ฉ์๊ฐ ์์ฑํ ์ผ๊ธฐ๋ฅผ ๋ถ์ํ์ฌ
โ ์ด๋ ํ ๊ฐ์ ์ ๋ง์ด ํ์ถํ๋์ง ์๋ ค์ฃผ๊ณ ,
โก ๋์ ๋ ์ผ๊ธฐ๋ค๋ก ํ๋ณตํ ์ ๋์ ์ฐ์ธํ ์ ๋๋ฅผ ๋จ๊ณ๋ก ๋ถ๋ฅํ์ฌ ์ ๋์ ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ค๊ณ ํ๋ค.
โ ์ค๋์ ๊ฐ์ top 3
ํ๋ฃจ๋์ ์์ฑ๋ ์ผ๊ธฐ์์ ์ด๋ ํ ๊ฐ์ ์ ๋ง์ด ํ์ถํ๋์ง๋ ์๋์ ๊ฐ์ด ๊ณ์ฐํ ๊ฒ์ด๋ค.
์ด ๋ชจ๋ธ์ ํ์ฉํ์ฌ ๊ฐ ๋ฌธ์ฅ, ์ฆ ๋จ๋ฐ์ ์ผ๋ก ๋ณด์ด๋ ๊ฐ์ ๋ค์ ์ถ์ถํ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ค๋ฆฝ์ ์ ์ธํ ๊ฐ์ ์ด ๋ณด์ด๋ ๋ฌธ์ฅ๋ค์ ํผ์ผํธ๋ฅผ ๋๋์ด top3์ ๊ฐ์ ์ ๋ํ๋ผ ๊ฒ์ด๋ค.
์ฝ๊ฒ ์์๋ฅผ ๋ค์ด๋ณด์๋ค.
ํ ์ฌ์ฉ์๋ 20๋ฌธ์ฅ์ ์ ์๋๋ฐ, 10 ๋ฌธ์ฅ์ด ์ค๋ฆฝ์ผ๋ก ํ๋จ๋๊ณ ๋๋จธ์ง 10๋ฌธ์ฅ์ด ์ค๋ฆฝ ์ด์ธ์ ๊ฐ์ ์ ๋ณด์๋ค. 10 ๋ฌธ์ฅ ์ค ํ๋ณต์ผ๋ก ํ๋จ๋ ๋ฌธ์ฅ์ 5๊ฐ, ๋ถ๋ ธ๋ก ํ๋จ๋ ๋ฌธ์ฅ์ 4๋ฌธ์ฅ, ์ฌํ์ผ๋ก ํ๋จ๋ ๋ฌธ์ฅ์ 1๋ฌธ์ฅ์ด์๋ค.
→ ์ค๋์ ๋ํ ๊ฐ์ ์ผ๋ก ํ๋ณต์ ์ถ์ฒํด์ฃผ๊ณ , ๊ฐ์ ์ ํ๋ณต 50%, ๋ถ๋ ธ 40%, ์ฌํ 10%์ผ๋ก ๊ธฐ๋ก๋ ๊ฒ์ด๋ค.
โก ์ข ํฉ ํ๋ณต ์ง์, ์ข ํฉ ์ฐ์ธ ์ง์
๋์ ๋ ์ผ๊ธฐ๋ค๋ก๋ถํฐ ํ๋ณต ์ง์์ ์ฐ์ธ ์ง์๋ฅผ ๊ณ์ฐํ๋ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
ํ๋ณต์ ๊ฒฝ์ฐ, ๊ธ์ ์ ์ผ๋ก ํ๋จ๋๋ ๊ฐ์ ๋ถ๋ฅ ํด๋์ค๊ฐ ํ๋ ๋ฐ์ ์์ผ๋ฏ๋ก, ์ง๋ 30์ผ๋์ ์ด ์ผ๊ธฐ์ ๊ฐ ๋ ์ง ํ๋ณต ํผ์ผํธ๋ฅผ ์ฐ์ ํ๊ท ๋ธ ๊ฐ์ ํ์ฉํ ๊ฒ์ด๋ค.
์ฐ์ธ๋์ ๊ฒฝ์ฐ, ๋จผ์ ๋ถ์ ์ ์ผ๋ก ํ๋จ๋ ๊ฐ์ ํผ์ผํธ๋ฅผ ํฉ์น๊ณ ์ถ๊ฐ์ ์ผ๋ก ์ฐ์ธํจ์ด ๋ฌป์ด๋๋ ๋ฌธ์ฅ์ ๋ํด์๋ ๊ฐ์ค์น๋ฅผ ๋ํด ์ฐ์ ํ๊ท ์ ๋ธ ๊ฐ์ ํ์ฉํ ๊ฒ์ด๋ค.
์ด๋ ์ฝ๊ฒ ์์๋ฅผ ๋ค์ด๋ณด์๋ค.
์ง๋ 30์ผ ๋์ 5๋ฒ์ ์ผ๊ธฐ๋ฅผ ์ด ์ฌ์ฉ์๊ฐ ์๋ค.
ํ๋ณต์ด ๊ฐ๊ฐ์ ์ผ๊ธฐ์์ 90%, 50%, 10%, 60%, 20% ์์น๋ก ๋ํ๋ฌ์ ๋,
(0.9+0.5+0.1+0.6+0.2)/5 = 0.46๋ก ๊ณ์ฐ์ด ๋๋ค.
์ด๋ ๊ฒ ๊ณ์ฐ๋ ์์น๋ฅผ ๊ธ๊ฐ์ผ๋ก ๋๋์ด ํ๋ณต๋๋ก ๋ณด์ฌ์ค ๊ฒ์ด๋ค.
๋ถ์ ์ ๊ฐ์ ์ ๊ฐ๊ฐ์ ์ผ๊ธฐ์์ ๋ฐ๋๋ก 10%, 50%, 90%, 40%, 80% ์์น๋ก ๋ํ๋ฌ๊ณ , ์ถ๊ฐ์ ์ผ๋ก 2๋ฒ์งธ์ ์ผ๊ธฐ์์ "๋๋ฌด ์ฐ์ธํ๋ค"์ ๊ฐ์ด ์ง๊ด์ ์ผ๋ก "์ฐ์ธ"์ด๋ผ ํ๋จ๋ ๋ฌธ์ฅ์ด ์์๋ค๋ฉด ๊ฐ ๋ฌธ์ฅ ๋น ํด๋น ๋ ์ ๋ถ์ ์ ํผ์ผํธ์์ 5%~10%์ ๊ฐ์ค์น๋ฅผ ๋ํด์ค ๊ฒ์ด๋ค. ์ฐ์ธ๋ก ํ๋จ๋ ๋ฌธ์ฅ์ด ๋ง์๋ ๊ฐ ๋ ์ง์ ๋ถ์ ์ ์์น๋ ์ต๋ 100%๋ก ์ ํ์ ๋๋ค.
๊ณ์ฐ ํด๋ณด๋ฉด, 2๋ฒ์งธ ์ผ๊ธฐ์์ ์ฐ์ธ๊ณผ ์ง์ ์ ์ผ๋ก ์ฐ๊ด๋ ๋ฌธ์ฅ์ด 3๋ฒ ๋ํ๋ฌ์ ๋
(0.1+(0.5+0.1*3)+0.9+0.4+0.8)/5 = 0.6์ผ๋ก ๊ณ์ฐ์ด ๋๋ค.
์ด๋ ๊ฒ ๊ณ์ฐ๋ ์์น๋ฅผ ๊ธ๊ฐ์ผ๋ก ๋๋์ด ์ฐ์ธ๋๋ก ๋ณด์ฌ์ค ๊ฒ์ด๋ค.
6. ์์ผ๋ก์ ๊ธฐ๋
์ฐ๋ฆฌ ํ์ด ๊ตฌ์ํ๊ณ ์๋ ์๋น์ค๋ ์ผ๊ธฐ ์๋น์ค์ด๋ฏ๋ก ์ผ๊ธฐ์ ์๋ง๋ ๊ฐ์ ์ ๋ถ๋ฅํ๊ณ ์ ํ๋ค.
"๊ธฐ์จ/ํ์จ/์ฌํ/๋ถ๋ ธ/๋ถ์/ํผ๊ณค" 6๊ฐ์ง๋ก ๊ฐ์ ์ ์ฌ๋ถ๋ฅํ์ฌ ํ์ฌ ์ด ํฌ์คํ ์์ ๋๋ ค๋ณธ ๋ฐ์ดํฐ์ ์ผ๋ก ๊ฐ์ ์ ์ง์ ์ฌ๋ถ๋ฅํ์ฌ fine tuning ํ๊ณ ์๋ค.
๊ฒฐ๊ตญ ์ฃผ์ด์ง ๋ฐ์ดํฐ์ ์ด ๋ชจ๋ธ์ ์ ํ๋๋ฅผ ํ๊ฐ๋ฆํ๋ค. ์ง์ ๋ฐ์ดํฐ์ ์ ์ฌ๋ถ๋ฅํ๊ณ ์์ผ๋ฏ๋ก ๋ฐ์ดํฐ๋ฅผ ์กฐ๊ธ์ฉ ์ ์ ํ์ฌ ์ ํ๋๊ฐ ๋์์ง ๋ชจ์ต์ ๋ณด์ฌ๋๋ฆฌ๊ณ ์ ํ๋ค.
๋ํ, KcBERT ๋ฑ ํ์๋ ๋ชจ๋ธ๋ค์ด ๊ณ์ ์๊ฒจ๋๊ณ ์๋๋ฐ, ๋ ๋ค์ํ ๋ชจ๋ธ์ ๋ํด์ ์ ํ๋ ๊ฒ์ฆ์ ํด๋ณผ ์์ ์ด๋ค.
'NLP' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ฌ๋ฆฌ ์ผ์ด ์ฑ๋ด(kogpt2, kobert) ๊ตฌํํด ๋ฐฐํฌํด๋ณด์ (4) | 2022.05.17 |
---|