🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

질의응답 QA 시스템 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 2. · 67 Views

질의응답 QA 시스템 완벽 가이드

BERT 기반의 질의응답 시스템을 처음부터 끝까지 배워봅니다. SQuAD 데이터셋의 구조부터 Start/End Position 예측 원리, 한국어 KorQuAD까지 실무에 바로 적용할 수 있는 QA 시스템 구현법을 다룹니다.


목차

  1. QA_태스크_유형
  2. SQuAD_데이터셋
  3. Start_End_Position_예측
  4. BERT_for_QA_구조
  5. 한국어_QA_KorQuAD
  6. 실전_QA_시스템_구현

1. QA 태스크 유형

김개발 씨는 사내 문서 검색 시스템을 개선하라는 과제를 받았습니다. 단순히 키워드로 문서를 찾는 것이 아니라, "우리 회사 연차 정책이 뭐야?"라고 물으면 정확한 답변을 돌려주는 시스템을 만들어야 합니다.

막막해하던 김개발 씨에게 박시니어 씨가 다가왔습니다. "QA 시스템을 공부해봐요.

질문에 답하는 AI의 세계로 안내해줄게요."

**QA(Question Answering)**는 말 그대로 질문에 답변하는 태스크입니다. 마치 도서관 사서가 질문을 듣고 적절한 책에서 정확한 구절을 찾아주는 것과 같습니다.

QA 시스템은 크게 **추출형(Extractive)**과 **생성형(Generative)**으로 나뉘며, 이를 이해하면 다양한 지능형 서비스를 구축할 수 있습니다.

다음 코드를 살펴봅시다.

# QA 태스크 유형 구분 예시
from enum import Enum

class QAType(Enum):
    EXTRACTIVE = "extractive"    # 문서에서 답 추출
    GENERATIVE = "generative"    # 답을 새로 생성
    OPEN_DOMAIN = "open_domain"  # 외부 지식 활용

# 추출형 QA 예시 구조
def extractive_qa(context: str, question: str) -> dict:
    # 문서(context)에서 답이 위치한 구간을 찾습니다
    answer_start = find_answer_start(context, question)
    answer_end = find_answer_end(context, question)

    return {
        "answer": context[answer_start:answer_end],
        "start": answer_start,
        "end": answer_end
    }

김개발 씨는 입사 2년 차 개발자입니다. 최근 팀에서 사내 지식 검색 시스템을 고도화하라는 미션을 받았습니다.

기존 시스템은 키워드 기반이라 "연차 신청 방법"을 검색하면 관련 문서 목록만 보여줄 뿐, 정확한 답변을 주지 못했습니다. 박시니어 씨가 화이트보드 앞에 섰습니다.

"QA 시스템에는 크게 세 가지 유형이 있어요. 하나씩 알아볼까요?" 첫 번째는 **추출형 QA(Extractive QA)**입니다.

이 방식은 마치 형광펜으로 책에서 중요한 부분을 표시하는 것과 같습니다. 주어진 문서 안에서 질문에 대한 답이 있는 정확한 위치를 찾아 그 부분을 추출합니다.

답이 반드시 원문에 존재해야 한다는 특징이 있습니다. 두 번째는 **생성형 QA(Generative QA)**입니다.

이 방식은 선생님이 학생의 질문에 자신의 말로 설명해주는 것과 비슷합니다. 문서의 내용을 이해한 뒤, 모델이 직접 답변을 생성합니다.

GPT 같은 대형 언어 모델이 이 방식을 사용합니다. 세 번째는 **오픈 도메인 QA(Open-Domain QA)**입니다.

특정 문서가 주어지지 않아도 방대한 지식 베이스에서 답을 찾아냅니다. 마치 박학다식한 전문가에게 아무 질문이나 던지는 것과 같습니다.

김개발 씨가 고개를 끄덕였습니다. "그럼 우리 회사 문서에서 답을 찾으려면 추출형이 적합하겠네요?" 박시니어 씨가 웃으며 답했습니다.

