인공지능/자연어 처리

Python NLP - BERT Binary Classification (hugging Face Transformer library)

이게될까 2024. 6. 19. 23:43
728x90
728x90
import numpy as np
import tensorflow as tf

print("Num GPUs Available: ", len(tf.config.list_physical_devices('GPU')))

gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
    try:
        for gpu in gpus:
            tf.config.experimental.set_memory_growth(gpu, True)
    except RuntimeError as e:
        print(e)

with open('/kaggle/input/2024-1-nlp-5/Korean_movie_reviews_2016.txt/Korean_movie_reviews_2016.txt', encoding='utf-8') as f:
    docs = [doc.strip().split('\t') for doc in f]
    docs = [(doc[0], int(doc[1])) for doc in docs if len(doc) == 2]
    texts, labels = zip(*docs)
    
words_list = [doc.strip().split() for doc in texts]
print(words_list[:2])
print(len(texts))

Num GPUs Available:  1
[['부산', '행', '때문', '너무', '기대하고', '봤'], ['한국', '좀비', '영화', '어색하지', '않게', '만들어졌', '놀랍']]
165384

GPU 있는지 확인하고, 데이터 읽어옵니다.

from tensorflow.keras.utils import to_categorical
y_one_hot = to_categorical(labels)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(texts, y_one_hot, test_size=0.2, random_state=0)

y 이진 분류 문제니까 나눠주고, 테스트 트레인도 나눠줍니다.

from transformers import BertTokenizer, TFAlbertForSequenceClassification, AdamW,Trainer,TrainingArguments
tokenizer= BertTokenizer.from_pretrained("kykim/albert-kor-base") #허깅 페이스에 존재하는 토크나이저이다.

이번에 사용할 모델은 Albert이다.

X_train_tokenized = tokenizer(X_train, return_tensors="np", max_length=30, padding='max_length', truncation=True)
X_test_tokenized = tokenizer(X_test, return_tensors="np", max_length=30, padding='max_length', truncation=True) # 파라미터에 맞춰 토큰화

데이터 토큰화도 진행해주고

model = TFAlbertForSequenceClassification.from_pretrained("kykim/albert-kor-base", num_labels=2, from_pt=True)
model.summary()# 토크나이저랑 모델이름이 보통 같다. 프리트레이닝이 끝난 모델들이다.

모델도 불러옵니다.

모델 사이즈는 약 50MB정도


from keras.optimizers import Adam
from transformers import create_optimizer

epoch = 3
batch_size = 20
num_train_steps = len(X_train_tokenized['input_ids'])//batch_size * epoch  # 총 학습 스텝 수 (에포크 * 배치 수)
optimizer, schedule = create_optimizer(init_lr=3e-5, num_warmup_steps=0, num_train_steps=num_train_steps)

model.compile(optimizer=optimizer, loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True), metrics=['accuracy'])

train_dataset = tf.data.Dataset.from_tensor_slices((
    {
        'input_ids': X_train_tokenized['input_ids'],
        'token_type_ids': X_train_tokenized['token_type_ids'],
        'attention_mask': X_train_tokenized['attention_mask']
    },
    tf.cast(y_train, tf.float32)
)).batch(batch_size)

# 모델 훈련
model.fit(train_dataset, epochs=epoch)
#model.fit(X_train_tokenized,y_train, batch_size = batch_size, epochs=epoch)

학습을 진행합니다.

1 epoch당 대락 10분정도 걸리고 정확도도 확실하게 오르네요

y_preds = model.predict(dict(X_test_tokenized))
prediction_probs = tf.nn.softmax(y_preds.logits,axis=1).numpy()
y_predictions = np.argmax(prediction_probs, axis=1)
y_test = np.argmax(y_test, axis=1)
from sklearn.metrics import classification_report
print(classification_report(y_predictions, y_test))

평가로는  Precision, recall모두 괜찮게 나와 F1 score도 높게 나왔습니다.

이번엔 DistillBERT를 사용해보겠습니다.

from transformers import DistilBertTokenizer, TFDistilBertForSequenceClassification,BertTokenizer

tokenizer= BertTokenizer.from_pretrained("monologg/distilkobert") # 토크나이저랑 모델 맞춰주기 
X_train_tokenized = tokenizer(X_train, return_tensors="np", max_length=30, padding='max_length', truncation=True)
X_test_tokenized = tokenizer(X_test, return_tensors="np", max_length=30, padding='max_length', truncation=True)

model_distillBERT = TFDistilBertForSequenceClassification.from_pretrained("monologg/distilkobert", num_labels=2, from_pt=True)
model_distillBERT.summary() # 버트의 한국어 버전이다. 알버트 보다는 파라미터가 두배 더 많다.

크기는 대략 2배정도 큽니다.

학습 속도는 왜 더 빠른지 모르겠네여 ㅎㅎ....

그렇지만 정확도도 무지하게 떨어집니다. 이유는....

더보기

모델의 크기와 학습 속도, 정확도는 여러 요인에 의해 결정됩니다. monologg/distilkobert가 kykim/albert-kor-base보다 크지만 학습 속도가 빠르고 정확도가 낮은 이유는 다음과 같습니다:

  1. 모델 아키텍처 차이:
    • DistilKoBERT는 DistilBERT 아키텍처를 기반으로 하여 BERT의 층을 절반으로 줄이고, 일부 매개변수를 제거하여 크기를 줄이고 속도를 높였습니다. 하지만 이 과정에서 표현력도 줄어들어 정확도가 낮아질 수 있습니다.
  2. 모델 최적화:
    • DistilKoBERT는 원래 빠른 추론과 학습 속도를 목표로 설계되었습니다. 따라서 BERT보다 경량화되어 있어 빠르게 학습할 수 있지만, 정확도는 약간 희생됩니다.
  3. 훈련 데이터와 파인튜닝:
    • 두 모델이 학습된 데이터의 차이도 성능에 영향을 미칠 수 있습니다. ALBERT는 BERT의 성능을 유지하면서도 파라미터 효율성을 높이기 위해 설계되었습니다. 이는 학습 시간이 길어질 수 있지만, 일반적으로 더 나은 정확도를 제공합니다.

따라서, 모델의 크기 외에도 아키텍처와 최적화 방법, 훈련 데이터가 모델의 학습 속도와 정확도에 중요한 영향을 미칩니다.

GPT 바보....

 

평가도 처참해진 것을 볼 수 있습니다.

동일한 학습 시간으로 해도 증가치가 높지 않아서 굳이 할 이유가 없을 것 같습니다...

 

마지막으로 기본 BERT

from transformers import TFBertForSequenceClassification,BertTokenizer

tokenizer= BertTokenizer.from_pretrained("kykim/bert-kor-base") 
X_train_tokenized = tokenizer(X_train, return_tensors="np", max_length=30, padding='max_length', truncation=True)
X_test_tokenized = tokenizer(X_test, return_tensors="np", max_length=30, padding='max_length', truncation=True)

model_BERT = TFBertForSequenceClassification.from_pretrained("kykim/bert-kor-base", num_labels=2, from_pt=True)
model_BERT.summary()

사이즈가 제일 큽니다 ....

ALBERT의 10배입니다 ㄷㄷ...

그렇지만 학습 속도는 1.1배 정도밖에 차이가 나지 않습니다.

f1 score도 큰 차이가 없는 것을 봐선 굳이 쓸 이유가 없을 것 같네요

 

모델 사이즈 학습 속도 F1 score
BERT 451.27MB 740 s/epoch 0.93
ALBERT 50.31MB 650 s/epoch 0.92
DistillBERT 108.32MB 210 s/epoch 0.77
728x90