열렬히.뛰기

4장 : 문서분류 모델

머신러닝 & 딥러닝 > 자연어처리 (1) : BERT와 GPT > 4장 : 문서분류 모델

문서가 주어졌을 때, 해당 문서의 범주를 분류하는 모델 설계

  • 뉴스 → 어떤 종류의 뉴스인가? 경제, 정치, 사회 등등
  • 영화 리뷰 → 긍정적인가? 부정적인가?

네이버 영화리뷰 말뭉치 사용.

영화 리뷰 문장 → 긍정/부정 확률 → 결과값 도출

1. 구조파악

모델 구조

  • 토큰 시퀀스 앞뒤로 CLS와 SEP를 붙인다.
  • 이를 BERT 모델에 입력, 후 문장 수준의 벡터(pooler_output) 도출
  • 여기에 다시 작은 추가 모듈을 붙임.
  • 최종적으로 [긍정일 확률, 부정일 확률] 구조의 벡터가 도출

<추가 모듈의 구조>

  1. 드롭아웃 적용 : 과적합 방지를 위한 수단
  2. 가중치 행렬 곱
  3. 소프트맥스 함수 취하기

모델 출력이 정답 레이블과 같아질 때까지 이 과정을 반복.

이를 파인튜닝 이라고 함.

2. 모델 만들기 1 : 모델의 학습

중요 : TPU 설정!

TPU를 쓰고 싶다면 사전에 무조건 여기 들어가서 설정 확인하기

colab.research.google.com/github/pytorch/xla/blob/master/contrib/colab/getting-started.ipynb

관련 코드 모조리 입력하고 시작! 안 그러면 TPU를 구동할 수 없음.

코랩 노트북 초기화

python
# 의존성 패키지 설치
!pip install ratsnlp

# 구글 드라이브와 연결
from google.colab import drive
drive.mount('/gdrive', force_remount=True)

각종 설정하기

python
# 모델 환경설정
import torch
from ratsnlp.nlpbook.classification import ClassificationTrainArguments
args = ClassificationTrainArguments(
    pretrained_model_name="beomi/kcbert-base",
    downstream_corpus_name="nsmc",
    downstream_model_dir="/gdrive/My Drive/nlpbook/checkpoint-doccls",
    batch_size=32 if torch.cuda.is_available() else 4,
    learning_rate=5e-5,
    max_seq_length=128,
    epochs=3,
    tpu_cores=0 if torch.cuda.is_available() else 8,
    seed=7,
)

파라미터 설명

  • batch_size = gpu / tpu에 따른 배치 사이즈.
  • learning rate = 러닝 레이트 (보폭). 1회 스텝에서 얼마나 업데이트할지에 대한 크기
  • epoch = 에포크
    • 순전파와 역전파 1번을 왔다갔다 한 과정을 1 에포크라고 함.
  • seed = 랜덤 시드
python
# 랜덤 시드 고정 : 학습 재현을 위해 랜덤 시드를 고정합니다.
from ratsnlp import nlpbook
nlpbook.set_seed(args)
python
# 로거 설정 : 메세지 출력 등을 위한 logger를 설정합니다.
nlpbook.set_logger(args)

네이버 말뭉치 다운로드

python
# 네이버 말뭉치 다운로드
from Korpora import Korpora
Korpora.fetch(
    corpus_name=args.downstream_corpus_name,
    root_dir=args.downstream_corpus_root_dir,
    force_download=True,
)

토크나이저 준비

python
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained(
args.pretrained_model_name,
do_lower_case=False,
)

데이터전처리 준비

python
# 학습데이터 구축
from torch.utils.data import DataLoader, SequentialSampler, RandomSampler
from ratsnlp.nlpbook.classification import NsmcCorpus, ClassificationDataset
corpus = NsmcCorpus()
train_dataset = ClassificationDataset(
    args=args,
    corpus=corpus,
    tokenizer=tokenizer,
    mode="train",
)
train_dataloader = DataLoader(
    train_dataset,
    batch_size=args.batch_size,
    sampler=RandomSampler(train_dataset, replacement=False),
    collate_fn=nlpbook.data_collator,
    drop_last=False,
    num_workers=args.cpu_workers,
)

NsmcCorpus : CSV 파일 형식의 NSMC 데이터를 문장과 레이블로 읽어들임

classificationDataset : 문장 & 레이블을 모델이 학습가능한 형태 4가지로 변경

  • input_ids : 인덱스로 변환한 토큰 시퀀스
  • attention_mask : 해당 토큰이 패딩 토큰인지 아닌지 나타냄. 패딩 토큰이란? 클릭
  • token_type_ids : 세그먼트 정보 (첫번째 문서인지 아닌지 나타내 준다.)
  • label : 정수로 바뀐 레이블 정보
python
# 테스트 데이터 구축 : 학습 중에 평가할 테스트 데이터를 구축합니다.
val_dataset = ClassificationDataset(
    args=args,
    corpus=corpus,
    tokenizer=tokenizer,
    mode="test",
)
val_dataloader = DataLoader(
    val_dataset,
    batch_size=args.batch_size,
    sampler=SequentialSampler(val_dataset),
    collate_fn=nlpbook.data_collator,
    drop_last=False,
    num_workers=args.cpu_workers,
)

DataLoader : 배치 크기만큼 인스턴스를 뽑아 배치 형태로 가공

  • sampler : 샘플링 방식을 정의.
    • SequentialSampler : 항상 같은 순서로 샘플 추출
  • collate_fn : 인스턴스를 배치로 만드는 함수

