딥 러닝을 이용한 자연어 처리: KoBERT를 이용한 네이버 영화 리뷰 분류하기
최근 몇 년간 인공지능(AI) 기술이 비약적으로 발전하면서, 자연어 처리(NLP) 분야에서도 많은 진전을 이루었습니다. 특히, 딥 러닝 기반의 모델들이 언어 이해 및 생성에서 뛰어난 성능을 보여주고 있습니다. 이번 글에서는 BERT(Bidirectional Encoder Representations from Transformers) 모델을 한국어에 최적화한 KoBERT를 활용하여 네이버 영화 리뷰를 분류하는 방법에 대해 다뤄보겠습니다.
1. 프로젝트 개요
본 프로젝트의 목적은 네이버 영화 리뷰 데이터를 바탕으로 사용자 리뷰가 긍정적인지, 부정적인지를 분류하는 것입니다. 이를 통해 자연어 처리의 기본 개념과 KoBERT 모델을 사용하는 방법을 이해하고, 실습을 통해 데이터 전처리와 모델 학습 과정을 경험할 수 있습니다.
2. KoBERT 소개
KoBERT는 구글의 BERT 모델을 한국어에 맞게 학습시킨 모델입니다. BERT는 두 가지 주요 요소를 기반으로 하고 있는데, 첫 번째는 ‘마스킹된 언어 모델’로, 문장에서 일부 단어를 랜덤하게 마스킹하여 모델이 이 단어를 예측하는 방식입니다. 두 번째는 ‘다음 문장 예측’으로, 두 문장이 주어졌을 때 두 번째 문장이 첫 번째 문장의 다음에 오는 문장인지 아닌지를 판단하는 것입니다. 이러한 전이 학습(Transfer Learning) 기법은 많은 자연어 처리 작업에서 효과적임을 입증했습니다.
3. 데이터 준비
이번 프로젝트에서는 네이버 영화 리뷰 데이터를 사용할 것입니다. 이 데이터셋은 영화에 대한 사용자 리뷰와 해당 리뷰의 긍정적 또는 부정적 레이블로 구성됩니다. 데이터는 CSV 형식으로 제공되며, 필요한 라이브러리를 설치한 후 데이터셋을 준비합니다.
import pandas as pd
# 데이터셋 로드
df = pd.read_csv('naver_movie_reviews.csv')
df.head()
데이터셋의 각 열에는 영화 리뷰와 해당 리뷰의 감정 레이블이 포함되어 있습니다. 이 데이터를 분석하기 위해 필요한 전처리 과정을 거쳐야 합니다.
4. 데이터 전처리
데이터 전처리는 머신러닝에서 매우 중요한 단계입니다. 리뷰 텍스트를 모델에 적합한 형태로 변환하기 위해 다음과 같은 작업을 수행합니다:
- 불용어 제거: 의미가 없는 일반적인 단어를 제거합니다.
- 토큰화: 문장을 단어로 분리합니다.
- 정규화: 같은 의미를 가지는 단어들을 통일합니다.
from sklearn.model_selection import train_test_split
from transformers import BertTokenizer
# KoBERT 토크나이저 로드
tokenizer = BertTokenizer.from_pretrained('kykim/bert-kor-base')
# 리뷰 텍스트와 레이블 분리
sentences = df['review'].values
labels = df['label'].values
# 학습 데이터와 테스트 데이터로 분리
X_train, X_test, y_train, y_test = train_test_split(sentences, labels, test_size=0.1, random_state=42)
5. 데이터셋 클래스 정의
PyTorch를 사용하여 KoBERT 모델을 학습하기 위해 데이터셋 클래스를 정의합니다. 이 클래스는 입력 데이터를 모델이 처리할 수 있는 형태로 변환하는 역할을 합니다.
from torch.utils.data import Dataset
class NaverMovieDataset(Dataset):
def __init__(self, texts, labels, tokenizer, max_length=128):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = self.texts[idx]
label = self.labels[idx]
encoding = self.tokenizer(
text,
truncation=True,
padding='max_length',
max_length=self.max_length,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].squeeze(0),
'attention_mask': encoding['attention_mask'].squeeze(0),
'labels': torch.tensor(label, dtype=torch.long)
}
6. 모델 구축 및 학습, 평가, 예측 클래스 정의
모델의 학습, 평가, 예측을 하나의 클래스로 정의하여 코드를 깔끔하게 관리합니다.
import torch
from torch.utils.data import DataLoader
from transformers import BertForSequenceClassification, AdamW
from sklearn.metrics import classification_report
class KoBERTSentimentClassifier:
def __init__(self, model_name='kykim/bert-kor-base', num_labels=2, learning_rate=1e-5):
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
self.model = BertForSequenceClassification.from_pretrained(model_name, num_labels=num_labels).to(self.device)
self.optimizer = AdamW(self.model.parameters(), lr=learning_rate)
def train(self, train_dataset, batch_size=16, epochs=3):
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
self.model.train()
for epoch in range(epochs):
for batch in train_dataloader:
self.optimizer.zero_grad()
input_ids = batch['input_ids'].to(self.device)
attention_mask = batch['attention_mask'].to(self.device)
labels = batch['labels'].to(self.device)
outputs = self.model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
self.optimizer.step()
print(f"Epoch: {epoch + 1}, Loss: {loss.item()}")
def evaluate(self, test_dataset, batch_size=16):
test_dataloader = DataLoader(test_dataset, batch_size=batch_size)
self.model.eval()
predictions, true_labels = [], []
with torch.no_grad():
for batch in test_dataloader:
input_ids = batch['input_ids'].to(self.device)
attention_mask = batch['attention_mask'].to(self.device)
labels = batch['labels'].to(self.device)
outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
logits = outputs.logits
predictions.extend(torch.argmax(logits, dim=1).cpu().numpy())
true_labels.extend(labels.cpu().numpy())
print(classification_report(true_labels, predictions))
def predict(self, texts, tokenizer, max_length=128):
self.model.eval()
inputs = tokenizer(
texts,
truncation=True,
padding='max_length',
max_length=max_length,
return_tensors='pt'
).to(self.device)
with torch.no_grad():
outputs = self.model(input_ids=inputs['input_ids'], attention_mask=inputs['attention_mask'])
predictions = torch.argmax(outputs.logits, dim=1)
return predictions.cpu().numpy()
7. 결론
이번 글을 통해 KoBERT를 이용한 네이버 영화 리뷰 분류 과정을 살펴보았습니다. 딥 러닝 기반의 자연어 처리 모델을 활용하여 텍스트 데이터를 처리하는 방법을 배움으로써, 자연어 처리의 기초를 익힐 수 있는 좋은 기회가 되었기를 바랍니다. 이제 이 기술을 기반으로 다양한 자연어 처리 프로젝트를 진행할 수 있는 발판이 마련되었습니다.