"정확해요. 추출형 QA는 답의 출처가 명확하고, 환각(hallucination) 문제도 적어서 기업용 시스템에 많이 사용됩니다." 추출형 QA의 핵심은 **컨텍스트(Context)**와 **질문(Question)**이라는 두 입력을 받아, 컨텍스트 내에서 답의 시작 위치끝 위치를 예측하는 것입니다.

이것이 바로 SQuAD 같은 데이터셋이 설계된 방식이기도 합니다. 실무에서는 고객 서비스 챗봇, FAQ 자동 응답, 법률 문서 검색 등 다양한 분야에서 추출형 QA가 활용됩니다.

문서가 명확하고 답이 그 안에 있다면 추출형이 최선의 선택입니다. 주의할 점도 있습니다.

추출형 QA는 답이 반드시 문서 안에 있어야 합니다. 만약 "내일 날씨 어때?"처럼 문서에 없는 정보를 물으면 답을 찾지 못합니다.

이런 경우에는 생성형이나 외부 API 연동이 필요합니다. 김개발 씨는 메모장에 정리했습니다.

"추출형은 정확성, 생성형은 유연성. 우리 시스템은 사내 문서 기반이니 추출형으로 시작하자!"

실전 팁

💡 - 답의 출처가 명확해야 한다면 추출형 QA를 선택하세요

  • 생성형 QA는 강력하지만 환각 문제에 주의가 필요합니다

2. SQuAD 데이터셋

추출형 QA를 공부하기로 결심한 김개발 씨는 어디서부터 시작해야 할지 막막했습니다. 박시니어 씨가 말했습니다.

"QA의 기본은 SQuAD야. 이 데이터셋을 이해하면 절반은 끝난 거야." 김개발 씨는 노트북을 열고 SQuAD 데이터셋을 다운로드하기 시작했습니다.

**SQuAD(Stanford Question Answering Dataset)**는 스탠포드 대학에서 만든 읽기 이해 데이터셋입니다. 위키피디아 문서에서 추출한 단락과 그에 대한 질문, 그리고 단락 내 답변 위치로 구성됩니다.

이 데이터셋은 추출형 QA 모델의 표준 벤치마크로 자리 잡았습니다.

다음 코드를 살펴봅시다.

# SQuAD 데이터셋 구조 이해하기
import json

# SQuAD 데이터 예시
squad_example = {
    "context": "인공지능은 1956년 다트머스 회의에서 처음 제안되었다. "
               "존 매카시가 이 용어를 만들었으며, 기계가 인간처럼 "
               "사고할 수 있는지 연구하는 분야이다.",
    "question": "인공지능이라는 용어를 누가 만들었나요?",
    "answers": {
        "text": ["존 매카시"],
        "answer_start": [28]  # 컨텍스트에서 답이 시작하는 위치
    }
}

# answer_end 계산
answer_text = squad_example["answers"]["text"][0]
answer_start = squad_example["answers"]["answer_start"][0]
answer_end = answer_start + len(answer_text)
print(f"답: {answer_text}, 위치: {answer_start}~{answer_end}")

김개발 씨가 SQuAD 데이터셋을 처음 열어보았습니다. JSON 형식의 데이터가 화면에 펼쳐졌습니다.

"이게 뭐지? 구조가 좀 복잡해 보이는데..." 박시니어 씨가 옆에 앉았습니다.

"SQuAD는 세 가지 핵심 요소로 이루어져 있어요. context, question, 그리고 answers입니다." Context는 답을 찾아야 할 문서입니다.

위키피디아에서 추출한 단락으로, 보통 100~300 단어 정도 됩니다. 마치 시험 문제의 지문과 같습니다.

Question은 컨텍스트에 대한 질문입니다. "누가?", "언제?", "왜?" 같은 다양한 유형의 질문이 포함되어 있습니다.

크라우드소싱으로 수집되어 자연스러운 표현이 많습니다. Answers가 가장 중요합니다.

