1. 개요
- 질문에 답을 하는 과제
- 질문에 대한 답을 지문에서 찾는 것.
예시
-
예시 지문
한글은 홀소리와 닿소리 모두 소리틀을 본떠 만든 음소문자로 한글 맞춤법에서는 닿소리 14개와 홀소리 10개, 모두 24개를 표준으로 삼는다. "나랏말이 중국과 달라" 문제를 느낀 조선의 세종대왕이 한국어는 물론 이웃 나랏말까지 나타내도록 1443년 창제하여 1446년 반포하였다. -
질문: 한글이 창제된 연도는?
-
답변: 1443년
여기서 모델의 입력은 질문과 지문이다.
또한 출력은 “입력의 각 토큰 = [정답의 시작일 확률, 정답의 끝일 확률]” 이다.
답을 생각해보면 지문 속의 “1443”과 “년”을 합친 것이 바로 답.
전체 지문을 토큰화 하면 [”한글”, “은”, … , “하였다”] 일 것이다.
\begin{matrix}
& \text{정답의 시작} & \text{정답의 끝} \\[8pt]
\text{한글} & 0.01 & 0.01 \\[5pt]
\vdots & \vdots & \vdots \\[5pt]
1443 & 0.93 & 0.01 \\[5pt]
\text{년} & 0.01 & 0.90 \\[5pt]
\vdots & \vdots & \vdots
\end{matrix}
데이터와 구조
데이터
데이터는 LG CNS가 공개한 KorQuAD 1.0 데이터를 기반으로 한다.
구조
- 질문과 지문을 각각 [
CLS질문SEP지문SEP] 으로 구분한다. (토큰화)CLS: 문장 시작을 알리는 스페셜 토큰SEP: 질문과 지문을 서로 구분해주는 스페셜 토큰
- 이를 BERT 모델에 입력
- BERT 모델 마지막 레이어의 출력을 뽑는다.
- 추가 모듈을 붙여 각 토큰 = [정답의 시작일 확률, 정답의 끝일 확률] 이 되도록 한다.
- 마지막 레이어의 개별 토큰 벡터(x)에 각각에 드롭아웃을 적용
- 가중치 행렬을 곱해 2차원 행렬(h)로 변환
- 2차원인 이유: 정답의 시작인지, 끝인지의 정보를 나타내기 때문
- 2차원 벡터(h)의 첫 번째 값만 모아 softmax 함수에 넣는다.
- 정답의 시작과 관련한 모델의 출력 y 도출
- 2차원 벡터(h)의 두 번째 값만 모아 softmax 함수에 넣는다.
- 정답의 끝과 관련한 모델의 출력 y 도출
2. 모델 학습하기
설정하기
TPU 관련 패키지를 설치
python
# TPU 사용시 아래 라인 첫 문자(#)를 지우고 수행하세요.
# !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.12-cp37-cp37m-linux_x86_64.whl
python
!pip install ratsnlp
랜덤 시드 고정
학습 재현을 위해 랜덤 시드를 고정합니다.
python
from ratsnlp import nlpbook
nlpbook.set_seed(args)
로거 설정
메세지 출력 등을 위한 logger를 설정합니다.
python
nlpbook.set_logger(args)
말뭉치 다운로드
실습에 사용할 말뭉치(KorQuAD-V1)를 다운로드합니다.
python
nlpbook.download_downstream_dataset(args)
토크나이저 준비
토큰화를 수행하는 토크나이저를 선언합니다
python
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained(
args.pretrained_model_name,
do_lower_case=False,
)
학습데이터 구축
학습데이터를 만듭니다.
python
from ratsnlp.nlpbook.qa import KorQuADV1Corpus, QADataset
from torch.utils.data import DataLoader, SequentialSampler, RandomSampler
corpus = KorQuADV1Corpus()
train_dataset = QADataset(
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,
)
테스트 데이터 구축
학습 중에 평가할 테스트 데이터를 구축합니다.
python
val_dataset = QADataset(
args=args,
corpus=corpus,
tokenizer=tokenizer,
mode="val",
)
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,
)
학습 시작
모델 초기화
프리트레인이 완료된 BERT 모델을 읽고, 질의 응답을 수행할 모델을 초기화합니다.
python
from transformers import BertConfig
pretrained_model_config = BertConfig.from_pretrained(
args.pretrained_model_name,
)
from transformers import BertForQuestionAnswering
model = BertForQuestionAnswering.from_pretrained(
args.pretrained_model_name,
config=pretrained_model_config,
)
학습 준비
Task와 Trainer를 준비합니다.
python
from ratsnlp.nlpbook.qa import QATask
task = QATask(model, args)
python
trainer = nlpbook.get_trainer(args)
학습
python
trainer.fit(
task,
train_dataloaders=train_dataloader,
val_dataloaders=val_dataloader,
)
3. 실전 투입하기
준비하기
패키지 설치
pip 명령어로 의존성 있는 패키지를 설치합니다.
python
!pip install ratsnlp
구글 드라이브 연동하기
모델 체크포인트 등을 저장해 둘 구글 드라이브를 연결
python
from google.colab import drive
drive.mount('/gdrive', force_remount=True)
각종 설정
모델 하이퍼파라메터(hyperparameter)와 저장 위치 등 설정 정보를 선언합니다.
python
from ratsnlp.nlpbook.qa import QADeployArguments
args = QADeployArguments(
pretrained_model_name="beomi/kcbert-base",
downstream_model_dir="/gdrive/My Drive/nlpbook/checkpoint-qa",
max_seq_length=128,
max_query_length=32,
)
모델 로딩
파인튜닝을 마친 모델과 토크나이저를 읽어 들입니다.
python
import torch
from transformers import BertConfig, BertForQuestionAnswering
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,
)
model = BertForQuestionAnswering(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(question, context):
if question and context:
truncated_query = tokenizer.encode(
question,
add_special_tokens=False,
truncation=True,
max_length=args.max_query_length
)
inputs = tokenizer.encode_plus(
text=truncated_query,
text_pair=context,
truncation="only_second",
padding="max_length",
max_length=args.max_seq_length,
return_token_type_ids=True,
)
with torch.no_grad():
outputs = model(**{k: torch.tensor([v]) for k, v in inputs.items()})
start_pred = outputs.start_logits.argmax(dim=-1).item()
end_pred = outputs.end_logits.argmax(dim=-1).item()
pred_text = tokenizer.decode(inputs['input_ids'][start_pred:end_pred+1])
else:
pred_text = ""
return {
'question': question,
'context': context,
'answer': pred_text,
}
웹서비스 준비
ngrok 토큰 준비
python
!mkdir /root/.ngrok2 && echo "authtoken: {#코드 입력#}" > /root/.ngrok2/ngrok.yml
웹서비스 개시
python
from ratsnlp.nlpbook.qa import get_web_service_app
app = get_web_service_app(inference_fn)
app.run()
만약 웹서비스가 안될 경우
python
while 1:
x = input("지문: ")
y = input("질문: ")
if y == "그만":
break
a,b,c = inference_fn(x, y)
print(c)