[1] kogpt2 ๊ธฐ๋ฐ ์ฌ๋ฆฌ ์ผ์ด ์ฑ๋ด
ํ์ค ์ค๋ช
KoGPT2 ๋ชจ๋ธ์ ๋ฌธ์ฅ์ "์์ฑ"ํด๋ด๋ ๋ชจ๋ธ์ ๋๋ค.
์ฌ๋ฆฌ ์ผ์ด ๋ชฉ์ ์ ์ฑ๋ด์ ๊ตฌํํ๊ธฐ ์ํด ์ ๋ ฅ ๋ฐ์ ๋ด์ฉ์ ๋ํด ์๋กํ๊ฑฐ๋ ๊ณต๊ฐํ๊ฑฐ๋ ๋ถ๋๋ฌ์ด ํํ์ผ๋ก ๋ฐ์ํ๊ณ ๋ฌธ์ฅ์ ์์ฑํด๋ด๋๋ก ํ์ธ ํ๋์ ์งํํ์ต๋๋ค.
ํ์ต ์ฝ๋ ๋งํฌ์ ๋๋ค.
์ฌ์ฉํ ๋ฐ์ดํฐ์
1. ai hub ์ ๊ณต, ์ฐ๋์ค ๋ํ ์คํฌ๋ฆฝํธ ๋ฐ์ดํฐ์
AI hub > ์ธ๋ถ๋ฐ์ดํฐ > KETI R&D๋ฐ์ดํฐ >์ธ์๊ธฐ์ (์ธ์ด์ง๋ฅ) > ์ฐ๋์ค ๋ํ ์คํฌ๋ฆฝํธ ๋ฐ์ดํฐ์
2. @songys (์ก์์๋) ์ ๊ณต, ์ฑ๋ด ๋ฐ์ดํฐ์
๋ ๋ฐ์ดํฐ๋ฅผ ์๋์ ๋ฐ์ดํฐ ํ์์ ๋ง๊ฒ ๊ฐ๊ณตํ์ต๋๋ค.
ํ์ต ํ๊ฒฝ
colab ๊ธฐ๋ณธ ํ๊ฒฝ์์๋ ๊ฐ๋ฅํฉ๋๋ค๋ง, ์๊ฐ์ด ๊ฝค ์ค๋๊ฑธ๋ฆฌ๋ ํธ์ ๋๋ค!
1 epoch์ 15~16๋ถ ์ ๋ ๊ฑธ๋ฆฌ๋๋ฐ epoch ์ต๋ 5๋ฒ ์ ๋๊น์ง๊ฐ ์ ์ ์ ์ด๋ผ๊ณ ์๊ฐํด์ ๋ฐํ์์ด ๋์ด์ง์ง๋ง ์์ผ๋ฉด ์ถฉ๋ถํ ๊ฐ๋ฅํฉ๋๋ค.
์ ๋ ํ๊ต์์ ์ง์๋ฐ์ gpu ์๋ฒ์์ ํ์ต์์ผฐ์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
์ฌ์ค ํ์ต์ํฌ ๋ ์์กด์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด ๊ฐ์ฅ ํฌ๊ธฐ ๋๋ฌธ์ ์ ๊ฐ ํ์ต์์ผฐ๋ ํ๊ฒฝ์ ๋ํด์ ์์ธํ ์ ์ด๋๊ฒ ์ต๋๋ค.
linux ํ๊ฒฝ์์ (๋ ์์ธํ๋ ubuntu)
์ ๋ python ๋ฒ์ ์ด 3.8.10 ์ ๋๋ค.
requirements_linux.txt ์ ํ์ํ ์์กด์ฑ์ ์์ฑํด๋์์ต๋๋ค.
transformers==4.5.1
pytorch_lightning==1.2.10
pandas
์ค์นํฉ๋๋ค. ์๋์ ๋ช ๋ น์ด๋ก ์คํ์ํฌ ์ ์์ต๋๋ค.
pip install -r requirements_linux.txt
์๋์ ๋ช ๋ น์ด๋ก torch์ ๋ฒ์ ์ผ๋ก ์ฌ์ค์นํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
graphic card(nvidia ๊ฐ์, ์ ๋_RTX 3080)๊ฐ ์ง์ํ๋ torch ๋ฒ์ ์ด ๋ช ๊ฐ ์์ด์ ๊ตฌ๊ธ๋งํด๋ณด๋ฉด ์ ๋ฅผ ๋จน๊ณ ์๋ ์ฌ๋๋ค์ด ๊ต์ฅํ ๋ง๋ค.
capability sm_86 is not compatible
The current PyTorch install supports CUDA capabilities sm_37 sm_50 sm_60 sm_70 sm_75
์ด๋ฐ ๋ฌธ๊ตฌ๋ค์ ์ฌ์ฌ์ฐฎ๊ฒ ๋ณผ ์ ์๋ค.
https://pytorch.org/get-started/previous-versions/
pytorch ๊ณต์ ์ฌ์ดํธ์ด๊ณ ์ด์ ๋ฒ์ ์ค์น ๋ช ๋ น์ด๋ฅผ ์์ธํ ๊ธฐ๋กํด๋ ๋งํฌ์ด๋ค.
ํธํ์ด ๋๋ torch๋ฅผ ์ฐพ์ ๋๊น์ง ์ผ์์ด ์ค์นํด๋ณด๊ณ ์คํํด๋ณด๋ฉด ๋๋ค.
cuda ๋ฒ์ ์ด ์๋ง์๋ ์ผ๋จ ์๋ํด๋ณด๋ ๊ฒ์ด ์ข๋ค..!
์ฝ๋ ์ค๋ช
๋ ํผ๋ฐ์ค ๋งํฌ์
๋๋ค.
์ ๋ ์ฌ๋ฆฌ ์ผ์ด ๋ชฉ์ ์ ๋ง์ถ์ด ๊ฐ๊ณตํ ๋ฐ์ดํฐ์ ์กฐ๊ธ์ฉ ์์ ํด ํ์ต์์ผฐ๋ ์ฝ๋๋ฅผ ์ ์ฅํ๊ธฐ ์ํด github repo๋ฅผ ์์ฑํด ์ ์ฅํด๋์์ต๋๋ค.
์ ๋ ํฌ์ trainer_s.py ์ฝ๋์ ๋ํด ์ค๋ช ํด๋๋ฆฌ๊ฒ ์ต๋๋ค.
import argparse
import logging
import numpy as np
import pandas as pd
import torch
from pytorch_lightning import Trainer
from pytorch_lightning.callbacks import ModelCheckpoint
from pytorch_lightning.core.lightning import LightningModule
from torch.utils.data import DataLoader, Dataset
from transformers.optimization import AdamW, get_cosine_schedule_with_warmup
from transformers import PreTrainedTokenizerFast, GPT2LMHeadModel
pytorch lightning์ ํ์ฉํด ๊ตฌํ๋์์ต๋๋ค.
parser = argparse.ArgumentParser(description='Simsimi based on KoGPT-2')
parser.add_argument('--chat',
action='store_true',
default=False,
help='response generation on given user input')
parser.add_argument('--sentiment',
type=str,
default='0',
help='sentiment for system. 0 is neutral, 1 is negative, 2 is positive.')
parser.add_argument('--model_params',
type=str,
default='model_chp/model_-last.ckpt',
help='model binary for starting chat')
parser.add_argument('--train',
action='store_true',
default=False,
help='for training')
ArgumentParser๋ ์ปค๋งจ๋ ๋ผ์ธ์์ ํ๋ก๊ทธ๋จ์ ์คํํ ๋ ์ธ์๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ ์ ์๋๋ก ๋์ํ๋ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
python trainer_s.py --train
ํ์ต์ ์์ํ๊ธฐ ์ํด์๋ commend line์์ --train ์ต์ ์ ์ฃผ์ด ์คํํด์ผ ํฉ๋๋ค.
python trainer_s.py --chat
๋ช ๋ น์ผ๋ก ์ฑํ ํ ์ ์์ต๋๋ค.
logger = logging.getLogger()
logger.setLevel(logging.INFO)
Python์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ logging ๋ชจ๋์ ์ฌ์ฉํด ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด ๋ถ๋ฌ์ต๋๋ค.
INFO level๋ก ์ค์ ํด ์์ ์ด ์ ์์ ์ผ๋ก ์๋ํ๊ณ ์๋ค๋ ํ์ธ ๋ฉ์์ง๋ ๋ณด์ฌ๋ฌ๋ผ๊ณ ์ค์ ํ์ต๋๋ค.
๋ก๊ทธ๊ฐ ๊ธธ๊ฒ ๋จ๋ ๊ฒ ์ซ๋ค๋ฉด ์ด ๋ถ๋ถ์ ์ง์์ฃผ์๊ฑฐ๋ ๋ก๊ฑฐ level์ ์กฐ์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
U_TKN = '<usr>'
S_TKN = '<sys>'
BOS = '</s>'
EOS = '</s>'
MASK = '<unused0>'
SENT = '<unused1>'
PAD = '<pad>'
TOKENIZER = PreTrainedTokenizerFast.from_pretrained("skt/kogpt2-base-v2",
bos_token=BOS, eos_token=EOS, unk_token='<unk>',
pad_token=PAD, mask_token=MASK)
์ค์ ํ๊ณ ์ถ์ ํ ํฐ์ ์์๋ก ์ค์ ํ๊ณ hugging face์ ์ฌ๋ผ์์๋ skt/kogpt2-base-v2 ๋ฒ์ ์ ํ ํฌ๋์ด์ ๋ฅผ ๋ค์ด๋ก๋ํฉ๋๋ค.
PreTrainedTokenizerFast ์ ์์ฑ๊ฐ์ hugging face์ ๊ณต์ ๋ฌธ์์์ ํ์ธํ ์ ์์ต๋๋ค.
class CharDataset(Dataset):
def __init__(self, chats, max_len=32):
self._data = chats
self.first = True
self.q_token = U_TKN
self.a_token = S_TKN
self.sent_token = SENT
self.bos = BOS
self.eos = EOS
self.mask = MASK
self.pad = PAD
self.max_len = max_len
self.tokenizer = TOKENIZER
def __len__(self):
return len(self._data)
def __getitem__(self, idx):
turn = self._data.iloc[idx]
q = turn['Q']
a = turn['A']
sentiment = str(turn['label'])
q_toked = self.tokenizer.tokenize(self.q_token + q + \
self.sent_token + sentiment)
q_len = len(q_toked)
a_toked = self.tokenizer.tokenize(self.a_token + a + self.eos)
a_len = len(a_toked)
if q_len + a_len > self.max_len:
a_len = self.max_len - q_len
if a_len <= 0:
q_toked = q_toked[-(int(self.max_len/2)):]
q_len = len(q_toked)
a_len = self.max_len - q_len
assert a_len > 0
a_toked = a_toked[:a_len]
a_len = len(a_toked)
assert a_len == len(a_toked), f'{a_len} ==? {len(a_toked)}'
# [mask, mask, ...., mask, ..., <bos>,..A.. <eos>, <pad>....]
labels = [
self.mask,
] * q_len + a_toked[1:]
if self.first:
logging.info("contexts : {}".format(q))
logging.info("toked ctx: {}".format(q_toked))
logging.info("response : {}".format(a))
logging.info("toked response : {}".format(a_toked))
logging.info('labels {}'.format(labels))
self.first = False
mask = [0] * q_len + [1] * a_len + [0] * (self.max_len - q_len - a_len)
self.max_len
labels_ids = self.tokenizer.convert_tokens_to_ids(labels)
while len(labels_ids) < self.max_len:
labels_ids += [self.tokenizer.pad_token_id]
token_ids = self.tokenizer.convert_tokens_to_ids(q_toked + a_toked)
while len(token_ids) < self.max_len:
token_ids += [self.tokenizer.pad_token_id]
return(token_ids, np.array(mask),
labels_ids)
CharDataset์ Dataset์ ์์๋ฐ์์ผ๋ฏ๋ก init, len, getitem ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ผํฉ๋๋ค.
Dataset์ torch.utils.data.Dataset ์ ์์นํด ์๋ ๋ฐ์ดํฐ์ ์ ๋ํ๋ด๋ ์ถ์ํด๋์ค์ ๋๋ค.
len์ ๋ฐ์ดํฐ์ ์ ํฌ๊ธฐ๋ฅผ ๋ฆฌํดํ๊ณ getitem์ i๋ฒ์งธ ์ํ์ ์ฐพ๋๋ฐ ์ฌ์ฉํฉ๋๋ค.
class KoGPT2Chat(LightningModule):
def __init__(self, hparams, **kwargs):
super(KoGPT2Chat, self).__init__()
self.hparams = hparams
self.neg = -1e18
self.kogpt2 = GPT2LMHeadModel.from_pretrained('skt/kogpt2-base-v2')
self.loss_function = torch.nn.CrossEntropyLoss(reduction='none')
@staticmethod
def add_model_specific_args(parent_parser):
# add model specific args
parser = argparse.ArgumentParser(parents=[parent_parser], add_help=False)
parser.add_argument('--max-len',
type=int,
default=64,
help='max sentence length on input (default: 32)')
parser.add_argument('--batch-size',
type=int,
default=96,
help='batch size for training (default: 96)')
parser.add_argument('--lr',
type=float,
default=5e-5,
help='The initial learning rate')
parser.add_argument('--warmup_ratio',
type=float,
default=0.1,
help='warmup ratio')
return parser
def forward(self, inputs):
# (batch, seq_len, hiddens)
output = self.kogpt2(inputs, return_dict=True)
return output.logits
def training_step(self, batch, batch_idx):
token_ids, mask, label = batch
out = self(token_ids)
mask_3d = mask.unsqueeze(dim=2).repeat_interleave(repeats=out.shape[2], dim=2)
mask_out = torch.where(mask_3d == 1, out, self.neg * torch.ones_like(out))
loss = self.loss_function(mask_out.transpose(2, 1), label)
loss_avg = loss.sum() / mask.sum()
self.log('train_loss', loss_avg)
return loss_avg
def configure_optimizers(self):
# Prepare optimizer
param_optimizer = list(self.named_parameters())
no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
{'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01},
{'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
optimizer = AdamW(optimizer_grouped_parameters,
lr=self.hparams.lr, correct_bias=False)
# warm up lr
num_train_steps = len(self.train_dataloader()) * self.hparams.max_epochs
num_warmup_steps = int(num_train_steps * self.hparams.warmup_ratio)
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=num_warmup_steps, num_training_steps=num_train_steps)
lr_scheduler = {'scheduler': scheduler, 'name': 'cosine_schedule_with_warmup',
'monitor': 'loss', 'interval': 'step',
'frequency': 1}
return [optimizer], [lr_scheduler]
def _collate_fn(self, batch):
data = [item[0] for item in batch]
mask = [item[1] for item in batch]
label = [item[2] for item in batch]
return torch.LongTensor(data), torch.LongTensor(mask), torch.LongTensor(label)
def train_dataloader(self):
data = pd.read_csv('chatbot_dataset_s.csv')
self.train_set = CharDataset(data, max_len=self.hparams.max_len)
train_dataloader = DataLoader(
self.train_set, batch_size=self.hparams.batch_size, num_workers=2,
shuffle=True, collate_fn=self._collate_fn)
return train_dataloader
def chat(self, sent='0'):
tok = TOKENIZER
sent_tokens = tok.tokenize(sent)
with torch.no_grad():
p = input('user > ')
q = p.strip()
a = ''
while 1:
input_ids = torch.LongTensor(tok.encode(U_TKN + q + SENT + sent + S_TKN + a)).unsqueeze(dim=0)
pred = self(input_ids)
gen = tok.convert_ids_to_tokens(
torch.argmax(
pred,
dim=-1).squeeze().numpy().tolist())[-1]
if gen == EOS:
break
a += gen.replace('โ', ' ')
print("Chatbot > {}".format(a.strip()))
return q
hyperparameter ๋ค์ ArgumentParser๋ฅผ ์ฌ์ฉํด ์ปค๋งจ๋ ๋ผ์ธ์์ ์ต์ ์ผ๋ก ์ง์ ํด์ค ์ ์๋๋ก ์ค์ ํฉ๋๋ค.
pytorch lightning์์๋ trainer์ ๋ชจ๋ธ์ด ์ํธ์์ฉ์ ํ ์ ์๋๋ก pytorch์ nn.Module์ ์์ ํด๋์ค์ธ LightningModule์ ๊ตฌํํด์ผ ํฉ๋๋ค. LightningModule์ ์ ์ํ๊ธฐ ์ํด LightningModule ํด๋์ค๋ฅผ ์์๋ฐ๊ณ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ๊ตฌํํด์ผ ํฉ๋๋ค.
forward๋ ๋ชจ๋ธ์ ์ถ๋ก ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํ๊ณ ์ถ์ ๋ ์ฌ์ฉํฉ๋๋ค. ๊ผญ ์ ์ํด์ผ ํ๋ ๋ฉ์๋๋ ์๋๋๋ค. ํ์ง๋ง self(์
๋ ฅ)๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ค์ด์ฃผ๋ฏ๋ก ๊ตฌํํด์ฃผ๋ฉด ํธ๋ฆฌํฉ๋๋ค.
training_step๊ณผ configure_optimizers ํ์์ ์ผ๋ก ๊ตฌํํด์ผ ํฉ๋๋ค.
training_step์ ํ์ต ๋ฃจํ์ body ๋ถ๋ถ์ ๋ํ๋
๋๋ค. ์ด ๋ฉ์๋์์๋ ์ธ์๋ก training dataloader๊ฐ ์ ๊ณตํ๋ batch์ ํด๋น batch์ index๊ฐ ์ฃผ์ด์ง๊ณ train loss๋ฅผ ๊ณ์ฐํ์ฌ ๋ฐํํฉ๋๋ค.
configure_optimizers์์๋ ๋ชจ๋ธ์ ์ต์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐพ์ ๋ ์ฌ์ฉํ optimizer์ scheduler๋ฅผ ๊ตฌํํฉ๋๋ค. ์ด ์ฝ๋์์๋ ํ์ตํด์ผ ํ ๋ชจ๋ธ์ด ํ๋์ด๋ฏ๋ก ํ๋์ Adam optimzer๋ง ์ฌ์ฉํฉ๋๋ค.
train_dataloader๋ batch ๊ธฐ๋ฐ์ผ๋ก ๋ชจ๋ธ์ ํ์ต์ํค๊ธฐ ์ํด ๋ฐ์ดํฐ์
์ ์
๋ ฅ์ผ๋ก ๋ฐ์ batch size๋ก ์ฌ๋ผ์ด์ฑํ๋ ์ญํ ์ ํฉ๋๋ค.
DataLoader์๋ batchsize๋ฅผ ํฌํจํ์ฌ ์ฌ๋ฌ๊ฐ์ง ํ๋ผ๋ฏธํฐ๋ค์ด ์๋๋ฐ, ์ด ์ค ํ๋๊ฐ ๋ฐ๋ก collate_fn์
๋๋ค.
_collate_fn ํจ์๋ก ์ปค์คํ
ํด์ค ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ๊ฐ๋ณ ๊ธธ์ด์ input์ batch๋ก ์ ๋ฌถ์ด์ DataLoader๋ก ๋๊ฒจ์ฃผ๋ ์ญํ ์ ํฉ๋๋ค.
chat ์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅ์ ํ๋ฉด self(ํ ํฐํ๋ ์ ๋ ฅ) ์ผ๋ก forward๊ฐ ์คํ๋์ด ์ฑ๋ด์ ์๋ต์ ๋ฐํํ๋ ํจ์์ ๋๋ค.
parser = KoGPT2Chat.add_model_specific_args(parser)
parser = Trainer.add_argparse_args(parser)
args = parser.parse_args()
logging.info(args)
if __name__ == "__main__":
# python trainer_s.py --train --gpus 1 --max_epochs 5
if args.train:
checkpoint_callback = ModelCheckpoint(
dirpath='model_chp',
filename='{epoch:02d}-{train_loss:.2f}',
verbose=True,
save_last=True,
monitor='train_loss',
mode='min',
prefix='model_'
)
model = KoGPT2Chat(args)
model.train()
trainer = Trainer.from_argparse_args(
args,
checkpoint_callback=checkpoint_callback, gradient_clip_val=1.0)
trainer.fit(model)
logging.info('best model path {}'.format(checkpoint_callback.best_model_path))
# python trainer_s.py --chat --gpus 1
if args.chat:
model = KoGPT2Chat.load_from_checkpoint(args.model_params)
model.chat()
ํ์ต์ ์ํค๊ณ ๋์ ์๋์ ๊ฐ์ ๊ฒฝ๋ก๋ก 2๊ฐ์ ๋ชจ๋ธ์ด ์ ์ฅ์ด ๋ฉ๋๋ค.
model_chp/model_-epoch=04-train_loss=16.33.ckpt
model_chp/model_-last.ckpt
์ฒซ๋ฒ์งธ ๋ชจ๋ธ์ train_loss๊ฐ ๊ฐ์ฅ ์์ ๋ชจ๋ธ ํ์ผ์ด๊ณ ,
๋๋ฒ์งธ ํ์ผ์ ์ง์ ํ epoch ์๋งํผ ํ์ต์ํค๊ณ ๋ ๋ค ์ป์ ๋ชจ๋ธ ํ์ผ์ ๋๋ค.
python trainer_s.py --chat ๋ช ๋ น์ด๋ก ์ ๋ ฅ์ ๋ํ ์ฑ๋ด์ ์๋ต์ ํ์ธํ ์ ์์ต๋๋ค.
์ถ๋ ฅ ๊ฒฐ๊ณผ
์๋์ ๊ฒฐ๊ณผ๋ flask ์น ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ด์ ์คํํด๋ณธ ๊ฒฐ๊ณผ์ ๋๋ค.
1๋ฒ๋ถํฐ 7๋ฒ๊น์ง๋ ๋ถ์ ์ ์ธ ์ ๋ ฅ์ด๊ณ 8๋ฒ๋ถํฐ 10๋ฒ๊น์ง๋ ๊ธ์ ์ ์ธ ์ ๋ ฅ์ ๋ํด ํ ์คํธํ ๊ฒฐ๊ณผ์ ๋๋ค.
์ ๋ฐ์ ์ผ๋ก ์งง๊ฒ ์๋ตํด์ค๋๋ค. ์ฃผ์ ์ ๋ฒ์ด๋๋ ์๋ต๋ ์์ง๋ง ์ด์ ๋๋ฉด ๊ฝค ์๋ต์ ์ํด์ฃผ๊ณ ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ค์ ๋ก ์ฌ์ฉํด๋ณด๋ฉด ๋ฐ์ ๋ถ์๊ธฐ์ ๋ฌธ์ฅ์ ๋ฃ์ผ๋ฉด ์๊ธด ๋๋ต์ ๋ฐ์ ๋๋ ์์ต๋๋ค.
ํ์ต์ํจ ๋ฐ์ดํฐ์ ๊ณผ ๋ค๋ฅธ ๋ฌธ์ฅ์ผ๋ก ๋๋ตํ๋ ๊ฒฝ์ฐ๊ฐ ํจ์ฌ ๋ง์ต๋๋ค. KoGPT2๋ ๋ฌธ์ฅ์ ์์ฑํด๋ด๋ ๋ชจ๋ธ์ด๋ผ ๋น์ฐํ ์ด์ผ๊ธฐ์ ๋๋ค.
[2] kobert ๊ธฐ๋ฐ ์ฌ๋ฆฌ ์ผ์ด ์ฑ๋ด
ํ์ค ์ค๋ช
kobert๋ pretrain ๋์ด ์๋ ๊ธฐ๊ณ๋ฒ์ญ ๋ชจ๋ธ์ ๋๋ค.
kobert๋ ๋ค์ค ๋ถ๋ฅ์ ๋ง์ด ํ์ฉ๋๊ณ ์์ต๋๋ค.
kobert ๊ธฐ๋ฐ ์ฌ๋ฆฌ ์ผ์ด ์ฑ๋ด์ ์ ๋ ฅ์ 359๊ฐ์ง์ ํน์ ์ํฉ์ผ๋ก ๋ถ๋ฅํ ๋ค์, ํด๋น ํด๋์ค์์ ์ ํด์ง ๋ต๋ณ ์ค ํ๋๋ฅผ ๋๋ค์ผ๋ก ์๋ตํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ์์ต๋๋ค.
ํ์ต ์ฝ๋ ๋งํฌ์ ๋๋ค.
์ฌ์ฉํ ๋ฐ์ดํฐ์
ai hub ์ ๊ณต, ์ฐ๋์ค ๋ํ ์คํฌ๋ฆฝํธ ๋ฐ์ดํฐ์
๊ฐ๋จ ์ธ๋ธ๋์ค์์ ์ ๋ฌ๋ฐ์ ์๋ด๋ฐ์ดํฐ๊ฐ ๋ํ ์๋์ ๋ฐ๋ผ 359๊ฐ ์ํฉ์ผ๋ก ๋ถ๋ฅ๋์ด ์์ต๋๋ค.
AI hub > ์ธ๋ถ๋ฐ์ดํฐ > KETI R&D๋ฐ์ดํฐ >์ธ์๊ธฐ์ (์ธ์ด์ง๋ฅ) > ์ฐ๋์ค ๋ํ ์คํฌ๋ฆฝํธ ๋ฐ์ดํฐ์
ํ์ต ํ๊ฒฝ
colab pro ํ๊ฒฝ์ด๊ฑฐ๋ gpu ์๋ฒ์์ ๊ฐ๋ฅํฉ๋๋ค.
ํด๋์ค๊ฐ 359๊ฐ์ง ๋๊ธฐ ๋๋ฌธ์ epoch๋ฅผ 50๋ฒ ์ด์์ผ๋ก ํ์ต์์ผ์ค์ผ ํฉ๋๋ค. ์ฐธ๊ณ ๋ก ํด๋น hyper parameter๋ ๊ฒฝํ์ ์ผ๋ก ์ป์ ์์น์ ๋๋ค.
colab ๊ธฐ๋ณธ ํ๊ฒฝ์์๋
batch size๋ฅผ 1๋ก ํ์ ๋ 1 epoch ๋น 16๋ถ ์ ๋ ์์๋ฉ๋๋ค. batch size๋ฅผ 2 ์ด์์ผ๋ก ๋์ด๋ฉด colab์์ RuntimeError: CUDA out of memory ๋ผ๊ณ ์๋ฌ๋ฅผ ๋ผ ๊ฒ์ ๋๋ค. gpu ram์ด ๋ถ์กฑํ ๊ฒ์ด์ฃ . ์ฌ์ค์ ์ด๋ ต๋ค๊ณ ๋ณผ ์ ์์ต๋๋ค.
colab pro ํ๊ฒฝ์์๋
batch size๋ฅผ 16 → 8 → 4 ๊น์ง ์ค์์ ๋ ๊ฐ๋ฅํ๊ณ 1 epoch ๋น 3๋ถ 40~50์ด ์ ๋ ๊ฑธ๋ ธ์ต๋๋ค.
RTX 3080 GPU ์๋ฒ์์๋
batch size๋ฅผ 16์ผ๋ก ์ค์ ํด๋ ๊ฐ๋ฅํ๊ณ , 1 epoch ๋น 1๋ถ 50์ด ์ ๋ ๊ฑธ๋ ธ์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
colab์์๋
!pip install kobert-transformers==0.4.1
!pip install transformers==3.0.2
!pip install torch
!pip install tokenizers==0.8.1rc1
linux์์๋
pip install kobert-transformers==0.4.1
pip install transformers==3.0.2
pip install tokenizers==0.8.1rc1
pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
์ฝ๋ ์ค๋ช
๋ ํผ๋ฐ์ค ๋งํฌ์
๋๋ค.
์ง์ ํ์ต์์ผ๋ณด๋ฉด์ ์ค๋ฅ๋ฅผ ํด๊ฒฐํ์ฌ ์์ ํ ๋ถ๋ถ์ด ์๊ฒจ github repo๋ก ์์ฑํด ์ ์ฅํด๋์์ต๋๋ค.
์ ๋ ํฌ์ ์ฝ๋๋ฅผ ๊ธฐ์ค์ผ๋ก ์ค๋ช
ํด๋๋ฆฌ๊ฒ ์ต๋๋ค.
ํ ํ์ผ ๋ด์์ ์์ฑ๋์ง ์์ ๋๋ ํฐ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋จผ์ ๋ณด์ฌ๋๋ฆฌ๊ณ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
data ํด๋ ์์ ์๋ ํ ์คํธ ํ์ผ์ ๊ฐ๋จํ ์ ์ฒ๋ฆฌ ์ฝ๋๋ฅผ ๊ฑฐ์ณ ๋ง๋ค์ด์ง ํ์ผ์ ๋๋ค.
์์๋๋ก category, answer, for_text_classification_all ํ์ผ์ ๋ด์ฉ์ ๋๋ค.
๋ด์ฉ์ ๋ณด๋ฉด ๊ฐ์ด ์ค์คํ ๋ฐ์, ๊ฐ์ฅ ์ค๋ฅธ์ชฝ์ ๋ฐ์ดํฐ๋ฅผ ํ์ต์์ผ ๋ช ๋ฒ class์ ์ํ๋์ง ๋ชจ๋ธ์ ํตํด ๋ถ๋ฅํ๊ณ
์ดํ์๋ ๋ถ๋ฅ๋ class ๋ฒํธ๋ก category ์ ๋ณด์ answer๋ก ์ฑ๋ด์ด ์๋ตํฉ๋๋ค.
์ ์ฒ๋ฆฌ ์ฝ๋๋ ๋งค์ฐ ๊ฐ๋จํ๊ธฐ ๋๋ฌธ์ ์ฝ๋ ๋งํฌ๋ก ์ฐ๊ฒฐํด๋๊ฒ ์ต๋๋ค.
import torch
import torch.nn as nn
from kobert_transformers import get_kobert_model
from torch.nn import CrossEntropyLoss, MSELoss
from transformers import BertPreTrainedModel
from model.configuration import get_kobert_config
class KoBERTforSequenceClassfication(BertPreTrainedModel):
def __init__(self,
num_labels=359,
hidden_size=768,
hidden_dropout_prob=0.1,
):
super().__init__(get_kobert_config())
self.num_labels = num_labels
self.kobert = get_kobert_model()
self.dropout = nn.Dropout(hidden_dropout_prob)
self.classifier = nn.Linear(hidden_size, num_labels)
self.init_weights()
def forward(
self,
input_ids=None,
attention_mask=None,
token_type_ids=None,
position_ids=None,
head_mask=None,
inputs_embeds=None,
labels=None,
):
outputs = self.kobert(
input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids,
position_ids=position_ids,
head_mask=head_mask,
inputs_embeds=inputs_embeds,
)
pooled_output = outputs[1]
pooled_output = self.dropout(pooled_output)
logits = self.classifier(pooled_output)
outputs = (logits,) + outputs[2:] # add hidden states and attention if they are here
if labels is not None:
if self.num_labels == 1:
# We are doing regression
loss_fct = MSELoss()
loss = loss_fct(logits.view(-1), labels.view(-1))
else:
loss_fct = CrossEntropyLoss()
loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
outputs = (loss,) + outputs
return outputs # (loss), logits, (hidden_states), (attentions)
๋ค์์ model ํด๋ ์์ ํ์ผ์ ๋ณด๊ฒ ์ต๋๋ค.
์ ์ฝ๋๋ classifier.py ํ์ผ์ ์ฝ๋์ ๋๋ค.
์ด๋ฒ ์ฑ๋ด ๋ชจ๋ธ์ kobert ๋ชจ๋ธ์ ์ฌ์ฉํด ๋จผ์ ์ด๋ค ์ํฉ์ธ์ง ๋ถ๋ฅํด์ผํ๋ ๊ฒ์ด ๋ชฉ์ ์ ๋๋ค.
๊ทธ๋์ classifier (๋ถ๋ฅ๊ธฐ)๊ฐ ํ์ํฉ๋๋ค. ์ด ๋ถ๋ถ์ train ๋ฟ๋ง ์๋๋ผ testํ ๋์๋ ์ฌ์ฉํฉ๋๋ค.
import logging
from transformers import BertConfig
logger = logging.getLogger(__name__)
#KoBERT
kobert_config = {
'attention_probs_dropout_prob': 0.1,
'hidden_act': 'gelu',
'hidden_dropout_prob': 0.1,
'hidden_size': 768,
'initializer_range': 0.02,
'intermediate_size': 3072,
'max_position_embeddings': 512,
'num_attention_heads': 12,
'num_hidden_layers': 12,
'type_vocab_size': 2,
'vocab_size': 8002
}
def get_kobert_config():
return BertConfig.from_dict(kobert_config)
configuration.py ์ ์ฝ๋์ ๋๋ค.
configuration.py ๋ชจ๋ธ ๊ตฌ์กฐ ๋ฑ์ ์ค์ ๊ฐ์ ์ง์ ํด๋ ํ์ผ์ด๊ณ , classifier.py ์ฝ๋์์ ๋ถ๋ฌ์ ์ฌ์ฉํฉ๋๋ค.
import torch
from kobert_transformers import get_tokenizer
from torch.utils.data import Dataset
class WellnessTextClassificationDataset(Dataset):
def __init__(self,
file_path="./data/wellness_dialog_for_text_classification_all.txt",
num_label=359,
device='cpu',
max_seq_len=512, # KoBERT max_length
tokenizer=None
):
self.file_path = file_path
self.device = device
self.data = []
self.tokenizer = tokenizer if tokenizer is not None else get_tokenizer()
file = open(self.file_path, 'r', encoding='utf-8')
while True:
line = file.readline()
if not line:
break
datas = line.split(" ")
index_of_words = self.tokenizer.encode(datas[0])
token_type_ids = [0] * len(index_of_words)
attention_mask = [1] * len(index_of_words)
# Padding Length
padding_length = max_seq_len - len(index_of_words)
# Zero Padding
index_of_words += [0] * padding_length
token_type_ids += [0] * padding_length
attention_mask += [0] * padding_length
# Label
label = int(datas[1][:-1])
data = {
'input_ids': torch.tensor(index_of_words).to(self.device),
'token_type_ids': torch.tensor(token_type_ids).to(self.device),
'attention_mask': torch.tensor(attention_mask).to(self.device),
'labels': torch.tensor(label).to(self.device)
}
self.data.append(data)
file.close()
def __len__(self):
return len(self.data)
def __getitem__(self, index):
item = self.data[index]
return item
if __name__ == "__main__":
dataset = WellnessTextClassificationDataset()
print(dataset)
์๋ dataloader.py ์ ์ฝ๋์ ๋๋ค.
dataloader.py๋ csv ์ ๊ฐ์ ํํ์ ๋ฐ์ดํฐ๋ฅผ pytorch ๋ชจ๋ธ๋ก ํ์ต์ํค๊ธฐ ์ํด์ Dataset ํด๋์ค๋ฅผ ์์๋ฐ์ ๊ตฌํํด์ผ ํฉ๋๋ค.
์์ KoGPT2 ๊ธฐ๋ฐ Dataset๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, WellnessTextClassificationDataset์ Dataset์ ์์๋ฐ์์ผ๋ฏ๋ก init, len, getitem ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ผํฉ๋๋ค.
len์ ๋ฐ์ดํฐ์ ์ ํฌ๊ธฐ๋ฅผ ๋ฆฌํดํ๊ณ getitem์ i๋ฒ์งธ ์ํ์ ์ฐพ๋๋ฐ ์ฌ์ฉํฉ๋๋ค.
import gc
import os
import numpy as np
import torch
from torch.utils.data import dataloader
from tqdm import tqdm
from transformers import AdamW
from model.classifier import KoBERTforSequenceClassfication
from model.dataloader import WellnessTextClassificationDataset
def train(device, epoch, model, optimizer, train_loader, save_step, save_ckpt_pa th, train_step=0):
losses = []
train_start_index = train_step + 1 if train_step != 0 else 0
total_train_step = len(train_loader)
model.train()
with tqdm(total=total_train_step, desc=f"Train({epoch})") as pbar:
pbar.update(train_step)
for i, data in enumerate(train_loader, train_start_index):
optimizer.zero_grad()
outputs = model(**data)
loss = outputs[0]
losses.append(loss.item())
loss.backward()
optimizer.step()
pbar.update(1)
pbar.set_postfix_str(f"Loss: {loss.item():.3f} ({np.mean(losses):.3f })")
if i >= total_train_step or i % save_step == 0:
torch.save({
'epoch': epoch, # ํ์ฌ ํ์ต epoch
'model_state_dict': model.state_dict(), # ๋ชจ๋ธ ์ ์ฅ
'optimizer_state_dict': optimizer.state_dict(), # ์ตํฐ๋ง์ด ์ ์ ์ฅ
'loss': loss.item(), # Loss ์ ์ฅ
'train_step': i, # ํ์ฌ ์งํํ ํ์ต
'total_train_step': len(train_loader) # ํ์ฌ epoch์ ํ์ต ํ ์ด train step
}, save_ckpt_path)
return np.mean(losses)
if __name__ == '__main__':
gc.collect()
torch.cuda.empty_cache()
root_path = "."
data_path = f"{root_path}/data/wellness_dialog_for_text_classification_all.t xt"
checkpoint_path = f"{root_path}/checkpoint"
save_ckpt_path = f"{checkpoint_path}/kobert-wellness-text-classification.pth "
n_epoch = 60 # Num of Epoch
batch_size = 4 # ๋ฐฐ์น ์ฌ์ด์ฆ #Colab์ด ๋์๊ฐ์ง ์์ 4๋ก ํ์ผ๋ฉฐ, ์ฆ๊ฐ์์ผ๋ ๋ฌด๋ฐฉ
ctx = "cuda" if torch.cuda.is_available() else "cpu"
device = torch.device(ctx)
save_step = 100 # ํ์ต ์ ์ฅ ์ฃผ๊ธฐ
learning_rate = 5e-6 # Learning Rate
# WellnessTextClassificationDataset Data Loader
dataset = WellnessTextClassificationDataset(file_path=data_path, device=devi ce)
train_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, s huffle=True)
model = KoBERTforSequenceClassfication()
model.to(device)
# 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 fo r 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)
pre_epoch, pre_loss, train_step = 0, 0, 0
if os.path.isfile(save_ckpt_path):
checkpoint = torch.load(save_ckpt_path, map_location=device)
pre_epoch = checkpoint['epoch']
train_step = checkpoint['train_step']
total_train_step = checkpoint['total_train_step']
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
print(f"load pretrain from: {save_ckpt_path}, epoch={pre_epoch}")
losses = []
offset = pre_epoch
for step in range(n_epoch):
epoch = step + offset
loss = train(device, epoch, model, optimizer, train_loader, save_step, s ave_ckpt_path, train_step)
losses.append(loss)
์ ์ฝ๋๋ ํ๋ก์ ํธ ํด๋ ์ต์๋จ์ ์์นํ๋ train.py ์ฝ๋์ ๋๋ค.
ํ์ต์ํค๋ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
์์ ํ๋ฅผ ๊ฑฐ์ณ (์ด ๋ KoBERTforSequenceClassfication์ forward๊ฐ ์คํ๋ฉ๋๋ค) loss๋ฅผ ๊ณ์ฐํ๊ณ backpropagation ์ํค๋ฉด์ ๋ชจ๋ธ ํ์ผ์ ์ ์ฅํฉ๋๋ค.
import torch
import torch.nn as nn
import random
from model.classifier import KoBERTforSequenceClassfication
from kobert_transformers import get_tokenizer
def load_wellness_answer():
root_path = "."
category_path = f"{root_path}/data/wellness_dialog_category.txt"
answer_path = f"{root_path}/data/wellness_dialog_answer.txt"
c_f = open(category_path, 'r')
a_f = open(answer_path, 'r')
category_lines = c_f.readlines()
answer_lines = a_f.readlines()
category = {}
answer = {}
for line_num, line_data in enumerate(category_lines):
data = line_data.split(' ')
category[data[1][:-1]] = data[0]
for line_num, line_data in enumerate(answer_lines):
data = line_data.split(' ')
keys = answer.keys()
if (data[0] in keys):
answer[data[0]] += [data[1][:-1]]
else:
answer[data[0]] = [data[1][:-1]]
return category, answer
def kobert_input(tokenizer, str, device=None, max_seq_len=512):
index_of_words = tokenizer.encode(str)
token_type_ids = [0] * len(index_of_words)
attention_mask = [1] * len(index_of_words)
# Padding Length
padding_length = max_seq_len - len(index_of_words)
# Zero Padding
index_of_words += [0] * padding_length
token_type_ids += [0] * padding_length
attention_mask += [0] * padding_length
data = {
'input_ids': torch.tensor([index_of_words]).to(device),
'token_type_ids': torch.tensor([token_type_ids]).to(device),
'attention_mask': torch.tensor([attention_mask]).to(device),
}
return data
if __name__ == "__main__":
root_path = "."
checkpoint_path = f"{root_path}/checkpoint"
save_ckpt_path = f"{checkpoint_path}/kobert-wellness-text-classification.pth"
# ๋ต๋ณ๊ณผ ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฌ์ค๊ธฐ
category, answer = load_wellness_answer()
ctx = "cuda" if torch.cuda.is_available() else "cpu"
device = torch.device(ctx)
# ์ ์ฅํ Checkpoint ๋ถ๋ฌ์ค๊ธฐ
checkpoint = torch.load(save_ckpt_path, map_location=device)
model = KoBERTforSequenceClassfication()
model.load_state_dict(checkpoint['model_state_dict'])
model.to(ctx)
model.eval()
tokenizer = get_tokenizer()
while 1:
sent = input('\nQuestion: ') # '์์ฆ ๊ธฐ๋ถ์ด ์ฐ์ธํ ๋๋์ด์์'
data = kobert_input(tokenizer, sent, device, 512)
if '์ข
๋ฃ' in sent:
break
output = model(**data)
logit = output[0]
softmax_logit = torch.softmax(logit, dim=-1)
softmax_logit = softmax_logit.squeeze()
max_index = torch.argmax(softmax_logit).item()
max_index_value = softmax_logit[torch.argmax(softmax_logit)].item()
answer_list = answer[category[str(max_index)]]
answer_len = len(answer_list) - 1
answer_index = random.randint(0, answer_len)
print(f'Answer: {answer_list[answer_index]}, index: {max_index}, softmax_value: {max_index_value}')
print('-' * 50)
๋ง๋ค์ด์ง ๋ชจ๋ธ ํ์ผ์ ์ฝ์ด ์ฑ๋ด๊ณผ ๋ํํ๊ธฐ ์ํ test.py ์ฝ๋์ ๋๋ค.
kobert_input ํจ์๋ก ์ ๋ ฅ์ ํ ํฐํํด์ฃผ์ด ๋ชจ๋ธ์ input์ผ๋ก ๋ฃ์ด์ฃผ๋ฉด ๋ชจ๋ธ์ด ์ด๋ค ์ํฉ์ธ์ง ๋ถ๋ฅํด๋ ๋๋ค.
load_wellness_answer ํจ์๋ก category.txt, answer.txt ํ์ผ์ ์ฝ์ด์ ์ด๋ค ์นดํ ์ฝ๋ฆฌ๋ก ๋ถ๋ฅ๋์๋์ง ์ค๋ช ๊ณผ ์ฑ๋ด์ ์๋ต์ ํ์ธํ ์ ์์ต๋๋ค.
์ถ๋ ฅ ๊ฒฐ๊ณผโ
์ถ๋ ฅ๋ ๊ฐ์ด ์ด๋ค ๊ฒ์ ์๋ฏธํ๋์ง ์ค๋ช ํ๊ฒ ์ต๋๋ค.
์๋ต ๋ค์ ์ถ๋ ฅ๋ ์ ์ '22'๋ ๋ถ๋ฅ๋ ํด๋์ค์ ๋ฒํธ์ด๊ณ , '๊ฐ์ /๋๋ฌผ'์ 22๋ฒ ํด๋์ค์ ๋ํ ์ค๋ช ์ด๊ณ , ๋ง์ง๋ง ์ค์๊ฐ์ softmax ๊ฐ์ ๋๋ค. 22๋ฒ ํด๋์ค์ผ ํ๋ฅ ์ด 97ํ๋ก์ด๊ณ ๊ฐ์ฅ ๋์ ํ๋ฅ ์ด๊ธฐ ๋๋ฌธ์ 22๋ฒ ํด๋์ค๋ก ๋ถ๋ฅ๋ ๊ฒ์ ๋๋ค.
์์ kogpt2 ์ฑ๋ด๊ณผ ๋์ผํ ์ ๋ ฅ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด๋ณด์์ต๋๋ค.
1๋ฒ๋ถํฐ 7๋ฒ๊น์ง๋ ๋ถ์ ์ ์ธ ์ ๋ ฅ์ด๊ณ 8๋ฒ๋ถํฐ 10๋ฒ๊น์ง๋ ๊ธ์ ์ ์ธ ์ ๋ ฅ์ ๋ํด ํ ์คํธํ ๊ฒฐ๊ณผ์ ๋๋ค.
๋ถ์ ์ ์ธ ์ํฉ์ ๋ํด์๋ softmax๊ฐ์ด ํ์ฐํ ๋ฎ์ ๊ฒฐ๊ณผ๋ ์์ง๋ง ๊ต์ฅํ ์ ๋ถ๋ฆฌํด๋ด๊ณ ์ํฉ์ ์ ์ ํ ์๋ต์ ํด์ฃผ๊ณ ์์ต๋๋ค. ํ์ง๋ง ๊ธ์ ์ ์ธ ์ ๋ ฅ์๋ ์ ํ ์ ๋ ฅ๋ ๋ฌธ์ฅ๊ณผ ํธ์์ด ๋๊ณ ์์ง ์์ต๋๋ค.
์ด๋ ๋น์ฐํ ๊ฒฐ๊ณผ ์ ๋๋ค. ํ์ต์ํจ ๋ฐ์ดํฐ์ ์ ์๋ด ๋ฐ์ดํฐ์ด๊ธฐ ๋๋ฌธ์ ๊ธ์ ์ ์ธ ์ํฉ์ ๋ํ ๋ฐ์ดํฐ๋ ๊ฑฐ์ ์๋ ์์ค์ ๋๋ค. ๊ทธ๋์ KoBERT ๊ธฐ๋ฐ ์ฑ๋ด์ ์๋กํ ์ฑ๋ด์ผ๋ก ์ด๋ฆ์ ์ ํ๊ณ , ์์ KoGPT2 ๊ธฐ๋ฐ ์ฑ๋ด๊ณผ ํจ๊ป ์๋น์คํ๊ณ ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ ์ดํ์์ ์ํ๋ ์ฑ๋ด์ ์ค์ ํ ์ ์๊ฒ ๊ตฌํํ์ต๋๋ค. ์ด์ ๋ํด์๋ ์ด ๊ธ์ ํ๋ฐ๋ถ์ธ [4] ํ์ฉ ํํฉ์์ ๋ ์์ธํ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค.
[3] api ์๋ฒ
์ฝ๋ ์ ๋ฌธ์ ๋ํ ๋งํฌ์ ๋๋ค.
flask ์น ํ๋ ์์ํฌ
IDE ์ค์น, ๊ฐ์ ํ๊ฒฝ ์์ฑ
์ ๋ IDE๋ก PyCharm ์ ์ ํํ์ต๋๋ค. JetBrains ์ฌ์ ์ ํ์ด ์ต์ํด์ ์ ํํ์ต๋๋ค.
ํด๋น IDE์์ new project๋ฅผ ๋ง๋ค ๋ ์ต์ ์ ํด๋ฆญํด ์ฝ๊ฒ ๊ฐ์ ํ๊ฒฝ์ ๋ง๋ค ์ ์์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด์ฃผ์ด์ผ ํฉ๋๋ค.
๊ฐ์ ๋ถ๋ฅ ๋ชจ๋ธ, kobert ๊ธฐ๋ฐ ์ฑ๋ด, kogpt2 ๊ธฐ๋ฐ ์ฑ๋ด ๋ชจ๋ธ์ด ๋ชจ๋ ๊ฐ์ ํ๊ฒฝ์์ ์คํ๋์ด์ผํ๊ธฐ ๋๋ฌธ์ ์ถฉ๋์ด ์ผ์ด๋ ํ๋ฅ ์ด ํจ์ฌ ๋์ต๋๋ค.
๋จผ์ ํ์ตํ ๋ ์ฌ์ฉํ๋ requirements.txt ํ์ผ์ ์ด์ฉํด ์ค์น๋ฅผ ํ๋๋ฐ, ์ถฉ๋์ด ์ผ์ด๋๋ค๋ฉด ์๋ฌ๋ฅผ ์ ์ฝ๊ณ ์ต๋ํ ๋ฒ์ ์ค๋ฅ๊ฐ ์๋๊ฒ ํ๋ฉด ์ข์ต๋๋ค.
pip install --no-deps numpy==์ํ๋๋ฒ์
ํ์ง๋ง ์ ๋ ๊ทธ๋ ๊ฒ ํด๊ฒฐํ ์๊ฐ ์์ด์, ๊ฒฐ๊ตญ ์์กด์ฑ์ ๋ฌด์ํ๊ณ ๊ฐ์ ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด๋ณด๋ฉด์ ๊ฒฝํ์ ์ผ๋ก ๊ฐ๋ฅํ ํ๊ฒฝ์ ์ฐพ์์ต๋๋ค. --no-deps ๊ฐ ์์กด์ฑ์ ๋ฌด์ํ๊ณ ๊ฐ์ ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๋ผ๋ ์ต์ ์ ๋๋ค.
pip freeze > requirements.txt
ํ๊ฒฝ์ ๋ชจ๋ ๊ตฌ์ถํ ๋ค์์๋ ํ์ฌ ์ค์น๋ ์ํฉ์ ๋ช ๋ น์ด ๊ทธ๋๋ก requirements.txt ํ์ผ์ ์ ์ฅํด ์ผ๋ ค๋ก๋๋ค. ํ์ผ ์ด๋ฆ์ ์์ ๋กญ๊ฒ ์ง์ ํ ์ ์์ต๋๋ค.
pip install --no-deps -r requirements.txt
์ดํ์ ์๋ก์ด ํ๊ฒฝ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ค์นํด์ผํ ๋์๋ ์์ ๋ช ๋ น์ด๋ก ์ค์นํ ์ ์์ต๋๋ค.
์ฝ๋ ์์ฑ
ํน์ ๋ชจ๋ธ์ ํ์ํ ํ์ผ์ directory ๋ณ๋ก ๊ตฌ๋ถํด๋์์ต๋๋ค.
kobert ๊ธฐ๋ฐ ์ฑ๋ด์ ๊ตฌํํ๋๋ฐ ํ์ํ ํ์ผ์ model.chatbot.kobert ํด๋์,
kogpt2 ๊ธฐ๋ฐ ์ฑ๋ด์ ๊ตฌํํ๋๋ฐ ํ์ํ ํ์ผ์ model.chatbot.kogpt2 ํด๋์,
์ด ๊ธ์์๋ ๋ค๋ฃจ์ง ์์์ง๋ง ๊ฐ์ ๋ถ๋ฅํ๋๋ฐ ํ์ํ ํ์ผ์ model.emotion ํด๋์ ๋ฃ์์ต๋๋ค.
ํ์ต์ ์ํค๋ ๊ฒ์ด ์๋๋ผ์
ํผ์งํ๊ฒ ์๊ฐํ๋ฉด ๋ถ๋ฅ ๋ชจ๋ธ์ ๋ชจ๋ธ์ ๋ถ๋ฌ์จ ๋ค์ ๋ถ๋ฅ๊ธฐ๋ง ์์ผ๋ฉด ๋๊ณ ,
kogpt2๋ ๋ชจ๋ธ์ ๋ถ๋ฌ์ค๋ ๋ถ๋ถ๋ง ์์ผ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ๊ฐ๋จํ ์ ๋ฆฌํด ๊ตฌ์ฑํ์ต๋๋ค.
์ด ์ธ์ checkpoint์๋ ์์ trainํ ๋ ์ ์ฅํ๋ ๋ชจ๋ธ ํ์ผ์ ์ ์ฅํด๋์์ต๋๋ค.
data์๋ .txt, .csv ์ ๊ฐ์ ๋ฌธ์ ํ์ผ์ ์ ์ฅํด๋์์ต๋๋ค.
preprocess ์๋ ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌํ ๋ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ์ ์ฅํด๋์์ต๋๋ค.
util ์๋ ๊ฐ์ ๊ด๋ จ class๋ฅผ ๋ง๋ค์ด ๋ก์ง์์ ์ค๋ณต๋๋ ๋ถ๋ถ์ ์ง์ด class์ method๋ก ์์ฑํด๋์์ต๋๋ค.
์๋์ ์ฝ๋ ๋ด์ฉ์ ํ๋ก์ ํธ root์ ์๋ app.py์ ๋ด์ฉ์ ๋๋ค.
import os
from model.chatbot.kogpt2 import chatbot as ch_kogpt2
from model.chatbot.kobert import chatbot as ch_kobert
from model.emotion import service as emotion
from util.emotion import Emotion
from util.depression import Depression
from flask import Flask, request, jsonify
from kss import split_sentences
์์ directory ๊ตฌ์กฐ๋ฅผ ๋์ถฉ ์ดํด๋ณด์๊ธฐ ๋๋ฌธ์ ์ด๋ค ๊ฒ์ ์๋ฏธํ๋ ๊ฒ์ธ์ง ๋์ ๋๋ ๊ฒ์ด ๋ง์ ๊ฒ์ ๋๋ค.
๋งจ ๋ง์ง๋ง ์ค์ NLP์์ ๋๋ฆฌ ์ฌ์ฉ๋๊ณ ์๋ kss๋ผ๋ ํ๊ตญ์ด ๋ฌธ์ฅ ๋ถ๋ฅ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ๋ถ๋ฌ์์ต๋๋ค.
app = Flask(__name__)
Emotion = Emotion()
Depression = Depression()
@app.route('/')
def hello():
return "deep learning server is running ๐"
ํต์ ์ด ๋ ์ ์๋ ์ํฉ์ธ์ง ์๊ธฐ ์ํด root ๊ฒฝ๋ก ํธ์ถํ์ ๋ ๊ฐ๋จํ string์ ๋ฐํํ๋๋ก api๋ฅผ ์์ฑํ์ต๋๋ค.
@app.route('/emotion')
def classifyEmotion():
sentence = request.args.get("s")
if sentence is None or len(sentence) == 0:
return jsonify({
"emotion_no": 2,
"emotion": "์ค๋ฆฝ"
})
result = emotion.predict(sentence)
print("[*] ๊ฐ์ ๋ถ์ ๊ฒฐ๊ณผ: " + Emotion.to_string(result))
return jsonify({
"emotion_no": int(result),
"emotion": Emotion.to_string(result)
})
ํธ์ถ ๋ฐฉ๋ฒ: /emotion?s=๋ถ์ํด์ฃผ๊ธฐ๋ฅผ ์ํ๋ ๋ฌธ์ฅ
ํ ๋ ๋ฌธ์ฅ์ ์ ๋ ฅ์ผ๋ก ๋ฐ๊ณ ์ด๋ค ๊ฐ์ ์ ํด๋นํ๋์ง ๋ถ๋ฅํด ์๋ตํฉ๋๋ค.
request query์ ๊ฐ์ด ๋น์ด์์ผ๋ฉด BadRequest 400์ผ๋ก ์๋ตํ๋๋ฐ, ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค๋ฆฝ์ ๋๋ ๊ฒ์ด ๋ ๋์ ๊ฒ ๊ฐ์ ๊ธฐ๋ณธ๊ฐ์ ์ง์ ํ์ต๋๋ค.
@app.route('/diary')
def classifyEmotionDiary():
sentence = request.args.get("s")
if sentence is None or len(sentence) == 0:
return jsonify({
"joy": 0,
"hope": 0,
"neutrality": 0,
"anger": 0,
"sadness": 0,
"anxiety": 0,
"tiredness": 0,
"regret": 0,
"depression": 0
})
predict, dep_predict = predictDiary(sentence)
return jsonify({
"joy": predict[Emotion.JOY],
"hope": predict[Emotion.HOPE],
"neutrality": predict[Emotion.NEUTRALITY],
"anger": predict[Emotion.ANGER],
"sadness": predict[Emotion.SADNESS],
"anxiety": predict[Emotion.ANXIETY],
"tiredness": predict[Emotion.TIREDNESS],
"regret": predict[Emotion.REGRET],
"depression": dep_predict
})
def predictDiary(s):
total_cnt = 0.0
dep_cnt = 0
predict = [0.0 for _ in range(8)]
for sent in split_sentences(s):
total_cnt += 1
predict[emotion.predict(sent)] += 1
if emotion.predict_depression(sent) == Depression.DEPRESS:
dep_cnt += 1
for i in range(8):
predict[i] = float("{:.2f}".format(predict[i] / total_cnt))
dep_cnt = float("{:.2f}".format(dep_cnt/total_cnt))
return predict, dep_cnt
ํธ์ถ ๋ฐฉ๋ฒ: /diary?s=๋ถ์ํด์ฃผ๊ธฐ๋ฅผ ์ํ๋ ์ผ๊ธฐ
์ผ๊ธฐ ํ ๊ฐ์ ๋ชจ๋ ๋ด์ฉ์ ์ ๋ ฅ์ผ๋ก ๋ฐ๊ณ ๊ฐ ๊ฐ์ ์ ๋น์จ์ ๊ณ์ฐํ๊ณ ์ฐ์ธํ ๋ฌธ์ฅ์ ๋น์จ๋ ๊ณ์ฐํ์ฌ ํจ๊ป ์๋ตํฉ๋๋ค.
@app.route('/chatbot/g')
def reactChatbotV1():
sentence = request.args.get("s")
if sentence is None or len(sentence) == 0:
return jsonify({
"answer": "๋ฃ๊ณ ์์ด์. ๋ ๋ง์ํด์ฃผ์ธ์~ (๋๋๋๋)"
})
answer = ch_kogpt2.predict(sentence)
return jsonify({
"answer": answer
})
@app.route('/chatbot/b')
def reactChatbotV2():
sentence = request.args.get("s")
if sentence is None or len(sentence) == 0:
return jsonify({
"answer": "๋ฃ๊ณ ์์ด์. ๋ ๋ง์ํด์ฃผ์ธ์~ (๋๋๋๋)"
})
answer, category, desc, softmax = ch_kobert.chat(sentence)
return jsonify({
"answer": answer
})
ํธ์ถ ๋ฐฉ๋ฒ: /chatbot/g?s=์ ๋ ฅ, /chatbot/b?s=์ ๋ ฅ
๋ค๋ฅธ ๋ฒ์ ์ chatbot์ ๋ค๋ฅธ url์ ์ฌ์ฉํด ํธ์ถํด์ผ ํ๋๋ก ๊ตฌํํ์์ต๋๋ค.
์ ๋ ฅ์ด ๋น์๋๋ผ๋ ๊ธฐ๋ณธ์ ์ผ๋ก "๋ฃ๊ณ ์์ด์. ๋ ๋ง์ํด์ฃผ์ธ์~ (๋๋๋๋)"์ผ๋ก ์๋ตํฉ๋๋ค.
if __name__ == '__main__':
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
ip์ port๋ฅผ ์ง์ ํ์ฌ flask ์ดํ์ ์คํ์ํค๋ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
docker image ๋น๋, ์ ๋ก๋
Dockerfile
ํ๋ก์ ํธ ์ต์๋จ ๊ฒฝ๋ก์ Dockerfile์ ์์ฑํฉ๋๋ค.
FROM python:3.8.5
WORKDIR /app
COPY . .
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
EXPOSE 5000
CMD python ./app.py
python์ ์ค์นํ๊ณ /app ๋ฐ์ working directory๋ก ์ง์ ํฉ๋๋ค. .dockerignore์ ์์ฑ๋ ํ์ผ์ด๋ ํด๋๋ฅผ ๋ฌด์ํ๊ณ ๋๋จธ์ง์ ํ์ผ๋ค์ ๋ณต์ฌํฉ๋๋ค. pip์ ์ต์ ๋ฒ์ ์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ ๋ค requirements.txt์ ์์ฑ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํฉ๋๋ค. 5000๋ฒ ํฌํธ๋ฅผ ์ธ๋ถ๋ก ๊ฐ๋ฐฉํ ๊ฒ์ด๋ผ๊ณ ์ค์ ํ ๋ค ์คํ ๋ช ๋ น์ด๋ฅผ ์์ฑํด์ค๋๋ค.
.dockerignore
๊ผญ ํ์ํ ํ์ผ๋ก๋ง image๋ฅผ buildํ๋๋ก ํ์ฌ, docker image๋ฅผ ์ต์ ํํ๋ ๋ฐฉ๋ฒ ์ค ํ๋์ ๋๋ค.
.git
.gitignore
.idea
.cache
.DS_Store
__pycache__
Scripts
Lib
*.md
*.cfg
preprocess
kss_example.py
*/__pycache__/*
์ํ๋ ํ์ผ, ํด๋๋ช , ํน์ ํ์ฅ์๋ก ๋๋๋ ํ์ผ ๋ฑ๋ฑ ์์ ๋กญ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
ํ๋ก์ ํธ ์ต์๋จ ๊ฒฝ๋ก์ ์์นํด์ผํ๋ฉฐ, Dockerfile์ด ์คํ๋ ๋ ์๋์ผ๋ก ํด๋น ํ์ผ์ ์ธ์ํ์ฌ ์ ํ ํ์ผ์ด๋ ํด๋๋ ๋ฌด์ํฉ๋๋ค.
image build
docker build --tag attiary_model:2.0 .
--tag ์ต์ ์ ์ด๋ฏธ์ง ์ด๋ฆ๊ณผ ํ๊ทธ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
์ฃผ์ํ ๋ถ๋ถ์ ๋งจ ๋ง์ง๋ง์ . ์ ๋นผ๋จน์ง ์๊ณ ์์ฑํด์ผ ํฉ๋๋ค.
์ ๋ ์ด ๊ณผ์ ์์ 7~8๋ถ ์ ๋ ๊ฑธ๋ฆฝ๋๋ค.
image push
docker image tag attiary_model:2.0 hoit1302/attiary_model:latest
docker push hoit1302/attiary_model:latest
์ด๋ฏธ์ง ํ๊ทธ์ ์ด๋ฆ์ dockerhub์ ์๋ username/reponame:์ํ๋ํ๊ทธ ๋ก ๋ณ๊ฒฝํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ pushํ๋ฉด ๋ฉ๋๋ค.์ด ๊ณผ์ ์์ ๋ก๊ทธ์ธ์ ์๊ตฌํ ์๋ ์์ต๋๋ค.
๋คํธ์ํฌ ์ํฉ์ ๋ฐ๋ผ ์ ๋ก๋์ ์์๋๋ ์๊ฐ์ ๋ค์ํฉ๋๋ค. ์ข์ ๋๋ 20๋ถ ์ ๋์์ ์ ๋ก๋๊ฐ ์๋ฃ๋ ๋๋ ์๊ณ ์์ข์ ๋๋ 4์๊ฐ์ด ์์๋ ์ ๋ ์์ต๋๋ค.
pushํ repository์์ ์ push ๋์๋์ง ํ์ธํ ์ ์์ต๋๋ค.
ํด๋ผ์ฐ๋ ์๋ฒ๋ก ๋ฐฐํฌ
๋ฌธ์ ์
๋ฐฑ์๋ ๊ฐ๋ฐ ํ์ ์๋ฒ AWS๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ๋ฅผ ๋ช ๋ฒ ์ด์ํด๋ณด์๋ ๊ฒฝํ์ ๋น์ถ์ด ์์ฝ๊ฒ ํ๋ฆฌ ํฐ์ด์ ec2 ์๋ฒ์ docker container๋ฅผ ์ฌ๋ ธ๋๋ ์ดํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ๋์ํค๋ค๊ฐ Killed๋ผ๋ ๊ฐ๋ ฌํ ๋ฌธ๊ตฌ๋ฅผ ๋จ๊ธฐ๊ณ ์ฃฝ์ด๋ฒ๋ ธ์ต๋๋ค. ์ฌ์ง์ด ์๋ฒ ์ปดํจํฐ์์ ์ ํ ์๋ต์ ๋ฐ์ ์ ์์ด์ ์ฌ๋ถํ ์์ผ์ผ ํ์ต๋๋ค.
memory๊ฐ ์ ๋์ ์ผ๋ก ๋ถ์กฑํ๋ ๊ฒ์ ๋๋ค.
ํ๋ฆฌํฐ์ด์์๋ ๋์คํฌ ๊ณต๊ฐ์ memory๋ก ์ธ ์ ์๋๋ก swap ์์ผ๋ดค์ ์ต๋ 2GB์๊ธฐ ๋๋ฌธ์ ๋ฅ๋ฌ๋ ์๋ฒ๋ฅผ ๊ตฌ๋์ํค๊ธฐ์๋ ์ญ๋ถ์กฑ์ด์์ต๋๋ค.
์ด๋ ๊ฒ ํ์ค์ ์ธ ๋ฒฝ์ ๋ถ๋ชํ์ ๋, ํ๊ต ์ธก์์ 4์ ๋ง์ tencent cloud๋ฅผ ์ง์ํด์ฃผ์์ต๋๋ค.
ํ ์ผํธ ํด๋ผ์ฐ๋
GPU based์ instance๋ฅผ ๋์ฐ๊ณ ์์ ํ์ต๋๋ค.
deploy.sh
๊ฐ๋จํ docker ๋ช ๋ น์ด๋ฅผ ๋ชจ์๋ deploy shell script๋ฅผ ์์ฑํ์์ต๋๋ค.
echo "[*] ์คํ๋๊ณ ์๋ ์ปจํ
์ด๋ ์ค์ง"
sudo docker stop atti_model
echo "[*] ์ค์ง๋ ์ปจํ
์ด๋ ์ญ์ "
sudo docker rm atti_model
echo "[*] ์๋ก์ด ์ด๋ฏธ์ง pull ๋ฐ๊ธฐ"
sudo docker pull hoit1302/attiary_model:latest
echo "[*] ์๋ก์ด ์ด๋ฏธ์ง ํ์ธํ๊ธฐ"
sudo docker images
echo "[*] ์๋ก์ด ์ด๋ฏธ์ง ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์คํํ๊ธฐ"
sudo docker run --name atti_model -d -p 5000:5000 hoit1302/attiary_model:latest
echo "[*] ์คํ๋๊ณ ์์ง ์๋ ์ด๋ฏธ์ง ์ญ์ ํ๊ธฐ"
sudo docker image prune -a -f
echo "[*] ์ญ์ ๋ ์ด๋ฏธ์ง ํ์ธํ๊ธฐ"
sudo docker images
atti_model ๋ถ๋ถ์๋ ์ํ๋ container ์ด๋ฆ์ ์ ๊ณ
hoit1302/attiary_model์ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฆฐ docker์ ๊ณ์ ๊ณผ repo๋ฅผ ์ง์ ํ๋ฉด ๋ฉ๋๋ค.
run ๋ช ๋ นํ ๋ -d ์ต์ ์ด background๋ก ์คํํ ์ ์๋ ์ต์ ์ ๋๋ค.
ํด๋น ์ต์ ์ ๋นผ๊ณ ์คํ์ํค๋ฉด ์ ์ปดํจํฐ์ ์๋ฒ ์ปดํจํฐ์ ์ฐ๊ฒฐ ์ธ์ ์ด ๋๊ฒผ์ ๋ ์ปจํ ์ด๋๊ฐ ์ค๋จ๋ฉ๋๋ค.
๋ก๊ทธ ๋ณด๊ธฐ
๋์ปค ์ปจํ ์ด๋์ ๋ก๊ทธ๋ฅผ ๋ณด๋ ๋ช ๋ น์ด์ ๋๋ค.
docker logs atti_model
์๋ 10์ค์ ๋ก๊ทธ ๋ณด๋ ๋ฐฉ๋ฒ์ ๋๋ค. --tail ์ต์ ์ ์ํ๋ ์ซ์๋ฅผ ๋ฃ์ ์ ์์ต๋๋ค.
docker logs --tail 10 atti_model
docker image hoit1302/attiary_model:2.1์ gpu ์๋ฒ์์ ์คํ์ํจ ๋ก๊ทธ์ ๋๋ค.
5์ผ ์ ์ ์ฌ๋ฆฐ ์ปจํ ์ด๋๊ฐ ์ ์คํ๋๊ณ ์์์ ํ์ธํ ์ ์์ต๋๋ค.
[4] ํ์ฉ ํํฉ
์ ๋ ์กธ์ ํ๋ก์ ํธ๋ก ์ฌ๋ฆฌ ์ผ์ด ๋ชฉ์ ์ ์ผ๊ธฐ ์ดํ, "์๋ ์ด๋ฆฌ"์ ๊ฐ๋ฐํ์ต๋๋ค.
1. ์ฑ๋ด
์ฑ๋ด์ ์ผ๊ธฐ๋ฅผ ์์ฑํ๊ณ ์์ ๋ ์ํฐํค๋ฅผ ๋๋ฅด๋ฉด ์ค๋ฐ๊ฟ์ด ๋๊ณ ์
๋ ฅ๋ ๋ด์ฉ์ ๋ํ ๋ฐ์์ ์๋ฒ์ ์์ฒญํ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ฉด ๋
ธ๋ ๋ณ์๋ฆฌ์ ์๋ ๋ผ๋ ์น๊ตฌ๊ฐ ์ฌ์ฉ์๊ฐ ์์ฑํ ๋ด์ฉ์ ๋ํด ๊ณต๊ฐํ๊ฑฐ๋ ์๋กํด์ฃผ๋ ๋ฐ์์ ๋ณด์
๋๋ค.
์ค๋์ ๋๋ฌด ์ฌํ ๋ ์ด์์ด.
์น๊ตฌ๊ฐ ๋น์ผ์ ์ฝ์์ ์ทจ์ํ๊ฑฐ๋ .
์ฌ์ค ์์ฆ๋ค์ด ์ฐ๋ฝ์ด ์ ์๋๋ค๋ ๋๋์ด ์์๊ฑฐ๋ .
์์ํด... ๋ด ์๋ชป๊ฐ๊ณ ...
์ค๋์ ์ผ์ฐ ์๊ฑฐ์ผ.
๊ฐ์ ์ผ๊ธฐ ๋ด์ฉ์ ๋ํ ์ฑ๋ด์ ์๋ต์ ๋น๊ตํด๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ kobert ์ฑ๋ด์ ๋๋ค.
์ ๋ง ์๋ก๋ฅผ ์ํด์ฃผ๊ณ ์๋ ๋ชจ์ต์ ๋ณผ ์ ์์ต๋๋ค. ๊ทธ๋์ kobert ์ฑ๋ด์ ์ด๋ฆ์ ์๋กํ ์๋ ๋ก ์ง์์ต๋๋ค. ๋ฐ๋ปํ ์ฑ๊ฒฉ์ ๊ฐ์ง๊ณ ์์ด ๋ถ์ ์ ์ธ ๊ฐ์ ์ด ๋๊ปด์ง ๋ ์ฌ์ฉํ๋ฉด ์ข๋ค๊ณ ์ฌ์ฉ์์๊ฒ ์ดํ ๋ด์์ ์๋ดํ๊ณ ์์ต๋๋ค.
kogpt2 ์ฑ๋ด์ ์ด๋ค ๋ฐ์์ ํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ ๋ฐ์ ์ผ๋ก ๋ต๋ณ์ด ์งง๋ค๋ ๊ฒ์ ๋๋ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์กฐ๊ธ ๋ ์ผ์์ ์ธ ๋ํ๋ฅผ ํ๊ณ ์๋ ๊ฒ์ฒ๋ผ ๋๊ปด์ง๋๋ค.
๊ทธ๋์ kogpt2 ์ฑ๋ด์ ์ด๋ฆ์ ๊ณต๊ฐํ ์๋ ๋ก ์ง์์ต๋๋ค.
๋ด์ฉ์ ๋ฐ๋ผ ๋ค์ํ ๋ฐ์์ ๋ณด์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋ํ ์ฑ๊ฒฉ์ ๊ฐ์ง๊ณ ์๋ค๊ณ ์ฌ์ฉ์์๊ฒ ์๋ดํ๊ณ ์์ต๋๋ค.
๋ ์ฑ๋ด ๋ชจ๋ ์๋ต ์๋๋ ๋น ๋ฅธ ํธ์ด๊ณ ์ฌ์ฉ์๋ ์ฌ์ฉํ๊ณ ์ถ์ ์ฑ๋ด์ ์ค์ ํ ์ ์๊ฒ ์ดํ์ ๊ตฌํํ์์ต๋๋ค.
2. ์ค๋ฆฝ/์ฌํ/๋ถ๋ ธ/๋ถ์/ํผ๊ณค/ํํ ๋ถ๋ฅ
์๋ ์ด๋ฆฌ๋ ์ผ๊ธฐ๋ฅผ ์์ฑํ๊ณ ์์ ๋ ์ํฐํค๋ฅผ ๋๋ฅด๋ฉด ์ค๋ฐ๊ฟ์ด ๋๊ณ ์ฑ๋ด์ด ๋ฐ์์ ํด์ฃผ๊ธฐ๋ ํ๋๋ฐ, ์ด ๋ ๊ฐ์ง๋ ๊ฐ์ ๊ณผ ์๋ง๋ ๋ฐฐ๊ฒฝ์์ ์ผ๋ก ๋ณ๊ฒฝํด ๋ค๋ ค์ฃผ๋ ๊ธฐ๋ฅ๋ ์์ต๋๋ค.
์ด ๊ธฐ๋ฅ (์ค์๊ฐ์ผ๋ก ๊ฐ์ ์ ๊ฐ์งํด ๋ฐฐ๊ฒฝ ์์ ์ ๋ณ๊ฒฝํด์ฃผ๋ ๊ธฐ๋ฅ)๋ ์ฌ์ฉ์๊ฐ ์ฌ์ฉ์ ์ํ์ง ์์ผ๋ฉด ๋๋๋ก ์ค์ ํ ์ ์์ต๋๋ค.
๋ง์ด ๊ฐ์ง๋ ๊ฐ์ ์์ผ๋ก ๋ณด์ฌ์ฃผ๊ณ ์ฒซ๋ฒ์งธ ๊ฐ์ ์ด ๋ํ ๊ฐ์ ์ด๋ฏธ์ง์ ์ฐ๊ฒฐ๋ฉ๋๋ค.
๊ฐ ๊ฐ์ ๋ณ๋ก 3๋จ๊ณ์ฉ ๋ค๋ฅธ ์ด๋ฏธ์ง๊ฐ ์กด์ฌํฉ๋๋ค. ์ฒซ๋ฒ์งธ ๋๋ฒ์งธ์ ๋ํ ๊ฐ์ ์ ๊ธฐ์จ์ด์ง๋ง ์ ๋์ ๋ฐ๋ผ ๋ค๋ฅธ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์์ต๋๋ค.
์๋ ๊ฐ ํ๊ณ ์ถ์ ๋ง์ด ์๋์! ๋ถ๋ถ๋ ๊ฐ์ง๋ ๋ํ ๊ฐ์ ์ ๋ฐ๋ผ ๋ด์ฉ์ด ๋ฌ๋ผ์ง๋๋ค.
์ด๋ฐ ๊ธฐ๋ฅ์ ๊ฐ์ ๋ถ๋ฅ๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์ฒ์์๋ ํ๋ฒ์ ๊ธฐ์จ/ํฌ๋ง/์ค๋ฆฝ/์ฌํ/๋ถ๋ ธ/๋ถ์/ํผ๊ณค/ํํ 8๊ฐ์ง ๊ฐ์ ์ ๋ถ๋ฅํด๋ผ ์ ์๋ ๋ชจ๋ธ์ ์ ์ํ์ต๋๋ค. ํ ๋ฌ ๋์ ๊ฑฐ์ ๋งค์ผ ๋ฐค๋ง๋ค ์จ๋ผ์ธ ํ์๋ก ๋ชจ์ฌ ์ง์ ๋ง ๊ฐ์ ๋ฌธ์ฅ์ ๊ฐ์ ์ผ๋ก ๋ถ๋ฅํ๋ ์์ ์ ํ์ต๋๋ค. ํ์ง๋ง ํด๋น ๋ชจ๋ธ์ ์ ํ๋๊ฐ 80%์ ๊ทธ์ณค์ต๋๋ค. ํ๋ฆด ํ๋ฅ ์ด 20% ์ ๋๋ ๋๋ ๋ชจ๋ธ์ ์ฌ์ฉํ ์๋ ์์์ต๋๋ค.
๊ทธ๋์ ๋ฐฉ๋ฒ์ ๋ฐ๊พธ์ด ์ด ๊ธ์์ ๊ธฐ์ ํ KoBERT ์ํฉ ํ๋จ ๋ถ๋ฅ ๋ชจ๋ธ์ ํ์ฉํ์ต๋๋ค. 359๊ฐ์ ๊ฐ ํด๋์ค๋ฅผ ํน์ ๊ฐ์ ๊ณผ 1:1๋ก ๋์์์ผฐ์ต๋๋ค.
ํ์ต์ํจ ๋ฐ์ดํฐ๋ ์๋ด ๋ฐ์ดํฐ์ด๊ธฐ ๋๋ฌธ์ ๋ถ์ ์ ์ธ ๋ฐ์ดํฐ๊ฐ ์ฃผ๋ฅผ ์ด๋ฃจ๊ณ ์์ต๋๋ค. ๊ทธ๋์ ๋จผ์ ๊ธ์ ๊ณผ ์ค๋ฆฝ ๋ถ์ ์ผ๋ก 3์ค ๋ถ๋ฅ ๋ชจ๋ธ์ ๊ฑฐ์น ํ ๋ถ์ ์ผ๋ก ๋ถ๋ฅ๋ ์ ๋ ฅ ๋ฐ์ดํฐ๋ ๋ค์ KoBERT ์ํฉ ํ๋จ ๋ถ๋ฅ ๋ชจ๋ธ์ ๊ฑฐ์ณ 6๊ฐ์ง ๊ฐ์ (์ค๋ฆฝ/์ฌํ/๋ถ๋ ธ/๋ถ์/ํผ๊ณค/ํํ) ์ค ํ ๊ฐ์ง๋ก ๋ถ๋ฅ๋ฉ๋๋ค.
3. ์ฐ์ธ ๋ถ๋ฅ
์ผ๊ธฐ๋ฅผ ๋ค ์ฐ๊ณ ๋๋ฉด ์บ๋ฆฐ๋ ํ๋ฉด์์ ๊ฐ ๋ฌ์ ์ข ํฉ ์ฐ์ธ์ง์์ ํ๋ณต์ง์๋ฅผ ๊ณ์ฐํด ๋ณด์ฌ์ค๋๋ค.
์ข ํฉ ์ฐ์ธ์ง์์ ํ๋ณต์ง์๋ฅผ ๋ํ๋ด๋ ๋ถ๋ถ์ ํด๋ฆญํ๋ฉด ํด๋นํ๋ ๋ฌ์ ์ผ๊ธฐ๋ฅผ ๋ค๋ฅธ ํ์์ผ๋ก ๋ณด์ฌ์ค๋๋ค.
๋ํ ๊ฐ์ ๋ค์ ํ์ด์ฐจํธ๋ก ๋ณด์ฌ์ฃผ๊ณ , ํ๋ณต ์ง์์ ์ฐ์ธ ์ง์์ ์ถ์ด๋ฅผ ๊ทธ๋ํ๋ก ๋ณด์ฌ์ค๋๋ค.
์ข ํฉ ํ๋ณต์ง์๋ ํ๋ณต/ํฌ๋ง์ด ๋ํ๋ ๋น์จ๋ก ๊ฐ๋จํ๊ฒ ์ฐ์ ํ๊ท ์ ๋ ๋๋ค.์ข ํฉ ์ฐ์ธ์ง์๋ ์ฌํ/๋ถ๋ ธ/๋ถ์/ํผ๊ณค/ํํ ๊ฐ์ ๊ณผ ๋๋ถ์ด ์ฐ์ธ์ ์ถ๊ฐ๋ก ๊ฐ์งํ์ฌ ๊ฐ์ค์น๋ฅผ ๋ก๋๋ค.
๋ฐ๋ผ์ ์ฐ์ธ๊ณผ ๋น์ฐ์ธ์ ๋ถ๋ฅํ๋ ๋ชจ๋ธ์ด ํ์ํ์ต๋๋ค.
์ ํฌ ํ์ ์์ ์ค๋ช ํ KoBERT ๊ธฐ๋ฐ 359๊ฐ์ง์ ๋ถ๋ฅ ๋ชจ๋ธ์ ๋ ํ์ฉํ์ฌ ์ฐ์ธ/๋น์ฐ์ธ ๋ถ๋ฅ๊ธฐ๋ฅผ ์์ฝ๊ฒ ๊ตฌํํ์ต๋๋ค.โ
ํ์๋ค๊ณผ ํ์ํ์ฌ ์์ ์ํฉ๋ค๋ก ๋ถ๋ฅ๋์์ ๋ ์ฐ์ธ๋ก ํ๋จ๋๋๋ก ๊ตฌํํ์ต๋๋ค.
์ฐ์ธ ์ง์๊ฐ ํน์ ๊ฐ ์ด์์ผ๋ก ์ง์๋๋ ๊ฒฝ์ฐ์๋ ์๋ด์ ๊ถ์ ํ๊ฑฐ๋ ์ ๋ฌธ์์ ์ฐ๊ฒฐ์ง์ด์ฃผ๋ ๋ฐฉ์์ผ๋ก ์๋น์ค๋ฅผ ์กฐ๊ธ ๋ ํ์ฅ์ํฌ ์ ์์ ๊ฒ ๊ฐ์ต๋๋ค.
[5] ๋๋ ์
๊ธฐ์ ๊ณผ๋ ๊ด๋ จ์ด ์๊ณ , ๊ฐ์ธ์ ์ผ๋ก ๋๋ ์ ์ ์์ฑํ์ต๋๋ค.
1. HW์๋ ๊ด์ฌ์ ๊ฐ์ง์
ํ์ต์ ์ํฌ ๋์๋, classifier๋ง ์๋ ์น ์๋ฒ๋ฅผ ์คํ์ํฌ ๋์๋ ํ๋์จ์ด์ ํ๊ณ์ ์ฐธ ๋ง์ด ๋ถ๋ชํ๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ฝ๋๋ฅผ ๋ณด๊ณ ๋๋ด๋ ์ด์ ๋ ์ด์ ์ค๋ฅ๊ฐ ์์ด๋ณด์ด๋๋ฐ ๋์๊ฐ์ง ์๋ ์ด์ ๋ ๊ทผ๋ณธ์ ์ผ๋ก ํ๋์จ์ด ์์์ด ๋ถ์กฑํด์ ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์ต๋๋ค. software ๊ฐ๋ฐ์์ด์ง๋ง hardware์๋ ๊ด์ฌ์ ๊ฐ์ง๊ณ ์ฃผ๋ชฉํด์ผํ๋ค๊ณ ํ๋ ๊ต์๋์ ๋ง์์ด ๋ง์ด ๋ ์ฌ๋์ต๋๋ค.
2. ํ์์๋ ๊ณต๋ถ๋ ์๊ตฌ๋
GPU๊ฐ ์ฌ๋ฌ ๋์ธ ํ๊ฒฝ์์ ๋ณ๋ ฌ์ ์ผ๋ก ์ฝ๋๋ฅผ ์ํํ๋ ๋ถ๋ถ๋ ์ ๊ธฐํ์ต๋๋ค. ํด๋ผ์ฐ๋ ์์ ์๊ฐ์ ๋ฐฐ์ ๋๋ฐ ์ด๋ ๊ฒ ๋นจ๋ฆฌ ๋ณ๋ ฌ ์ปดํจํ ์ง์์ ํ์ฉํด ์ค์ ์์ ์ค๋ฅ๋ฅผ ํ์ด๋ผ๊ฑฐ๋ผ๊ณ ๋ ์๊ฐ๋ ๋ชปํ์ต๋๋ค.
3. ์๋ก์ด ๊ธฐ์ ๋ ์คํ ๋ง์ธ๋๋ก
๋ฅ๋ฌ๋/์ธ๊ณต์ง๋ฅ์ ๋ํ ๊ธฐ๋ฐ ์ง์์ด ์ ํ ์์ ๋ ํ๋ก์ ํธ๋ฅผ ์์ํ๊ณ ์ฃผ์ธ์ด๋ python์ด ์๋๋ผ์ ์ ๋ง ๊ฐ๋จํ ์ฝ๋๋ฅผ ์์ฑํ๊ฑฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ ๋ฌธ์ ์๋ ์ ๋ง ๋ง์ด ํ๋ค์ด ํ๋ ๊ธฐ์ต์ด ๋ฉ๋๋ค. ๋ง๋ก๋ง ๋ค์ด๋ณด์๋ conda, jupyter ๋ชจ๋ ๊ฐ๋จํ๊ฒ ๋๋ง ๋ค๋ค๋ณด๊ฒ ๋์๊ณ ์๋กญ๊ฒ ์ ํ ๊ธฐ์ ์ด ์ ๋ง ๋ง์ ๊ฒ ๊ฐ์ต๋๋ค.
์ด ๊ธ์์๋ ๋ค๋ฃจ์ง ์์์ง๋ง ์๋๋ก์ด๋ ๊ฐ๋ฐ์๋ ๊ฝค ์ฐธ์ฌํ์ต๋๋ค. ์๋๋ก์ด๋ ์ญ์ ์ฒ์์ผ๋ก ๊ฐ๋ฐํด๋ณด๊ฒ ๋์๊ณ , ์ธ์ด๋ ๋๋ฌด๋ ์์ํ ์ฝํ๋ฆฐ์ผ๋ก ์์ํ๊ฒ ๋์๋๋ฐ ๋์๊ด์์ ๋น๋ฆฐ ์ฑ ๋ช ๊ถ์ ๋ฐ์ทํด ๋ณด๊ณ ๊ตฌ๊ธ๋งํ๋ฉด์ ๋ ๊ฐ๋ฐ์ด ๋์๋ค์.
์๋ก์ด ๋ถ์ผ์ ๋ํ ๋๋ ค์์ ํญ์ ์๋๋ฐ ํ๋ก์ ํธ๊ฐ ๋๋ ๋ฌด๋ ต์ด๋ฉด ๋ง์ด ์ฑ์ฅํ ๊ฒ ๊ฐ์์ ๋๋ฌด ๋ฟ๋ฏํฉ๋๋ค. ์ด๋ฐ ์ฑ์ทจ๊ฐ์ด ์ข์์ ๊ฐ๋ฐ์๋ฅผ ๊ฟ๊พธ๊ฒ ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
4. ์ข์ ํ์...!
ํ๋ก์ ํธ๊ฐ ์ํํ๊ฒ ์ด์ด์ ธ์๊ณ ๊ฐ๋ฐ๋ ์์ฑ์ด ํ ๊ฒ์๋ ํ์๋ค์ ์ ๋ง๋ ์ํฅ์ด ํฐ ๊ฒ ๊ฐ์ต๋๋ค. ๊ฐ์ ๋งก์ ๋ถ๋ถ์ ๋ํด์ ๊ณ ํต์ ํ ๋กํ๊ธด ํ์ง๋ง ๊ฒฐ๊ตญ ๊ฐ์๊ฐ ๋งก์ ๋ถ๋ถ์ ๋ฌต๋ฌตํ ํด๋์ต๋๋ค. ๊ฐ๋ฐํ๋ค๋ณด๋ฉด ๊ธฐ๋ฅ์ ์์ ๊ฑฐ๋ ์ถ์ํ๊ฒ ๋๋ ๊ฒ ๋ค๋ฐ์ฌ์ธ๋ฐ ์์ ํ ๋ถ๋ถ์ด ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ฐธ ๋ชจ๋๊ฐ ๋ฉ์ ธ์!!(ํ๋ช NICER๐ฅฐ) ์ด๋ฐ ์กฐ์ง์ ๋ค์ด๊ฐ๊ธฐ ์ํด์ ๋ง์ ๋ ธ๋ ฅ์ ๋ค์ผ ํ์๊ฐ ์๋ค๋ ๋๊ธฐ๋ถ์ฌ๋ ๋ง์ด ์ป์ ๊ฒ ๊ฐ์ต๋๋ค.
'NLP' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Python, KoBERT] ๋ค์ค ๊ฐ์ ๋ถ๋ฅ ๋ชจ๋ธ ๊ตฌํํ๊ธฐ (huggingface๋ก ์ด์ ๋ฐฉ๋ฒ O) (19) | 2021.11.23 |
---|