vì dụ như 1 file có cấu trúc như sau """ <Đề 1> <...
Created on: June 17, 2025
Created on: June 17, 2025
vì dụ như 1 file có cấu trúc như sau
"""
<Đề 1>
<Đề 2>
<Đề 3>
<Đáp án đề 1>
"""
Cũng có thể là
"""
<Đề 1>
<Đề 2>
<Đề 3>
<Đáp án 5 câu khó đề 3>
<Đáp án 10 câu đầu đề 2>
"""
Tôi có thể truy cập vào nội dung các câu của phần đề hoặc đáp án
nhưng tôi ko biết câu đó là đề hoặc đáp án và nó là đề đáp án đề nào
làm sao phát triển 1 thuật toán ( bạn có thể dùng 1 chút nlp, ml )
để ghép nối các đề thi và đáp án
Dưới đây là một lộ trình “từ dễ → khó” để tự động tách & ghép nối các khối đề và đáp án trong một tệp văn bản lẫn lộn, chỉ dùng Python + một chút NLP/ML. Bạn có thể dừng ở bước 2 (hoàn toàn rule-based) hoặc đi xa hơn tới bước 4 nếu tệp của bạn quá “lệch chuẩn”.
textĐọc file ➜ chuẩn hóa dòng (strip, loại BOM) ➜ tách khối dựa trên dòng trống liên tiếp
pythonblocks = re.split(r'\n\s*\n', raw_text.strip(), flags=re.MULTILINE)
Mỗi block
bây giờ là một đoạn: tiêu đề, nhóm câu hỏi, hay nhóm đáp án.
Loại khối | Regex gợi ý | Giải thích |
---|---|---|
Tiêu đề đề | <\s*Đ[ềe]\s*(\d+)\s*> hoặc Đề\s+(\d+) | Trích id đề |
Đáp án | Đáp\s*án | Bắt đầu khối đáp án |
Câu hỏi | ^\s*Câu\s+\d+ hoặc ^\d+\. … | Số thứ tự câu, dấu chấm |
Thuật toán dòng-chạy:
textcurrent_exam = None exam_dict = {} # {exam_id: {"qs": [], "ans": []}} for block in blocks: if re_title.match(block): current_exam = exam_id exam_dict.setdefault(current_exam, {"qs": [], "ans": []}) elif re_answer_header.match(block): target_exam = extract_exam_id(block) or current_exam # nếu header nêu rõ "đề 3" exam_dict.setdefault(target_exam, {"qs": [], "ans": []}) exam_dict[target_exam]["ans"].append(block) else: # Mặc định: khối câu hỏi if current_exam: exam_dict[current_exam]["qs"].append(block)
Giả sử bạn đọc được tiêu đề “Đáp án 5 câu khó đề 3” ⇒ dùng regex:
pythontarget = re.search(r'đề\s*(\d+)', header, re.I).group(1)
Nếu header KHÔNG nêu rõ đề đích, quy về current_exam
(đề cuối vừa đọc).
TITLE
| QUESTION
| ANSWER
Feature | Cách lấy |
---|---|
Từ khoá | “đáp án”, “đề”, “câu” |
Ký hiệu | tỉ lệ ký tự : ; . ( ) |
Pattern | ^\d+[\).] (đầu dòng số/chấm) |
Context | 1 dòng trước / sau (bi-gram của nhãn) |
Sau khi có mô-hình, chạy qua toàn file để nhận nhãn thay vì regex cứng.
Nếu đáp án ghi “Câu 7: C” mà KHÔNG ghi “đề x”, ta cần đoán:
Ghép theo số câu
7 ≤ n
của đề hiện tại, coi đó là đáp án của đề hiện tại.Ghép bằng ngữ nghĩa
vi-sbert
):pythonfrom sentence_transformers import SentenceTransformer, util model = SentenceTransformer("keepitreal/vietnamese-sbert") sim = util.cos_sim(model.encode(answer), model.encode(question))
max(sim)
> threshold (ví dụ 0.45) rồi gán.Hồi quy nhiều nhãn
pythonclass ExamParser: def __init__(self): self.title_re = re.compile(r'<\s*Đ[ềe]\s*(\d+)\s*>', re.I) self.ans_header = re.compile(r'Đáp\s*án', re.I) def parse(self, raw_text): blocks = re.split(r'\n\s*\n', raw_text.strip()) exams = {} cur = None for blk in blocks: if m := self.title_re.search(blk): cur = m.group(1) exams.setdefault(cur, {"qs": [], "ans": []}) elif self.ans_header.search(blk): target = self._extract_exam_in_header(blk) or cur exams.setdefault(target, {"qs": [], "ans": []}) exams[target]["ans"].append(blk) else: if cur: exams[cur]["qs"].append(blk) return exams def _extract_exam_in_header(self, header): m = re.search(r'đề\s*(\d+)', header, re.I) return m.group(1) if m else None
|answers_correct| / |answers_total|
|answers_correct| / |ground_truth_answers|
.Nếu bạn muốn sample code đầy đủ hơn hoặc demo trên dữ liệu cụ thể, cứ gửi một file ví dụ, mình sẽ hỗ trợ chi tiết!