여기에는 답변 텍스트와 함께 answer_start라는 숫자가 들어있습니다. 이 숫자는 컨텍스트에서 답이 시작하는 문자 위치(인덱스)입니다.

김개발 씨가 눈을 크게 떴습니다. "아, 그래서 추출형이라고 하는 거군요!

답이 문서의 몇 번째 글자에서 시작하는지 알려주는 거네요." 박시니어 씨가 고개를 끄덕였습니다. "정확해요.

모델은 이 시작 위치와 끝 위치를 예측하도록 학습됩니다. SQuAD 1.1에는 약 10만 개의 질문-답변 쌍이 있어요." SQuAD 2.0은 한 단계 더 나아갔습니다.

**답이 없는 질문(Unanswerable Questions)**이 추가되었습니다. 실제 상황에서는 모든 질문에 답이 있는 것은 아니니까요.

모델은 "이 질문에는 답할 수 없습니다"라고 판단하는 능력도 갖춰야 합니다. 데이터 전처리 시 주의할 점이 있습니다.

answer_start는 문자 단위 인덱스입니다. 토크나이저를 적용하면 토큰 단위로 변환해야 합니다.

이 과정에서 위치가 어긋나지 않도록 offset mapping을 활용해야 합니다. 김개발 씨는 샘플 데이터를 하나씩 살펴보며 패턴을 익혔습니다.

"context[28:32]를 출력하면 '존 매카시'가 나오겠구나. 이제 감이 오기 시작했어요!"

실전 팁

💡 - SQuAD 2.0은 답이 없는 질문도 포함하므로 더 현실적입니다

  • answer_start는 문자 인덱스이므로 토큰 인덱스로 변환이 필요합니다

3. Start End Position 예측

SQuAD 데이터 구조를 이해한 김개발 씨는 이제 본격적인 의문이 생겼습니다. "모델이 대체 어떻게 답의 위치를 찾아내는 거지?" 박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다.

"자, 이제 QA의 핵심 메커니즘을 알려줄게요."

추출형 QA 모델은 컨텍스트의 각 토큰에 대해 Start 확률End 확률을 계산합니다. 시작 확률이 가장 높은 토큰부터 끝 확률이 가장 높은 토큰까지의 구간이 바로 예측된 답변입니다.

마치 책에서 형광펜의 시작점과 끝점을 찍는 것과 같습니다.

다음 코드를 살펴봅시다.

import torch
import torch.nn.functional as F

# Start/End Position 예측 로직
def predict_answer_span(start_logits, end_logits, context_tokens):
    # 각 토큰의 시작/끝 확률 계산
    start_probs = F.softmax(start_logits, dim=-1)
    end_probs = F.softmax(end_logits, dim=-1)

    # 가장 확률 높은 시작/끝 위치 찾기
    start_idx = torch.argmax(start_probs).item()
    end_idx = torch.argmax(end_probs).item()

    # 유효성 검사: end가 start보다 앞서면 안 됨
    if end_idx < start_idx:
        end_idx = start_idx

    # 답변 토큰 추출
    answer_tokens = context_tokens[start_idx:end_idx + 1]
    confidence = start_probs[start_idx] * end_probs[end_idx]

    return answer_tokens, confidence.item()

박시니어 씨가 화이트보드에 문장을 적었습니다. "존 매카시가 인공지능 용어를 만들었다." "이 문장을 토큰으로 나누면 ['존', '매카시가', '인공지능', '용어를', '만들었다', '.'] 이렇게 됩니다.

모델은 각 토큰마다 두 가지 점수를 계산해요." 첫 번째는 Start Logits입니다. 이 토큰이 답의 시작일 확률을 나타냅니다.

"존 매카시가 누구냐"라는 질문에 대해 '존' 토큰의 시작 점수가 가장 높을 것입니다. 두 번째는 End Logits입니다.

이 토큰이 답의 끝일 확률입니다. 같은 질문에서 '매카시가' 토큰의 끝 점수가 가장 높을 것입니다.