모델 초기화

python
from transformers import BertConfig, BertForSequenceClassification
pretrained_model_config = BertConfig.from_pretrained(
    args.pretrained_model_name,
    num_labels=corpus.num_labels,
)
python
model = BertForSequenceClassification.from_pretrained(
        args.pretrained_model_name,
        config=pretrained_model_config,
)

학습 준비

Task와 Trainer를 준비.

python
from ratsnlp.nlpbook.classification import ClassificationTask
task = ClassificationTask(model, args)

ClassificationTask에는 아담 옵티마이저 사용. (아담 옵티마이저란? 클릭)

러닝 레이트 스케줄러에는 ExponentialLR 사용. ExponentialLR ??

python
# 트레이너: 귀찮은 설정을 한방에 해결.
trainer = nlpbook.get_trainer(args)

학습

준비한 데이터와 모델로 학습을 시작합니다. 학습 결과물(체크포인트)은 미리 연동해둔 구글 드라이브의 준비된 위치 (/gdrive/My Drive/nlpbook/checkpoint-doccls)에 저장.

python
trainer.fit(
    task,
    train_dataloaders=train_dataloader,
    val_dataloaders=val_dataloader,
)

3. 모델 만들기 2 : 모델 투입

코랩 노트북 초기화

  • bit.ly/3leMWYr 에 접속 (모든 코드가 다 있음)
  • 런타임 유형 > None으로.

환경 설정하기

python
# 의존성 패키지 설치
!pip install ratsnlp

# 구글 드라이브 연결
from google.colab import drive
drive.amount('\gdrive', force_remount=True)

# 인퍼런스 설정
from ratsnlp.nlpbook.classification import ClassificationDeployArguments
args = ClassificationDeployArguments(
		pretrained_model_name = "beomi/kcbert-base",
		downstream_model_dir = "/gdrive/My Drive/nlpbook/checkpoint-doccls",
		max_seq_length = 128,
)

각종 설정

모델 하이퍼파라메터(hyperparameter)와 저장 위치 등 설정 정보를 선언합니다.

python
from ratsnlp.nlpbook.classification import ClassificationDeployArguments
args = ClassificationDeployArguments(
    pretrained_model_name="beomi/kcbert-base",
    downstream_model_dir="/gdrive/My Drive/nlpbook/checkpoint-doccls",
    max_seq_length=128,
)

모델 로딩

파인튜닝을 마친 모델과 토크나이저를 읽어 들입니다.

python
import torch
from transformers import BertConfig, BertForSequenceClassification
fine_tuned_model_ckpt = torch.load(
    args.downstream_model_checkpoint_fpath,
    map_location=torch.device("cpu")
)
pretrained_model_config = BertConfig.from_pretrained(
    args.pretrained_model_name,
    num_labels=fine_tuned_model_ckpt['state_dict']['model.classifier.bias'].shape.numel(),
)
model = BertForSequenceClassification(pretrained_model_config)
model.load_state_dict({k.replace("model.", ""): v for k, v in fine_tuned_model_ckpt['state_dict'].items()})
model.eval()
python
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained(
    args.pretrained_model_name,
    do_lower_case=False,
)

인퍼런스 함수 선언

인퍼런스 함수를 선언합니다.

python
def inference_fn(sentence):
    inputs = tokenizer(
        [sentence],
        max_length=args.max_seq_length,
        padding="max_length",
        truncation=True,
    )
    with torch.no_grad():
        outputs = model(**{k: torch.tensor(v) for k, v in inputs.items()})
        prob = outputs.logits.softmax(dim=1)
        positive_prob = round(prob[0][1].item(), 4)
        negative_prob = round(prob[0][0].item(), 4)
        pred = "긍정 (positive)" if torch.argmax(prob) == 1 else "부정 (negative)"
    return {
        'sentence': sentence,
        'prediction': pred,
        'positive_data': f"긍정 {positive_prob}",
        'negative_data': f"부정 {negative_prob}",
        'positive_width': f"{positive_prob * 100}%",
        'negative_width': f"{negative_prob * 100}%",
    }

문장의 토큰화를 수행한 뒤, input_ids와 attention_mask, token_type_ids를 만듬.

이들을 텐서로 바꾼 뒤, 모델에 입력.

모델 출력 값은 로짓 형태. 여기에 softmax() 함수로 모델 출력을 [부정확률, 긍정확률]로 바꿈.

웹서비스 만들기 준비

ngrok은 코랩 로컬에서 실행 중인 웹서비스를 안전하게 외부에서 접근 가능하도록 해주는 도구.

ngrok을 실행하려면 회원가입로그인을 한 뒤 이곳에 접속해 인증 토큰(authtoken)을 확인해야 합니다. 예를 들어 확인된 authtokentest111이라면 다음과 같이 실행한다.

python
!mkdir /root/.ngrok2 && echo "authtoken: test111" > /root/.ngrok2/ngrok.yml

웹 서비스 배포

아래처럼 실행해 인퍼런스 함수를 웹서비스로 만든다.

python
from ratsnlp.nlpbook.classification import get_web_service_app
app = get_web_service_app(inference_fn)
app.run()

만약 웹 서비스가 안된다면?

  • 직접 문장을 입력한 뒤 함수에 밀어넣자.
  • while문을 쓰면 실시간 서비스 마냥 해볼 수 있다.
python
while 1:
	s = input("문장을 입력하세요: ")
	inference_fn(s)