🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
đŸ’ŧ Servicesâ„šī¸ Aboutâœ‰ī¸ ContactView Pricing Plansfrom $10

Question Answering

Generative NLPExtractive and Generative QA SystemsđŸŸĸ Free Lesson

Advertisement

Question Answering

Question answering (QA) systems aim to automatically answer questions posed in natural language. QA spans extractive, generative, and retrieval-based approaches.

QA Types Comparison

TypeInputOutputExample
Extractive(Question, Context)Span from contextSQuAD
Generative(Question, Context)Free-form answerNatural Questions
Open-domainQuestionAnswer + sourcesTriviaQA
Closed-bookQuestionAnswer onlyWebQuestions
Multi-hop(Question, Contexts)Chain of reasoningHotpotQA

Extractive QA

Extractive QA identifies a span of text from the context that answers the question.

from transformers import (
    AutoModelForQuestionAnswering,
    AutoTokenizer,
    pipeline
)

# Using Hugging Face pipeline
qa_pipeline = pipeline(
    "question-answering",
    model="distilbert-base-cased-distilled-squad"
)

context = """
BERT was developed by Google and released in 2018.
It achieved state-of-the-art results on eleven NLP tasks.
BERT stands for Bidirectional Encoder Representations from Transformers.
"""

question = "When was BERT released?"
result = qa_pipeline(question=question, context=context)

print(f"Answer: {result['answer']}")
print(f"Score: {result['score']:.4f}")
print(f"Start: {result['start']}, End: {result['end']}")
# Answer: 2018
# Score: 0.9876

Custom Extractive QA Model

import torch
import torch.nn as nn
from transformers import AutoModel

class ExtractiveQA(nn.Module):
    def __init__(self, model_name="bert-base-uncased"):
        super().__init__()
        self.encoder = AutoModel.from_pretrained(model_name)
        hidden_size = self.encoder.config.hidden_size

        self.qa_outputs = nn.Linear(hidden_size, 2)  # start + end logits

    def forward(self, input_ids, attention_mask, token_type_ids=None):
        outputs = self.encoder(
            input_ids=input_ids,
            attention_mask=attention_mask,
            token_type_ids=token_type_ids
        )

        sequence_output = outputs.last_hidden_state
        logits = self.qa_outputs(sequence_output)  # (batch, seq_len, 2)

        start_logits, end_logits = logits.split(1, dim=-1)
        start_logits = start_logits.squeeze(-1)
        end_logits = end_logits.squeeze(-1)

        return start_logits, end_logits

def train_qa_step(model, batch, optimizer):
    model.train()
    start_logits, end_logits = model(
        batch['input_ids'],
        batch['attention_mask'],
        batch.get('token_type_ids')
    )

    # Loss: negative log-likelihood of start and end positions
    start_loss = nn.functional.cross_entropy(start_logits, batch['start_positions'])
    end_loss = nn.functional.cross_entropy(end_logits, batch['end_positions'])
    loss = (start_loss + end_loss) / 2

    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    return loss.item()

SQuAD Dataset

VersionTrainDevDescription
SQuAD 1.187,59910,570Extractive QA
SQuAD 2.0130,31911,877+ Unanswerable questions
Natural Questions307,3737,844Real Google queries
TriviaQA95,00013,838Trivia questions
HotpotQA90,5647,405Multi-hop reasoning

Generative QA

Generative QA produces free-form answers rather than extracting spans.

from transformers import AutoModelForCausalLM, AutoTokenizer

class GenerativeQA:
    def __init__(self, model_name="google/gemma-2b"):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForCausalLM.from_pretrained(model_name)

    def answer(self, question, context="", max_length=200):
        prompt = f"Question: {question}\nContext: {context}\nAnswer:"
        inputs = self.tokenizer(prompt, return_tensors="pt")

        outputs = self.model.generate(
            **inputs,
            max_length=max_length,
            temperature=0.7,
            top_p=0.9,
            do_sample=True
        )

        return self.tokenizer.decode(outputs[0], skip_special_tokens=True)

qa = GenerativeQA()
answer = qa.answer(
    "What is machine learning?",
    "Machine learning is a subset of AI that enables systems to learn from data."
)

Multi-Hop Question Answering

Multi-hop QA requires reasoning across multiple pieces of evidence.

def multi_hop_qa(question, paragraphs, model):
    # Hop 1: Identify relevant paragraphs
    relevance_scores = [model.score(question, p) for p in paragraphs]
    top_paragraphs = sorted(range(len(paragraphs)),
                           key=lambda i: relevance_scores[i], reverse=True)[:3]

    # Hop 2: Iteratively refine answer
    current_context = ""
    for idx in top_paragraphs:
        current_context += paragraphs[idx] + "\n"
        intermediate_answer = model.extract_answer(question, current_context)

        if is_final_answer(intermediate_answer):
            return intermediate_answer

    return intermediate_answer

def is_final_answer(answer):
    # Heuristic: if answer is a named entity or number, likely final
    return len(answer.split()) <= 5 and not answer.startswith("According")

Evaluation Metrics

MetricDescriptionRange
Exact Match (EM)Percentage of exact matches0-100
F1 ScoreToken-level overlap0-100
ROUGE-LLongest common subsequence0-1
BERTScoreSemantic similarity0-1

Need Expert NLP Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement