How can I use Hugging Face AI models (like BERT, GPT, T5, etc.) to automatically create quiz questions or reading comprehension exercises for students inside an education app?
1 Like
For example, if you were to use the T5 model, it would look something like this.
"""
Simple QG → QA-verify → MCQ demo using Hugging Face Transformers.
Models:
- QG (end-to-end): valhalla/t5-base-e2e-qg
https://huggingface.co/valhalla/t5-base-e2e-qg
- QA verifier (extractive, SQuAD2): deepset/roberta-base-squad2
https://huggingface.co/deepset/roberta-base-squad2
- Distractor generator (optional): voidful/bart-distractor-generation-both
https://huggingface.co/voidful/bart-distractor-generation-both
Transformers pipelines docs:
https://huggingface.co/docs/transformers/en/main_classes/pipelines
"""
from typing import List, Dict
from transformers import pipeline
# 1) Load pipelines (cache by default). Adjust device_map if you have a GPU.
qg = pipeline("text2text-generation",
model="valhalla/t5-base-e2e-qg", # e2e QG from paragraph
max_new_tokens=64)
qa = pipeline("question-answering",
model="deepset/roberta-base-squad2") # verifies answerability
# Optional distractor generator. Comment out if you prefer retrieval-based distractors.
try:
dg = pipeline("text2text-generation",
model="voidful/bart-distractor-generation-both",
max_new_tokens=24)
except Exception:
dg = None # falls back to simple distractors later
def _split_e2e_output(text: str) -> List[str]:
"""
Many e2e QG checkpoints return multiple questions separated by tokens/newlines.
This splitter is conservative. Tweak for your chosen model’s formatting.
"""
parts = []
for sep in ["<sep>", "\n", " ? ", "? "]:
if sep in text:
parts = [p.strip() for p in text.split(sep)]
break
if not parts:
parts = [text.strip()]
# Re-append '?' where missing.
return [p if p.endswith("?") else (p + "?") for p in parts if p]
def generate_items(context: str, max_q: int = 5) -> List[Dict]:
"""
Input: passage string.
Output: list of dicts with {question, answer, distractors}.
"""
# Stage 1: propose questions
out = qg(context, do_sample=False)
raw = out[0]["generated_text"] if isinstance(out, list) else out["generated_text"]
questions = _split_e2e_output(raw)[:max_q]
items = []
for q in questions:
# Stage 2: verify with extractive reader
pred = qa(question=q, context=context)
ans = pred.get("answer", "").strip()
conf = float(pred.get("score", 0.0))
# Keep only confident, non-empty answers
if not ans or conf < 0.35: # tune threshold per your QA model
continue
# Stage 3: distractors (seq2seq) or a trivial fallback
distractors = []
if dg is not None:
d_in = f"question: {q} answer: {ans} context: {context}"
try:
d = dg(d_in, do_sample=False)[0]["generated_text"].strip()
if d and d.lower() != ans.lower():
distractors.append(d)
except Exception:
pass
if not distractors:
# naive fallback: take distinct words from context far from the answer
# Replace with retrieval-based candidates in production.
tokens = [t.strip(",.;:()") for t in context.split() if len(t) > 3]
distractors = list({t for t in tokens if t.lower() not in ans.lower()})[:3]
items.append({
"question": q,
"answer": ans,
"distractors": distractors[:3]
})
return items
if __name__ == "__main__":
sample = (
"The Moon is Earth's only natural satellite. It formed about 4.5 billion years ago, "
"likely after a giant impact. The average distance to the Moon is about 384,400 kilometers, "
"and its gravitational influence causes ocean tides on Earth."
)
mcqs = generate_items(sample, max_q=4)
for i, it in enumerate(mcqs, 1):
print(f"\nQ{i}. {it['question']}")
print(f" A: {it['answer']}")
for j, d in enumerate(it['distractors'], 1):
print(f" D{j}: {d}")
"""
Q1. What is Earth's only natural satellite?
A: The Moon
D1: C E n the E , , G G G
Q2. How long ago did the Moon form?
A: 4.5 billion years ago
D1: C E n n n , , G G G
Q3. What is the average distance to the Moon?
A: 384,400 kilometers
D1: The E n n n , , , G
"""