본문 바로가기
Bigdata

자연어 처리(Natural Language Processing, NLP) - transformers 기초, Bert 감정 분류 예제

by 올엠 2024. 8. 11.
반응형

Transformers 라이브러리?

transformers는 딥러닝 기반의 자연어 처리(Natural Language Processing, NLP) 작업을 위한 파이썬 라이브러리이다.

주로 최신의 자연어 처리 모델들을 구현하고 사용할 수 있도록 도와준다.

Transformers 라이브러리는 Hugging Face라는 회사에서 개발하고 유지 관리하고 있으며, 

다양한 언어 모델과 사전 훈련된 가중치를 제공합니다. 가장 유명한 모델로는 BERT, GPT, GPT-2, RoBERTa, XLNet, DistilBERT 등이 있다.

 

Transformers 라이브러리를 사용하면 다음과 같은 작업을 수행할 수 있다.

토큰화(Tokenization): 자연어 문장을 작은 단위로 분리하여 모델에 입력할 수 있는 형태로 변환한다. transformers는 다양한 토크나이저를 제공하여 이 작업을 수행할 수 있다. 우리가 ML에서 자연어를 처리할 때 token이라는 얘기를 자주하는데 이부분이다. 

"토큰(Token)"은 텍스트 문장을 작은 단위로 나누는 과정을 말한다. 토큰은 문장을 이해하고 처리하기 위해 필요한 최소한의 단위이다.

일반적으로 자연어 처리에서 사용되는 토큰은 단어이지만 토큰은 단어뿐만 아니라 음절, 문자, 부분 단어 등 다양한 수준으로 나눌 수 있습니다. 이는 사용되는 토크나이저에 따라 달라진다.

토큰화(Tokenization)는 텍스트 문장을 토큰으로 나누는 과정을 말하며, 토큰화는 자연어 처리의 첫 단계로, 텍스트를 모델에 입력 가능한 형태로 변환하게 된다.

예를 들어, "나는 개를 좋아해"라는 문장을 토큰화하면 다음과 같은 토큰들로 나눌 수 있다.

 

방법 1: 단어 수준 토큰화

['나는', '개를', '좋아해']

 

방법 2: 음절 수준 토큰화

['나', '는', ' ', '개', '를', ' ', '좋', '아', '해']

 

방법 3: 문자 수준 토큰화

['나', '는', ' ', '개', '를', ' ', '좋', '아', '해']

모델 구축: transformers는 사전 훈련된 언어 모델의 구조를 제공하며, 사용자는 이를 활용하여 자신의 NLP 작업에 맞게 모델을 구축할 수 있다. 예를 들어, 문장 분류, 개체명 인식, 질의 응답 등 다양한 작업에 활용할 수 있다.

 

사전 훈련된 가중치 활용: transformers는 많은 사전 훈련된 언어 모델의 가중치를 제공이 가능하다. 이를 사용하여 새로운 NLP 작업에 적용하거나, 특정 작업에 맞게 추가적인 훈련을 수행할 수 있게된다.

 

전이 학습(Transfer Learning): transformers는 전이 학습을 통해 사전 훈련된 모델의 일부를 가져와서 새로운 작업에 맞게 세부 조정할 수 있습니다. 이를 통해 데이터가 부족한 작업에서도 좋은 성능을 얻을 수 있다.

 

이는 Bert의 공식 예제로 약간의 설명과 함께 진행해 보도록 하겠다.

먼저 transformers를 설치한다. transformers은 PyTorch 기반으로 동작하기 때문에 torch를 같이 설치해주자.

pip install transformers torch

 

여기에서는 간단히 데이터세트를 만들어서 진행해볼 예정이다.

'bert-base-multilingual-cased' 는 다국어 BERT 모델로, 여러 언어에 대한 자연어 처리 작업에 사용할 수 있다.

먼저 간단한 테스트 데이터를 만들고, BERT 기본 모델을 불려오도록 하자.

import torch

from torch import nn

from torch.utils.data import DataLoader