김개발 씨가 물었습니다. "그럼 시작 점수 1등과 끝 점수 1등 사이를 답으로 잡으면 되는 건가요?" 박시니어 씨가 고개를 저었습니다.

"기본적으로는 맞지만, 주의할 점이 있어요. 끝 위치가 시작 위치보다 앞에 있으면 안 됩니다.

답은 연속된 구간이어야 하니까요." 실제 구현에서는 더 정교한 방법을 사용합니다. 모든 가능한 (start, end) 쌍에 대해 점수를 계산하고, 유효한 조합 중 가장 높은 점수를 가진 쌍을 선택합니다.

또한 답의 최대 길이를 제한하여 비합리적으로 긴 답변을 방지합니다. Softmax 함수를 적용하면 점수가 확률로 변환됩니다.

시작 확률과 끝 확률을 곱하면 해당 구간이 정답일 **신뢰도(Confidence)**를 얻을 수 있습니다. 이 신뢰도가 임계값 이하면 "답을 찾을 수 없습니다"라고 응답할 수도 있습니다.

학습 시에는 Cross Entropy Loss를 사용합니다. 정답 시작 위치와 예측 시작 위치 사이의 손실, 정답 끝 위치와 예측 끝 위치 사이의 손실을 합산합니다.

김개발 씨가 정리했습니다. "결국 분류 문제네요!

시작 위치 분류와 끝 위치 분류, 두 개의 분류 문제를 동시에 푸는 거군요." 박시니어 씨가 엄지를 들었습니다. "바로 그거예요.

이 간단하면서도 우아한 방식이 추출형 QA의 핵심입니다."

실전 팁

💡 - end_idx >= start_idx 조건을 반드시 검증하세요

  • 신뢰도 점수를 활용해 답변 불가 상황을 처리할 수 있습니다

4. BERT for QA 구조

Start/End Position 개념을 이해한 김개발 씨는 이제 실제 모델이 궁금해졌습니다. "어떤 모델이 이런 예측을 잘 할 수 있는 거죠?" 박시니어 씨가 웃으며 답했습니다.

"바로 BERT야. QA 태스크에 가장 널리 쓰이는 모델이지."

**BERT(Bidirectional Encoder Representations from Transformers)**는 양방향으로 문맥을 이해하는 사전학습 모델입니다. QA 태스크에서는 질문과 컨텍스트를 [SEP] 토큰으로 연결하여 입력하고, 각 토큰 위치에서 시작/끝 확률을 출력합니다.

사전학습된 언어 이해 능력 덕분에 적은 데이터로도 높은 성능을 보입니다.

다음 코드를 살펴봅시다.

from transformers import BertForQuestionAnswering, BertTokenizer
import torch

# BERT QA 모델 로드
model_name = "bert-base-uncased"
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForQuestionAnswering.from_pretrained(model_name)

# 입력 준비: [CLS] question [SEP] context [SEP]
question = "Who created the term artificial intelligence?"
context = "John McCarthy created the term artificial intelligence in 1956."

inputs = tokenizer(
    question, context,
    return_tensors="pt",
    max_length=384,
    truncation="only_second",  # 컨텍스트만 자르기
    padding="max_length"
)

# 예측 수행
with torch.no_grad():
    outputs = model(**inputs)
    start_logits = outputs.start_logits
    end_logits = outputs.end_logits

박시니어 씨가 BERT 모델의 구조를 설명하기 시작했습니다. "BERT는 마치 독해력이 뛰어난 학생과 같아요.

문장의 앞뒤 맥락을 모두 고려해서 의미를 파악하죠." BERT의 입력은 특별한 형식을 따릅니다. [CLS] 토큰으로 시작하고, 질문을 넣은 뒤 [SEP] 토큰으로 구분합니다.

그 다음 컨텍스트를 넣고 다시 **[SEP]**로 마무리합니다. 김개발 씨가 물었습니다.

"왜 질문을 먼저 넣나요?" 박시니어 씨가 설명했습니다. "BERT는 양방향으로 문맥을 읽어요.

