final

0
3 месяца назад
5 месяцев назад
3 месяца назад
3 месяца назад
3 месяца назад
3 месяца назад
3 месяца назад
5 месяцев назад
3 месяца назад
README.md

Assessment Question Difficulty Classifier 🧠📊

Описание ✨

Проект для автоматической классификации сложности вопросов тестов/экзаменов с использованием машинного обучения. Модель на основе текста вопроса, времени ответа и процента правильных ответов определяет уровень сложности:

easy
(легкий),
medium
(средний) или
hard
(сложный).

🎯 Цель проекта

  • Классифицировать вопросы на три уровня сложности:
    easy
    ,
    medium
    ,
    hard
    .
  • Сравнить эффективность разных моделей ML:
    • Random Forest 🌳
    • Support Vector Machine (SVM) ⚡
    • Naive Bayes 📦
  • Визуализировать метрики моделей и важность признаков.
  • Сохранить метрики и модели для дальнейшего использования.

Зачем это нужно? 🤔

  • Автоматизация оценки сложности вопросов

  • Оптимизация создания сбалансированных тестов

  • Анализ эффективности образовательных материалов

  • Сокращение времени ручной оценки на 80%

Установка ⚙️

Предварительные требования

  • Python 3.8+ 🐍

  • pip (менеджер пакетов)

Настройка окружения

bash 1. Клонируйте репозиторий git clone <repo-url> cd question-difficulty-classifier 2. Создайте виртуальное окружение python -m venv venv 3. Активируйте окружение На Windows: venv\Scripts\activate На macOS/Linux: source venv/bin/activate 4. Установите зависимости pip install -r requirements.txt

Использование 🚀

Быстрый старт

  1. Подготовьте CSV файл с вопросами:
csv question_text,avg_time_sec,correct_rate,difficulty "What is 2+2?",15,0.95,easy "Explain quantum entanglement",120,0.35,hard "Define photosynthesis",45,0.70,medium
  1. Запустите обучение модели:
bash python src/main.py

При запуске скрипт:

  • Проверяет наличие файла questions.csv ✅

  • Извлекает признаки из текста вопроса 📝

  • Делит данные на train/test наборы 🔀

  • Обучает три модели: Random Forest, SVM, Naive Bayes 🤖

  • Выводит метрики и сохраняет их в reports/metrics.json 📊

  • Строит графики и сохраняет их в папку images:

    • Confusion Matrix

    • Class Distribution

    • Feature Importance (для Random Forest)

    • Сравнение моделей по точности

  • Все модели сохраняются в папку models/ с уникальными именами:

    • RandomForest_model_20251229_153045.pkl

    • SVM_model_20251229_153045.pkl

    • NaiveBayes_model_20251229_153045.pkl

Пример работы кода

python src/main.py

🖥️ Вывод в консоли

Данные загружены успешно. Размер: (43, 4) Колонки: ['question_text', 'avg_time_sec', 'correct_rate', 'difficulty'] === Training RandomForest === RandomForest CV Accuracy: 0.98 ± 0.02 RandomForest model saved as RandomForest_model_20240512_153210.pkl === Training SVM === SVM CV Accuracy: 0.96 ± 0.03 SVM model saved as SVM_model_20240512_153215.pkl === Training NaiveBayes === NaiveBayes CV Accuracy: 0.89 ± 0.04 NaiveBayes model saved as NaiveBayes_model_20240512_153218.pkl All metrics saved to metrics.json

Пример результатов

reports/metrics.json:

