[NLP] BERT로 영화 리뷰 분류하기

2023. 9. 20. 10:11ML&DL/NLP

🤗 Hugging Face

- Transformer과 같은 다양한 인공지능 모델을 사용할 수 있도록 라이브러리를 제공하는 모듈

- 대표적인 모델로 Transformer 기반인 BERT, GPT 모델 등이 있음

 

📌 목표

- 네이버 영화 리뷰를 감성(긍정/부정) 분류해보기! 

 

💻 실습 코드

 

Hugging Face가 제공하는 transformers 라이브러리 설치

!pip install transformers

 

라이브러리 및 패키지 import 

import pandas as pd
import numpy as np
import urllib.request
import os
from tqdm import tqdm
import tensorflow as tf
from transformers import BertTokenizer, TFBertModel

 

train & test 데이터 다운 / 확인

# train & test 데이터 설치
urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt", filename="ratings_train.txt")
urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt", filename="ratings_test.txt")

train_data = pd.read_table('ratings_train.txt')
test_data = pd.read_table('ratings_test.txt')
# 데이터 확인
print('훈련용 리뷰 개수 :',len(train_data)) # 훈련용 리뷰 개수 출력
print('테스트용 리뷰 개수 :',len(test_data)) # 테스트용 리뷰 개수 출력
# 데이터 확인 
train_data[:5] # 상위 5개 출력
test_data[:5] # 상위 5개 출력

train & test 전처리

# 데이터 전처리 - 결측치 제거

train_data = train_data.dropna(how = 'any') # Null 값이 존재하는 행 제거
train_data = train_data.reset_index(drop=True)
print(train_data.isnull().values.any()) # Null 값이 존재하는지 확인

test_data = test_data.dropna(how = 'any') # Null 값이 존재하는 행 제거
test_data = test_data.reset_index(drop=True)
print(test_data.isnull().values.any()) # Null 값이 존재하는지 확인

train & test 토큰화

# BERT 모델의 토크나이저 가져오기
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
#텍스트 인코딩
print(tokenizer.encode("보는내내 그대로 들어맞는 예측 카리스마 없는 악역"))

#텍스트 토큰화
print(tokenizer.tokenize("보는내내 그대로 들어맞는 예측 카리스마 없는 악역"))

# 인코딩된 문장을 원래 문장으로 디코딩
tokenizer.decode(tokenizer.encode("보는내내 그대로 들어맞는 예측 카리스마 없는 악역"))

Bert 모델에 알맞은 입력값으로 데이터 변환해주기

def convert_examples_to_features(examples, labels, max_seq_len, tokenizer):

    input_ids, attention_masks, token_type_ids, data_labels = [], [], [], []

    for example, label in tqdm(zip(examples, labels), total=len(examples)):
        # input_id는 워드 임베딩을 위한 문장의 정수 인코딩
        input_id = tokenizer.encode(example, max_length=max_seq_len, pad_to_max_length=True)

        # attention_mask는 실제 단어가 위치하면 1, 패딩의 위치에는 0인 시퀀스.
        padding_count = input_id.count(tokenizer.pad_token_id)
        attention_mask = [1] * (max_seq_len - padding_count) + [0] * padding_count

        # token_type_id는 세그먼트 임베딩을 위한 것으로 이번 예제는 문장이 1개이므로 전부 0으로 통일.
        token_type_id = [0] * max_seq_len

        assert len(input_id) == max_seq_len, "Error with input length {} vs {}".format(len(input_id), max_seq_len)
        assert len(attention_mask) == max_seq_len, "Error with attention mask length {} vs {}".format(len(attention_mask), max_seq_len)
        assert len(token_type_id) == max_seq_len, "Error with token type length {} vs {}".format(len(token_type_id), max_seq_len)

        input_ids.append(input_id)
        attention_masks.append(attention_mask)
        token_type_ids.append(token_type_id)
        data_labels.append(label)

    input_ids = np.array(input_ids, dtype=int)
    attention_masks = np.array(attention_masks, dtype=int)
    token_type_ids = np.array(token_type_ids, dtype=int)

    data_labels = np.asarray(data_labels, dtype=np.int32)

    return (input_ids, attention_masks, token_type_ids), data_labels

형태에 맞게 변환된 데이터로 Train / Test 지정