질문을 먼저 보면 컨텍스트를 읽을 때 '아, 이게 질문에서 물어본 거구나'하고 연결할 수 있습니다. Attention 메커니즘 덕분이에요." QA용 BERT는 기본 BERT 위에 두 개의 출력 레이어를 추가합니다.

하나는 시작 위치 예측용, 다른 하나는 끝 위치 예측용입니다. 각 레이어는 BERT의 hidden state를 입력받아 스칼라 점수를 출력합니다.

BertForQuestionAnswering는 Hugging Face에서 제공하는 편리한 클래스입니다. 모델 로드, 토크나이저 준비, 추론까지 몇 줄의 코드로 가능합니다.

실무에서 가장 많이 쓰이는 방식입니다. 토크나이저 사용 시 **truncation="only_second"**가 중요합니다.

입력이 너무 길면 컨텍스트만 잘라내고 질문은 보존합니다. 질문이 잘리면 모델이 무엇을 찾아야 하는지 모르기 때문입니다.

max_length=384는 BERT의 표준 입력 길이입니다. 더 긴 컨텍스트는 여러 청크로 나누어 처리하는 Sliding Window 기법을 사용합니다.

김개발 씨가 코드를 실행해보았습니다. "오, 정말 몇 줄 안 되네요.

start_logits와 end_logits가 나왔어요!" 박시니어 씨가 덧붙였습니다. "이제 argmax로 가장 높은 점수의 위치를 찾고, tokenizer.decode로 토큰을 텍스트로 변환하면 답이 완성됩니다." BERT 기반 QA의 장점은 **전이 학습(Transfer Learning)**입니다.

대량의 텍스트로 사전학습된 언어 이해 능력을 QA 태스크에 활용하므로, 비교적 적은 QA 데이터로도 높은 성능을 달성할 수 있습니다.

실전 팁

💡 - 긴 문서는 Sliding Window로 나누어 처리하세요

  • distilbert를 사용하면 속도를 2배 높일 수 있습니다

5. 한국어 QA KorQuAD

영어 QA 시스템 구현에 자신감이 붙은 김개발 씨는 문득 고민이 생겼습니다. "우리 회사 문서는 한국어인데...

영어 모델을 그대로 쓸 수 있나?" 박시니어 씨가 웃으며 답했습니다. "당연히 한국어용 데이터셋과 모델이 따로 있지.

KorQuAD를 소개해줄게."

**KorQuAD(Korean Question Answering Dataset)**는 한국어 기계독해 데이터셋입니다. SQuAD를 한국어에 맞게 재구성했으며, 한국어 위키피디아 문서를 기반으로 합니다.

KorQuAD 1.0은 약 7만 개, KorQuAD 2.0은 10만 개 이상의 질문-답변 쌍을 포함합니다.

다음 코드를 살펴봅시다.

from transformers import AutoModelForQuestionAnswering, AutoTokenizer
import torch

# 한국어 QA 모델 로드 (KoBERT 기반)
model_name = "monologg/kobert-question-answering"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForQuestionAnswering.from_pretrained(model_name)

# 한국어 QA 예시
question = "인공지능이라는 용어는 누가 만들었나요?"
context = "인공지능이라는 용어는 1956년 존 매카시가 다트머스 회의에서 처음 사용했다."

inputs = tokenizer(question, context, return_tensors="pt")

with torch.no_grad():
    outputs = model(**inputs)

# 답변 추출
start_idx = torch.argmax(outputs.start_logits)
end_idx = torch.argmax(outputs.end_logits)
answer = tokenizer.decode(inputs["input_ids"][0][start_idx:end_idx+1])
print(f"답변: {answer}")

김개발 씨는 KorQuAD 데이터셋을 열어보았습니다. 익숙한 SQuAD 구조에 한글이 들어있었습니다.

"오, 구조는 거의 똑같네요!" 박시니어 씨가 고개를 끄덕였습니다. "맞아요.

KorQuAD는 SQuAD의 한국어 버전이에요. LG CNS와 네이버 등 국내 기업들이 협력해서 만들었죠." KorQuAD 1.0은 SQuAD 1.1과 동일한 형식입니다.

모든 질문에 답이 있는 구조입니다. 위키피디아 문서에서 추출한 약 7만 개의 질문-답변 쌍으로 구성됩니다.

KorQuAD 2.0은 한 단계 진화했습니다. 더 긴 문서, HTML 테이블, 리스트 등 복잡한 형식을 포함합니다.

또한 답이 없는 질문도 추가되어 더 현실적인 시나리오를 다룹니다. 한국어 QA에서 가장 큰 도전은 형태소 분석입니다.

한국어는 교착어이기 때문에 "매카시가", "매카시를", "매카시의"가 모두 다른 토큰으로 인식됩니다. 영어의 "McCarthy"는 하나인데 말이죠.

이 문제를 해결하기 위해 KoBERT, KoELECTRA, KoGPT 등 한국어 특화 모델들이 개발되었습니다. 이 모델들은 한국어 형태소 분석기를 내장하거나, 서브워드 토크나이저를 한국어에 맞게 학습했습니다.

김개발 씨가 물었습니다. "그럼 어떤 모델을 쓰는 게 좋을까요?" 박시니어 씨가 답했습니다.

"성능만 따지면 KoELECTRA가 좋은 선택이에요. 속도와 성능의 균형을 원한다면 DistilKoBERT도 괜찮고요." Hugging Face Hub에는 이미 KorQuAD로 파인튜닝된 모델들이 많습니다.

**"korean question answering"**으로 검색하면 바로 사용할 수 있는 모델들을 찾을 수 있습니다. 주의할 점도 있습니다.

한국어는 띄어쓰기가 일정하지 않은 경우가 많습니다. 사용자 입력을 정규화하는 전처리 단계가 중요합니다.

또한 answer_start 계산 시 유니코드 처리에 신경 써야 합니다. 김개발 씨는 환하게 웃었습니다.

"이제 우리 회사 한국어 문서로 QA 시스템을 만들 수 있겠어요!"

실전 팁

💡 - KoELECTRA는 한국어 QA에서 가장 좋은 성능을 보입니다

  • 띄어쓰기 정규화와 유니코드 처리를 꼼꼼히 하세요

6. 실전 QA 시스템 구현

이론 공부를 마친 김개발 씨는 드디어 실전에 돌입했습니다. "이제 진짜 QA 시스템을 만들어볼 시간이에요!" 박시니어 씨가 옆에서 조언했습니다.

"좋아요, 처음부터 끝까지 함께 만들어봅시다. 실제 서비스에 적용할 수 있는 수준으로요."

실전 QA 시스템은 데이터 전처리, 모델 파인튜닝, 추론 파이프라인의 세 단계로 구성됩니다. 특히 긴 문서 처리를 위한 청킹, 신뢰도 기반 필터링, 그리고 여러 후보 중 최적 답변 선택 로직이 중요합니다.

다음 코드를 살펴봅시다.

from transformers import (
    AutoModelForQuestionAnswering,
    AutoTokenizer,
    TrainingArguments,
    Trainer
)
from datasets import load_dataset
import torch

class QASystem:
    def __init__(self, model_name="deepset/roberta-base-squad2"):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForQuestionAnswering.from_pretrained(model_name)
        self.model.eval()

    def answer(self, question: str, context: str, threshold: float = 0.1):
        # 토크나이징
        inputs = self.tokenizer(
            question, context,
            return_tensors="pt",
            max_length=384,
            truncation=True,
            return_offsets_mapping=True
        )
        offset_mapping = inputs.pop("offset_mapping")[0]

        # 추론
        with torch.no_grad():
            outputs = self.model(**inputs)

        # 확률 계산
        start_probs = torch.softmax(outputs.start_logits, dim=-1)[0]
        end_probs = torch.softmax(outputs.end_logits, dim=-1)[0]

        # 최적 span 찾기
        start_idx = torch.argmax(start_probs).item()
        end_idx = torch.argmax(end_probs).item()

        confidence = (start_probs[start_idx] * end_probs[end_idx]).item()

        # 신뢰도 체크
        if confidence < threshold or end_idx < start_idx:
            return {"answer": "답을 찾을 수 없습니다", "confidence": 0.0}

        # offset으로 원본 텍스트 추출
        start_char = offset_mapping[start_idx][0].item()
        end_char = offset_mapping[end_idx][1].item()

        return {
            "answer": context[start_char:end_char],
            "confidence": confidence
        }