{ "RandomForest": { "confusion_matrix": [ [ 2, 0, 0 ], [ 0, 3, 0 ], [ 1, 0, 3 ] ], "precision_macro": 0.8888888888888888, "recall_macro": 0.9166666666666666, "classification_report": " precision recall f1-score support\n\n 0 0.67 1.00 0.80 2\n 1 1.00 1.00 1.00 3\n 2 1.00 0.75 0.86 4\n\n accuracy 0.89 9\n macro avg 0.89 0.92 0.89 9\nweighted avg 0.93 0.89 0.89 9\n", "cv_accuracy_mean": 1.0, "cv_accuracy_std": 0.0 }, "SVM": { "confusion_matrix": [ [ 2, 0, 0 ], [ 0, 3, 0 ], [ 0, 0, 4 ] ], "precision_macro": 1.0, "recall_macro": 1.0, "classification_report": " precision recall f1-score support\n\n 0 1.00 1.00 1.00 2\n 1 1.00 1.00 1.00 3\n 2 1.00 1.00 1.00 4\n\n accuracy 1.00 9\n macro avg 1.00 1.00 1.00 9\nweighted avg 1.00 1.00 1.00 9\n", "cv_accuracy_mean": 1.0, "cv_accuracy_std": 0.0 }, "NaiveBayes": { "confusion_matrix": [ [ 2, 0, 0 ], [ 0, 3, 0 ], [ 1, 0, 3 ] ], "precision_macro": 0.8888888888888888, "recall_macro": 0.9166666666666666, "classification_report": " precision recall f1-score support\n\n 0 0.67 1.00 0.80 2\n 1 1.00 1.00 1.00 3\n 2 1.00 0.75 0.86 4\n\n accuracy 0.89 9\n macro avg 0.89 0.92 0.89 9\nweighted avg 0.93 0.89 0.89 9\n", "cv_accuracy_mean": 0.9777777777777779, "cv_accuracy_std": 0.04444444444444447 } }

models/NaiveBayes_model_20251229_005744.pkl:

Pickled Data 0: \x80 PROTO 4 2: \x95 FRAME 1178 11: \x8c SHORT_BINUNICODE 'sklearn.naive_bayes' 32: \x94 MEMOIZE (as 0) 33: \x8c SHORT_BINUNICODE 'GaussianNB' 45: \x94 MEMOIZE (as 1) 46: \x93 STACK_GLOBAL 47: \x94 MEMOIZE (as 2) 48: ) EMPTY_TUPLE 49: \x81 NEWOBJ 50: \x94 MEMOIZE (as 3) 51: } EMPTY_DICT 52: \x94 MEMOIZE (as 4) 53: ( MARK 54: \x8c SHORT_BINUNICODE 'priors' 62: \x94 MEMOIZE (as 5) 63: N NONE 64: \x8c SHORT_BINUNICODE 'var_smoothing' 79: \x94 MEMOIZE (as 6) 80: G BINFLOAT 1e-09 89: \x8c SHORT_BINUNICODE 'classes_' 99: \x94 MEMOIZE (as 7) 100: \x8c SHORT_BINUNICODE 'numpy._core.multiarray' 124: \x94 MEMOIZE (as 8) 125: \x8c SHORT_BINUNICODE '_reconstruct' 139: \x94 MEMOIZE (as 9) 140: \x93 STACK_GLOBAL 141: \x94 MEMOIZE (as 10) 142: \x8c SHORT_BINUNICODE 'numpy' 149: \x94 MEMOIZE (as 11) 150: \x8c SHORT_BINUNICODE 'ndarray' 159: \x94 MEMOIZE (as 12) 160: \x93 STACK_GLOBAL 161: \x94 MEMOIZE (as 13) 162: K BININT1 0 164: \x85 TUPLE1 165: \x94 MEMOIZE (as 14) 166: C SHORT_BINBYTES b'b' 169: \x94 MEMOIZE (as 15) 170: \x87 TUPLE3 171: \x94 MEMOIZE (as 16) 172: R REDUCE 173: \x94 MEMOIZE (as 17) 174: ( MARK 175: K BININT1 1 177: K BININT1 3 179: \x85 TUPLE1 180: \x94 MEMOIZE (as 18) 181: h BINGET 11 183: \x8c SHORT_BINUNICODE 'dtype' 190: \x94 MEMOIZE (as 19) 191: \x93 STACK_GLOBAL 192: \x94 MEMOIZE (as 20) 193: \x8c SHORT_BINUNICODE 'i8' 197: \x94 MEMOIZE (as 21) 198: \x89 NEWFALSE 199: \x88 NEWTRUE 200: \x87 TUPLE3 201: \x94 MEMOIZE (as 22) 202: R REDUCE 203: \x94 MEMOIZE (as 23) 204: ( MARK 205: K BININT1 3 207: \x8c SHORT_BINUNICODE '<' 210: \x94 MEMOIZE (as 24) 211: N NONE 212: N NONE 213: N NONE 214: J BININT -1 219: J BININT -1 224: K BININT1 0 226: t TUPLE (MARK at 204) 227: \x94 MEMOIZE (as 25) 228: b BUILD 229: \x89 NEWFALSE 230: C SHORT_BINBYTES b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00' 256: \x94 MEMOIZE (as 26) 257: t TUPLE (MARK at 174) 258: \x94 MEMOIZE (as 27) 259: b BUILD 260: \x8c SHORT_BINUNICODE 'feature_names_in_' 279: \x94 MEMOIZE (as 28) 280: h BINGET 10 282: h BINGET 13 284: K BININT1 0 286: \x85 TUPLE1 287: \x94 MEMOIZE (as 29) 288: h BINGET 15 290: \x87 TUPLE3 291: \x94 MEMOIZE (as 30) 292: R REDUCE 293: \x94 MEMOIZE (as 31) 294: ( MARK 295: K BININT1 1 297: K BININT1 8 299: \x85 TUPLE1 300: \x94 MEMOIZE (as 32) 301: h BINGET 20 303: \x8c SHORT_BINUNICODE 'O8' 307: \x94 MEMOIZE (as 33) 308: \x89 NEWFALSE 309: \x88 NEWTRUE 310: \x87 TUPLE3 311: \x94 MEMOIZE (as 34) 312: R REDUCE 313: \x94 MEMOIZE (as 35) 314: ( MARK 315: K BININT1 3 317: \x8c SHORT_BINUNICODE '|' 320: \x94 MEMOIZE (as 36) 321: N NONE 322: N NONE 323: N NONE 324: J BININT -1 329: J BININT -1 334: K BININT1 63 336: t TUPLE (MARK at 314) 337: \x94 MEMOIZE (as 37) 338: b BUILD 339: \x89 NEWFALSE 340: ] EMPTY_LIST 341: \x94 MEMOIZE (as 38) 342: ( MARK 343: \x8c SHORT_BINUNICODE 'text_length' 356: \x94 MEMOIZE (as 39) 357: \x8c SHORT_BINUNICODE 'word_count' 369: \x94 MEMOIZE (as 40) 370: \x8c SHORT_BINUNICODE 'flesch_kincaid' 386: \x94 MEMOIZE (as 41) 387: \x8c SHORT_BINUNICODE 'punct_count' 400: \x94 MEMOIZE (as 42) 401: \x8c SHORT_BINUNICODE 'digit_count' 414: \x94 MEMOIZE (as 43) 415: \x8c SHORT_BINUNICODE 'upper_count' 428: \x94 MEMOIZE (as 44) 429: \x8c SHORT_BINUNICODE 'avg_time_sec' 443: \x94 MEMOIZE (as 45) 444: \x8c SHORT_BINUNICODE 'correct_rate' 458: \x94 MEMOIZE (as 46) 459: e APPENDS (MARK at 342) 460: t TUPLE (MARK at 294) 461: \x94 MEMOIZE (as 47) 462: b BUILD 463: \x8c SHORT_BINUNICODE 'n_features_in_' 479: \x94 MEMOIZE (as 48) 480: K BININT1 8 482: \x8c SHORT_BINUNICODE 'epsilon_' 492: \x94 MEMOIZE (as 49) 493: h BINGET 8 495: \x8c SHORT_BINUNICODE 'scalar' 503: \x94 MEMOIZE (as 50) 504: \x93 STACK_GLOBAL 505: \x94 MEMOIZE (as 51) 506: h BINGET 20 508: \x8c SHORT_BINUNICODE 'f8' 512: \x94 MEMOIZE (as 52) 513: \x89 NEWFALSE 514: \x88 NEWTRUE 515: \x87 TUPLE3 516: \x94 MEMOIZE (as 53) 517: R REDUCE 518: \x94 MEMOIZE (as 54) 519: ( MARK 520: K BININT1 3 522: h BINGET 24 524: N NONE 525: N NONE 526: N NONE 527: J BININT -1 532: J BININT -1 537: K BININT1 0 539: t TUPLE (MARK at 519) 540: \x94 MEMOIZE (as 55) 541: b BUILD 542: C SHORT_BINBYTES b'\x06|\xbbMp\xd7\xd2>' 552: \x94 MEMOIZE (as 56) 553: \x86 TUPLE2 554: \x94 MEMOIZE (as 57) 555: R REDUCE 556: \x94 MEMOIZE (as 58) 557: \x8c SHORT_BINUNICODE 'theta_' 565: \x94 MEMOIZE (as 59) 566: h BINGET 10 568: h BINGET 13 570: K BININT1 0 572: \x85 TUPLE1 573: \x94 MEMOIZE (as 60) 574: h BINGET 15 576: \x87 TUPLE3 577: \x94 MEMOIZE (as 61) 578: R REDUCE 579: \x94 MEMOIZE (as 62) 580: ( MARK 581: K BININT1 1 583: K BININT1 3 585: K BININT1 8 587: \x86 TUPLE2 588: \x94 MEMOIZE (as 63) 589: h BINGET 54 591: \x89 NEWFALSE 592: C SHORT_BINBYTES b"\x00\x00\x00\x00\x000A@\x00\x00\x00\x00\x00\x80\x1b@\xa8\x9ePpk\xbd\x08@\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xd0?\x00\x00\x00\x00\x00\x00\x02@\x00\x00\x00\x00\x00\x80&@\xeaQ\xb8\x1e\x85\xeb\xec?\x8a\x9d\xd8\x89\x9d\xd8D@;\xb1\x13;\xb1\x13\x17@\xf4\xb4\xe8$\xe8\xdb'@\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x8a\x9d\xd8\x89\x9d\xd8\xf9?'vb'v\x02e@\xd7\xa3p=\n\xd7\xd3?O\xec\xc4N\xec\xc4A@\xec\xc4N\xec\xc4N\x18@\xcfV\x06\xff\x899\x19@\x00\x00\x00\x00\x00\x00\xf0?\x14;\xb1\x13;\xb1\xb3?O\xec\xc4N\xec\xc4\xfe?O\xec\xc4N\xec\xc4J@\xdc\xc8\x8d\xdc\xc8\x8d\xe4?" 786: \x94 MEMOIZE (as 64) 787: t TUPLE (MARK at 580) 788: \x94 MEMOIZE (as 65) 789: b BUILD 790: \x8c SHORT_BINUNICODE 'var_' 796: \x94 MEMOIZE (as 66) 797: h BINGET 10 799: h BINGET 13 801: K BININT1 0 803: \x85 TUPLE1 804: \x94 MEMOIZE (as 67) 805: h BINGET 15 807: \x87 TUPLE3 808: \x94 MEMOIZE (as 68) 809: R REDUCE 810: \x94 MEMOIZE (as 69) 811: ( MARK 812: K BININT1 1 814: K BININT1 3 816: K BININT1 8 818: \x86 TUPLE2 819: \x94 MEMOIZE (as 70) 820: h BINGET 54 822: \x89 NEWFALSE 823: C SHORT_BINBYTES b'Np\xd7\x12\x00/[@o\x13\xdc\xb5\x04\xc0\xfd?JL\xc5\xf4\xd7\xe5"@\x06|\xbbMp\xd7\xd2>\xbbMp\xd7\x12\x00\xdc?o\x13\xdc\xb5\x04\x00\xfb?n\x82\xbb\x96\x00\xe0&@}UV\x8buqY?\xed\xaf\xbd;\xdd\xbe\\@\xd1Gm\'T\xba\r@/\xdf\xa5\xee4\x10@@\x06|\xbbMp\xd7\xd2>\x06|\xbbMp\xd7\xd2>\x90\xed\xcc\xafh\x8e\xf2?\x11uYy\xfbJ{@\x0e\xdd\xb6\xddq\nr?\xec\xe1\x04%\xd4TT@;\xfe9\xa8e\xc0\xf9?\x0e\xf2~{\xd7\xa0$@\x06|\xbbMp\xd7\xd2>\x17F\xc7\xf9\xbc-\xb2?Eu\x7f\xfb\xa8B\x04@M\t{\x87\xfbJT@\x97^\xc5\xce\xee\xd8\\?' 1017: \x94 MEMOIZE (as 71) 1018: t TUPLE (MARK at 811) 1019: \x94 MEMOIZE (as 72) 1020: b BUILD 1021: \x8c SHORT_BINUNICODE 'class_count_' 1035: \x94 MEMOIZE (as 73) 1036: h BINGET 10 1038: h BINGET 13 1040: K BININT1 0 1042: \x85 TUPLE1 1043: \x94 MEMOIZE (as 74) 1044: h BINGET 15 1046: \x87 TUPLE3 1047: \x94 MEMOIZE (as 75) 1048: R REDUCE 1049: \x94 MEMOIZE (as 76) 1050: ( MARK 1051: K BININT1 1 1053: K BININT1 3 1055: \x85 TUPLE1 1056: \x94 MEMOIZE (as 77) 1057: h BINGET 54 1059: \x89 NEWFALSE 1060: C SHORT_BINBYTES b'\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00*@\x00\x00\x00\x00\x00\x00*@' 1086: \x94 MEMOIZE (as 78) 1087: t TUPLE (MARK at 1050) 1088: \x94 MEMOIZE (as 79) 1089: b BUILD 1090: \x8c SHORT_BINUNICODE 'class_prior_' 1104: \x94 MEMOIZE (as 80) 1105: h BINGET 10 1107: h BINGET 13 1109: K BININT1 0 1111: \x85 TUPLE1 1112: \x94 MEMOIZE (as 81) 1113: h BINGET 15 1115: \x87 TUPLE3 1116: \x94 MEMOIZE (as 82) 1117: R REDUCE 1118: \x94 MEMOIZE (as 83) 1119: ( MARK 1120: K BININT1 1 1122: K BININT1 3 1124: \x85 TUPLE1 1125: \x94 MEMOIZE (as 84) 1126: h BINGET 54 1128: \x89 NEWFALSE 1129: C SHORT_BINBYTES b'\x1e\x1e\x1e\x1e\x1e\x1e\xce?xxxxxx\xd8?xxxxxx\xd8?' 1155: \x94 MEMOIZE (as 85) 1156: t TUPLE (MARK at 1119) 1157: \x94 MEMOIZE (as 86) 1158: b BUILD 1159: \x8c SHORT_BINUNICODE '_sklearn_version' 1177: \x94 MEMOIZE (as 87) 1178: \x8c SHORT_BINUNICODE '1.8.0' 1185: \x94 MEMOIZE (as 88) 1186: u SETITEMS (MARK at 53) 1187: b BUILD 1188: . STOP highest protocol among opcodes = 4 Load Full Readable Pickle

Структура проекта 📁

final/ ├─ .github/ │ └─ workflows/ │ ├─ tests.yml ├─ .gitverse/ │ └─ workflows/ │ └─ gitverse-ci.yaml ├─ data/ │ └─ questions.csv ├─ docs/ │ ├─ ANSWER.md │ ├─ evaluation-matrix.md │ ├─ llm-checker-guide.md │ ├─ project-ideas.md │ ├─ quick-start.md │ ├─ rubric-15-points.md │ └─ student-guide.md ├─ images/ │ ├─ class_distribution_NaiveBayes.png │ ├─ class_distribution_RandomForest.png │ ├─ class_distribution_SVM.png │ ├─ confusion_matrix_NaiveBayes.png │ ├─ confusion_matrix_RandomForest.png │ ├─ confusion_matrix_SVM.png │ ├─ feature_importance.png │ └─ model_comparison.png ├─ models/ │ ├─ NaiveBayes_model_20251229_005744.pkl │ ├─ RandomForest_model_20251229_005735.pkl │ └─ SVM_model_20251229_005740.pkl ├─ reports/ │ └─ metrics.json ├─ src/ │ ├─ init.py │ └─ main.py ├─ tests/ │ ├─ init.py │ └─ test_my.py ├─ .gitignore ├─ README-package.md ├─ README.md └─ requirements.txt

Требования 📦

  • Python 3.8+ 🐍

  • NumPy >= 1.21.0 - численные вычисления

  • Pandas >= 1.3.0 - обработка данных

  • Scikit-learn >= 1.0.0 - машинное обучение

  • Matplotlib >= 3.5.0 - визуализация

  • Seaborn >= 0.11.0 - статистические графики

  • TextStat >= 0.7.0 - анализ читабельности текста

Возможности модели 🔧

Извлекаемые признаки:

  • 📏 Длина текста вопроса

  • 📝 Количество слов

  • 🎓 Уровень читабельности (Flesch-Kincaid)

  • 🔢 Количество цифр и знаков препинания

  • ⏱️ Среднее время ответа

  • ✅ Процент правильных ответов

Используемые алгоритмы:

  • 🌲 Random Forest - ансамблевый метод

  • 🎯 SVM - метод опорных векторов

  • 🧪 Naive Bayes - вероятностный классификатор

Тестирование 🧪

Запуск тестов

pytest tests/test_my.py

Входные данные 📋

CSV файл должен содержать следующие колонки:

КолонкаТипОписаниеПример
question_text
stringТекст вопроса"What is the capital of France?"
avg_time_sec
floatСреднее время ответа в секундах45.2
correct_rate
floatПроцент правильных ответов (0-1)0.75
difficulty
stringУровень сложности"easy"/"medium"/"hard"

Визуализации 📊

Проект автоматически генерирует:

  • Матрицы ошибок для каждой модели

  • Распределение классов (фактические vs предсказанные)

  • Важность признаков для Random Forest

  • Сравнение моделей по точности

📈 Графики

  • confusion_matrix_<model>.png
    — матрица ошибок для каждой модели

Матрица ошибок RandomForest

Матрица ошибок SVM

Матрица ошибок NaiveBayers

  • class_distribution_<model>.png
    — распределение фактических и предсказанных классов

Распределение ошибок RandomForest

Распределение ошибок SVM

Распределение ошибок NaiveBayers

  • feature_importance.png
    — важность признаков для Random Forest 🌳

важность признаков для Random Forest

  • model_comparison.png
    — сравнение моделей по макро-прецизионной точности 📊

сравнение моделей

Будущие улучшения 🚀

  • Добавление нейросетевых моделей (BERT)

  • Веб-интерфейс для удобства использования

  • API для интеграции с LMS системами

  • Анализ мультиязычных вопросов

  • Автоматическая генерация отчетов

Автор 💫

Бажеева Алисия Владимировна

студентка 1 курса ИРИТ-РТФ группы РИ-150942/1.