from transformers import BertModel, BertTokenizer



data = [

    ("이 영화 정말 좋아요!", "긍정"),

    ("전반적으로 스토리가 볼만했습니다.", "긍정"),

    ("연기가 훌륭했어요.", "긍정"),

    ("정말 멋진 결말이었어요!", "긍정"),

    ("이 영화 너무 실망스러웠어요.", "부정"),

    ("스토리가 마음에 들지 않았습니다.", "부정"),

    ("연기가 형편없었어요.", "부정"),

    ("보는 걸 후회했어요.", "부정"),

    ("이 영화는 그저 그랬어요.", "중립"),

    ("별로 좋지도 않고 나쁘지도 않았어요.", "중립")

]



# BERT 모델 및 토크나이저 불러오기

model_name = 'bert-base-multilingual-cased'

model = BertModel.from_pretrained(model_name)

tokenizer = BertTokenizer.from_pretrained(model_name)


테스트를 위해서 토큰이 잘 동작하는지 확인해보면, 아래와 같이 단어들이 토큰화된것을 알 수 있다.

 

##이 붙은 이유는 단어의 서브 워드라는 의미로써, 이것이 에서 것이 ##을 통해 앞 단어와 연결된다는 것을 표현하여 원래 단어의 의미를 유지 할 수 있다.

이제 우리 학습 데이터를 토큰화 하는 작업이 필요하다. 

전처리 방법은 먼저 데이터를 나누고, 이후 평가 라벨과 평가 입력 데이터로 변환하는 방식으로 진행된다.

3가지 타입으로 긍정은 2, 부정은 1, 그외는 0으로 작업한다.

# 데이터 전처리

def preprocess_data(dataset):

    texts, labels = zip(*dataset)

    encoded_texts = tokenizer(texts, padding=True, truncation=True, return_tensors='pt')

    encoded_labels = torch.tensor([2 if label == '긍정' else 1 if label == '부정' else 0 for label in labels])

    return encoded_texts, encoded_labels

전처리에서 사용한 옵션은

padding=True는 입력 시퀀스의 길이를 동일하게 맞추기 위해 패딩을 적용하는 옵션이다.

BERT 모델은 일정한 입력 길이를 필요로 하기 때문에, 입력 시퀀스의 길이를 맞추기 위해 짧은 문장에 패딩 토큰을 추가한다.

truncation=True는 입력 시퀀스가 모델의 최대 입력 길이보다 길 경우, 초과된 부분을 자르는 옵션이다.

최대 입력 길이를 초과하는 토큰은 버려지며, 입력 시퀀스의 중요한 부분을 유지하기 위해 사용된다.

return_tensors='pt'는 토큰화 및 패딩된 입력을 PyTorch 텐서로 반환하도록 지정하는 옵션이다. 반환된 텐서는 PyTorch 모델에 직접 입력으로 사용할 수 있다.

이 옵션을 사용하면 토큰화된 입력이 텐서 형태로 반환되므로, PyTorch를 사용하기 위해 별도의 데이터 변환이 필요하지 않다.

train_inputs 내용을 살펴보면 다음과 같이 확인이 텐서 형태로 변환이 되고 길이에 맞에 패딩이 추가된 것을 알 수 있다.

 

 

이제 모델을 학습하기 위해 데이터로더를 생성하도록 하자. 

# 하이퍼파라미터 설정

batch_size = 4

learning_rate = 2e-5

num_epochs = 3



train_data_loader = DataLoader(list(zip(train_inputs['input_ids'], train_inputs['attention_mask'], train_labels)),

                               batch_size=batch_size, shuffle=True)

데이터로더 생성시 하이퍼파라미터를 함께 지정하게 되는데, 각 다음과 같은 의미를 가진다.

batch_size는 모델이 한 번에 처리하는 데이터의 개수를 의미한다. 이를 통해 메모리나 처리 속도를 향상 시킬 수 있다.

