NLP 감정 분류 실습 (feat. 데이콘)
면접이 끝나고 미뤄왔던 감성 분류 대회에 참여하기 위해 돌아왔다.
일단은 데이콘에서 제공한 Bert 베이스라인을 따라하면서 전체적인 코드 진행과 구성을 익혀보도록 하자
1
|
!pip install transformers
|
cs |
일단 transformer 기반 만능 라이브러리인 transformers를 설치해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import pandas as pd
import numpy as np
from sklearn.metrics import f1_score
from sklearn.preprocessing import LabelEncoder
import random
import os
import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Dataset
from tqdm.notebook import tqdm
from transformers import BertTokenizer
from transformers import BertModel
from torch.optim import Adam
import matplotlib as mpl
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings(action='ignore')
|
cs |
각종 라이브러리를 설치해주고
1
|
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
|
cs |
GPU device를 정의해준다.
1
2
3
4
5
6
|
CFG = {
'EPOCHS': 3,
'LEARNING_RATE':1e-5,
'BATCH_SIZE':8,
'SEED':41
}
|
cs |
파라미터를 설정해주고
1
2
3
4
5
6
7
8
9
10
|
def seed_everything(seed):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = True
seed_everything(CFG['SEED'])
|
cs |
seed도 설정해준 후 고정시켜준다
1
2
3
4
|
train = pd.read_csv('/content/drive/MyDrive/competitions/dacon/emotion/train.csv')
test = pd.read_csv('/content/drive/MyDrive/competitions/dacon/emotion/test.csv')
submit = pd.read_csv('/content/drive/MyDrive/competitions/dacon/emotion/sample_submission.csv')
train
|
cs |
제공해주는 train 데이터는 위와 같다.
예전에 했던 대회처럼 speaker랑 dialogue같은 부가적인 고려 요소가 있지만 일단 baseline에서는
Utterance로만 진행하는 것 같다.
1
2
3
4
5
6
7
8
9
10
|
le = LabelEncoder()
le=le.fit(train['Target'])
train['Target']=le.transform(train['Target'])
valid=train[train['Dialogue_ID'].isin([i for i in range(1016,1039)])].reset_index(drop=True)
train=train[~train['Dialogue_ID'].isin([i for i in range(1016,1039)])].reset_index(drop=True)
train_len=len(train)
val_len=len(valid)
|
cs |
Target 레이블을 인코딩 해준다.
1
|
tokenizers = BertTokenizer.from_pretrained('bert-base-cased')
|
cs |
토크나이저를 Bert로 정의해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class CustomDataset(Dataset):
def __init__(self, data, mode = "train"):
self.dataset = data
self.tokenizer = tokenizers
self.mode = mode
def __len__(self):
return len(self.dataset)
def __getitem__(self, idx):
text = self.dataset['Utterance'][idx]
inputs = self.tokenizer(text, padding='max_length', max_length = 512, truncation=True, return_tensors="pt")
input_ids = inputs['input_ids'][0]
token_type_ids = inputs['token_type_ids'][0]
attention_mask = inputs['attention_mask'][0]
if self.mode == "train":
y = self.dataset['Target'][idx]
return input_ids, token_type_ids, attention_mask, y
else:
return input_ids, token_type_ids, attention_mask
|
cs |
1
2
3
4
5
|
train = CustomDataset(train, mode = "train")
valid = CustomDataset(valid, mode = "train")
train_dataloader = torch.utils.data.DataLoader(train, batch_size= CFG['BATCH_SIZE'], shuffle=True)
val_dataloader = torch.utils.data.DataLoader(valid, batch_size= CFG['BATCH_SIZE'], shuffle=False)
|
cs |
데이터를 커스텀할 class를 정의하고 custom해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class BaseModel(nn.Module):
def __init__(self, dropout=0.5, num_classes=len(le.classes_)):
super(BaseModel, self).__init__()
self.bert = BertModel.from_pretrained('bert-base-cased')
self.dropout = nn.Dropout(dropout)
self.linear = nn.Linear(768, num_classes)
self.relu = nn.ReLU()
def forward(self, input_id, mask):
_, pooled_output = self.bert(input_ids= input_id, attention_mask=mask,return_dict=False)
dropout_output = self.dropout(pooled_output)
linear_output = self.linear(dropout_output)
final_layer = self.relu(linear_output)
return final_layer
|
cs |
bert base 모델을 정의해준다.
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
31
32
33
34
35
|
def train(model, optimizer, train_loader, test_loader, device):
model.to(device)
criterion = nn.CrossEntropyLoss().to(device)
best_score = 0
best_model = "None"
for epoch_num in range(CFG["EPOCHS"]):
model.train()
train_loss = []
for input_ids, token_type_ids, attention_mask, train_label in tqdm(train_loader):
optimizer.zero_grad()
train_label = train_label.to(device)
input_id = input_ids.to(device)
mask = attention_mask.to(device)
output = model(input_id, mask)
batch_loss = criterion(output, train_label.long())
train_loss.append(batch_loss.item())
batch_loss.backward()
optimizer.step()
val_loss, val_score = validation(model, criterion, test_loader, device)
print(f'Epoch [{epoch_num}], Train Loss : [{np.mean(train_loss) :.5f}] Val Loss : [{np.mean(val_loss) :.5f}] Val F1 Score : [{val_score:.5f}]')
if best_score < val_score:
best_model = model
best_score = val_score
return best_model
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
def competition_metric(true, pred):
return f1_score(true, pred, average="macro")
def validation(model, criterion, test_loader, device):
model.eval()
val_loss = []
model_preds = []
true_labels = []
with torch.no_grad():
for input_ids, token_type_ids, attention_mask, valid_label in tqdm(test_loader):
valid_label = valid_label.to(device)
input_id = input_ids.to(device)
mask = attention_mask.to(device)
output = model(input_id, mask)
batch_loss = criterion(output, valid_label.long())
val_loss.append(batch_loss.item())
model_preds += output.argmax(1).detach().cpu().numpy().tolist()
true_labels += valid_label.detach().cpu().numpy().tolist()
val_f1 = competition_metric(true_labels, model_preds)
return val_loss, val_f1
|
cs |
train 모델을 설정해준다.
1
2
3
4
5
|
model = BaseModel()
model.eval()
optimizer = torch.optim.Adam(params = model.parameters(), lr = CFG["LEARNING_RATE"])
infer_model = train(model, optimizer, train_dataloader, val_dataloader, device)
|
cs |
모델을 돌려준다..
인내의 시간 (처음 돌렸을 때 5시간인가? 나와서 재부팅했다. 그러니까 10분안에 해결)
1
2
|
test = CustomDataset(test, mode = "test")
test_dataloader = torch.utils.data.DataLoader(test, batch_size= CFG['BATCH_SIZE'], shuffle=False)
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
|
def inference(model, test_loader, device):
model.to(device)
model.eval()
test_predict = []
for input_ids, token_type_ids, attention_mask in tqdm(test_loader):
input_id = input_ids.to(device)
mask = attention_mask.to(device)
y_pred = model(input_id, mask)
test_predict += y_pred.argmax(1).detach().cpu().numpy().tolist()
print('Done.')
return test_predict
|
cs |
학습한 것을 바탕으로 train 데이터에도 적용시킨뒤 예측한다.
1
2
3
4
|
preds = inference(infer_model, test_dataloader, device)
preds = le.inverse_transform(preds)
submit['Target'] = preds
submit.head()
|
cs |
예측한 결과값이 나왔다면 submit 파일에 옮겨준 후 출력한다.
아주 간단하게! baseline을 따라가보았다.
실제로 이렇게 제출해보니 0.35 정도의 점수가 나왔다.
앞으로 여러 시도를 해볼텐데 일단 대회기간 동안은 비공개로 올려두었다가
대회가 끝나면 (초라한) 결과와 함께 (별거없는) 코드를 공개할 예정이다.
끝!