# 사용 예시
qa = QASystem()
result = qa.answer(
    question="파이썬은 누가 만들었나요?",
    context="파이썬은 1991년 귀도 반 로섬이 개발한 프로그래밍 언어이다."
)
print(result)

김개발 씨와 박시니어 씨는 회의실에 모였습니다. 화이트보드에는 QA 시스템 아키텍처가 그려져 있었습니다.

"자, 이제 하나씩 구현해봅시다." 첫 번째 단계는 모델 선택입니다. 영어라면 roberta-base-squad2, 한국어라면 koelectra-base-v3-finetuned-korquad가 좋은 선택입니다.

이미 QA 데이터로 파인튜닝되어 바로 사용할 수 있습니다. 두 번째는 입력 처리입니다.

토크나이저로 질문과 컨텍스트를 처리할 때 offset_mapping을 함께 받아두는 것이 핵심입니다. 이 매핑이 있어야 토큰 인덱스를 원본 텍스트의 문자 위치로 변환할 수 있습니다.

김개발 씨가 물었습니다. "offset_mapping이 뭐예요?" 박시니어 씨가 설명했습니다.

"토큰이 원본 텍스트의 몇 번째 글자부터 몇 번째 글자까지에 해당하는지 알려주는 거예요. 예를 들어 토큰 '귀도'가 원본의 10~12번째 문자라는 정보를 담고 있죠." 세 번째는 신뢰도 필터링입니다.

모든 질문에 답을 강제로 찾으면 엉뚱한 답이 나올 수 있습니다. 시작 확률과 끝 확률을 곱한 confidence score가 임계값(threshold) 이하면 "답을 찾을 수 없습니다"라고 응답하는 것이 좋습니다.

네 번째는 긴 문서 처리입니다. 384 토큰을 넘는 문서는 Sliding Window로 여러 청크로 나눕니다.

각 청크에서 답변 후보를 찾고, 가장 높은 신뢰도를 가진 답을 최종 선택합니다. 박시니어 씨가 중요한 팁을 공유했습니다.

"stride 파라미터를 128 정도로 설정하면 청크가 겹치게 됩니다. 답이 청크 경계에 걸리는 문제를 방지할 수 있어요." 다섯 번째는 후처리입니다.

답변에서 불필요한 공백이나 특수문자를 정리합니다. 한국어의 경우 조사 처리도 고려해야 합니다.

"존 매카시가"에서 "존 매카시"만 추출하고 싶을 수도 있으니까요. 김개발 씨가 코드를 실행했습니다.

"와, 정말 답을 잘 찾네요! 신뢰도가 0.85로 꽤 높아요." 마지막으로 박시니어 씨가 당부했습니다.

"실제 서비스에서는 캐싱도 중요해요. 같은 문서에 대한 임베딩을 미리 계산해두면 응답 속도가 크게 향상됩니다." 김개발 씨는 뿌듯한 표정으로 코드를 저장했습니다.

"이제 사내 QA 챗봇을 만들 수 있겠어요. 정말 감사합니다, 박시니어 씨!"

실전 팁

💡 - offset_mapping은 토큰을 원본 텍스트로 변환할 때 필수입니다

  • 긴 문서는 stride를 겹치게 하여 청킹하세요
  • 실서비스에서는 임베딩 캐싱으로 속도를 최적화하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#QA#BERT#SQuAD#KorQuAD#QA,NLP,BERT

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스