learning_rate는 머신 러닝 모델의 학습 속도를 제어하는 하이퍼파라미터로, 각 학습 단계에서 가중치 및 편향을 업데이트할 양을 결정하게 된다(경사 하강법(Gradient Descent) 의 가중치). 값으로는 0과 1을 가지며, 여기에서 지정한 learning_rate = 2e-5 는  0.00002를 의미한다.

num_epochs는 머신 러닝에서 사용되는 하이퍼파라미터 중 하나로, 전체 데이터셋을 몇 번 반복하여 학습를 의미한다.

반복 학습을 통해서 가증치를 줄 수 있어, 1번 학습한 학습 모델과 10번 학습한 학습 모델은 사로 다른 결과를 나올 수 있으므로, 이값 역시 중요한 파라미터라 할 수 있다.

 

이를 통해 모델을 정의하고 학습을 진행한다.

아래 코드는 참조 예제와 동일한 부분으로 수정할 부분이 없다.

torch.device는 cpu혹은 gpu등을 선택해 모델 실행을 준비하는 부분이며, 

이후 학습을 위해 num_epochs 만큼 반복 학습을 진행하게 된다.

# 모델 정의

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

model.to(device)

optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)

criterion = nn.CrossEntropyLoss()



# 모델 학습

model.train()

for epoch in range(num_epochs):

    for input_ids, attention_mask, labels in train_data_loader:

        input_ids = input_ids.to(device)

        attention_mask = attention_mask.to(device)

        labels = labels.to(device)



        optimizer.zero_grad()

        outputs = model(input_ids=input_ids, attention_mask=attention_mask)

        logits = outputs.last_hidden_state[:, 0, :]  # [CLS] token의 출력만 사용

        loss = criterion(logits, labels)

        loss.backward()

        optimizer.step()



    print(f'Epoch {epoch+1}/{num_epochs} - Loss: {loss.item()}')

 

추가로 알고 있으면 유용한 부분이 BERT 모델은 입력 문장에 특별한 토큰을 추가하여 문장의 시작과 끝을 나타내는 "[CLS]" 토큰이라는 것이 있다.

 

이제 학습이 모두 마무리 되었다.

평가를 진행해보기 위해서 평가 함수도 생성해야 한다.

평가시에도 토큰화 작업이 필요하다. 먼저 토큰 생성과 model을 평가 모드로 변경한 이후 입력 값에 대한 평가를 진행 할 수 있다.

def evaluate_input(input_text):

    encoded_text = tokenizer.encode_plus(input_text, add_special_tokens=True, return_tensors='pt')

    input_ids = encoded_text['input_ids'].to(device)

    attention_mask = encoded_text['attention_mask'].to(device)



    model.eval()  # 모델을 평가 모드로 설정

    with torch.no_grad():

        outputs = model(input_ids=input_ids, attention_mask=attention_mask)

        logits = outputs.last_hidden_state[:, 0, :]  # [CLS] token의 출력만 사용

        predicted_class = torch.argmax(logits, dim=1).item()



    # 예측 결과를 클래스 레이블로 변환

    label_mapping = {0: '중립', 1: '부정', 2: '긍정'}

    predicted_label = label_mapping[predicted_class]



    return predicted_label

 

위 생성한 함수를 이용해서 평가 결과를 테스트해보면 다음과 같다.

input_text = "이 영화 좀 그래"

predicted_label = evaluate_input(input_text)

print(predicted_label)


짱이라는 말로 테스트해보자.

부정으로 현재 모델은 판단했다. 아마 충분한 학습이 되지 않아서 발생하는 것으로 보인다.

그리고 학습을 잘 하더라도 예매한 표현을 구사하면 언제든지 잘못된 결과가 나올수 있다는 점도 유의하자.(만능은 아님...)

Bert를 이용해서 자연어 처리하는 부분에 대해 알아보았다.

GitHub - huggingface/transformers: 🤗 Transformers: State-of-the-art Machine Learning for Pytorch, TensorFlow, and JAX.

Google Colab

 

 

 

 

 

 

 

 

 

 

 

 

반응형