train_X, train_y = convert_examples_to_features(train_data['document'], train_data['label'], max_seq_len=max_seq_len, tokenizer=tokenizer)
test_X, test_y = convert_examples_to_features(test_data['document'], test_data['label'], max_seq_len=max_seq_len, tokenizer=tokenizer)

데이터 확인

# 최대 길이: 128
input_id = train_X[0][0]
attention_mask = train_X[1][0]
token_type_id = train_X[2][0]
label = train_y[0]

print('단어에 대한 정수 인코딩 :',input_id)
print('어텐션 마스크 :',attention_mask)
print('세그먼트 인코딩 :',token_type_id)
print('각 인코딩의 길이 :', len(input_id))
print('정수 인코딩 복원 :',tokenizer.decode(input_id))
print('레이블 :',label)

pre-trained BERT 모델 불러오기

model = TFBertModel.from_pretrained("bert-base-multilingual-cased")

모델 학습 전에 input 값 정의해주기

max_seq_len = 128
input_ids_layer = tf.keras.layers.Input(shape=(max_seq_len,), dtype=tf.int32)
attention_masks_layer = tf.keras.layers.Input(shape=(max_seq_len,), dtype=tf.int32)
token_type_ids_layer = tf.keras.layers.Input(shape=(max_seq_len,), dtype=tf.int32)

outputs = model([input_ids_layer, attention_masks_layer, token_type_ids_layer])

모델 정의

class TFBertForSequenceClassification(tf.keras.Model):
    def __init__(self, model_name):
        super(TFBertForSequenceClassification, self).__init__()
        self.bert = TFBertModel.from_pretrained(model_name, from_pt=True)
        self.classifier = tf.keras.layers.Dense(1,
                                                kernel_initializer=tf.keras.initializers.TruncatedNormal(0.02),
                                                activation='sigmoid',
                                                name='classifier')

    def call(self, inputs):
        input_ids, attention_mask, token_type_ids = inputs
        outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        cls_token = outputs[1]
        prediction = self.classifier(cls_token)

        return prediction
with strategy.scope():
  model = TFBertForSequenceClassification("bert-base-multilingual-cased")
  optimizer = tf.keras.optimizers.Adam(learning_rate=5e-5)
  loss = tf.keras.losses.BinaryCrossentropy()
  model.compile(optimizer=optimizer, loss=loss, metrics = ['accuracy'])

모델 학습 / 테스트 결과

model.fit(train_X, train_y, epochs=2, batch_size=64, validation_split=0.2)
results = model.evaluate(test_X, test_y, batch_size=1024)
print("test loss, test acc: ", results)

 

분류 결과 테스트 해보기

def sentiment_predict(new_sentence):
  input_id = tokenizer.encode(new_sentence, max_length=max_seq_len, pad_to_max_length=True)

  padding_count = input_id.count(tokenizer.pad_token_id)
  attention_mask = [1] * (max_seq_len - padding_count) + [0] * padding_count
  token_type_id = [0] * max_seq_len

  input_ids = np.array([input_id])
  attention_masks = np.array([attention_mask])
  token_type_ids = np.array([token_type_id])

  encoded_input = [input_ids, attention_masks, token_type_ids]
  score = model.predict(encoded_input)[0][0]
  print(score)

  if(score > 0.5):
    print("{:.2f}% 확률로 긍정 리뷰입니다.\n".format(score * 100))
  else:
    print("{:.2f}% 확률로 부정 리뷰입니다.\n".format((1 - score) * 100))

결과

부정 리뷰

sentiment_predict("보던거라 계속보고있는데 전개도 느리고 주인공인 은희는 한두컷 나오면서 소극적인모습에 ")

 

긍정 리뷰

sentiment_predict('와 개쩐다 정말 세계관 최강자들의 영화다')

 

출처 : https://github.com/ukairia777/tensorflow-nlp-tutorial

 

GitHub - ukairia777/tensorflow-nlp-tutorial: tensorflow를 사용하여 텍스트 전처리부터, Topic Models, BERT, GPT와

tensorflow를 사용하여 텍스트 전처리부터, Topic Models, BERT, GPT와 같은 최신 모델의 다운스트림 태스크들을 정리한 Deep Learning NLP 저장소입니다. - GitHub - ukairia777/tensorflow-nlp-tutorial: tensorflow를 사용하

github.com