Bert(Bidirectional Encoder Representations from Transformers)
트렌스포머의 인코더 구조만 차용해서 만든 자연어 처리(NLP) 대규모 언어 모델.
디코더 부분 없음 -> 자연어 생성보다 자연어 이해해 초점 맞춤.
트렌스포머 인코더 레이어를 여러 개 쌓아서 임베딩을 만들고, 마지막에 클래시피파이어 레이어를 붙여서 원하는 결과물을 만든다.
사전학습 모델 (레이블링 되지 않은 큰 데이터셋에서 먼저 데이터를 학습하고, 모델이 일반적인 데이터셋의 분포를 배우게 함 -> 이후 특정 태스크에 대해서 다시 학습시켜서 모델을 좀 더 뾰족하게 만드는 파인 튜닝 거침) - 강의 내용 요약(Overview of Generative LLMs)
TinyBERT
BERT 모델의 경량화 버전. BERT에 비해 매개변수 수와 모델 크기가 크게 감소.
"TinyBERT는 특히 리소스가 제한된 환경에서 BERT와 유사한 성능을 제공하면서도 더 빠르고 효율적으로 작동할 수 있는 솔루션을 제공합니다. 이는 실시간 처리가 필요하거나 계산 리소스가 제한된 애플리케이션에서 매우 유용할 수 있습니다." -클로드
실습
TinyBert 모델을 사용해 영화 리뷰 데이터를 학습시키고, 문장에 담긴 감정을 postive/nagative로 분류하게 만들기
1. 데이터 세팅
import pandas as pd
#데이터 가져오기
data = pd.read_csv('https://raw.githubusercontent.com/laxmimerit/All-CSV-ML-Data-Files-Download/master/IMDB-Dataset.csv')
#Hugging Face의 datasets 라이브러리에서 Dataset 클래스는
#머신러닝, 특히 자연어 처리(NLP) 작업을 위한 데이터셋을 효율적으로 관리하고 처리하는 도구
from datasets import Dataset
#pandas DataFrame을 Hugging Face의 Dataset 객체로 변환
dataset = Dataset.from_pandas(data)
#데이터셋을 훈련세트와 테스트세트로 쉽게 분할
dataset = dataset.train_test_split(test_size=0.3)
판다스로 csv를 읽어온다. (강사님이 준 링크 그대로 사용했는데, 출처를 찾아보니, Laxmi Kant라는 사람의 깃허브 코드 중에 저 파일이 들어가있었다. 강사님이 이분 데이터로 공부하셨나..?ㅋㅋㅋ) https://github.com/laxmimerit
어쨌든 이렇게 csv를 읽어온 다음에 자연어처리 작업을 효율적으로(?) 하기 위해서 Dataset이라는 객체로 변환한다. 허깅페이스에서 만든 datasets 라는 라이브러리를 사용한다. 대규모 데이터셋을 다룰 때 메모리에 전부 로드하는 것이 아니라 필요할 때에만 접근하는 방식을 써서 메모리를 효율적으로 사용할 수 있다고 한다.
그리고 데이터를 분할해주니 아래와 같이 결과가 나왔다.
sentiment에 postive, negative와 같이 감성 레이블을 1과 0으로 바꾼 레이블을 하나 더 추가한다.
#텍스트 레이블을 숫자로 매핑.
label2id = {'negative':0, 'positive':1}
#x['sentiment'] = 원래 써있던 텍스트 레이블(negative/positive)를 label2id대로 변환
dataset = dataset.map(lambda x: {'label':label2id[x['sentiment']]})
dataset
DatasetDict({
train: Dataset({ features: ['review', 'sentiment', 'label'], num_rows: 35000 })
test: Dataset({ features: ['review', 'sentiment', 'label'], num_rows: 15000 })
})
이렇게 숫자 레이블을 추가하면 모델의 출력을 좀 더 쉽게 해석할 수 있다. 예를 들어 0.5 이상은 긍정, 미만은 부정.
2. 모델 임포트
# 데이터 준비가 끝났습니다. => 모델을 불러와야죠.
from transformers import AutoTokenizer
import torch
device = torch.device('cuda') if torch.cuda.is_available() else torch.device("cpu")
# Hugginc Face에서 tinybert 검색 -> huawei-noah 모델 선택
model = 'huawei-noah/TinyBERT_General_4L_312D'
tokenizer = AutoTokenizer.from_pretrained(model, use_fast=True)
tokenizer
'''
tokenizer 결과
BertTokenizerFast(name_or_path='huawei-noah/TinyBERT_General_4L_312D', vocab_size=30522, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}, clean_up_tokenization_spaces=True), added_tokens_decoder={
0: AddedToken("[PAD]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
100: AddedToken("[UNK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
101: AddedToken("[CLS]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
102: AddedToken("[SEP]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
103: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}
'''
실습에서 사용한 모델은 TinyBERT 모델 중 huawei-noah/TinyBERT_General_4L_312D
허깅페이스 들어가서 tinybert 검색하면 맨 위에 뜬다. 다운받은 횟수도 제일 많고. (화웨이에서 개발했나봄?)
그 다음에 토크나이저를 초기화하는데, AutoTokenizer.from_pretrained(): 이 부분이 선택한 모델에 맞는 토크나이저를 자동으로 로드한다고 한다. (모델의 설정 파일을 읽어 적절한 토크나이저 클래스를 결정, 모델과 함께 저장된 어휘 파일과 토크나이저 설정을 로드)
근데 토크나이저가 뭔데...?
-> 입력 테스트 전처리, 모델에 입력할 수 있게 변환!
텍스트를 모델이 이해할 수 있도록 작은 단위(토큰)으로 나누는 도구. (모델을 다운받으면 캐시 디렉토리에 함께 저장된다.)
예: "I love NLP" → ["I", "love", "NLP"] 또는 ["I", "love", "N", "##LP"]
- 텍스트 분할: 문장을 단어나 하위 단어 단위로 나눕니다.
- 숫자 변환: 각 토큰을 고유한 정수 ID로 변환합니다.
- 특수 토큰 추가: [CLS], [SEP] 등 모델이 요구하는 특수 토큰을 추가합니다.
- 패딩/자르기: 입력을 일정한 길이로 맞춥니다.
각 모델은 고유한 어휘 사전을 가지고 있다. 토큰화 방식에도 단어 단위로 할지, 문장 단위로 할지 등 다양한 방식이 있다. 전처리 규칙도 대소문자를 어떻게 처리할지, 특수문자는 어떻게 처리할지가 모델마다 다르다.
이렇게 토크나이저를 거치면 이렇게 문장이 토큰으로 변환된 것을 볼 수 있다.
tokenizer("Today is monday")
'''
결과
{'input_ids': [101, 2651, 2003, 6928, 102],
'token_type_ids': [0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1]}
'''
그 다음에 데이터셋의 텍스트를 모델에 넣기 좋게 잘라주는 토큰화 함수를 정의하고, 전체 데이터셋에 토큰화를 적용한다.
def tokenize(batch):
# padding=True => 텍스트 데이터를 동일한 길이로 패딩 처리
# truncation=True => 맥스 토큰 갯수를 넘어가는 문장은 잘라내겠습니다.
# max_length=300 => 토큰화된 결과의 최대 길이를 300개 (300개 이상의 토큰은 자른다. 300개 이하는 패딩처리)
temp = tokenizer(batch['review'], padding=True, truncation=True, max_length=300)
return temp
'''
temp 결과에는
input_ids: 토큰의 숫자 ID 시퀀스
attention_mask: 실제 토큰과 패딩 토큰을 구분하는 마스크
token_type_ids: (필요한 경우) 문장 구분을 위한 ID
가 들어있다.
'''
dataset = dataset.map(tokenize, batched=True, batch_size=None)
dataset
'''
DatasetDict({
train: Dataset({
features: ['review', 'sentiment', 'label', 'input_ids', 'token_type_ids', 'attention_mask'],
num_rows: 35000
})
test: Dataset({
features: ['review', 'sentiment', 'label', 'input_ids', 'token_type_ids', 'attention_mask'],
num_rows: 15000
})
})
'''
이제 사전학습된 TinyBERT 모델을 감성 분석에 맞게 로드하고 설정한다.
from transformers import AutoModelForSequenceClassification
id2label = {0:'negative', 1:'positive'}
#모델의 숫자 출력을 텍스트 레이블로 해석할 때 사용. 예측 결과를 해석할 때.
model = AutoModelForSequenceClassification.from_pretrained(
model, # 사전 학습된 모델의 이름. 여기서는 huawei-noah/TinyBERT_General_4L_312D'
num_labels = len(label2id), # 레이블 갯수. 분류할 클래스의 숫자, 여기서는 긍정과 부정 2개
label2id = label2id, # 레이블 문자열 데이터를 숫자로 매핑 딕셔너리
id2label = id2label
)
* 긍정/부정을 나누는 작업 => 시퀀스 분류 작업?
시퀀스라는 말이 시계열이랑 살짝 헷갈려서 알아봄.
시퀀스는 '연속된 데이터'를 의미한다. 그래서 텍스트에 단어나 문자가 연속되어 있는 것도 시퀀스라고 부른다. 이 시퀀스는 데이터의 순차적 구조를 나타낸다. 텍스트가 긍정적인지 부정적인지 분류하는 것뿐만 아니라, 이메일이 스팸인지 아닌지, 뉴스 기사의 주제가 무엇인지 등도 시퀀스 분류 작업이라고 할 수 있다. 인풋은 단어들의 시퀀스(문장, 문단)이고 출력은 전체 시퀀스에 대한 단일한 분류 결과(긍정/부정, 맞다/틀리다 등) 전체 시퀀스에 대해 하나의 레이블 예측하면 시퀀스 분류!
다음은 모델 성능 평가를 위한 함수
import evaluate
import numpy as np
accuracy = evaluate.load('accuracy')
def compute_metrics(eval_pred): # 평가지표
predctions, labels = eval_pred
predctions = np.argmax(predctions, axis=1)
return accuracy.compute(predictions=predctions, references=labels) # 예측값, 실제 정답 값
참고로 evaluate는 허깅페이스에서 나온 평가 메트릭 라이브러리이고, 그중에서 정확도를 로드했다.
이제 학습 시작!
from transformers import Trainer, TrainingArguments
#Trainer: 모델 훈련을 관리하는 클래스
#TrainingArguments: 훈련 설정을 정의하는 클래스
#훈련 인자 설정하기
args = TrainingArguments(
output_dir='train_dir', # 학습 결과를 저장할 디렉토리
overwrite_output_dir=True,
num_train_epochs=3,
learning_rate=2e-5,
per_device_train_batch_size=32, # 각 디바이스당 학습 배치 크기
per_device_eval_batch_size=32, # 각 디바이스당 평가 배치 크기
eval_strategy='epoch' # 매 에포크마다 평가
)
#훈련 초기화
trainer = Trainer(
model=model,
args=args,
train_dataset=dataset['train'],
eval_dataset=dataset['test'],
compute_metrics=compute_metrics, #위에서 정의한 함수. 평가 지표를 계산
tokenizer=tokenizer # 텍스트를 토큰으로 변환하는 도구
)
#훈련 시작
trainer.train()
이부분에서 계속 임포트 에러가 나는데
해결하고 다시 써야겠다..
'공부방 > Upstage AI Lab 4기' 카테고리의 다른 글
판다스 | 결측치 제거팁 (1) | 2024.10.02 |
---|---|
아파트실거래가 예측 코드에 MLflow 덧붙이기 (3) | 2024.10.01 |
[FastAPI] 이미지 분류하는 API 모델 서빙 따라하기 (1) | 2024.09.26 |
[FastAPI] FastAPI 기본 사용법, 프로젝트 세팅하기 (1) | 2024.09.26 |
가상환경 깔고나서 가상환경이 있던 폴더 이름 바꾸지 말자!!! (0) | 2024.09.25 |