{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ๐ŸŽฏ [TRN02] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ์ž๋™์ฑ„์ ยท์˜ค๋‹ต์œ ํ˜• ๋ถ„์„๊ธฐ\n", "### Google Colab ์‹ค์Šต ํ™˜๊ฒฝ (๊ต์œกยทํ›ˆ๋ จ / Level 2)\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/edunlab/milicontent2/blob/main/uploads/TRN02/TRN02_solution.ipynb)\n", "\n", "---\n", "\n", "## ๐Ÿ“Œ 1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š” ๋ฐ ๋ฐฐ๊ฒฝ\n", "\n", "| ํ•ญ๋ชฉ | ๋‚ด์šฉ |\n", "| :--- | :--- |\n", "| **๊ณผ์ • ๋ถ„๋ฅ˜** | ๊ต์œกยทํ›ˆ๋ จ (Level 2) |\n", "| **ํ•™์Šต ์†Œ์š” ์‹œ๊ฐ„** | 10๊ฐœ ๋ฏธ์…˜ (5์ผ / ์ด 27.5์‹œ๊ฐ„) |\n", "| **ํ™˜๊ฒฝ ์‚ฌ์–‘** | Python 3.10+, numpy, pandas, matplotlib, scipy, scikit-learn |\n", "\n", "### ๐Ÿ“– ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ\n", "๋ฏธ๊ตญ-์ด๋ž€ ์ „์Ÿ ์‚ฌ๋ก€์—์„œ ๋ณด๋“ฏ ๋“œ๋ก ๊ณผ ๋ฏธ์‚ฌ์ผ์ด ์„ž์—ฌ ๋‚ ์•„์˜ค๋Š” ์ „์žฅ์—์„œ๋Š” ์žฅ๋ณ‘ ํ•œ ์‚ฌ๋žŒ ํ•œ ์‚ฌ๋žŒ์ด ๋น„ํ–‰์ฒด ํ˜•์ƒ์„ ์‹๋ณ„ํ•˜๊ณ , ์•„๊ตฐยท๋ฏผ๊ฐ„๊ธฐ์™€ ๊ตฌ๋ถ„ํ•˜๋ฉฐ, ์ •ํ•ด์ง„ ๋ณด๊ณ ยทํ†ต์ œ ์ ˆ์ฐจ๋ฅผ ์ง€ํ‚ค๋Š” ๋Šฅ๋ ฅ์ด ๋ฐฉ๊ณต์˜ ๊ธฐ์ดˆ๊ฐ€ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ถ€๋Œ€ ๋‹จ์œ„ ๋ฐฉ๊ณต ์‹๋ณ„ ๊ต์œก์€ ์ฑ„์ ๊ณผ ์˜ค๋‹ต ๋ถ„์„์— ๊ต๊ด€์˜ ์†์ด ๋งŽ์ด ๋“ค์–ด ๊ฐœ์ธ๋ณ„ ์•ฝ์ ์„ ์ œ๋•Œ ์งš์–ด ์ฃผ๊ธฐ ์–ด๋ ต๋‹ค. ๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ต์œก์šฉ ๊ฐ€์ƒ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต์„ ์ฝ”๋“œ๋กœ ๋งŒ๋“ค์–ด, ๋ฌธํ•ญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ •์˜, ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑ, ์ž๋™ ์ฑ„์ , ์˜ค๋‹ต ์œ ํ˜•(๊ฐœ๋… ํ˜ผ๋™ยทํ”ผ์•„ ์˜ค์ธยท์ ˆ์ฐจ ๋ˆ„๋ฝ) ๋ถ„๋ฅ˜, ์ทจ์•ฝ ์˜์—ญ ์ถ”์ฒœ, ๊ต์ˆ˜์ž ๋Œ€์‹œ๋ณด๋“œ, ๊ต๊ด€ ๊ฒ€ํ† (HITL), ๋Œ€๊ทœ๋ชจ ๋™์‹œ ์‘์‹œ ๋ถ€ํ•˜ ์‹œํ—˜, AAR ๋ฌธํ•ญ ๋ถ„์„๊ณผ ์žฌ๋ณด์ •, ์ตœ์ข… ํ†ตํ•ฉ๊นŒ์ง€ ์ฑ„์ ยทํ”ผ๋“œ๋ฐฑ ์ฒด๊ณ„๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ๊ตฌํ˜„ํ•œ๋‹ค. ์ตœ์ข… ๋ชฉํ‘œ๋Š” ์ฑ„์ ์€ ๋น ๋ฅด๊ณ  ์ •ํ™•ํ•˜๊ฒŒ ์ž๋™ํ™”ํ•˜๋˜, ์ด์˜ ์ œ๊ธฐ์™€ ์„œ์ˆ ํ˜• ํŒ๋‹จ์€ ๊ต๊ด€์ด ํ™•์ •ํ•˜๋Š” ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๊ณต ๊ต์œก ํ‰๊ฐ€ ๋„๊ตฌ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.\n", "\n", "> **โš ๏ธ [๊ต์œก์šฉ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ์…‹ ์›์น™]**\n", "> - ๋ณธ ๋…ธํŠธ๋ถ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ(์ œ์›, ๋กœ๊ทธ, ์‹œ๋‚˜๋ฆฌ์˜ค ๋“ฑ)๋Š” ๋ณด์•ˆ ๊ทœ์ •์„ ์ค€์ˆ˜ํ•˜์—ฌ **์ฝ”๋“œ ๋‚ด ๋‚œ์ˆ˜ ๊ธฐ๋ฐ˜ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ๋กœ ์ƒ์„ฑ**๋ฉ๋‹ˆ๋‹ค.\n", "> - ์‹ค์ œ ๋ถ€๋Œ€ ์œ„์น˜, ์ž‘์ „ ์ธ๊ฐ€, ์‹ค์ œ ์ „์žฅ ์ •๋ณด, ๊ตฐ์‚ฌ ๊ธฐ๋ฐ€ ๋ฐ์ดํ„ฐ๋Š” ์ผ์ฒด ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\n", "\n", "---\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ============================================================================\n", "# [Google Colab ํ™˜๊ฒฝ ์„ค์ •] ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ์ž„ํฌํŠธ\n", "# ============================================================================\n", "!pip install -q numpy pandas matplotlib scipy scikit-learn\n", "\n", "import os\n", "import sys\n", "import time\n", "import math\n", "import json\n", "import re\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# ๊ทธ๋ž˜ํ”„ ์˜๋ฌธ ํฐํŠธ ๊ธฐ๋ณธ ์„ค์ • (ํ™˜๊ฒฝ ๋…๋ฆฝ์„ฑ ๋ณด์žฅ)\n", "plt.rcParams['font.sans-serif'] = 'DejaVu Sans'\n", "plt.rcParams['axes.unicode_minus'] = False\n", "\n", "print(f\"์‹คํ–‰ ํ™˜๊ฒฝ ์ค€๋น„ ์™„๋ฃŒ (Python {sys.version.split()[0]})\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 01. ๋ฐฉ๊ณต ์‹๋ณ„ ๋ฐ ๋Œ€๋“œ๋ก  ๊ต์ „ ์ˆ˜์น™ ํ€ด์ฆˆ ๋ฌธํ•ญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ํ‰๊ฐ€ ๊ธฐ์ค€ ์ˆ˜๋ฆฝ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 1.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋ฐฉ๊ณต ์‹๋ณ„ ๋ฐ ๋Œ€๋“œ๋ก  ๊ต์ „ ์ˆ˜์น™ ํ€ด์ฆˆ ๋ฌธํ•ญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ํ‰๊ฐ€ ๊ธฐ์ค€์„ ์ˆ˜๋ฆฝํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **ํ˜•์ƒ์‹๋ณ„ยทํ”ผ์•„์‹๋ณ„ยท๊ฒฝ๋ณด๋ณด๊ณ ยท๊ต์ „ํ†ต์ œยท๋Œ€๋“œ๋ก ์ผ๋ฐ˜ 5๊ฐœ ์˜์—ญ์˜ ํ€ด์ฆˆ ๋ฌธํ•ญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์กฐ์™€ ๋‚œ์ด๋„๋ณ„ ๋ฐฐ์ ยทํ•ฉ๊ฒฉ ๊ธฐ์ค€์„ ์ •ํ•˜๊ณ , ์ž˜๋ชป๋œ ๋ฌธํ•ญยท์‘๋‹ต ๊ธฐ๋ก์„ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ๊ฒ€์ฆ๊ธฐ๋ฅผ ๋งŒ๋“ ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์„ ๋‹คํ˜•ยท๋‹จ๋‹ตํ˜•ยท์ˆœ์„œํ˜• ๋ฌธํ•ญ์˜ ํ•„๋“œ(๋ฌธํ•ญID, ์˜์—ญ, ๋‚œ์ด๋„, ์ •๋‹ต, ์˜ค๋‹ต ๋ณด๊ธฐ๋ณ„ ์˜ค๋‹ต ์œ ํ˜•)์™€ ์‘๋‹ต ๊ธฐ๋ก ํ•„๋“œ(์‘์‹œ์ž, ๋ฌธํ•ญ, ์›๋ณธ ์‘๋‹ต, ์ œ์ถœ ์‹œ๊ฐ), ์‘์‹œ ์ƒํ™ฉ ํ•„๋“œ(์žฅ์†Œยทํ†ต์‹  ์ƒํƒœ ๋“ฑ) ์ •์˜ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `data/schema/trn02_input_schema.json ๋ฐ docs/trn02_data_spec.md` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ต์œกยทํ›ˆ๋ จ ์‹ค๋ฌด ๋ฐ ๋ฏธ๊ตญ-์ด๋ž€ ์ „์Ÿ ์ „์žฅ ํ™˜๊ฒฝ๊ณผ์˜ ๊ตฐ์‚ฌ์  ๋ถ€ํ•ฉ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์Šคํ‚ค๋งˆ ํ•„๋“œ ์ •์˜ ์™„์„ฑ๋„ 100%, ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ ์˜ค๋ฅ˜ 0๊ฑด\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ ํ†ต๊ณผ ๋ฐ ๋ช…์„ธ์„œ ๋ฌธ์„œํ™” ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: JSON Schema ๊ฒ€์ฆ ๋„๊ตฌ ๋ฐ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"๋ฐฉ๊ณต ์‹๋ณ„ยท๋Œ€๋“œ๋ก  ๊ต์ „ ํ†ต์ œ ์›์น™ ํ€ด์ฆˆ์˜ ๋ฌธํ•ญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์‘๋‹ต ๊ธฐ๋ก ์Šคํ‚ค๋งˆ, ๋‚œ์ด๋„๋ณ„ ๋ฐฐ์ ยทํ•ฉ๊ฒฉ ๊ธฐ์ค€์„ ์ •์˜ํ•˜๊ณ , ๊ฐ€์ƒ ๋ฌธํ•ญยท์‘๋‹ต ์ƒ˜ํ”Œ์„ ๊ฒ€์ฆํ•œ๋‹ค.\"\"\"\n", "import copy\n", "import re\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# 1) ์Šคํ‚ค๋งˆ: ๋ฌธํ•ญ(์œ ํ˜•ยท์˜์—ญยท๋‚œ์ด๋„ยท์ •๋‹ตยท์˜ค๋‹ต ์œ ํ˜• ๋งคํ•‘ยท์ƒํ™ฉ ์กฐ๊ฑด) / ์‘๋‹ต(์‘์‹œ์ž ๊ฐ€๋ช…ยท๋ฌธํ•ญยท์›๋ณธ ์‘๋‹ตยท์ œ์ถœ ์‹œ๊ฐ)\n", "SCHEMA = {\n", " \"item\": {\n", " \"qid\": {\"type\": str, \"pattern\": r\"^Q\\d{2}$\"},\n", " \"type\": {\"type\": str, \"enum\": [\"MC\", \"SA\", \"ORDER\", \"ESSAY\"]},\n", " \"area\": {\"type\": str, \"enum\": AREAS},\n", " \"diff\": {\"type\": int, \"enum\": [1, 2, 3]},\n", " \"stem\": {\"type\": str, \"minlen\": 5},\n", " },\n", " \"context\": { # ๋ณด์™„ํ•œ ์ „์ˆ  ์ƒํ™ฉ ํŒŒ๋ผ๋ฏธํ„ฐ\n", " \"time\": {\"type\": str, \"enum\": [\"์ฃผ๊ฐ„\", \"์•ผ๊ฐ„\"]},\n", " \"weather\": {\"type\": str, \"enum\": [\"๋ง‘์Œ\", \"์•ˆ๊ฐœ\", \"๋น„\", \"๋ˆˆ\"]},\n", " \"comm\": {\"type\": str, \"enum\": [\"์ •์ƒ\", \"์ง€์—ฐ\", \"๋‘์ ˆ\"]},\n", " \"position_error_m\": {\"type\": (int, float), \"min\": 0, \"max\": 500},\n", " },\n", " \"response\": {\n", " \"rid\": {\"type\": str, \"pattern\": r\"^R\\d{5}$\"},\n", " \"sid\": {\"type\": str, \"pattern\": r\"^S\\d{3}$\"},\n", " \"qid\": {\"type\": str, \"pattern\": r\"^Q\\d{2}$\"},\n", " \"raw\": {\"type\": str},\n", " \"submitted_s\": {\"type\": (int, float), \"min\": 0, \"max\": 7200},\n", " },\n", "}\n", "RUBRIC = {\"๋ฐฐ์ (๋‚œ์ด๋„ 1/2/3)\": \"2 / 3 / 5์ \", \"์„œ์ˆ ํ˜•\": \"10์ (๊ต๊ด€ ์ฑ„์ )\", \"ํ•ฉ๊ฒฉ ๊ธฐ์ค€\": \"์ด์ ์˜ 70% ์ด์ƒ\", \"๋ฏธ์‘๋‹ต\": \"0์ , ๋ฏธ์‘๋‹ต์œผ๋กœ ๊ธฐ๋ก(์‚ญ์ œ ์•ˆ ํ•จ)\"}\n", "\n", "\n", "def check(name, v, r):\n", " if not isinstance(v, r[\"type\"]) or isinstance(v, bool):\n", " return [f\"{name}: ์ž๋ฃŒํ˜• ์˜ค๋ฅ˜({type(v).__name__})\"]\n", " e = []\n", " if \"enum\" in r and v not in r[\"enum\"]:\n", " e.append(f\"{name}: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ '{v}'\")\n", " if \"pattern\" in r and not re.match(r[\"pattern\"], v):\n", " e.append(f\"{name}: ํ˜•์‹ ๋ถˆ์ผ์น˜ '{v}'\")\n", " if \"minlen\" in r and len(v) < r[\"minlen\"]:\n", " e.append(f\"{name}: ๊ธธ์ด ๋ถ€์กฑ\")\n", " if \"min\" in r and v < r[\"min\"]:\n", " e.append(f\"{name}: ์ตœ์†Œ๊ฐ’ ๋ฏธ๋งŒ\")\n", " if \"max\" in r and v > r[\"max\"]:\n", " e.append(f\"{name}: ์ตœ๋Œ€๊ฐ’ ์ดˆ๊ณผ\")\n", " return e\n", "\n", "\n", "def validate_item(it):\n", " e = []\n", " for g in (\"item\", \"context\"):\n", " src = it if g == \"item\" else it.get(\"context\", {})\n", " for k, r in SCHEMA[g].items():\n", " e += [f\"{g}.{k}: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\"] if k not in src else check(f\"{g}.{k}\", src[k], r)\n", " if it.get(\"type\") == \"MC\":\n", " if len(it.get(\"options\", [])) != 4 or it.get(\"answer\") not in (1, 2, 3, 4):\n", " e.append(\"MC: ๋ณด๊ธฐ 4๊ฐœยท์ •๋‹ต ๋ฒˆํ˜ธ(1~4) ํ•„์š”\")\n", " elif set(it.get(\"dist\", {})) != {1, 2, 3, 4} - {it[\"answer\"]} or not set(it[\"dist\"].values()) <= set(ETYPES):\n", " e.append(\"MC: ์˜ค๋‹ต ๋ณด๊ธฐ๋ณ„ ์˜ค๋‹ต ์œ ํ˜• ๋งคํ•‘ ๋ถˆ์™„์ „\")\n", " if it.get(\"type\") == \"SA\" and (not it.get(\"accept\") or not set(it.get(\"wrong\", {}).values()) <= set(ETYPES)):\n", " e.append(\"SA: ์ธ์ • ๋‹ต์•ˆยท์˜ค๋‹ต ์œ ํ˜• ๋ชฉ๋ก ํ•„์š”\")\n", " return e\n", "\n", "\n", "def validate_resp(r):\n", " e = []\n", " for k, rule in SCHEMA[\"response\"].items():\n", " e += [f\"response.{k}: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\"] if k not in r else check(f\"response.{k}\", r[k], rule)\n", " if r.get(\"qid\") not in QIDX:\n", " e.append(\"response.qid: ๋ฌธํ•ญ DB์— ์—†๋Š” ๋ฌธํ•ญ\")\n", " return e\n", "\n", "\n", "rng = np.random.default_rng(1)\n", "items = []\n", "for q in BANK:\n", " it = copy.deepcopy(q)\n", " it[\"context\"] = {\"time\": str(rng.choice([\"์ฃผ๊ฐ„\", \"์•ผ๊ฐ„\"])), \"weather\": str(rng.choice([\"๋ง‘์Œ\", \"์•ˆ๊ฐœ\", \"๋น„\", \"๋ˆˆ\"])),\n", " \"comm\": str(rng.choice([\"์ •์ƒ\", \"์ง€์—ฐ\"])), \"position_error_m\": float(round(rng.uniform(5, 80), 1))}\n", " items.append(it)\n", "resp = make_responses(5, seed=3).head(50)\n", "recs = [{\"rid\": r.rid, \"sid\": r.sid, \"qid\": r.qid, \"raw\": r.raw, \"submitted_s\": float(i * 12)} for i, r in enumerate(resp.itertuples())]\n", "ni, nr = sum(not validate_item(i) for i in items), sum(not validate_resp(r) for r in recs)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฌธํ•ญ {len(items)}๊ฐœ ์ค‘ ๊ฒ€์ฆ ํ†ต๊ณผ {ni}๊ฐœ, ์‘๋‹ต {len(recs)}๊ฑด ์ค‘ ํ†ต๊ณผ {nr}๊ฑด (๋ชฉํ‘œ ์˜ค๋ฅ˜ 0๊ฑด: {'๋‹ฌ์„ฑ' if ni == len(items) and nr == len(recs) else '๋ฏธ๋‹ฌ'})\")\n", "\n", "bad = copy.deepcopy(items[0]); bad[\"qid\"] = \"Q1\"; bad[\"diff\"] = 4; bad[\"dist\"].pop(4); del bad[\"context\"][\"comm\"]\n", "bad_r = dict(recs[0], sid=\"๋ณ‘์‚ฌ1\", qid=\"Q99\", submitted_s=9999)\n", "print(\"\\n[์˜ค๋ฅ˜ ์ฃผ์ž… ๊ฒฐ๊ณผ]\")\n", "for e in validate_item(bad) + validate_resp(bad_r):\n", " print(\" -\", e)\n", "\n", "print(\"\\n[๋ฌธํ•ญ ๊ตฌ์„ฑ]\")\n", "print(pd.crosstab(pd.Series([i[\"area\"] for i in items], name=\"์˜์—ญ\"), pd.Series([i[\"type\"] for i in items], name=\"์œ ํ˜•\"), margins=True, margins_name=\"ํ•ฉ๊ณ„\"))\n", "print(\"\\n[ํ‰๊ฐ€ ๊ธฐ์ค€]\")\n", "for k, v in RUBRIC.items():\n", " print(f\" - {k}: {v}\")\n", "print(f\" - ๋งŒ์ : {sum(POINTS[i['diff']] for i in items)}์ (์„œ์ˆ ํ˜• ์ œ์™ธ), ์˜ค๋‹ต ์œ ํ˜•: {', '.join(ETYPES)}\")\n", "n_fields = sum(len(v) for v in SCHEMA.values())\n", "print(f\"\\n์Šคํ‚ค๋งˆ ํ•„๋“œ {n_fields}๊ฐœ ์ •์˜(๋ฌธํ•ญ {len(SCHEMA['item'])}, ์ƒํ™ฉ {len(SCHEMA['context'])}, ์‘๋‹ต {len(SCHEMA['response'])}), ์ •์˜ ์™„์„ฑ๋„ 100%\")\n", "print(\"๋ฌธํ•ญ ์˜ˆ์‹œ:\", items[3][\"qid\"], items[3][\"stem\"], \"| ์ •๋‹ต\", items[3][\"answer\"], \"| ์˜ค๋‹ต ์œ ํ˜•\", items[3][\"dist\"])\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 01 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฌธํ•ญ 32๊ฐœ ์ค‘ ๊ฒ€์ฆ ํ†ต๊ณผ 32๊ฐœ, ์‘๋‹ต 50๊ฑด ์ค‘ ํ†ต๊ณผ 50๊ฑด (๋ชฉํ‘œ ์˜ค๋ฅ˜ 0๊ฑด: ๋‹ฌ์„ฑ)\n", "\n", "[์˜ค๋ฅ˜ ์ฃผ์ž… ๊ฒฐ๊ณผ]\n", " - item.qid: ํ˜•์‹ ๋ถˆ์ผ์น˜ 'Q1'\n", " - item.diff: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ '4'\n", " - context.comm: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\n", " - MC: ์˜ค๋‹ต ๋ณด๊ธฐ๋ณ„ ์˜ค๋‹ต ์œ ํ˜• ๋งคํ•‘ ๋ถˆ์™„์ „\n", " - response.sid: ํ˜•์‹ ๋ถˆ์ผ์น˜ '๋ณ‘์‚ฌ1'\n", " - response.submitted_s: ์ตœ๋Œ€๊ฐ’ ์ดˆ๊ณผ\n", " - response.qid: ๋ฌธํ•ญ DB์— ์—†๋Š” ๋ฌธํ•ญ\n", "\n", "[๋ฌธํ•ญ ๊ตฌ์„ฑ]\n", "์œ ํ˜• MC ORDER SA ํ•ฉ๊ณ„\n", "์˜์—ญ \n", "๊ฒฝ๋ณด๋ณด๊ณ  4 1 2 7\n", "๊ต์ „ํ†ต์ œ 4 1 1 6\n", "๋Œ€๋“œ๋ก ์ผ๋ฐ˜ 4 1 1 6\n", "ํ”ผ์•„์‹๋ณ„ 4 1 2 7\n", "ํ˜•์ƒ์‹๋ณ„ 4 0 2 6\n", "ํ•ฉ๊ณ„ 20 4 8 32\n", "\n", "[ํ‰๊ฐ€ ๊ธฐ์ค€]\n", " - ๋ฐฐ์ (๋‚œ์ด๋„ 1/2/3): 2 / 3 / 5์ \n", " - ์„œ์ˆ ํ˜•: 10์ (๊ต๊ด€ ์ฑ„์ )\n", " - ํ•ฉ๊ฒฉ ๊ธฐ์ค€: ์ด์ ์˜ 70% ์ด์ƒ\n", " - ๋ฏธ์‘๋‹ต: 0์ , ๋ฏธ์‘๋‹ต์œผ๋กœ ๊ธฐ๋ก(์‚ญ์ œ ์•ˆ ํ•จ)\n", " - ๋งŒ์ : 99์ (์„œ์ˆ ํ˜• ์ œ์™ธ), ์˜ค๋‹ต ์œ ํ˜•: ๊ฐœ๋… ํ˜ผ๋™, ํ”ผ์•„ ์˜ค์ธ, ์ ˆ์ฐจ ๋ˆ„๋ฝ\n", "\n", "์Šคํ‚ค๋งˆ ํ•„๋“œ 14๊ฐœ ์ •์˜(๋ฌธํ•ญ 5, ์ƒํ™ฉ 4, ์‘๋‹ต 5), ์ •์˜ ์™„์„ฑ๋„ 100%\n", "๋ฌธํ•ญ ์˜ˆ์‹œ: Q04 ์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€? | ์ •๋‹ต 2 | ์˜ค๋‹ต ์œ ํ˜• {1: '์ ˆ์ฐจ ๋ˆ„๋ฝ', 3: '์ ˆ์ฐจ ๋ˆ„๋ฝ', 4: 'ํ”ผ์•„ ์˜ค์ธ'}\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 02. ์žฅ๋ณ‘ ์‘๋‹ต ํ…์ŠคํŠธ/์„ ํƒ์ง€ ์ •์ œ ๋ฐ ์‹œํ—˜ ๋‹ต์•ˆ ํŒŒ์‹ฑ ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์žฅ๋ณ‘ ์‘๋‹ต ํ…์ŠคํŠธ/์„ ํƒ์ง€ ์ •์ œ ๋ฐ ์‹œํ—˜ ๋‹ต์•ˆ ํŒŒ์‹ฑ ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์žฅ๋ณ‘ ์‘๋‹ต์˜ ์ „๊ฐ ์ˆซ์žยท์›๋ฌธ์žยท๊ณต๋ฐฑยท์–ด๋ฏธ ๊ฐ™์€ ํ‘œ๊ธฐ ์ฐจ์ด๋ฅผ ์ •์ œํ•˜๊ณ  ๋ฌธํ•ญ ์œ ํ˜•๋ณ„๋กœ ๋‹ต์•ˆ์„ ํŒŒ์‹ฑํ•˜๋Š” ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์œ ๋‹ˆ์ฝ”๋“œ ์ •๊ทœํ™”(NFKC), ์›๋ฌธ์žยท์˜๋ฌธ ๋ณด๊ธฐยท'๋ฒˆ' ํ‘œ๊ธฐ ํ†ต์ผ, ๋‹จ๋‹ตํ˜• ๊ณต๋ฐฑยท๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ, ์ˆœ์„œํ˜• ๊ตฌ๋ถ„์ž ์ œ๊ฑฐ ๊ตฌํ˜„ํ•˜๊ณ  ๋™์ž‘์„ ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/preprocess/trn02_preprocessor.py ๋ฐ ์ „์ฒ˜๋ฆฌ ์ „ํ›„ ๋ฐ์ดํ„ฐ์…‹` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ „์ˆ  ํ™˜๊ฒฝ ํŠน์œ ์˜ ๋…ธ์ด์ฆˆ(์Šคํ‘ธํ•‘, ๋ฐ˜์‚ฌํŒŒ, ์„ผ์„œ ๋‹จ์ ˆ ๋“ฑ) ์ œ๊ฑฐ ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋ฐ์ดํ„ฐ ์ •์ œ ์ฒ˜๋ฆฌ ์†๋„ 1,000๊ฑด/์ดˆ ์ด์ƒ, ์ „์ฒ˜๋ฆฌ ์˜ค๋ฅ˜์œจ 0%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ •์ œ ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ํ™” ๋ฐ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค 5์ข… ํ†ต๊ณผ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: pytest ๊ธฐ๋ฐ˜ ์ „์ฒ˜๋ฆฌ ์ •ํ•ฉ์„ฑ ํ…Œ์ŠคํŠธ ๋ฐ ๊ฒฐ์ธก์น˜ ๋ณด์ •์œจ ํ™•์ธ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์žฅ๋ณ‘ ์‘๋‹ต์˜ ์ „๊ฐยท์›๋ฌธ์žยท๊ณต๋ฐฑยท์–ด๋ฏธ ๋“ฑ ํ‘œ๊ธฐ ์ฐจ์ด๋ฅผ ์ •์ œํ•˜๊ณ  ์„ ๋‹คํ˜•ยท๋‹จ๋‹ตํ˜•ยท์ˆœ์„œํ˜• ๋‹ต์•ˆ์„ ํŒŒ์‹ฑํ•˜๋ฉฐ, ํ†ต์‹  ์ƒํƒœ์— ๋”ฐ๋ผ ์žฌ์ „์†ก ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ณ  ๋ฏธ์‘๋‹ตยทํ˜•์‹ ์˜ค๋ฅ˜๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์ฒ˜๋ฆฌ ์†๋„์™€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ 5์ข…์„ ํ™•์ธํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import re\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "DUP_WINDOW_S = {\"์ •์ƒ\": 5, \"์ง€์—ฐ\": 30} # ํ†ต์‹  ์ƒํƒœ๋ณ„ ์žฌ์ „์†ก ํŒ๋‹จ ์‹œ๊ฐ„(๋™์  ์ž„๊ณ„๊ฐ’)\n", "\n", "\n", "def inject_noise(df, seed=1):\n", " \"\"\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์žฌ์ „์†ก ์ค‘๋ณตยทํ˜•์‹ ์˜ค๋ฅ˜ยท์ œ์ถœ ์‹œ๊ฐยทํ†ต์‹  ์ƒํƒœ๋ฅผ ๋ง๋ถ™์ธ๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " df = df.copy()\n", " df[\"comm\"] = np.where(df[\"unit\"] == \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"์ง€์—ฐ\", \"์ •์ƒ\")\n", " df[\"ts\"] = np.arange(len(df)) * 3.0\n", " bad = rng.choice(len(df), 20, replace=False)\n", " df.loc[bad, \"raw\"] = rng.choice([\"7\", \"๋ชจ๋ฆ„\", \"??\"], 20)\n", " df.loc[bad, \"truth_correct\"] = False\n", " dup = df.sample(frac=0.03, random_state=seed).copy()\n", " dup[\"ts\"] = dup[\"ts\"] + np.where(dup[\"comm\"] == \"์ง€์—ฐ\", 20.0, 2.0)\n", " dup[\"rid\"] = dup[\"rid\"] + \"D\"\n", " return pd.concat([df, dup]).sort_values(\"ts\").reset_index(drop=True)\n", "\n", "\n", "def preprocess(df):\n", " \"\"\"์ค‘๋ณต ์ œ๊ฑฐ -> ํŒŒ์‹ฑ -> ์ƒํƒœ ๋ถ„๋ฅ˜. ๋ฏธ์‘๋‹ต์€ ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  ์ƒํƒœ๋กœ ๋‚จ๊ธด๋‹ค(๊ฒฐ์ธก ๋ณด์ •).\"\"\"\n", " df = df.sort_values(\"ts\")\n", " win = df[\"comm\"].map(DUP_WINDOW_S)\n", " prev_ts = df.groupby([\"sid\", \"qid\"])[\"ts\"].shift(1)\n", " df = df.assign(dup=(df[\"ts\"] - prev_ts) <= win)\n", " keep = df[~df.groupby([\"sid\", \"qid\"])[\"dup\"].shift(-1, fill_value=False)] # ์žฌ์ „์†ก์ด๋ฉด ๋งˆ์ง€๋ง‰ ์ œ์ถœ๋งŒ ์œ ์ง€\n", " pv = [parse(QIDX[q], r) for q, r in zip(keep[\"qid\"], keep[\"raw\"])]\n", " return keep.assign(val=[p[0] for p in pv], status=[p[1] for p in pv]).drop(columns=\"dup\"), len(df) - len(keep)\n", "\n", "\n", "data = inject_noise(make_responses(40, seed=7))\n", "preprocess(data.head(100))\n", "t0 = time.perf_counter()\n", "clean, n_dup = preprocess(data)\n", "dt = time.perf_counter() - t0\n", "print(f\"์‘๋‹ต {len(data):,}๊ฑด ์ „์ฒ˜๋ฆฌ {dt * 1000:.0f} ms, {len(data) / dt:,.0f}๊ฑด/์ดˆ (๋ชฉํ‘œ 1,000๊ฑด/์ดˆ ์ด์ƒ: {'๋‹ฌ์„ฑ' if len(data) / dt >= 1000 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"์žฌ์ „์†ก ์ค‘๋ณต ์ œ๊ฑฐ {n_dup}๊ฑด, ๋‚จ์€ ์‘๋‹ต {len(clean):,}๊ฑด(์‘์‹œ์ž 120๋ช… x 32๋ฌธํ•ญ = 3,840๊ฑด๊ณผ ์ผ์น˜: {len(clean) == 3840})\")\n", "print(\"์ƒํƒœ ๋ถ„ํฌ:\", clean[\"status\"].value_counts().to_dict())\n", "err = clean[(clean[\"status\"] == \"์ •์ƒ\") & clean.apply(lambda r: QIDX[r.qid][\"type\"] == \"MC\" and r.val not in \"1234\", axis=1)]\n", "print(f\"์ „์ฒ˜๋ฆฌ ์˜ค๋ฅ˜(์ •์ƒ์ธ๋ฐ ํŒŒ์‹ฑ ์‹คํŒจ) {len(err)}๊ฑด, ์˜ค๋ฅ˜์œจ {len(err) / len(clean):.1%}\")\n", "\n", "# ๋‹จ์œ„ ํ…Œ์ŠคํŠธ 5์ข…\n", "Q = QIDX\n", "tests = {\n", " \"T1 ์ „๊ฐ ์ˆซ์ž '๏ผ“' -> 3\": parse(Q[\"Q01\"], \"๏ผ“\")[0] == \"3\",\n", " \"T2 ์›๋ฌธ์žยท์˜๋ฌธยท'๋ฒˆ' ํ‘œ๊ธฐ ํ†ต์ผ\": [parse(Q[\"Q01\"], s)[0] for s in [\"โ‘ข\", \"c)\", \"3๋ฒˆ\", \" 3 \"]] == [\"3\"] * 4,\n", " \"T3 ๋‹จ๋‹ต ๊ณต๋ฐฑยท์–ด๋ฏธ ์ œ๊ฑฐ\": parse(Q[\"Q24\"], \"์ง€ ํœ˜ ๊ด€ ์ž…๋‹ˆ๋‹ค\")[0] == \"์ง€ํœ˜๊ด€\" and parse(Q[\"Q21\"], \"๏ผฉ๏ผฆ๏ผฆ\")[0] == \"iff\",\n", " \"T4 ์ˆœ์„œํ˜• ๊ตฌ๋ถ„์ž ์ œ๊ฑฐ\": parse(Q[\"Q30\"], \"a-b-c-d\") == (\"ABCD\", \"์ •์ƒ\") and parse(Q[\"Q30\"], \"A,B,B,D\")[1] == \"ํ˜•์‹์˜ค๋ฅ˜\",\n", " \"T5 ๋ฏธ์‘๋‹ต ๋ณด์กด(๊ฒฐ์ธก ๋ณด์ •์œจ 100%)\": (clean[\"status\"] == \"๋ฏธ์‘๋‹ต\").sum() == (data.drop_duplicates([\"sid\", \"qid\"])[\"raw\"] == \"\").sum(),\n", "}\n", "for k, v in tests.items():\n", " print(f\"[{'PASS' if v else 'FAIL'}] {k}\")\n", "print(f\"๋‹จ์œ„ ํ…Œ์ŠคํŠธ {sum(tests.values())}/5 ํ†ต๊ณผ\")\n", "\n", "print(\"\\n[์ „์ฒ˜๋ฆฌ ์ „ํ›„ ์˜ˆ์‹œ]\")\n", "ex = clean[(clean[\"raw\"] != clean[\"val\"]) & clean[\"qid\"].isin([\"Q05\", \"Q13\", \"Q21\", \"Q22\", \"Q24\", \"Q25\", \"Q29\", \"Q31\"])].groupby(\"qid\").head(1)\n", "print(ex[[\"qid\", \"raw\", \"val\", \"status\"]].rename(columns={\"raw\": \"์›๋ณธ\", \"val\": \"์ •์ œ๊ฐ’\", \"status\": \"์ƒํƒœ\"}).to_string(index=False))\n", "print(\"ํ†ต์‹  ์ง€์—ฐ ์ค‘๋Œ€๋Š” ์žฌ์ „์†ก ํŒ๋‹จ ์‹œ๊ฐ„์„ 30์ดˆ๋กœ ๋„“ํ˜€ ์ค‘๋ณต ์ œ์ถœ์„ ๋†“์น˜์ง€ ์•Š๋Š”๋‹ค.\")\n", "\n", "st = clean.groupby([\"unit\", \"status\"]).size().unstack(fill_value=0)\n", "st.index = [\"Co.1\", \"Co.2 (delayed comm)\", \"Co.3\"]\n", "st.columns = st.columns.map({\"์ •์ƒ\": \"valid\", \"๋ฏธ์‘๋‹ต\": \"blank\", \"ํ˜•์‹์˜ค๋ฅ˜\": \"format error\"})\n", "st.plot(kind=\"bar\", stacked=True, figsize=(7, 4))\n", "plt.title(\"Response status after preprocessing (synthetic)\"); plt.ylabel(\"responses\"); plt.xticks(rotation=0)\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 02 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "์‘๋‹ต 3,955๊ฑด ์ „์ฒ˜๋ฆฌ 18 ms, 220,239๊ฑด/์ดˆ (๋ชฉํ‘œ 1,000๊ฑด/์ดˆ ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "์žฌ์ „์†ก ์ค‘๋ณต ์ œ๊ฑฐ 115๊ฑด, ๋‚จ์€ ์‘๋‹ต 3,840๊ฑด(์‘์‹œ์ž 120๋ช… x 32๋ฌธํ•ญ = 3,840๊ฑด๊ณผ ์ผ์น˜: True)\n", "์ƒํƒœ ๋ถ„ํฌ: {'์ •์ƒ': 3753, '๋ฏธ์‘๋‹ต': 71, 'ํ˜•์‹์˜ค๋ฅ˜': 16}\n", "์ „์ฒ˜๋ฆฌ ์˜ค๋ฅ˜(์ •์ƒ์ธ๋ฐ ํŒŒ์‹ฑ ์‹คํŒจ) 0๊ฑด, ์˜ค๋ฅ˜์œจ 0.0%\n", "[PASS] T1 ์ „๊ฐ ์ˆซ์ž '๏ผ“' -> 3\n", "[PASS] T2 ์›๋ฌธ์žยท์˜๋ฌธยท'๋ฒˆ' ํ‘œ๊ธฐ ํ†ต์ผ\n", "[PASS] T3 ๋‹จ๋‹ต ๊ณต๋ฐฑยท์–ด๋ฏธ ์ œ๊ฑฐ\n", "[PASS] T4 ์ˆœ์„œํ˜• ๊ตฌ๋ถ„์ž ์ œ๊ฑฐ\n", "[PASS] T5 ๋ฏธ์‘๋‹ต ๋ณด์กด(๊ฒฐ์ธก ๋ณด์ •์œจ 100%)\n", "๋‹จ์œ„ ํ…Œ์ŠคํŠธ 5/5 ํ†ต๊ณผ\n", "\n", "[์ „์ฒ˜๋ฆฌ ์ „ํ›„ ์˜ˆ์‹œ]\n", "qid ์›๋ณธ ์ •์ œ๊ฐ’ ์ƒํƒœ\n", "Q05 b) 2 ์ •์ƒ\n", "Q13 3 3 ์ •์ƒ\n", "Q21 ํ”ผ์•„์‹๋ณ„์ž„ ํ”ผ์•„์‹๋ณ„ ์ •์ƒ\n", "Q25 ์ž˜๋ชป๋œ ๊ฒฝ๋ณด ์ž˜๋ชป๋œ๊ฒฝ๋ณด ์ •์ƒ\n", "Q31 A C D ACD ์ •์ƒ\n", "Q22 de lta delta ์ •์ƒ\n", "Q24 ์ง€ ํœ˜ ๊ด€ ์ง€ํœ˜๊ด€ ์ •์ƒ\n", "Q29 A,B,C,D ABCD ์ •์ƒ\n", "ํ†ต์‹  ์ง€์—ฐ ์ค‘๋Œ€๋Š” ์žฌ์ „์†ก ํŒ๋‹จ ์‹œ๊ฐ„์„ 30์ดˆ๋กœ ๋„“ํ˜€ ์ค‘๋ณต ์ œ์ถœ์„ ๋†“์น˜์ง€ ์•Š๋Š”๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 03. ์„ ๋‹คํ˜• ๋ฐ ๋‹จ๋‹ตํ˜• ๋ฐฉ๊ณต ํ€ด์ฆˆ ์ž๋™ ์ฑ„์  Baseline ๋กœ์ง ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์„ ๋‹คํ˜• ๋ฐ ๋‹จ๋‹ตํ˜• ๋ฐฉ๊ณต ํ€ด์ฆˆ ์ž๋™ ์ฑ„์  Baseline ๋กœ์ง์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์„ ๋‹คํ˜•ยท๋‹จ๋‹ตํ˜•ยท์ˆœ์„œํ˜• ํ€ด์ฆˆ๋ฅผ ์ž๋™ ์ฑ„์ ํ•˜๋Š” Baseline์„ ๊ตฌํ˜„ํ•˜๊ณ  ๊ต๊ด€ ๊ธฐ์ค€ ํŒ์ •๊ณผ ๋น„๊ตํ•ด ์ •ํ™•๋„๋ฅผ ํ™•์ธํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ •ํ™• ์ผ์น˜ ์ฑ„์ ๊ณผ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ์œ ์‚ฌ๋„ ์ฑ„์ ์„ ๊ตฌํ˜„ํ•˜๊ณ , ์œ ์‚ฌ๋„ ๊ธฐ์ค€๊ฐ’์„ ๋ฐ”๊ฟ” ๊ฐ€๋ฉฐ ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„๋ฅผ ๋น„๊ต ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 5๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/models/trn02_baseline_model.py ๋ฐ ๊ธฐ๋ณธ ์ถ”๋ก  ๊ฒฐ๊ณผ ๋ณด๊ณ ์„œ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋…ผ๋ฆฌ์  ํ๋ฆ„ ๋ฐ ๊ตฐ์‚ฌ์  ํŒ๋‹จ ๊ธฐ์ค€์˜ ํˆฌ๋ช…์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ์…‹ ๋Œ€์ƒ ํƒ์ง€/์ฒ˜๋ฆฌ ์ •ํ™•๋„ 75% ์ด์ƒ ๋‹ฌ์„ฑ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋‹จ์œ„ ๊ธฐ๋Šฅ ๋™์ž‘ ํ™•์ธ ๋ฐ ๊ธฐ๋ณธ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๊ธฐ์ค€ ๋ฒค์น˜๋งˆํฌ ๋ฐ์ดํ„ฐ์…‹ ๋Œ€์ƒ ์ถ”๋ก  ๊ฒฐ๊ณผ ์˜ค์ฐจ์œจ ๋น„๊ต\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์„ ๋‹คํ˜•ยท๋‹จ๋‹ตํ˜•ยท์ˆœ์„œํ˜• ๋ฐฉ๊ณต ํ€ด์ฆˆ๋ฅผ ์ž๋™ ์ฑ„์ ํ•˜๋Š” Baseline์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์ •ํ™• ์ผ์น˜ ์ฑ„์ ๊ณผ ์˜คํƒˆ์ž ํ—ˆ์šฉ ์ฑ„์ ์˜ ์ •ํ™•๋„(๊ต๊ด€ ๊ธฐ์ค€ ๋Œ€๋น„)ยท์ฒ˜๋ฆฌ ์ง€์—ฐ์„ ๋น„๊ตํ•˜๊ณ , ์‘์‹œ์ž๋ณ„ ์ ์ˆ˜์™€ ํ•ฉ๊ฒฉ ์—ฌ๋ถ€, ์‹คํ–‰ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import re\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "PASS_RATE = 0.7\n", "df = make_responses(40, seed=7)\n", "df[\"type\"] = df[\"qid\"].map(lambda q: QIDX[q][\"type\"])\n", "pv = [parse(QIDX[q], r) for q, r in zip(df[\"qid\"], df[\"raw\"])]\n", "df[\"val\"] = [p[0] for p in pv]\n", "\n", "t0 = time.perf_counter()\n", "df[\"exact\"] = [grade_exact(QIDX[q], v) for q, v in zip(df[\"qid\"], df[\"val\"])]\n", "t1 = time.perf_counter()\n", "df[\"fuzzy\"] = [grade_fuzzy(QIDX[q], v) for q, v in zip(df[\"qid\"], df[\"val\"])]\n", "t2 = time.perf_counter()\n", "\n", "rows = []\n", "for name, col, t in [(\"์ •ํ™• ์ผ์น˜\", \"exact\", t1 - t0), (\"์˜คํƒˆ์ž ํ—ˆ์šฉ\", \"fuzzy\", t2 - t1)]:\n", " rows.append({\"์ฑ„์  ๋ฐฉ์‹\": name, \"์ฑ„์  ์ •ํ™•๋„\": (df[col] == df[\"truth_correct\"]).mean(),\n", " **{f\"{k} ์ •ํ™•๋„\": (g[col] == g[\"truth_correct\"]).mean() for k, g in df.groupby(\"type\")},\n", " \"๊ฑด๋‹น ms\": t / len(df) * 1000})\n", "res = pd.DataFrame(rows)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์‘์‹œ์ž 120๋ช… x 32๋ฌธํ•ญ = {len(df):,}๊ฑด ์ฑ„์  (๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต๊ณผ ๋น„๊ต)\")\n", "print(res.round(4).to_string(index=False))\n", "acc = res.loc[1, \"์ฑ„์  ์ •ํ™•๋„\"]\n", "print(f\"\\n๋ชฉํ‘œ(์ฑ„์  ์ •ํ™•๋„ 75% ์ด์ƒ): {'๋‹ฌ์„ฑ' if acc >= 0.75 else '๋ฏธ๋‹ฌ'} ({acc:.1%}), ๊ฑด๋‹น ์ฒ˜๋ฆฌ {res.loc[1, '๊ฑด๋‹น ms']:.3f} ms\")\n", "\n", "print(\"\\n[์ •ํ™• ์ผ์น˜ ์ฑ„์ ์ด ํ‹€๋ฆฐ ์‚ฌ๋ก€(์˜คํƒˆ์žยท๋„์–ด์“ฐ๊ธฐ)]\")\n", "miss = df[(df[\"exact\"] != df[\"truth_correct\"])].groupby(\"qid\").head(1).head(5)\n", "print(miss[[\"qid\", \"raw\", \"val\", \"truth_correct\", \"exact\", \"fuzzy\"]].rename(columns={\"truth_correct\": \"๊ต๊ด€ ํŒ์ •\", \"exact\": \"์ •ํ™•์ผ์น˜\", \"fuzzy\": \"์˜คํƒˆ์žํ—ˆ์šฉ\"}).to_string(index=False))\n", "\n", "print(\"์˜คํƒˆ์ž ํ—ˆ์šฉ ์ฑ„์ ๋„ ๋†“์น˜๋Š” '๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„'์€ ์ด์˜ ์ œ๊ธฐยท๊ต๊ด€ ๊ฒ€ํ† (๋ฏธ์…˜ 7) ๋Œ€์ƒ์œผ๋กœ ๋„˜๊ธด๋‹ค.\")\n", "print(\"\\n[ํœด๋ฆฌ์Šคํ‹ฑ 1์ฐจ ํŠœ๋‹: ์œ ์‚ฌ๋„ ๊ธฐ์ค€๊ฐ’]\")\n", "sa = df[df[\"type\"] == \"SA\"]\n", "for cut in [0.6, 0.7, 0.75, 0.8, 0.9]:\n", " a = np.mean([grade_fuzzy(QIDX[q], v, cut) == t for q, v, t in zip(sa[\"qid\"], sa[\"val\"], sa[\"truth_correct\"])])\n", " print(f\"๊ธฐ์ค€ {cut}: ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„ {a:.3f}\")\n", "\n", "df[\"score\"] = [score_of(QIDX[q], c) for q, c in zip(df[\"qid\"], df[\"fuzzy\"])]\n", "full = sum(POINTS[q[\"diff\"]] for q in BANK)\n", "sc = df.groupby([\"sid\", \"unit\"])[\"score\"].sum().reset_index()\n", "sc[\"ํ•ฉ๊ฒฉ\"] = sc[\"score\"] >= full * PASS_RATE\n", "print(f\"\\n๋งŒ์  {full}์ , ํ‰๊ท  {sc['score'].mean():.1f}์ , ํ•ฉ๊ฒฉ๋ฅ  {sc['ํ•ฉ๊ฒฉ'].mean():.1%}\")\n", "print(sc.groupby(\"unit\").agg(ํ‰๊ท ์ ์ˆ˜=(\"score\", \"mean\"), ํ•ฉ๊ฒฉ๋ฅ =(\"ํ•ฉ๊ฒฉ\", \"mean\")).round(2).to_string())\n", "print(f\"์‹คํ–‰ ๋กœ๊ทธ: ๋ฌธํ•ญ {len(BANK)}๊ฐœ, ์‘๋‹ต {len(df):,}๊ฑด, ๋ฏธ์‘๋‹ต {(df['raw'] == '').sum()}๊ฑด, ์ฑ„์  ์˜ˆ์™ธ 0๊ฑด\")\n", "\n", "plt.figure(figsize=(7, 4))\n", "plt.hist(sc[\"score\"], bins=15, color=\"tab:blue\"); plt.axvline(full * PASS_RATE, color=\"red\", ls=\"--\", label=\"pass line (70%)\")\n", "plt.xlabel(\"total score\"); plt.ylabel(\"soldiers\"); plt.legend(); plt.title(\"Auto-graded score distribution (synthetic)\"); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 03 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์‘์‹œ์ž 120๋ช… x 32๋ฌธํ•ญ = 3,840๊ฑด ์ฑ„์  (๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต๊ณผ ๋น„๊ต)\n", " ์ฑ„์  ๋ฐฉ์‹ ์ฑ„์  ์ •ํ™•๋„ MC ์ •ํ™•๋„ ORDER ์ •ํ™•๋„ SA ์ •ํ™•๋„ ๊ฑด๋‹น ms\n", " ์ •ํ™• ์ผ์น˜ 0.9516 1.0 1.0 0.8062 0.0017\n", "์˜คํƒˆ์ž ํ—ˆ์šฉ 0.9901 1.0 1.0 0.9604 0.0070\n", "\n", "๋ชฉํ‘œ(์ฑ„์  ์ •ํ™•๋„ 75% ์ด์ƒ): ๋‹ฌ์„ฑ (99.0%), ๊ฑด๋‹น ์ฒ˜๋ฆฌ 0.007 ms\n", "\n", "[์ •ํ™• ์ผ์น˜ ์ฑ„์ ์ด ํ‹€๋ฆฐ ์‚ฌ๋ก€(์˜คํƒˆ์žยท๋„์–ด์“ฐ๊ธฐ)]\n", "qid raw val ๊ต๊ด€ ํŒ์ • ์ •ํ™•์ผ์น˜ ์˜คํƒˆ์žํ—ˆ์šฉ\n", "Q25 ์ž˜๋ชป๋œ ๊ฒฝ๋ณด ์ž˜๋ชป๋œ๊ฒฝ๋ณด True False False\n", "Q21 ์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„ ์•„๊ตฐ์ ๊ตฐ๊ตฌ๋ถ„ True False False\n", "Q28 ์ง€ํœ˜ํ†ต ์ง€ํœ˜ํ†ต True False True\n", "Q22 ํƒ€๋ธ์ต ํƒ€๋ธ์ต True False True\n", "Q26 ๋ฏผ๊ธฐ๊ฐ„ ๋ฏผ๊ธฐ๊ฐ„ True False True\n", "์˜คํƒˆ์ž ํ—ˆ์šฉ ์ฑ„์ ๋„ ๋†“์น˜๋Š” '๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„'์€ ์ด์˜ ์ œ๊ธฐยท๊ต๊ด€ ๊ฒ€ํ† (๋ฏธ์…˜ 7) ๋Œ€์ƒ์œผ๋กœ ๋„˜๊ธด๋‹ค.\n", "\n", "[ํœด๋ฆฌ์Šคํ‹ฑ 1์ฐจ ํŠœ๋‹: ์œ ์‚ฌ๋„ ๊ธฐ์ค€๊ฐ’]\n", "๊ธฐ์ค€ 0.6: ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„ 0.965\n", "๊ธฐ์ค€ 0.7: ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„ 0.960\n", "๊ธฐ์ค€ 0.75: ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„ 0.960\n", "๊ธฐ์ค€ 0.8: ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„ 0.956\n", "๊ธฐ์ค€ 0.9: ๋‹จ๋‹ตํ˜• ์ฑ„์  ์ •ํ™•๋„ 0.935\n", "\n", "๋งŒ์  99์ , ํ‰๊ท  67.4์ , ํ•ฉ๊ฒฉ๋ฅ  50.8%\n", " ํ‰๊ท ์ ์ˆ˜ ํ•ฉ๊ฒฉ๋ฅ \n", "unit \n", "๊ฐ€์ƒ1์ค‘๋Œ€ 75.40 0.68\n", "๊ฐ€์ƒ2์ค‘๋Œ€ 58.55 0.28\n", "๊ฐ€์ƒ3์ค‘๋Œ€ 68.18 0.57\n", "์‹คํ–‰ ๋กœ๊ทธ: ๋ฌธํ•ญ 32๊ฐœ, ์‘๋‹ต 3,840๊ฑด, ๋ฏธ์‘๋‹ต 72๊ฑด, ์ฑ„์  ์˜ˆ์™ธ 0๊ฑด\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 04. ์˜ค๋‹ต ์œ ํ˜•(๊ฐœ๋… ํ˜ผ๋™, ํ”ผ์•„ ์˜ค์ธ, ์ ˆ์ฐจ ๋ˆ„๋ฝ) ์ž๋™ ๋ถ„๋ฅ˜ ๋ฐ ํŒจํ„ด ๋ถ„์„ ์—”์ง„ ๊ณ ๋„ํ™”\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์˜ค๋‹ต ์œ ํ˜•(๊ฐœ๋… ํ˜ผ๋™, ํ”ผ์•„ ์˜ค์ธ, ์ ˆ์ฐจ ๋ˆ„๋ฝ) ์ž๋™ ๋ถ„๋ฅ˜ ์—”์ง„์„ ๊ณ ๋„ํ™”ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์˜ค๋‹ต์„ ๊ฐœ๋… ํ˜ผ๋™ยทํ”ผ์•„ ์˜ค์ธยท์ ˆ์ฐจ ๋ˆ„๋ฝ ์œ ํ˜•์œผ๋กœ ์ž๋™ ๋ถ„๋ฅ˜ํ•˜๋Š” ์—”์ง„์„ ๊ณ ๋„ํ™”ํ•˜๊ณ  ๊ฐœ์ธยท์ค‘๋Œ€๋ณ„ ์˜ค๋‹ต ํŒจํ„ด์„ ๋ถ„์„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์„ ๋‹คํ˜•์€ ์˜ค๋‹ต ๋ณด๊ธฐ๋ณ„ ์œ ํ˜• ๋งคํ•‘, ์ˆœ์„œํ˜•์€ ๋‹จ๊ณ„ ๋ˆ„๋ฝยท์ˆœ์„œ ๋’ค๋ฐ”๋€œ ๊ทœ์น™, ๋‹จ๋‹ตํ˜•์€ ์˜ค๋‹ต ๋ชฉ๋ก ๋Œ€์กฐ์™€ ์˜คํƒˆ์ž ๊ทผ์‚ฌ ์ผ์น˜์— ๋ฌธ์ž n-gram ๋ถ„๋ฅ˜ ๋ชจ๋ธ์„ ๋” ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/models/trn02_advanced_detector.py ๋ฐ ์˜ˆ์™ธ ํƒ์ง€ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ธฐ๋งŒ/๊ต๋ž€ ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ๋ฐฉ์–ด ๋กœ์ง์˜ ํ˜„์‹ค์„ฑ ๋ฐ ์‹ ๋ขฐ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋ณตํ•ฉ ์œ„ํ˜‘ ํƒ์ง€ ์žฌํ˜„์œจ(Recall) 85% ์ด์ƒ, ์ฒ˜๋ฆฌ ์ง€์—ฐ 200ms ์ดํ•˜\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋ณตํ•ฉ ์œ„ํ˜‘ ์‹œ๋‚˜๋ฆฌ์˜ค 10์ข…์— ๋Œ€ํ•œ ํƒ์ง€ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ํ•ฉ์„ฑ ์œ„ํ˜‘ ๋ฐ์ดํ„ฐ์…‹ ์ฃผ์ž… ์‹œ ์ด์ƒ์น˜ ํƒ์ง€ ์ •ํ™•๋„ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์˜ค๋‹ต์„ ๊ฐœ๋… ํ˜ผ๋™ยทํ”ผ์•„ ์˜ค์ธยท์ ˆ์ฐจ ๋ˆ„๋ฝ ์œ ํ˜•์œผ๋กœ ์ž๋™ ๋ถ„๋ฅ˜ํ•˜๋Š” ์—”์ง„์„ ๊ณ ๋„ํ™”ํ•œ๋‹ค. ๋ณด๊ธฐ ๋งคํ•‘ยท์ˆœ์„œ ๊ทœ์น™ยท์˜ค๋‹ต ๋ชฉ๋ก ๋Œ€์กฐ์— ๋ฌธ์ž n-gram ๋ถ„๋ฅ˜ ๋ชจ๋ธ์„ ๋”ํ•ด, ๋ณตํ•ฉ ์˜ค๋‹ต ์‹œ๋‚˜๋ฆฌ์˜ค 10์ข…์—์„œ ์œ ํ˜•๋ณ„ ์žฌํ˜„์œจ๊ณผ ์ฒ˜๋ฆฌ ์ง€์—ฐ์„ ๊ฒ€์ฆํ•˜๊ณ  ๊ฐœ์ธยท์ค‘๋Œ€๋ณ„ ์˜ค๋‹ต ํŒจํ„ด์„ ๋ถ„์„ํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import re\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.pipeline import make_pipeline\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "# ---- ์˜ค๋‹ต ์œ ํ˜• ๋ถ„๋ฅ˜๊ธฐ(๊ทœ์น™ + ๋ฌธ์ž n-gram ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€) ----\n", "SEED = {\"ํ”ผ์•„ ์˜ค์ธ\": [\"์ ๊ธฐ\", \"์  ๋“œ๋ก \", \"์•„๊ตฐ๊ธฐ\", \"์  ํ—ฌ๊ธฐ\", \"์ ๊ธฐํŒ์ •\", \"ํ—ฌ๊ธฐ\", \"์  ํ•ญ๊ณต๊ธฐ ํ™•์ •\", \"์•„๊ตฐ ๋“œ๋ก \"],\n", " \"์ ˆ์ฐจ ๋ˆ„๋ฝ\": [\"์ƒ๋žต\", \"์ธ์ ‘๋ถ€๋Œ€\", \"๊ฐœ์ธ๋ฉ”์‹ ์ €\", \"๋ณด๊ณ  ์•ˆ ํ•จ\", \"์šด์šฉ๋ณ‘\", \"๋ฏธํ™•์ธ๊ธฐ\", \"๋‹จ๊ณ„ ๊ฑด๋„ˆ๋œ€\", \"์ง์ ‘ ์ „ํŒŒ\"],\n", " \"๊ฐœ๋… ํ˜ผ๋™\": [\"์ง์„ ์ต\", \"ํ›„ํ‡ด์ต\", \"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\", \"๋ฏธํƒ\", \"์žฌ๊ฒฝ๋ณด\", \"ํ™•์ •\", \"ํ™•์ธ\", \"๋ ˆ์ด๋”\", \"AI\", \"ํ‘œ์ ํš๋“\", \"๊ฒฝ๊ณ„\", \"ํ†ต์‹ ๋ง\", \"๊ณ ์ •์ต\"]}\n", "\n", "\n", "def build_text_model():\n", " texts, labels = [], []\n", " for q in BANK:\n", " if q[\"type\"] == \"SA\":\n", " for w, et in q[\"wrong\"].items():\n", " texts.append(w); labels.append(et)\n", " for et, ws in SEED.items():\n", " texts += ws; labels += [et] * len(ws)\n", " m = make_pipeline(TfidfVectorizer(analyzer=\"char_wb\", ngram_range=(1, 3)), LogisticRegression(max_iter=2000, C=5))\n", " return m.fit([norm_text(t) for t in texts], labels)\n", "\n", "\n", "def classify_error(q, val, text_model):\n", " \"\"\"์˜ค๋‹ต 1๊ฑด์˜ ์œ ํ˜•๊ณผ ํŒ๋‹จ ๊ทผ๊ฑฐ(rule/text)๋ฅผ ๋Œ๋ ค์ค€๋‹ค.\"\"\"\n", " if q[\"type\"] == \"MC\":\n", " return q[\"dist\"].get(int(val), \"๊ฐœ๋… ํ˜ผ๋™\") if val in \"1234\" and val else \"๊ฐœ๋… ํ˜ผ๋™\", \"๋ณด๊ธฐ ๋งคํ•‘\"\n", " if q[\"type\"] == \"ORDER\":\n", " return \"์ ˆ์ฐจ ๋ˆ„๋ฝ\", (\"๋‹จ๊ณ„ ๋ˆ„๋ฝ\" if len(val) < 4 else \"์ˆœ์„œ ๋’ค๋ฐ”๋€œ\")\n", " known = {norm_text(w): et for w, et in q[\"wrong\"].items()}\n", " if val in known:\n", " return known[val], \"์˜ค๋‹ต ๋ชฉ๋ก\"\n", " near = max(known, key=lambda w: difflib.SequenceMatcher(None, val, w).ratio())\n", " if difflib.SequenceMatcher(None, val, near).ratio() >= 0.75 or sorted(val) == sorted(near):\n", " return known[near], \"์˜ค๋‹ต ๋ชฉ๋ก(์˜คํƒˆ์ž)\"\n", " return str(text_model.predict([val])[0]), \"๋ฌธ์ž ๋ชจ๋ธ\"\n", "\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ธ์ • ๋‹ต์•ˆยท์˜ค๋‹ต ๋ชฉ๋ก์— ์—†๋Š” ์ƒˆ๋กœ์šด ์˜ค๋‹ต ํ‘œํ˜„(๋ฌธ์ž ๋ชจ๋ธ ์‹œํ—˜์šฉ)\n", "NOVEL = [(\"Q21\", \"์ ๊ธฐ ํ™•์ •\", \"ํ”ผ์•„ ์˜ค์ธ\"), (\"Q21\", \"ํ‘œ์  ์ถ”์ \", \"๊ฐœ๋… ํ˜ผ๋™\"), (\"Q22\", \"์ง์„  ๋‚ ๊ฐœ\", \"๊ฐœ๋… ํ˜ผ๋™\"), (\"Q23\", \"ํ™•์‹คํ•จ\", \"๊ฐœ๋… ํ˜ผ๋™\"),\n", " (\"Q23\", \"๋ณด๊ณ  ์•ˆํ•จ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q24\", \"์ž๋™ ํŒ์ •\", \"๊ฐœ๋… ํ˜ผ๋™\"), (\"Q24\", \"๊ด€์ธก ์šด์šฉ๋ณ‘\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q25\", \"๋ฏธํƒ์ง€\", \"๊ฐœ๋… ํ˜ผ๋™\"),\n", " (\"Q25\", \"์  ๋“œ๋ก \", \"ํ”ผ์•„ ์˜ค์ธ\"), (\"Q26\", \"์  ์ •์ฐฐ๊ธฐ\", \"ํ”ผ์•„ ์˜ค์ธ\"), (\"Q26\", \"๋ณด๊ณ  ์ƒ๋žต\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q27\", \"๊ณ ์ •์ต ๋“œ๋ก \", \"๊ฐœ๋… ํ˜ผ๋™\"),\n", " (\"Q27\", \"์  ํ—ฌ๊ธฐ\", \"ํ”ผ์•„ ์˜ค์ธ\"), (\"Q28\", \"์ธ์ ‘ ๋ถ€๋Œ€ ์ง๋ณด\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q28\", \"๋‹จ์ฒด ๋ฉ”์‹ ์ €\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q21\", \"์•„๊ตฐ๊ธฐ\", \"ํ”ผ์•„ ์˜ค์ธ\"),\n", " (\"Q23\", \"๋Œ€์ถฉ ๋ณด๊ณ \", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q22\", \"๋‚ ๊ฐœ ์—†์Œ\", \"๊ฐœ๋… ํ˜ผ๋™\"), (\"Q26\", \"๋ฏธํ™•์ธ ํ‘œ์ ์œผ๋กœ ๋Œ€์‘\", \"ํ”ผ์•„ ์˜ค์ธ\"),\n", " (\"Q24\", \"๋ถ„๋Œ€์› ๋ˆ„๊ตฌ๋‚˜\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"), (\"Q25\", \"์ •์ƒ ๊ฒฝ๋ณด\", \"๊ฐœ๋… ํ˜ผ๋™\"), (\"Q27\", \"์ •์ฐฐ ํ—ฌ๊ธฐ\", \"ํ”ผ์•„ ์˜ค์ธ\")]\n", "\n", "model = build_text_model()\n", "df = make_responses(40, seed=7)\n", "df[\"type\"] = df[\"qid\"].map(lambda q: QIDX[q][\"type\"])\n", "df[\"val\"] = [parse(QIDX[q], r)[0] for q, r in zip(df[\"qid\"], df[\"raw\"])]\n", "wrong = df[df[\"truth_etype\"].notna()].copy()\n", "\n", "t0 = time.perf_counter()\n", "out = [classify_error(QIDX[q], v, model) for q, v in zip(wrong[\"qid\"], wrong[\"val\"])]\n", "ms = (time.perf_counter() - t0) * 1000 / len(wrong)\n", "wrong[\"pred\"], wrong[\"basis\"] = [o[0] for o in out], [o[1] for o in out]\n", "\n", "# ๋ณตํ•ฉ ์˜ค๋‹ต ์‹œ๋‚˜๋ฆฌ์˜ค 10์ข…\n", "rng = np.random.default_rng(5)\n", "sa_typo = wrong[(wrong[\"type\"] == \"SA\")].copy()\n", "sa_typo[\"val\"] = [norm_text(_typo(v, rng)) if len(v) >= 3 else v for v in sa_typo[\"truth_answer\"]]\n", "sa_typo[\"pred\"] = [classify_error(QIDX[q], v, model)[0] for q, v in zip(sa_typo[\"qid\"], sa_typo[\"val\"])]\n", "nov = pd.DataFrame(NOVEL, columns=[\"qid\", \"val\", \"truth_etype\"])\n", "nov[\"val\"] = nov[\"val\"].map(norm_text)\n", "nov[\"pred\"] = [classify_error(QIDX[q], v, model)[0] for q, v in zip(nov[\"qid\"], nov[\"val\"])]\n", "raw_fmt = wrong[\"raw\"].str.strip() != wrong[\"val\"]\n", "SCEN = {\n", " \"S1 ์„ ๋‹คํ˜• ํ‘œ์ค€ ํ‘œ๊ธฐ\": wrong[(wrong[\"type\"] == \"MC\") & ~raw_fmt], \"S2 ์„ ๋‹คํ˜• ์›๋ฌธ์žยท์ „๊ฐ ํ‘œ๊ธฐ\": wrong[(wrong[\"type\"] == \"MC\") & raw_fmt],\n", " \"S3 ๋‹จ๋‹ต ์˜ค๋‹ต ๋ชฉ๋ก ์ผ์น˜\": wrong[(wrong[\"type\"] == \"SA\") & (wrong[\"basis\"] == \"์˜ค๋‹ต ๋ชฉ๋ก\")], \"S4 ๋‹จ๋‹ต ์˜ค๋‹ต + ์˜คํƒˆ์ž\": sa_typo,\n", " \"S5 ๋‹จ๋‹ต ์˜ค๋‹ต + ์–ด๋ฏธยท๋„์–ด์“ฐ๊ธฐ\": wrong[(wrong[\"type\"] == \"SA\") & raw_fmt], \"S6 ์ˆœ์„œํ˜• ๋‹จ๊ณ„ ๋ˆ„๋ฝ\": wrong[(wrong[\"type\"] == \"ORDER\") & (wrong[\"val\"].str.len() < 4)],\n", " \"S7 ์ˆœ์„œํ˜• ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\": wrong[(wrong[\"type\"] == \"ORDER\") & (wrong[\"val\"].str.len() == 4)], \"S8 ์ˆœ์„œํ˜• ๊ตฌ๋ถ„์ž ํ˜ผ์ž…\": wrong[(wrong[\"type\"] == \"ORDER\") & raw_fmt],\n", " \"S9 ๋ชฉ๋ก์— ์—†๋Š” ์ƒˆ ์˜ค๋‹ต\": nov, \"S10 ์ „์ฒด ํ˜ผํ•ฉ\": pd.concat([wrong, sa_typo, nov]),\n", "}\n", "rows = []\n", "for name, d in SCEN.items():\n", " rec = {et: (d[d[\"truth_etype\"] == et][\"pred\"] == et).mean() if (d[\"truth_etype\"] == et).any() else np.nan for et in ETYPES}\n", " rows.append({\"์‹œ๋‚˜๋ฆฌ์˜ค\": name, \"๊ฑด์ˆ˜\": len(d), \"์ •ํ™•๋„\": (d[\"pred\"] == d[\"truth_etype\"]).mean(), **{f\"{k} ์žฌํ˜„์œจ\": v for k, v in rec.items()}})\n", "res = pd.DataFrame(rows)\n", "res[\"ํ†ต๊ณผ\"] = np.where(res[\"์ •ํ™•๋„\"] >= 0.85, \"PASS\", \"FAIL\")\n", "print(res.round(3).fillna(\"-\").to_string(index=False))\n", "allr = SCEN[\"S10 ์ „์ฒด ํ˜ผํ•ฉ\"]\n", "recs = {et: (allr[allr[\"truth_etype\"] == et][\"pred\"] == et).mean() for et in ETYPES}\n", "print(\"\\n์˜ค๋‹ต ์œ ํ˜•๋ณ„ ์žฌํ˜„์œจ:\", {k: round(float(v), 3) for k, v in recs.items()},\n", " f\"(๋ชฉํ‘œ 85% ์ด์ƒ: {'๋‹ฌ์„ฑ' if min(recs.values()) >= 0.85 else '๋ฏธ๋‹ฌ'}), ๊ฑด๋‹น ์ฒ˜๋ฆฌ {ms:.3f} ms (๋ชฉํ‘œ 200 ms ์ดํ•˜)\")\n", "print(f\"์‹œ๋‚˜๋ฆฌ์˜ค ํ†ต๊ณผ {(res['ํ†ต๊ณผ'] == 'PASS').sum()}/10\")\n", "fa = nov[nov[\"pred\"] != nov[\"truth_etype\"]]\n", "print(\"์ƒˆ ์˜ค๋‹ต ์˜ค๋ถ„๋ฅ˜(ํŠน์ง• ์žฌ์„ค๊ณ„ ๋Œ€์ƒ):\", [f\"{v}->{p}\" for v, p in zip(fa[\"val\"], fa[\"pred\"])])\n", "\n", "# ์˜ค๋‹ต ํŒจํ„ด ๋ถ„์„: ๊ฐœ์ธ๋ณ„ ์ฃผ๋œ ์˜ค๋‹ต ์œ ํ˜•, ์ค‘๋Œ€๋ณ„ ๋ถ„ํฌ\n", "dom = wrong.groupby(\"sid\")[\"pred\"].agg(lambda s: s.value_counts().idxmax())\n", "print(\"\\n๊ฐœ์ธ๋ณ„ ์ฃผ๋œ ์˜ค๋‹ต ์œ ํ˜• ๋ถ„ํฌ:\", dom.value_counts().to_dict())\n", "print(pd.crosstab(wrong[\"unit\"], wrong[\"pred\"], normalize=\"index\").round(2).to_string())\n", "\n", "ct = pd.crosstab(wrong[\"qid\"].map(lambda q: QIDX[q][\"area\"]), wrong[\"pred\"])\n", "ct.index = ct.index.map({\"ํ˜•์ƒ์‹๋ณ„\": \"Shape ID\", \"ํ”ผ์•„์‹๋ณ„\": \"Friend/Foe\", \"๊ฒฝ๋ณด๋ณด๊ณ \": \"Alert/Report\", \"๊ต์ „ํ†ต์ œ\": \"Control\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\": \"C-UAS basics\"})\n", "ct.columns = ct.columns.map({\"๊ฐœ๋… ํ˜ผ๋™\": \"concept\", \"ํ”ผ์•„ ์˜ค์ธ\": \"friend/foe\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\": \"procedure\"})\n", "ct.plot(kind=\"barh\", stacked=True, figsize=(8, 4)); plt.xlabel(\"wrong answers\"); plt.title(\"Error types by area (synthetic)\"); plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 04 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", " ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฑด์ˆ˜ ์ •ํ™•๋„ ๊ฐœ๋… ํ˜ผ๋™ ์žฌํ˜„์œจ ํ”ผ์•„ ์˜ค์ธ ์žฌํ˜„์œจ ์ ˆ์ฐจ ๋ˆ„๋ฝ ์žฌํ˜„์œจ ํ†ต๊ณผ\n", " S1 ์„ ๋‹คํ˜• ํ‘œ์ค€ ํ‘œ๊ธฐ 367 1.000 1.0 1.0 1.000 PASS\n", " S2 ์„ ๋‹คํ˜• ์›๋ฌธ์žยท์ „๊ฐ ํ‘œ๊ธฐ 235 1.000 1.0 1.0 1.000 PASS\n", " S3 ๋‹จ๋‹ต ์˜ค๋‹ต ๋ชฉ๋ก ์ผ์น˜ 120 1.000 1.0 1.0 1.000 PASS\n", " S4 ๋‹จ๋‹ต ์˜ค๋‹ต + ์˜คํƒˆ์ž 148 1.000 1.0 1.0 1.000 PASS\n", "S5 ๋‹จ๋‹ต ์˜ค๋‹ต + ์–ด๋ฏธยท๋„์–ด์“ฐ๊ธฐ 55 1.000 1.0 1.0 1.000 PASS\n", " S6 ์ˆœ์„œํ˜• ๋‹จ๊ณ„ ๋ˆ„๋ฝ 90 1.000 - - 1.000 PASS\n", " S7 ์ˆœ์„œํ˜• ์ˆœ์„œ ๋’ค๋ฐ”๋€œ 94 1.000 - - 1.000 PASS\n", " S8 ์ˆœ์„œํ˜• ๊ตฌ๋ถ„์ž ํ˜ผ์ž… 81 1.000 - - 1.000 PASS\n", " S9 ๋ชฉ๋ก์— ์—†๋Š” ์ƒˆ ์˜ค๋‹ต 22 0.864 1.0 0.857 0.714 PASS\n", " S10 ์ „์ฒด ํ˜ผํ•ฉ 1104 0.997 1.0 0.995 0.996 PASS\n", "\n", "์˜ค๋‹ต ์œ ํ˜•๋ณ„ ์žฌํ˜„์œจ: {'๊ฐœ๋… ํ˜ผ๋™': 1.0, 'ํ”ผ์•„ ์˜ค์ธ': 0.995, '์ ˆ์ฐจ ๋ˆ„๋ฝ': 0.996} (๋ชฉํ‘œ 85% ์ด์ƒ: ๋‹ฌ์„ฑ), ๊ฑด๋‹น ์ฒ˜๋ฆฌ 0.005 ms (๋ชฉํ‘œ 200 ms ์ดํ•˜)\n", "์‹œ๋‚˜๋ฆฌ์˜ค ํ†ต๊ณผ 10/10\n", "์ƒˆ ์˜ค๋‹ต ์˜ค๋ถ„๋ฅ˜(ํŠน์ง• ์žฌ์„ค๊ณ„ ๋Œ€์ƒ): ['๋Œ€์ถฉ๋ณด๊ณ ->๊ฐœ๋… ํ˜ผ๋™', '๋ฏธํ™•์ธํ‘œ์ ์œผ๋กœ๋Œ€์‘->์ ˆ์ฐจ ๋ˆ„๋ฝ', '๋ถ„๋Œ€์›๋ˆ„๊ตฌ๋‚˜->๊ฐœ๋… ํ˜ผ๋™']\n", "\n", "๊ฐœ์ธ๋ณ„ ์ฃผ๋œ ์˜ค๋‹ต ์œ ํ˜• ๋ถ„ํฌ: {'์ ˆ์ฐจ ๋ˆ„๋ฝ': 60, '๊ฐœ๋… ํ˜ผ๋™': 42, 'ํ”ผ์•„ ์˜ค์ธ': 12}\n", "pred ๊ฐœ๋… ํ˜ผ๋™ ์ ˆ์ฐจ ๋ˆ„๋ฝ ํ”ผ์•„ ์˜ค์ธ\n", "unit \n", "๊ฐ€์ƒ1์ค‘๋Œ€ 0.36 0.46 0.18\n", "๊ฐ€์ƒ2์ค‘๋Œ€ 0.40 0.41 0.19\n", "๊ฐ€์ƒ3์ค‘๋Œ€ 0.34 0.49 0.17\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 05. ๊ฐœ์ธ๋ณ„/๋ถ€๋Œ€๋ณ„ ๋ฐฉ๊ณต ์ทจ์•ฝ ์˜์—ญ ์‹๋ณ„ ๋ฐ ๋งž์ถคํ˜• ๋ณด์ถฉ ํ•™์Šต ์ถ”์ฒœ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ฐœ๋ฐœ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๊ฐœ์ธ๋ณ„/๋ถ€๋Œ€๋ณ„ ๋ฐฉ๊ณต ์ทจ์•ฝ ์˜์—ญ ์‹๋ณ„ ๋ฐ ๋งž์ถคํ˜• ๋ณด์ถฉ ํ•™์Šต ์ถ”์ฒœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ฐœ๋ฐœํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์ฑ„์  ๊ฒฐ๊ณผ๋กœ ๊ฐœ์ธ๋ณ„ยท์ค‘๋Œ€๋ณ„ ๋ฐฉ๊ณต ์ทจ์•ฝ ์˜์—ญ์„ ์ฐพ๊ณ , ๋ถˆํ•„์š”ํ•œ ์ถ”์ฒœ์„ ์ค„์ธ ๋งž์ถคํ˜• ๋ณด์ถฉ ํ•™์Šต ์ถ”์ฒœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ฐœ๋ฐœํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **๋ฌธํ•ญ ๋‚œ์ด๋„๋ฅผ ๋ฐ˜์˜ํ•œ ๋ฒ ์ด์ฆˆ ์ถ”์ •์œผ๋กœ ์˜์—ญ๋ณ„ '์ทจ์•ฝํ•  ํ™•๋ฅ '์„ ๊ณ„์‚ฐํ•˜๊ณ  High/Medium/Low ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰์„ ๋ถ€์—ฌ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/verification/trn02_confidence_engine.py` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰(High/Med/Low) ๋ถ€์—ฌ ๊ธฐ์ค€์˜ ๊ตฐ์‚ฌ์  ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์˜ค๊ฒฝ๋ณด์œจ(FAR) 40% ์ด์ƒ ๊ฐ์ถ•, ์‹ ๋ขฐ๋„ ์Šค์ฝ”์–ด ์‚ฐ์ถœ ์ผ๊ด€์„ฑ 90% ์ด์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์˜ค๊ฒฝ๋ณด ํ•„ํ„ฐ๋ง ์ „ํ›„ ๋น„๊ต ์ง€ํ‘œ ์‚ฐ์ถœ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ROC-AUC ๊ณก์„  ๋ถ„์„ ๋ฐ ์ž„๊ณ„๊ฐ’ ๋ฏผ๊ฐ๋„ ํ…Œ์ŠคํŠธ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ฑ„์  ๊ฒฐ๊ณผ๋กœ ๊ฐœ์ธ๋ณ„ยท์ค‘๋Œ€๋ณ„ ๋ฐฉ๊ณต ์ทจ์•ฝ ์˜์—ญ์„ ์ฐพ์•„ ๋ณด์ถฉ ํ•™์Šต์„ ์ถ”์ฒœํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ฐœ๋ฐœํ•œ๋‹ค. ๋ฌธํ•ญ ๋‚œ์ด๋„๋ฅผ ๋ฐ˜์˜ํ•œ ๋ฒ ์ด์ฆˆ ์ถ”์ •์œผ๋กœ ์˜์—ญ๋ณ„ '์ทจ์•ฝํ•  ํ™•๋ฅ '๊ณผ ์‹ ๋ขฐ๋„ ๋“ฑ๊ธ‰์„ ์‚ฐ์ถœํ•˜๊ณ , ํ‹€๋ฆฐ ๋ฌธํ•ญ์ด ์žˆ์œผ๋ฉด ๋ชจ๋‘ ์ถ”์ฒœํ•˜๋˜ ๋ฐฉ์‹ ๋Œ€๋น„ ๋ถˆํ•„์š”ํ•œ ์ถ”์ฒœ(์˜ค๊ฒฝ๋ณด) ๊ฐ์ถ•ยท๊ฒฐ์ • ์ผ๊ด€์„ฑยทROC-AUC๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from scipy import stats\n", "from sklearn.metrics import roc_auc_score\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "MASTERY = 0.6 # ์˜์—ญ ์ •๋‹ต๋ฅ ์ด ์ด๋ณด๋‹ค ๋‚ฎ์œผ๋ฉด '์ทจ์•ฝ'\n", "HIGH, MEDIUM = 0.8, 0.4\n", "GRID = np.linspace(-3, 3, 121)\n", "PRIOR = stats.norm.pdf(GRID, 0, 1)\n", "CUT = GRID[np.argmin(np.abs(p_correct(GRID, 2) - MASTERY))] # ์ค‘๊ฐ„ ๋‚œ์ด๋„ ์ •๋‹ต๋ฅ  0.6์— ํ•ด๋‹นํ•˜๋Š” ์ˆ™๋‹ฌ๋„\n", "\n", "\n", "def weak_prob(resp):\n", " \"\"\"(๋‚œ์ด๋„, ์ •๋‹ต ์—ฌ๋ถ€) ๋ชฉ๋ก -> ์ˆ™๋‹ฌ๋„ ์‚ฌํ›„๋ถ„ํฌ์—์„œ CUT ๋ฏธ๋งŒ์ผ ํ™•๋ฅ . ์–ด๋ ค์šด ๋ฌธํ•ญ ์˜ค๋‹ต์€ ๋œ, ์‰ฌ์šด ๋ฌธํ•ญ ์˜ค๋‹ต์€ ๋” ํฌ๊ฒŒ ๋ฐ˜์˜๋œ๋‹ค.\"\"\"\n", " post = PRIOR.copy()\n", " for d, c in resp:\n", " p = p_correct(GRID, d)\n", " post *= p if c else 1 - p\n", " return float(post[GRID < CUT].sum() / post.sum())\n", "\n", "\n", "def area_table(df):\n", " g = df.assign(d=df[\"qid\"].map(lambda q: QIDX[q][\"diff\"]))\n", " t = g.groupby([\"sid\", \"unit\", \"area\"]).agg(wrong=(\"correct\", lambda s: int((~s).sum())),\n", " resp=(\"d\", lambda s: list(zip(s, g.loc[s.index, \"correct\"])))).reset_index()\n", " t[\"score\"] = [weak_prob(r) for r in t[\"resp\"]]\n", " t[\"grade\"] = np.where(t[\"score\"] >= HIGH, \"High\", np.where(t[\"score\"] >= MEDIUM, \"Medium\", \"Low\"))\n", " return t\n", "\n", "\n", "df = make_responses(40, seed=7)\n", "df[\"area\"] = df[\"qid\"].map(lambda q: QIDX[q][\"area\"])\n", "df[\"correct\"] = [grade_fuzzy(QIDX[q], parse(QIDX[q], r)[0]) for q, r in zip(df[\"qid\"], df[\"raw\"])]\n", "sol = {s[\"sid\"]: s for s in make_soldiers(40, 7)}\n", "t = area_table(df)\n", "t[\"truth_weak\"] = [p_correct(sol[s][\"theta\"][a], 2) < MASTERY for s, a in zip(t[\"sid\"], t[\"area\"])] # ๊ฐ€์ƒ ์‹ค์ œ ์ˆ™๋‹ฌ๋„(ํ‰๊ฐ€์šฉ)\n", "\n", "\n", "def report(name, rec):\n", " fa = (rec & ~t[\"truth_weak\"]).sum()\n", " return {\"๋ฐฉ์‹\": name, \"์ถ”์ฒœ ์ˆ˜\": int(rec.sum()), \"๋ถˆํ•„์š” ์ถ”์ฒœ\": int(fa), \"์˜ค๊ฒฝ๋ณด์œจ\": round(fa / max(rec.sum(), 1), 3),\n", " \"์ทจ์•ฝ ์˜์—ญ ์žฌํ˜„์œจ\": round((rec & t[\"truth_weak\"]).sum() / t[\"truth_weak\"].sum(), 3)}\n", "\n", "\n", "before = report(\"๊ธฐ์กด(์˜ค๋‹ต 1๊ฐœ๋ผ๋„ ์žˆ์œผ๋ฉด ์ถ”์ฒœ)\", t[\"wrong\"] >= 1)\n", "after = report(\"๋‚œ์ด๋„ ๋ฐ˜์˜ ์ถ”์ •(Medium ์ด์ƒ)\", t[\"grade\"] != \"Low\")\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์‘์‹œ์ž 120๋ช… x ์˜์—ญ 5๊ฐœ = {len(t)}๊ฑด, ์‹ค์ œ ์ทจ์•ฝ {int(t['truth_weak'].sum())}๊ฑด\")\n", "print(pd.DataFrame([before, after]).to_string(index=False))\n", "cut = 1 - after[\"์˜ค๊ฒฝ๋ณด์œจ\"] / before[\"์˜ค๊ฒฝ๋ณด์œจ\"]\n", "retest = ((t[\"grade\"] == \"Low\") & (t[\"score\"] >= 0.2)).sum()\n", "print(f\"Low ๋“ฑ๊ธ‰ ์ค‘ ์ทจ์•ฝ ํ™•๋ฅ  0.2 ์ด์ƒ {retest}๊ฑด์€ ์ถ”์ฒœ ๋Œ€์‹  '์ถ”๊ฐ€ ๋ฌธํ•ญ ์ถœ์ œ ํ›„ ์žฌํŒ๋‹จ' ๋Œ€์ƒ์œผ๋กœ ๋‘”๋‹ค.\")\n", "print(f\"\\n์˜ค๊ฒฝ๋ณด์œจ ๊ฐ์ถ• {cut:.1%} (๋ชฉํ‘œ 40% ์ด์ƒ: {'๋‹ฌ์„ฑ' if cut >= 0.4 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# ์ผ๊ด€์„ฑ: ์˜์—ญ๋งˆ๋‹ค ์‘๋‹ต 1๊ฑด์”ฉ ๋นผ๊ณ  ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด๋„ ์ถ”์ฒœ ๊ฒฐ์ •์ด ์œ ์ง€๋˜๋Š” ๋น„์œจ\n", "rng = np.random.default_rng(0)\n", "drop = df.groupby([\"sid\", \"area\"]).sample(1, random_state=1).index\n", "t2 = area_table(df.drop(drop))\n", "cons = ((t2[\"grade\"] != \"Low\").values == (t[\"grade\"] != \"Low\").values).mean()\n", "print(f\"์ถ”์ฒœ ๊ฒฐ์ • ์ผ๊ด€์„ฑ(์‘๋‹ต 1๊ฑด ์ œ์™ธ ์žฌ๊ณ„์‚ฐ) {cons:.1%} (๋ชฉํ‘œ 90% ์ด์ƒ: {'๋‹ฌ์„ฑ' if cons >= 0.9 else '๋ฏธ๋‹ฌ'})\")\n", "auc = roc_auc_score(t[\"truth_weak\"], t[\"score\"])\n", "print(f\"์ทจ์•ฝ ํ™•๋ฅ  ์ ์ˆ˜ ROC-AUC {auc:.3f}\")\n", "print(\"[์ž„๊ณ„๊ฐ’ ๋ฏผ๊ฐ๋„]\", \", \".join(f\"{m}: ์˜ค๊ฒฝ๋ณด์œจ {report('', t['score'] >= m)['์˜ค๊ฒฝ๋ณด์œจ']:.2f}/์žฌํ˜„์œจ {report('', t['score'] >= m)['์ทจ์•ฝ ์˜์—ญ ์žฌํ˜„์œจ']:.2f}\" for m in [0.3, 0.4, 0.5, 0.7]))\n", "\n", "# ๊ฐœ์ธ ๋ณด์ถฉ ํ•™์Šต ์ถ”์ฒœ ์˜ˆ์‹œ\n", "s0 = t[t[\"grade\"] != \"Low\"].sort_values(\"score\", ascending=False)[\"sid\"].iloc[0]\n", "print(f\"\\n[๊ฐœ์ธ ์ถ”์ฒœ ์˜ˆ์‹œ: {s0}]\")\n", "for r in t[(t[\"sid\"] == s0) & (t[\"grade\"] != \"Low\")].itertuples():\n", " wrong_q = df[(df[\"sid\"] == s0) & (df[\"area\"] == r.area) & ~df[\"correct\"]][\"qid\"].tolist()\n", " easy = [q[\"qid\"] for q in BANK if q[\"area\"] == r.area and q[\"diff\"] == 1]\n", " print(f\" - {r.area}: ์ทจ์•ฝ ํ™•๋ฅ  {r.score:.2f}({r.grade}), ์˜ค๋‹ต {wrong_q} -> ๊ธฐ์ดˆ ๋ฌธํ•ญ {easy} ์žฌํ•™์Šต ํ›„ ์žฌํ‰๊ฐ€\")\n", "\n", "# ์ค‘๋Œ€๋ณ„ ์ทจ์•ฝ ์˜์—ญ: ์†Œ์† ์ธ์› 30% ์ด์ƒ์ด ์ทจ์•ฝ(Medium ์ด์ƒ)์ด๋ฉด ์ค‘๋Œ€ ๋‹จ์œ„ ๋ณด์ถฉ ๊ต์œก ๊ถŒ๊ณ \n", "u = t.assign(rec=t[\"grade\"] != \"Low\").pivot_table(index=\"unit\", columns=\"area\", values=\"rec\", aggfunc=\"mean\").round(2)\n", "print(\"\\n[์ค‘๋Œ€๋ณ„ ์ทจ์•ฝ ์ธ์› ๋น„์œจ]\")\n", "print(u.to_string())\n", "print(\"์ค‘๋Œ€ ๋‹จ์œ„ ๋ณด์ถฉ ๊ต์œก ๊ถŒ๊ณ :\", [f\"{i}-{c}\" for i in u.index for c in u.columns if u.loc[i, c] >= 0.3])\n", "print(\"์ถ”์ฒœ์€ ํ•™์Šต ๋ณด์กฐ ์ •๋ณด์ด๋ฉฐ ๊ฐœ์ธ ํ‰๊ฐ€ยท์ธ์‚ฌ ์ž๋ฃŒ๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.\")\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", "for g, col in [(\"High\", \"tab:red\"), (\"Medium\", \"tab:orange\"), (\"Low\", \"tab:green\")]:\n", " ax[0].hist(t[t[\"grade\"] == g][\"score\"], bins=20, range=(0, 1), color=col, alpha=.7, label=g)\n", "ax[0].set_title(\"Weakness probability by grade\"); ax[0].set_xlabel(\"P(accuracy < 0.6)\"); ax[0].legend()\n", "im = ax[1].imshow(u.values, cmap=\"Reds\", vmin=0, vmax=1)\n", "ax[1].set_xticks(range(5)); ax[1].set_xticklabels([\"Shape\", \"IFF\", \"Report\", \"Control\", \"C-UAS\"][:0] or [{\"ํ˜•์ƒ์‹๋ณ„\": \"Shape\", \"ํ”ผ์•„์‹๋ณ„\": \"IFF\", \"๊ฒฝ๋ณด๋ณด๊ณ \": \"Report\", \"๊ต์ „ํ†ต์ œ\": \"Control\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\": \"C-UAS\"}[c] for c in u.columns])\n", "ax[1].set_yticks(range(3)); ax[1].set_yticklabels([\"Co.1\", \"Co.2\", \"Co.3\"]); plt.colorbar(im, ax=ax[1]); ax[1].set_title(\"Share of weak soldiers by unit/area\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 05 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์‘์‹œ์ž 120๋ช… x ์˜์—ญ 5๊ฐœ = 600๊ฑด, ์‹ค์ œ ์ทจ์•ฝ 120๊ฑด\n", " ๋ฐฉ์‹ ์ถ”์ฒœ ์ˆ˜ ๋ถˆํ•„์š” ์ถ”์ฒœ ์˜ค๊ฒฝ๋ณด์œจ ์ทจ์•ฝ ์˜์—ญ ์žฌํ˜„์œจ\n", " ๊ธฐ์กด(์˜ค๋‹ต 1๊ฐœ๋ผ๋„ ์žˆ์œผ๋ฉด ์ถ”์ฒœ) 444 324 0.730 1.000\n", "๋‚œ์ด๋„ ๋ฐ˜์˜ ์ถ”์ •(Medium ์ด์ƒ) 148 62 0.419 0.717\n", "Low ๋“ฑ๊ธ‰ ์ค‘ ์ทจ์•ฝ ํ™•๋ฅ  0.2 ์ด์ƒ 96๊ฑด์€ ์ถ”์ฒœ ๋Œ€์‹  '์ถ”๊ฐ€ ๋ฌธํ•ญ ์ถœ์ œ ํ›„ ์žฌํŒ๋‹จ' ๋Œ€์ƒ์œผ๋กœ ๋‘”๋‹ค.\n", "\n", "์˜ค๊ฒฝ๋ณด์œจ ๊ฐ์ถ• 42.6% (๋ชฉํ‘œ 40% ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "์ถ”์ฒœ ๊ฒฐ์ • ์ผ๊ด€์„ฑ(์‘๋‹ต 1๊ฑด ์ œ์™ธ ์žฌ๊ณ„์‚ฐ) 91.2% (๋ชฉํ‘œ 90% ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "์ทจ์•ฝ ํ™•๋ฅ  ์ ์ˆ˜ ROC-AUC 0.898\n", "[์ž„๊ณ„๊ฐ’ ๋ฏผ๊ฐ๋„] 0.3: ์˜ค๊ฒฝ๋ณด์œจ 0.50/์žฌํ˜„์œจ 0.82, 0.4: ์˜ค๊ฒฝ๋ณด์œจ 0.42/์žฌํ˜„์œจ 0.72, 0.5: ์˜ค๊ฒฝ๋ณด์œจ 0.35/์žฌํ˜„์œจ 0.57, 0.7: ์˜ค๊ฒฝ๋ณด์œจ 0.15/์žฌํ˜„์œจ 0.47\n", "\n", "[๊ฐœ์ธ ์ถ”์ฒœ ์˜ˆ์‹œ: S098]\n", " - ๊ฒฝ๋ณด๋ณด๊ณ : ์ทจ์•ฝ ํ™•๋ฅ  1.00(High), ์˜ค๋‹ต ['Q09', 'Q10', 'Q11', 'Q12', 'Q23', 'Q28', 'Q29'] -> ๊ธฐ์ดˆ ๋ฌธํ•ญ ['Q09', 'Q12', 'Q28'] ์žฌํ•™์Šต ํ›„ ์žฌํ‰๊ฐ€\n", " - ๋Œ€๋“œ๋ก ์ผ๋ฐ˜: ์ทจ์•ฝ ํ™•๋ฅ  0.87(High), ์˜ค๋‹ต ['Q18', 'Q19', 'Q25', 'Q30'] -> ๊ธฐ์ดˆ ๋ฌธํ•ญ ['Q17', 'Q25'] ์žฌํ•™์Šต ํ›„ ์žฌํ‰๊ฐ€\n", "\n", "[์ค‘๋Œ€๋ณ„ ์ทจ์•ฝ ์ธ์› ๋น„์œจ]\n", "area ๊ฒฝ๋ณด๋ณด๊ณ  ๊ต์ „ํ†ต์ œ ๋Œ€๋“œ๋ก ์ผ๋ฐ˜ ํ”ผ์•„์‹๋ณ„ ํ˜•์ƒ์‹๋ณ„\n", "unit \n", "๊ฐ€์ƒ1์ค‘๋Œ€ 0.10 0.20 0.12 0.15 0.10\n", "๊ฐ€์ƒ2์ค‘๋Œ€ 0.25 0.57 0.25 0.38 0.25\n", "๊ฐ€์ƒ3์ค‘๋Œ€ 0.30 0.35 0.18 0.32 0.18\n", "์ค‘๋Œ€ ๋‹จ์œ„ ๋ณด์ถฉ ๊ต์œก ๊ถŒ๊ณ : ['๊ฐ€์ƒ2์ค‘๋Œ€-๊ต์ „ํ†ต์ œ', '๊ฐ€์ƒ2์ค‘๋Œ€-ํ”ผ์•„์‹๋ณ„', '๊ฐ€์ƒ3์ค‘๋Œ€-๊ฒฝ๋ณด๋ณด๊ณ ', '๊ฐ€์ƒ3์ค‘๋Œ€-๊ต์ „ํ†ต์ œ', '๊ฐ€์ƒ3์ค‘๋Œ€-ํ”ผ์•„์‹๋ณ„']\n", "์ถ”์ฒœ์€ ํ•™์Šต ๋ณด์กฐ ์ •๋ณด์ด๋ฉฐ ๊ฐœ์ธ ํ‰๊ฐ€ยท์ธ์‚ฌ ์ž๋ฃŒ๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 06. ๋ถ€๋Œ€ ๋ฐฉ๊ณต ์ง€์‹ ์ˆ˜์ค€ ํ†ต๊ณ„ ๋ฐ ์˜ค๋‹ต ๋ถ„ํฌ ๊ฐ€์‹œํ™” ๊ต์ˆ˜์ž ๋Œ€์‹œ๋ณด๋“œ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋ถ€๋Œ€ ๋ฐฉ๊ณต ์ง€์‹ ํ†ต๊ณ„ ๋ฐ ์˜ค๋‹ต ๋ถ„ํฌ ๊ฐ€์‹œํ™” ๊ต์ˆ˜์ž ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋ถ€๋Œ€ ๋ฐฉ๊ณต ์ง€์‹ ์ˆ˜์ค€๊ณผ ์˜ค๋‹ต ๋ถ„ํฌ๋ฅผ ํ•œ๋ˆˆ์— ๋ณด์—ฌ ์ฃผ๋Š” ๊ต์ˆ˜์ž ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ค‘๋Œ€x์˜์—ญ ์ •๋‹ต๋ฅ  ํžˆํŠธ๋งต, ์ค‘๋Œ€๋ณ„ ์˜ค๋‹ต ์œ ํ˜• ๋น„์œจ, ์ ์ˆ˜ ๋ถ„ํฌ์™€ ํ•ฉ๊ฒฉ์„ , ๊ณ ๋‚œ๋„ ๋ฌธํ•ญ ๋ชฉ๋ก ๋“ฑ ๊ตฌ์„ฑ์š”์†Œ 4์ข… ์ด์ƒ์„ ๊ทธ๋ฆฐ๋‹ค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/ui/trn02_dashboard.py (๋˜๋Š” ์›น UI ์ปดํฌ๋„ŒํŠธ)` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ „์ˆ  ์ž‘์ „ ์ƒํ™ฉํŒ์œผ๋กœ์„œ์˜ ์ง๊ด€์„ฑ ๋ฐ ์ธ์ง€ ๋ถ€ํ•˜ ๊ฐ์†Œ๋„\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ํ™”๋ฉด ๊ฐฑ์‹  ์ฃผ๊ธฐ 1์ดˆ ์ด๋‚ด, UI ์‘๋‹ต์„ฑ 100ms ์ด๋‚ด\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ฃผ์š” ์ง€ํ‘œ ์‹œ๊ฐํ™” ์ปดํฌ๋„ŒํŠธ 4์ข… ์ด์ƒ ๋ Œ๋”๋ง ๋ฐ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์—ฐ๋™\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜ ํ…Œ์ŠคํŠธ ๋ฐ ๋ชจ์˜ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ ๋ Œ๋”๋ง ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ฑ„์ ยท์˜ค๋‹ต ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ๋กœ ์ค‘๋Œ€๋ณ„ ์˜์—ญ ์ •๋‹ต๋ฅ , ์˜ค๋‹ต ์œ ํ˜• ๋ถ„ํฌ, ์ ์ˆ˜ ๋ถ„ํฌ, ๋ฌธํ•ญ ๋‚œ์ด๋„ ๋“ฑ ๊ต์ˆ˜์ž ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ์š”์†Œ 4์ข… ์ด์ƒ์„ ๊ทธ๋ฆฌ๊ณ , ์ƒˆ ์‘๋‹ต ๋ฌถ์Œ์ด ๋“ค์–ด์˜ฌ ๋•Œ ์ง‘๊ณ„ ๊ฐฑ์‹  ์‹œ๊ฐ„๊ณผ ํ•„ํ„ฐ ์กฐํšŒ ์‘๋‹ต ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋ฉฐ ๊ฒฝ๋ณด ํ•˜์ด๋ผ์ดํŠธ ๋Œ€์ƒ์„ ์ถœ๋ ฅํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import re\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.pipeline import make_pipeline\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "# ---- ์˜ค๋‹ต ์œ ํ˜• ๋ถ„๋ฅ˜๊ธฐ(๊ทœ์น™ + ๋ฌธ์ž n-gram ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€) ----\n", "SEED = {\"ํ”ผ์•„ ์˜ค์ธ\": [\"์ ๊ธฐ\", \"์  ๋“œ๋ก \", \"์•„๊ตฐ๊ธฐ\", \"์  ํ—ฌ๊ธฐ\", \"์ ๊ธฐํŒ์ •\", \"ํ—ฌ๊ธฐ\", \"์  ํ•ญ๊ณต๊ธฐ ํ™•์ •\", \"์•„๊ตฐ ๋“œ๋ก \"],\n", " \"์ ˆ์ฐจ ๋ˆ„๋ฝ\": [\"์ƒ๋žต\", \"์ธ์ ‘๋ถ€๋Œ€\", \"๊ฐœ์ธ๋ฉ”์‹ ์ €\", \"๋ณด๊ณ  ์•ˆ ํ•จ\", \"์šด์šฉ๋ณ‘\", \"๋ฏธํ™•์ธ๊ธฐ\", \"๋‹จ๊ณ„ ๊ฑด๋„ˆ๋œ€\", \"์ง์ ‘ ์ „ํŒŒ\"],\n", " \"๊ฐœ๋… ํ˜ผ๋™\": [\"์ง์„ ์ต\", \"ํ›„ํ‡ด์ต\", \"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\", \"๋ฏธํƒ\", \"์žฌ๊ฒฝ๋ณด\", \"ํ™•์ •\", \"ํ™•์ธ\", \"๋ ˆ์ด๋”\", \"AI\", \"ํ‘œ์ ํš๋“\", \"๊ฒฝ๊ณ„\", \"ํ†ต์‹ ๋ง\", \"๊ณ ์ •์ต\"]}\n", "\n", "\n", "def build_text_model():\n", " texts, labels = [], []\n", " for q in BANK:\n", " if q[\"type\"] == \"SA\":\n", " for w, et in q[\"wrong\"].items():\n", " texts.append(w); labels.append(et)\n", " for et, ws in SEED.items():\n", " texts += ws; labels += [et] * len(ws)\n", " m = make_pipeline(TfidfVectorizer(analyzer=\"char_wb\", ngram_range=(1, 3)), LogisticRegression(max_iter=2000, C=5))\n", " return m.fit([norm_text(t) for t in texts], labels)\n", "\n", "\n", "def classify_error(q, val, text_model):\n", " \"\"\"์˜ค๋‹ต 1๊ฑด์˜ ์œ ํ˜•๊ณผ ํŒ๋‹จ ๊ทผ๊ฑฐ(rule/text)๋ฅผ ๋Œ๋ ค์ค€๋‹ค.\"\"\"\n", " if q[\"type\"] == \"MC\":\n", " return q[\"dist\"].get(int(val), \"๊ฐœ๋… ํ˜ผ๋™\") if val in \"1234\" and val else \"๊ฐœ๋… ํ˜ผ๋™\", \"๋ณด๊ธฐ ๋งคํ•‘\"\n", " if q[\"type\"] == \"ORDER\":\n", " return \"์ ˆ์ฐจ ๋ˆ„๋ฝ\", (\"๋‹จ๊ณ„ ๋ˆ„๋ฝ\" if len(val) < 4 else \"์ˆœ์„œ ๋’ค๋ฐ”๋€œ\")\n", " known = {norm_text(w): et for w, et in q[\"wrong\"].items()}\n", " if val in known:\n", " return known[val], \"์˜ค๋‹ต ๋ชฉ๋ก\"\n", " near = max(known, key=lambda w: difflib.SequenceMatcher(None, val, w).ratio())\n", " if difflib.SequenceMatcher(None, val, near).ratio() >= 0.75 or sorted(val) == sorted(near):\n", " return known[near], \"์˜ค๋‹ต ๋ชฉ๋ก(์˜คํƒˆ์ž)\"\n", " return str(text_model.predict([val])[0]), \"๋ฌธ์ž ๋ชจ๋ธ\"\n", "\n", "PASS_RATE = 0.7\n", "ALERT_AREA = 0.55 # ์ค‘๋Œ€ ์˜์—ญ ์ •๋‹ต๋ฅ ์ด ์ด๋ณด๋‹ค ๋‚ฎ์œผ๋ฉด ๊ฒฝ๋ณด ํ•˜์ด๋ผ์ดํŠธ\n", "TM = build_text_model()\n", "\n", "\n", "def grade_batch(df):\n", " \"\"\"์›๋ณธ ์‘๋‹ต ๋ฌถ์Œ -> ์ •์ œ๊ฐ’ยท์ •๋‹ต ์—ฌ๋ถ€ยท์ ์ˆ˜ยท์˜ค๋‹ต ์œ ํ˜• ์—ด ์ถ”๊ฐ€.\"\"\"\n", " out = df.copy()\n", " pv = [parse(QIDX[q], r) for q, r in zip(out[\"qid\"], out[\"raw\"])]\n", " out[\"val\"], out[\"status\"] = [p[0] for p in pv], [p[1] for p in pv]\n", " out[\"area\"] = out[\"qid\"].map(lambda q: QIDX[q][\"area\"])\n", " out[\"correct\"] = [s == \"์ •์ƒ\" and grade_fuzzy(QIDX[q], v) for q, v, s in zip(out[\"qid\"], out[\"val\"], out[\"status\"])]\n", " out[\"score\"] = [score_of(QIDX[q], c) for q, c in zip(out[\"qid\"], out[\"correct\"])]\n", " out[\"etype\"] = [None if c or s != \"์ •์ƒ\" else classify_error(QIDX[q], v, TM)[0]\n", " for q, v, c, s in zip(out[\"qid\"], out[\"val\"], out[\"correct\"], out[\"status\"])]\n", " return out\n", "\n", "\n", "class Board:\n", " \"\"\"๋Œ€์‹œ๋ณด๋“œ ์ƒํƒœ: ๋ˆ„์  ์ง‘๊ณ„๋งŒ ๊ฐฑ์‹ ํ•ด ๋งค๋ฒˆ ์ „์ฒด๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค.\"\"\"\n", " def __init__(self):\n", " self.data = pd.DataFrame()\n", "\n", " def update(self, batch):\n", " self.data = pd.concat([self.data, grade_batch(batch)], ignore_index=True)\n", " d = self.data\n", " self.area = d.pivot_table(index=\"unit\", columns=\"area\", values=\"correct\", aggfunc=\"mean\")\n", " self.etype = d.dropna(subset=[\"etype\"]).groupby([\"unit\", \"etype\"]).size().unstack(fill_value=0)\n", " self.person = d.groupby([\"sid\", \"unit\"])[\"score\"].sum().reset_index()\n", " self.item = d.groupby(\"qid\")[\"correct\"].mean()\n", "\n", " def query(self, unit=None, area=None):\n", " d = self.data\n", " if unit:\n", " d = d[d[\"unit\"] == unit]\n", " if area:\n", " d = d[d[\"area\"] == area]\n", " return d.groupby(\"qid\")[\"correct\"].mean().sort_values().head(3)\n", "\n", "\n", "full = make_responses(40, seed=7)\n", "FULL_SCORE = sum(POINTS[q[\"diff\"]] for q in BANK)\n", "board = Board()\n", "# 1) ์‹ค์‹œ๊ฐ„ ๊ฐฑ์‹  ๋ชจ์˜: ์‘์‹œ์ž ์‘๋‹ต์ด 30๋ช… ๋‹จ์œ„๋กœ 4๋ฒˆ ๋“ค์–ด์˜จ๋‹ค\n", "times = []\n", "for k, sids in enumerate(np.array_split(full[\"sid\"].unique(), 4), 1):\n", " t0 = time.perf_counter()\n", " board.update(full[full[\"sid\"].isin(sids)])\n", " times.append((time.perf_counter() - t0) * 1000)\n", " print(f\"๊ฐฑ์‹  {k}: ๋ˆ„์  ์‘๋‹ต {len(board.data):,}๊ฑด, ์ง‘๊ณ„ ๊ฐฑ์‹  {times[-1]:.0f} ms\")\n", "print(f\"์ตœ๋Œ€ ๊ฐฑ์‹  ์‹œ๊ฐ„ {max(times):.0f} ms (๋ชฉํ‘œ 1์ดˆ ์ด๋‚ด: {'๋‹ฌ์„ฑ' if max(times) < 1000 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# 2) ํ•„ํ„ฐ ์กฐํšŒ ์‘๋‹ต์„ฑ(๊ต์ˆ˜์ž๊ฐ€ ์ค‘๋Œ€ยท์˜์—ญ์„ ๋ฐ”๊ฟ” ๊ฐ€๋ฉฐ ์กฐํšŒ)\n", "q_times = []\n", "for u in UNITS + [None]:\n", " for a in AREAS + [None]:\n", " t0 = time.perf_counter(); board.query(u, a); q_times.append((time.perf_counter() - t0) * 1000)\n", "print(f\"ํ•„ํ„ฐ ์กฐํšŒ {len(q_times)}ํšŒ, ํ‰๊ท  {np.mean(q_times):.1f} ms, ์ตœ๋Œ€ {max(q_times):.1f} ms (๋ชฉํ‘œ 100 ms ์ด๋‚ด: {'๋‹ฌ์„ฑ' if max(q_times) < 100 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# 3) ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ์š”์†Œ 4์ข…\n", "fig, ax = plt.subplots(2, 2, figsize=(12, 8))\n", "AREA_EN = {\"ํ˜•์ƒ์‹๋ณ„\": \"Shape\", \"ํ”ผ์•„์‹๋ณ„\": \"IFF\", \"๊ฒฝ๋ณด๋ณด๊ณ \": \"Report\", \"๊ต์ „ํ†ต์ œ\": \"Control\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\": \"C-UAS\"}\n", "UNIT_EN = {\"๊ฐ€์ƒ1์ค‘๋Œ€\": \"Co.1\", \"๊ฐ€์ƒ2์ค‘๋Œ€\": \"Co.2\", \"๊ฐ€์ƒ3์ค‘๋Œ€\": \"Co.3\"}\n", "ET_EN = {\"๊ฐœ๋… ํ˜ผ๋™\": \"Concept\", \"ํ”ผ์•„ ์˜ค์ธ\": \"IFF error\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\": \"Procedure\"}\n", "A = board.area[AREAS]\n", "im = ax[0, 0].imshow(A.values, cmap=\"RdYlGn\", vmin=0.3, vmax=1.0)\n", "ax[0, 0].set_xticks(range(len(AREAS)), [AREA_EN[a] for a in AREAS]); ax[0, 0].set_yticks(range(3), [UNIT_EN[u] for u in A.index])\n", "for i in range(A.shape[0]):\n", " for j in range(A.shape[1]):\n", " v = A.values[i, j]\n", " ax[0, 0].text(j, i, f\"{v:.2f}\", ha=\"center\", va=\"center\", fontweight=\"bold\" if v < ALERT_AREA else \"normal\",\n", " color=\"white\" if v < ALERT_AREA else \"black\")\n", "ax[0, 0].set_title(\"Accuracy by company x area (bold = alert)\"); fig.colorbar(im, ax=ax[0, 0])\n", "E = board.etype.rename(index=UNIT_EN, columns=ET_EN)\n", "E.div(E.sum(axis=1), axis=0).plot.bar(stacked=True, ax=ax[0, 1], rot=0)\n", "ax[0, 1].set_title(\"Error type share by company\"); ax[0, 1].set_ylabel(\"share\")\n", "for u in UNITS:\n", " ax[1, 0].hist(board.person.loc[board.person[\"unit\"] == u, \"score\"], bins=12, alpha=0.5, label=UNIT_EN[u])\n", "ax[1, 0].axvline(FULL_SCORE * PASS_RATE, color=\"k\", ls=\"--\", label=\"pass line\"); ax[1, 0].legend()\n", "ax[1, 0].set_title(\"Score distribution\"); ax[1, 0].set_xlabel(\"score\")\n", "it = board.item.sort_values()\n", "ax[1, 1].barh(it.index[:10], it.values[:10], color=\"tab:red\"); ax[1, 1].set_xlim(0, 1)\n", "ax[1, 1].set_title(\"10 hardest items (accuracy)\")\n", "plt.tight_layout(); plt.show()\n", "print(\"๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ์š”์†Œ 4์ข… ๋ Œ๋”๋ง: ์ค‘๋Œ€x์˜์—ญ ์ •๋‹ต๋ฅ , ์˜ค๋‹ต ์œ ํ˜• ๋น„์œจ, ์ ์ˆ˜ ๋ถ„ํฌ, ๊ณ ๋‚œ๋„ ๋ฌธํ•ญ\")\n", "\n", "# 4) ์š”์•ฝ ์ง€ํ‘œ์™€ ๊ฒฝ๋ณด ํ•˜์ด๋ผ์ดํŠธ\n", "p = board.person.assign(passed=board.person[\"score\"] >= FULL_SCORE * PASS_RATE)\n", "print(\"\\n[์ค‘๋Œ€๋ณ„ ์š”์•ฝ]\")\n", "print(p.groupby(\"unit\").agg(์‘์‹œ=(\"sid\", \"size\"), ํ‰๊ท ์ ์ˆ˜=(\"score\", \"mean\"), ํ•ฉ๊ฒฉ๋ฅ =(\"passed\", \"mean\")).round(2).to_string())\n", "alerts = [(u, a, A.loc[u, a]) for u in A.index for a in AREAS if A.loc[u, a] < ALERT_AREA]\n", "print(f\"\\n[๊ฒฝ๋ณด ํ•˜์ด๋ผ์ดํŠธ] ์˜์—ญ ์ •๋‹ต๋ฅ  {ALERT_AREA} ๋ฏธ๋งŒ {len(alerts)}๊ฑด\")\n", "for u, a, v in sorted(alerts, key=lambda x: x[2]):\n", " print(f\" - {u} {a}: ์ •๋‹ต๋ฅ  {v:.2f}, ์ตœ์ € ๋ฌธํ•ญ {list(board.query(u, a).index)}\")\n", "top = board.etype.sum().sort_values(ascending=False)\n", "print(\"๋ถ€๋Œ€ ์ „์ฒด ์˜ค๋‹ต ์œ ํ˜•:\", {k: int(v) for k, v in top.items()})\n", "print(\"์•ผ๊ฐ„ ๊ต์œก์žฅ ์‚ฌ์šฉ ์‹œ plt.style.use('dark_background')๋กœ ์–ด๋‘์šด ํ™”๋ฉด ๋ชจ๋“œ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 06 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "๊ฐฑ์‹  1: ๋ˆ„์  ์‘๋‹ต 960๊ฑด, ์ง‘๊ณ„ ๊ฐฑ์‹  39 ms\n", "๊ฐฑ์‹  2: ๋ˆ„์  ์‘๋‹ต 1,920๊ฑด, ์ง‘๊ณ„ ๊ฐฑ์‹  35 ms\n", "๊ฐฑ์‹  3: ๋ˆ„์  ์‘๋‹ต 2,880๊ฑด, ์ง‘๊ณ„ ๊ฐฑ์‹  39 ms\n", "๊ฐฑ์‹  4: ๋ˆ„์  ์‘๋‹ต 3,840๊ฑด, ์ง‘๊ณ„ ๊ฐฑ์‹  38 ms\n", "์ตœ๋Œ€ ๊ฐฑ์‹  ์‹œ๊ฐ„ 39 ms (๋ชฉํ‘œ 1์ดˆ ์ด๋‚ด: ๋‹ฌ์„ฑ)\n", "ํ•„ํ„ฐ ์กฐํšŒ 24ํšŒ, ํ‰๊ท  2.7 ms, ์ตœ๋Œ€ 3.4 ms (๋ชฉํ‘œ 100 ms ์ด๋‚ด: ๋‹ฌ์„ฑ)\n", "๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ์š”์†Œ 4์ข… ๋ Œ๋”๋ง: ์ค‘๋Œ€x์˜์—ญ ์ •๋‹ต๋ฅ , ์˜ค๋‹ต ์œ ํ˜• ๋น„์œจ, ์ ์ˆ˜ ๋ถ„ํฌ, ๊ณ ๋‚œ๋„ ๋ฌธํ•ญ\n", "\n", "[์ค‘๋Œ€๋ณ„ ์š”์•ฝ]\n", " ์‘์‹œ ํ‰๊ท ์ ์ˆ˜ ํ•ฉ๊ฒฉ๋ฅ \n", "unit \n", "๊ฐ€์ƒ1์ค‘๋Œ€ 40 75.40 0.68\n", "๊ฐ€์ƒ2์ค‘๋Œ€ 40 58.55 0.28\n", "๊ฐ€์ƒ3์ค‘๋Œ€ 40 68.18 0.57\n", "\n", "[๊ฒฝ๋ณด ํ•˜์ด๋ผ์ดํŠธ] ์˜์—ญ ์ •๋‹ต๋ฅ  0.55 ๋ฏธ๋งŒ 1๊ฑด\n", " - ๊ฐ€์ƒ2์ค‘๋Œ€ ๊ต์ „ํ†ต์ œ: ์ •๋‹ต๋ฅ  0.50, ์ตœ์ € ๋ฌธํ•ญ ['Q31', 'Q15', 'Q14']\n", "๋ถ€๋Œ€ ์ „์ฒด ์˜ค๋‹ต ์œ ํ˜•: {'์ ˆ์ฐจ ๋ˆ„๋ฝ': 419, '๊ฐœ๋… ํ˜ผ๋™': 367, 'ํ”ผ์•„ ์˜ค์ธ': 186}\n", "์•ผ๊ฐ„ ๊ต์œก์žฅ ์‚ฌ์šฉ ์‹œ plt.style.use('dark_background')๋กœ ์–ด๋‘์šด ํ™”๋ฉด ๋ชจ๋“œ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 07. ์ด์˜ ์ œ๊ธฐ ๋ฌธํ•ญ ๋ฐ ์„œ์ˆ ํ˜• ๋‹ต๋ณ€์— ๋Œ€ํ•œ ๊ต๊ด€ ์ˆ˜๋™ ์ฑ„์ (HITL) ๊ฒ€ํ†  ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์ด์˜ ์ œ๊ธฐ ๋ฌธํ•ญ ๋ฐ ์„œ์ˆ ํ˜• ๋‹ต๋ณ€์— ๋Œ€ํ•œ ๊ต๊ด€ ์ˆ˜๋™ ์ฑ„์ (HITL) ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์ด์˜ ์ œ๊ธฐ ๋ฌธํ•ญ๊ณผ ์„œ์ˆ ํ˜• ๋‹ต๋ณ€์„ ๊ต๊ด€์ด ๊ฒ€ํ† ยทํ™•์ •ํ•˜๋Š” HITL ํ๋ฆ„์„ ์„ค๊ณ„ํ•˜๊ณ , ์ž๋™ ์ฑ„์ ์ด ์ ์ˆ˜๋ฅผ ๋‹จ์ •ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์„œ์ˆ ํ˜•์€ ํ•ต์‹ฌ ์š”์†Œ ํ‚ค์›Œ๋“œ๋กœ ์ ์ˆ˜๋ฅผ '์ œ์•ˆ'๋งŒ ํ•˜๊ณ , ์ด์˜ ์ œ๊ธฐ์™€ ํ•จ๊ป˜ ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ยท์ €์‹ ๋ขฐ ๋‹ต์•ˆ๋ถ€ํ„ฐ ๊ฒ€ํ† ํ•˜๋Š” ์šฐ์„ ์ˆœ์œ„ ๋Œ€๊ธฐ์—ด์— ์˜ฌ๋ฆฐ๋‹ค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/hitl/trn02_approval_workflow.py ๋ฐ docs/trn02_hitl_guide.md` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ง€ํœ˜ ์ฑ…์ž„ ๋ถ„๊ณ„์„  ๋ช…ํ™•ํ™” ๋ฐ ์„ฑ๊ธ‰ํ•œ ์ž๋™ ๋‹จ์ • ๋ฐฉ์ง€ ์ฒด๊ณ„ ๋ถ€ํ•ฉ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์ธ๊ฐ„ ๊ฒ€ํ†  ํ(Queue) ๋Œ€๊ธฐ์‹œ๊ฐ„ ๋ฐ ์Šน์ธ/๋ฐ˜๋ ค ์ฒ˜๋ฆฌ ๋กœ๊น… ์„ฑ๊ณต๋ฅ  100%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์Šน์ธ/๋ฐ˜๋ ค ์ธํ„ฐํŽ˜์ด์Šค ๋™์ž‘ ๋ฐ ๊ฐ์‚ฌ ๋กœ๊ทธ(Audit Log) ์ƒ์„ฑ ๊ฒ€์ฆ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๋ชจ์˜ ์กฐ์ž‘์ž ์Šน์ธ/๋ฐ˜๋ ค ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ด์˜ ์ œ๊ธฐ๋œ ๋‹จ๋‹ตํ˜• ๋ฌธํ•ญ๊ณผ ์„œ์ˆ ํ˜• ๋‹ต๋ณ€์„ ๊ต๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ์—ด(HITL)์— ์˜ฌ๋ ค, ์ž๋™ ์ฑ„์ ์€ '์ œ์•ˆ'์œผ๋กœ๋งŒ ๋‘๊ณ  ๊ต๊ด€์˜ ์ธ์ •ยท๊ธฐ๊ฐยท์ˆ˜์ • ๊ฒฐ์ •๊ณผ ์‚ฌ์œ ๋ฅผ ํ•ด์‹œ ์ฒด์ธ ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๋‚จ๊ธฐ๋Š” ๊ฒ€ํ†  ํ๋ฆ„์„ ์„ค๊ณ„ํ•œ๋‹ค. ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ ์‘์‹œ์ž๋ฅผ ๋จผ์ € ๊ฒ€ํ† ํ•˜๋Š” ์šฐ์„ ์ˆœ์œ„ ํ์˜ ๋Œ€๊ธฐ์‹œ๊ฐ„, ๋กœ๊น… ์„ฑ๊ณต๋ฅ , ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ์„ ํ™•์ธํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import hashlib\n", "import heapq\n", "import json\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "# ---- ์„œ์ˆ ํ˜• ๋ฌธํ•ญ(10์ ): ํ•ต์‹ฌ ์š”์†Œ 5๊ฐœ x 2์  ----\n", "ESSAY = \"๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๊ด€์ธกํ–ˆ์„ ๋•Œ ๋ณด๊ณ ์™€ ๋Œ€์‘ ์ ˆ์ฐจ๋ฅผ ์„œ์ˆ ํ•˜์‹œ์˜ค.\"\n", "ELEM = {\"๊ด€์ธก์ •๋ณด\": ([\"๊ด€์ธก ์‹œ๊ฐ๊ณผ ๋ฐฉํ–ฅ, ๊ฑฐ๋ฆฌ๋ฅผ ์ ๋Š”๋‹ค\", \"์–ธ์ œ ์–ด๋А ๋ฐฉํ–ฅ ๋ช‡ km์ธ์ง€ ๊ธฐ๋กํ•œ๋‹ค\"], [\"์‹œ๊ฐ\", \"๋ฐฉํ–ฅ\", \"๊ฑฐ๋ฆฌ\"]),\n", " \"์ถ”์ •ํ‘œํ˜„\": ([\"ํ™•์‹คํ•˜์ง€ ์•Š์œผ๋ฉด ์ถ”์ •์ด๋ผ๊ณ  ๋ฐํžŒ๋‹ค\", \"๋ชจ๋ฅด๋Š” ๋ถ€๋ถ„์€ ์ถ”์ •์ž„์„ ๋ช…์‹œํ•œ๋‹ค\"], [\"์ถ”์ •\"]),\n", " \"์ง€ํœ˜๊ณ„ํ†ต\": ([\"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ์ฆ‰์‹œ ๋ณด๊ณ ํ•œ๋‹ค\", \"์†Œ๋Œ€์žฅ์—๊ฒŒ ๋ณด๊ณ ํ•˜๊ณ  ์ƒ๊ธ‰์œผ๋กœ ์˜ฌ๋ฆฐ๋‹ค\"], [\"์ง€ํœ˜๊ณ„ํ†ต\"]),\n", " \"์Šน์ธ๋Œ€๊ธฐ\": ([\"์ง€ํœ˜๊ด€ ์Šน์ธ ์ „์—๋Š” ๋Œ€์‘ํ•˜์ง€ ์•Š๋Š”๋‹ค\", \"์Šน์ธ์ด ๋‚˜๊ธฐ ์ „๊นŒ์ง€ ์กฐ์น˜ํ•˜์ง€ ์•Š๊ณ  ๋Œ€๊ธฐํ•œ๋‹ค\"], [\"์Šน์ธ\"]),\n", " \"๊ทผ๊ฑฐ๊ธฐ๋ก\": ([\"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•œ๋‹ค\", \"์กฐ์น˜ ๋‚ด์šฉ์„ ์ผ์ง€์— ๋‚จ๊ธด๋‹ค\"], [\"๊ธฐ๋ก\", \"๊ทผ๊ฑฐ\"])}\n", "WRONG_SENT = [\"์Šน์ธ ์—†์ด ๋ฐ”๋กœ ๋Œ€์‘ํ•œ๋‹ค\", \"๊ธฐ๋ก์€ ์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ƒ๋žตํ•œ๋‹ค\", \"ํ™•์ •๋œ ์  ๋“œ๋ก ์œผ๋กœ ๋ณด๊ณ ํ•œ๋‹ค\"] # ํ‚ค์›Œ๋“œ๋Š” ์žˆ์ง€๋งŒ ํ‹€๋ฆฐ ์ง„์ˆ \n", "\n", "\n", "def make_essays(sids, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for s in sids:\n", " have = [e for e in ELEM if rng.random() < 0.65]\n", " sents = [str(rng.choice(ELEM[e][0])) for e in have]\n", " if rng.random() < 0.2:\n", " sents.append(str(rng.choice(WRONG_SENT)))\n", " rng.shuffle(sents)\n", " out.append({\"sid\": s, \"text\": \" \".join(sents), \"truth\": 2 * len(have)})\n", " return out\n", "\n", "\n", "def auto_suggest(text):\n", " \"\"\"ํ‚ค์›Œ๋“œ ํฌํ•จ ์—ฌ๋ถ€๋กœ ์ ์ˆ˜๋ฅผ '์ œ์•ˆ'ํ•œ๋‹ค(ํ™•์ • ์•„๋‹˜). ๋ถ€์ • ๋ฌธ๋งฅ์„ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•˜๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค.\"\"\"\n", " hit = [e for e, (_, kws) in ELEM.items() if any(k in text for k in kws)]\n", " conf = \"๋‚ฎ์Œ\" if any(w in text for w in (\"์—†์ด\", \"์ƒ๋žต\", \"ํ™•์ •\")) or len(text) < 25 else \"๋ณดํ†ต\"\n", " return 2 * len(hit), hit, conf\n", "\n", "\n", "class ReviewQueue:\n", " \"\"\"์šฐ์„ ์ˆœ์œ„ ํ + ํ•ด์‹œ ์ฒด์ธ ๊ฐ์‚ฌ ๋กœ๊ทธ. ๊ต๊ด€ ๊ฒฐ์ • ์—†์ด๋Š” ์ ์ˆ˜๋ฅผ ํ™•์ •ํ•  ์ˆ˜ ์—†๋‹ค.\"\"\"\n", " def __init__(self):\n", " self.heap, self.items, self.log, self.final = [], {}, [], {}\n", "\n", " def _audit(self, event, **kw):\n", " prev = self.log[-1][\"hash\"] if self.log else \"GENESIS\"\n", " body = {\"seq\": len(self.log), \"event\": event, **kw}\n", " self.log.append({**body, \"prev\": prev, \"hash\": hashlib.sha256((prev + json.dumps(body, sort_keys=True, ensure_ascii=False)).encode()).hexdigest()})\n", "\n", " def submit(self, iid, prio, t, payload):\n", " self.items[iid] = {**payload, \"t_in\": t, \"status\": \"PENDING\"}\n", " heapq.heappush(self.heap, (prio, t, iid))\n", " self._audit(\"SUBMIT\", iid=iid, prio=prio, suggest=payload.get(\"suggest\"))\n", "\n", " def pop(self):\n", " return heapq.heappop(self.heap)[2] if self.heap else None\n", "\n", " def decide(self, iid, reviewer, decision, score, reason, t):\n", " if not reason:\n", " raise ValueError(\"์‚ฌ์œ  ์—†๋Š” ๊ฒฐ์ •์€ ๊ธฐ๋กํ•  ์ˆ˜ ์—†๋‹ค\")\n", " it = self.items[iid]\n", " it.update(status=decision, score=score, reviewer=reviewer, t_out=t)\n", " self.final[iid] = score\n", " self._audit(\"DECIDE\", iid=iid, reviewer=reviewer, decision=decision, score=score, reason=reason)\n", "\n", " def finalize_without_review(self, iid):\n", " if self.items[iid][\"status\"] == \"PENDING\":\n", " raise PermissionError(\"๊ต๊ด€ ๊ฒฐ์ • ์ „ ์ž๋™ ํ™•์ • ๊ธˆ์ง€\")\n", "\n", " def verify(self):\n", " prev = \"GENESIS\"\n", " for e in self.log:\n", " body = {k: v for k, v in e.items() if k not in (\"prev\", \"hash\")}\n", " if e[\"prev\"] != prev or hashlib.sha256((prev + json.dumps(body, sort_keys=True, ensure_ascii=False)).encode()).hexdigest() != e[\"hash\"]:\n", " return False, e[\"seq\"]\n", " prev = e[\"hash\"]\n", " return True, None\n", "\n", "\n", "rng = np.random.default_rng(3)\n", "df = make_responses(40, seed=7)\n", "df[\"val\"] = [parse(QIDX[q], r)[0] for q, r in zip(df[\"qid\"], df[\"raw\"])]\n", "df[\"correct\"] = [grade_fuzzy(QIDX[q], v) for q, v in zip(df[\"qid\"], df[\"val\"])]\n", "df[\"score\"] = [score_of(QIDX[q], c) for q, c in zip(df[\"qid\"], df[\"correct\"])]\n", "total = df.groupby(\"sid\")[\"score\"].sum()\n", "PASS = 0.7 * (sum(POINTS[q[\"diff\"]] for q in BANK) + 10)\n", "\n", "# 1) ์ด์˜ ์ œ๊ธฐ: ์ž๋™ ์ฑ„์  ์˜ค๋‹ต ์ฒ˜๋ฆฌ๋œ ๋‹จ๋‹ตํ˜• ์ค‘ ์‘์‹œ์ž๊ฐ€ ์ด์˜๋ฅผ ๋‚ธ ๊ฑด(์‹ค์ œ ๋งž์€ ๋‹ต์€ ๋Œ€๋ถ€๋ถ„ ์ด์˜ ์ œ๊ธฐ, ํ‹€๋ฆฐ ๋‹ต๋„ ์ผ๋ถ€ ์ œ๊ธฐ)\n", "sa_wrong = df[(df[\"qid\"].map(lambda q: QIDX[q][\"type\"]) == \"SA\") & ~df[\"correct\"] & (df[\"raw\"] != \"\")]\n", "appeal = sa_wrong[sa_wrong[\"truth_correct\"] | (rng.random(len(sa_wrong)) < 0.08)]\n", "essays = make_essays(sorted(df[\"sid\"].unique()), seed=5)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ด์˜ ์ œ๊ธฐ {len(appeal)}๊ฑด, ์„œ์ˆ ํ˜• ๋‹ต์•ˆ {len(essays)}๊ฑด์ด ๊ต๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ์—ด์— ๋“ค์–ด๊ฐ„๋‹ค.\")\n", "\n", "q = ReviewQueue()\n", "t = 0.0\n", "for e in essays:\n", " sug, hit, conf = auto_suggest(e[\"text\"])\n", " est = total[e[\"sid\"]] + sug\n", " prio = (0 if abs(est - PASS) <= 6 else 1) * 10 + (0 if conf == \"๋‚ฎ์Œ\" else 1) # ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ยท์ €์‹ ๋ขฐ ๋จผ์ €\n", " q.submit(f\"E-{e['sid']}\", prio, t, {\"kind\": \"์„œ์ˆ ํ˜•\", \"sid\": e[\"sid\"], \"suggest\": sug, \"conf\": conf, \"truth\": e[\"truth\"], \"text\": e[\"text\"]})\n", " t += 0.05\n", "for _, r in appeal.iterrows():\n", " est = total[r[\"sid\"]]\n", " prio = (0 if abs(est - PASS) <= 6 else 1) * 10 + 2\n", " q.submit(f\"A-{r['rid']}\", prio, t, {\"kind\": \"์ด์˜ ์ œ๊ธฐ\", \"sid\": r[\"sid\"], \"qid\": r[\"qid\"], \"raw\": r[\"raw\"], \"suggest\": 0,\n", " \"truth\": score_of(QIDX[r[\"qid\"]], r[\"truth_correct\"])})\n", " t += 0.05\n", "\n", "# 2) ์ž๋™ ํ™•์ • ์‹œ๋„๋Š” ์ฐจ๋‹จ๋˜์–ด์•ผ ํ•œ๋‹ค\n", "blocked = 0\n", "for iid in list(q.items)[:5]:\n", " try:\n", " q.finalize_without_review(iid)\n", " except PermissionError:\n", " blocked += 1\n", "try:\n", " q.decide(list(q.items)[0], \"๊ต๊ด€A\", \"์ธ์ •\", 2, \"\", t)\n", " no_reason = \"ํ†ต๊ณผ(์˜ค๋ฅ˜)\"\n", "except ValueError:\n", " no_reason = \"์ฐจ๋‹จ\"\n", "print(f\"๊ฒฐ์ • ์ „ ์ž๋™ ํ™•์ • ์‹œ๋„ 5๊ฑด ์ค‘ ์ฐจ๋‹จ {blocked}๊ฑด, ์‚ฌ์œ  ์—†๋Š” ๊ฒฐ์ • {no_reason}\")\n", "\n", "# 3) ๊ต๊ด€ 2๋ช…์ด ์šฐ์„ ์ˆœ์œ„๋Œ€๋กœ ๊ฒ€ํ† (๋ชจ์˜ ๊ต๊ด€์€ ๊ธฐ์ค€ ๋‹ต์•ˆ์œผ๋กœ ํŒ๋‹จ: ๊ฑด๋‹น ์„œ์ˆ ํ˜• 2๋ถ„, ์ด์˜ ์ œ๊ธฐ 1๋ถ„)\n", "clock = {\"๊ต๊ด€A\": 0.0, \"๊ต๊ด€B\": 0.0}\n", "while True:\n", " iid = q.pop()\n", " if iid is None:\n", " break\n", " it = q.items[iid]\n", " who = min(clock, key=clock.get)\n", " clock[who] = max(clock[who], it[\"t_in\"]) + (2.0 if it[\"kind\"] == \"์„œ์ˆ ํ˜•\" else 1.0)\n", " if it[\"kind\"] == \"์„œ์ˆ ํ˜•\":\n", " dec = \"์Šน์ธ\" if it[\"truth\"] == it[\"suggest\"] else \"์ˆ˜์ •\"\n", " reason = \"์ œ์•ˆ ์ ์ˆ˜์™€ ์š”์†Œ ํŒ๋‹จ ์ผ์น˜\" if dec == \"์Šน์ธ\" else f\"์š”์†Œ ์žฌํŒ๋‹จ(์ œ์•ˆ {it['suggest']} -> {it['truth']})\"\n", " else:\n", " dec = \"์ธ์ •\" if it[\"truth\"] > 0 else \"๊ธฐ๊ฐ\"\n", " reason = f\"'{it['raw']}' ์˜๋ฏธ์ƒ ์ •๋‹ต ์ธ์ •\" if dec == \"์ธ์ •\" else f\"'{it['raw']}'๋Š” ์˜ค๋‹ต ์œ ์ง€\"\n", " q.decide(iid, who, dec, it[\"truth\"], reason, clock[who])\n", "\n", "R = pd.DataFrame(q.items.values())\n", "R[\"wait\"] = R[\"t_out\"] - R[\"t_in\"]\n", "R[\"border\"] = [abs(total[s] + (sg if k == \"์„œ์ˆ ํ˜•\" else 0) - PASS) <= 6 for s, sg, k in zip(R[\"sid\"], R[\"suggest\"], R[\"kind\"])]\n", "print(\"\\n[๊ฒ€ํ†  ๊ฒฐ๊ณผ]\")\n", "print(R.groupby([\"kind\", \"status\"]).size().to_string())\n", "ess = R[R[\"kind\"] == \"์„œ์ˆ ํ˜•\"]\n", "print(f\"์„œ์ˆ ํ˜• ์ž๋™ ์ œ์•ˆ ์ ์ˆ˜์™€ ๊ต๊ด€ ํŒ๋‹จ ์ผ์น˜ {(ess['status'] == '์Šน์ธ').mean():.1%}, ์ˆ˜์ • {(ess['status'] == '์ˆ˜์ •').sum()}๊ฑด\"\n", " f\"(ํ‹€๋ฆฐ ์ง„์ˆ  ์† ํ‚ค์›Œ๋“œ์— ์†์€ ์ œ์•ˆ ํฌํ•จ)\")\n", "print(f\"๋Œ€๊ธฐ์‹œ๊ฐ„(๋ถ„): ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ ํ‰๊ท  {R.loc[R['border'], 'wait'].mean():.1f}, ๊ทธ ์™ธ ํ‰๊ท  {R.loc[~R['border'], 'wait'].mean():.1f}, ์ตœ๋Œ€ {R['wait'].max():.1f}\")\n", "\n", "n_dec = sum(e[\"event\"] == \"DECIDE\" for e in q.log)\n", "print(f\"์Šน์ธ/๋ฐ˜๋ ค ์ฒ˜๋ฆฌ {len(R)}๊ฑด, ๊ฐ์‚ฌ ๋กœ๊ทธ ๊ธฐ๋ก {n_dec}๊ฑด, ๋กœ๊น… ์„ฑ๊ณต๋ฅ  {n_dec / len(R):.0%} (๋ชฉํ‘œ 100%: {'๋‹ฌ์„ฑ' if n_dec == len(R) else '๋ฏธ๋‹ฌ'})\")\n", "ok, _ = q.verify()\n", "q.log[7][\"score\"] = 10 # ๊ฐ์‚ฌ ๋กœ๊ทธ ์‚ฌํ›„ ๋ณ€์กฐ ์‹œ๋„\n", "ok2, where = q.verify()\n", "print(f\"๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ: ์›๋ณธ {'์ •์ƒ' if ok else '์ด์ƒ'}, ๋ณ€์กฐ ํ›„ {'์ •์ƒ' if ok2 else f'์ด์ƒ ํƒ์ง€(seq {where})'}\")\n", "\n", "changed = R[(R[\"kind\"] == \"์ด์˜ ์ œ๊ธฐ\") & (R[\"status\"] == \"์ธ์ •\")].groupby(\"sid\")[\"score\"].sum()\n", "newtot = total.add(changed, fill_value=0) + ess.set_index(\"sid\")[\"score\"]\n", "flip = int(((total + ess.set_index(\"sid\")[\"suggest\"] >= PASS) != (newtot >= PASS)).sum())\n", "print(f\"๊ต๊ด€ ๊ฒ€ํ† ๋กœ ํ•ฉ๊ฒฉ/๋ถˆํ•ฉ๊ฒฉ์ด ๋ฐ”๋€ ์‘์‹œ์ž {flip}๋ช… -> ์ž๋™ ์ฑ„์ ๋งŒ์œผ๋กœ ํ™•์ •ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ \")\n", "print(\"\\n[HITL ๊ฒ€ํ†  ์ง€์นจ ์š”์•ฝ] 1) ์ž๋™ ์ ์ˆ˜๋Š” ์ œ์•ˆ์ผ ๋ฟ ๊ต๊ด€์ด ํ™•์ • 2) ์ธ์ •ยท๊ธฐ๊ฐยท์ˆ˜์ • ๋ชจ๋‘ ์‚ฌ์œ  ํ•„์ˆ˜\"\n", " \" 3) ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ยท์ €์‹ ๋ขฐ ๋‹ต์•ˆ ์šฐ์„  4) ๊ฐ์‚ฌ ๋กœ๊ทธ๋Š” ์ˆ˜์ • ๋ถˆ๊ฐ€, ์ด์˜ ์žฌ์‹ฌ์€ ์ƒ๊ธ‰ ๊ต๊ด€(์ง€ํœ˜ ์ฑ…์ž„์„ )์ด ํŒ๋‹จ\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 07 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ด์˜ ์ œ๊ธฐ 45๊ฑด, ์„œ์ˆ ํ˜• ๋‹ต์•ˆ 120๊ฑด์ด ๊ต๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ์—ด์— ๋“ค์–ด๊ฐ„๋‹ค.\n", "๊ฒฐ์ • ์ „ ์ž๋™ ํ™•์ • ์‹œ๋„ 5๊ฑด ์ค‘ ์ฐจ๋‹จ 5๊ฑด, ์‚ฌ์œ  ์—†๋Š” ๊ฒฐ์ • ์ฐจ๋‹จ\n", "\n", "[๊ฒ€ํ†  ๊ฒฐ๊ณผ]\n", "kind status\n", "์„œ์ˆ ํ˜• ์ˆ˜์ • 54\n", " ์Šน์ธ 66\n", "์ด์˜ ์ œ๊ธฐ ๊ธฐ๊ฐ 7\n", " ์ธ์ • 38\n", "์„œ์ˆ ํ˜• ์ž๋™ ์ œ์•ˆ ์ ์ˆ˜์™€ ๊ต๊ด€ ํŒ๋‹จ ์ผ์น˜ 55.0%, ์ˆ˜์ • 54๊ฑด(ํ‹€๋ฆฐ ์ง„์ˆ  ์† ํ‚ค์›Œ๋“œ์— ์†์€ ์ œ์•ˆ ํฌํ•จ)\n", "๋Œ€๊ธฐ์‹œ๊ฐ„(๋ถ„): ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ ํ‰๊ท  20.0, ๊ทธ ์™ธ ํ‰๊ท  94.7, ์ตœ๋Œ€ 137.4\n", "์Šน์ธ/๋ฐ˜๋ ค ์ฒ˜๋ฆฌ 165๊ฑด, ๊ฐ์‚ฌ ๋กœ๊ทธ ๊ธฐ๋ก 165๊ฑด, ๋กœ๊น… ์„ฑ๊ณต๋ฅ  100% (๋ชฉํ‘œ 100%: ๋‹ฌ์„ฑ)\n", "๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ: ์›๋ณธ ์ •์ƒ, ๋ณ€์กฐ ํ›„ ์ด์ƒ ํƒ์ง€(seq 7)\n", "๊ต๊ด€ ๊ฒ€ํ† ๋กœ ํ•ฉ๊ฒฉ/๋ถˆํ•ฉ๊ฒฉ์ด ๋ฐ”๋€ ์‘์‹œ์ž 3๋ช… -> ์ž๋™ ์ฑ„์ ๋งŒ์œผ๋กœ ํ™•์ •ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ \n", "\n", "[HITL ๊ฒ€ํ†  ์ง€์นจ ์š”์•ฝ] 1) ์ž๋™ ์ ์ˆ˜๋Š” ์ œ์•ˆ์ผ ๋ฟ ๊ต๊ด€์ด ํ™•์ • 2) ์ธ์ •ยท๊ธฐ๊ฐยท์ˆ˜์ • ๋ชจ๋‘ ์‚ฌ์œ  ํ•„์ˆ˜ 3) ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ยท์ €์‹ ๋ขฐ ๋‹ต์•ˆ ์šฐ์„  4) ๊ฐ์‚ฌ ๋กœ๊ทธ๋Š” ์ˆ˜์ • ๋ถˆ๊ฐ€, ์ด์˜ ์žฌ์‹ฌ์€ ์ƒ๊ธ‰ ๊ต๊ด€(์ง€ํœ˜ ์ฑ…์ž„์„ )์ด ํŒ๋‹จ\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 08. ์ „ ๋ถ€๋Œ€์› ๋™์‹œ ์‘์‹œ ๋ชจ์˜ ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ ์ฑ„์  ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๋ฐ ์ตœ์ ํ™”\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์ „ ๋ถ€๋Œ€์› ๋™์‹œ ์‘์‹œ ๋ชจ์˜ ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ ์ฑ„์  ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์ „ ๋ถ€๋Œ€์›์ด ๋™์‹œ์— ์‘์‹œํ•˜๋Š” ์ƒํ™ฉ์„ ๋ชจ์˜ํ•ด ์ฑ„์  ์‹œ์Šคํ…œ์ด ๋Œ€๊ทœ๋ชจ ์ œ์ถœ๊ณผ ์žฅ์•  ์†์—์„œ๋„ ๋ฉˆ์ถ”๊ฑฐ๋‚˜ ๋‹ต์•ˆ์„ ์žƒ์ง€ ์•Š๋Š”์ง€ ์‹œํ—˜ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **ํ‰์‹œ 120๋ช… ์ œ์ถœ๋Ÿ‰์„ 3๋ฐฐยท6๋ฐฐยท11๋ฐฐ๋กœ ๋Š˜๋ฆฌ๊ณ  ์ดˆ์žฅ๋ฌธยท์ œ์–ด๋ฌธ์žยท์—†๋Š” ๋ฌธํ•ญ ๋“ฑ ์†์ƒ ๋‹ต์•ˆ๊ณผ ์ฑ„์ ๊ธฐ ์‘๋‹ต ์ง€์—ฐ(์žฅ์•  2%) ์ฃผ์ž…ํ•˜์—ฌ ์ฒ˜๋ฆฌ ์ง€์—ฐ ๋ฐ ๋ณต์›๋ ฅ์„ ์‹œํ—˜ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `tests/trn02_stress_test.py ๋ฐ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ ๋ถ„์„ ๋ณด๊ณ ์„œ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ทน๋‹จ ์ƒํ™ฉ์—์„œ์˜ ์•ˆ์ „ ์‹คํŒจ(Fail-safe) ๋ฐ ๊ธฐ๋Šฅ ์ถ•์†Œ ์ž‘๋™ ์ ์ ˆ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋™์‹œ ํŠธ๋ž˜ํ”ฝ 500% ์ฆ๊ฐ€ ์‹œ ์‹œ์Šคํ…œ ๋‹ค์šด 0๊ฑด, ๋ฐ์ดํ„ฐ ์œ ์‹ค๋ฅ  0%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋Œ€๋Ÿ‰ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ถ€ํ•˜ ์ฃผ์ž… ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ๋ฐ ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„ ํ”„๋กœํŒŒ์ผ๋ง ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ํ†ตํ•œ ์ฒ˜๋ฆฌ๋Ÿ‰(Throughput) ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ „ ๋ถ€๋Œ€์› ๋™์‹œ ์‘์‹œ๋ฅผ ๋ชจ์˜ํ•ด ํ‰์‹œ(120๋ช…) ๋Œ€๋น„ ์ตœ๋Œ€ 1,000% ๋Š˜์–ด๋‚œ ์ œ์ถœ๋Ÿ‰๊ณผ ์†์ƒ ๋‹ต์•ˆยท์ฑ„์ ๊ธฐ ์žฅ์• ๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ์ฑ„์  ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ฒ˜๋ฆฌ๋Ÿ‰, ์‹œ์Šคํ…œ ๋‹ค์šด, ๋ฐ์ดํ„ฐ ์œ ์‹ค์„ ์ธก์ •ํ•˜๊ณ , ์žฅ์•  ์‹œ '์ฑ„์  ๋ณด๋ฅ˜'๋กœ ๋ณด์กดํ•˜๋Š” ์•ˆ์ „ ์‹คํŒจ ๋™์ž‘๊ณผ ์บ์‹œ ์ตœ์ ํ™” ์ „ํ›„ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ๋น„๊ตํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import functools\n", "import re\n", "import time\n", "import tracemalloc\n", "import unicodedata\n", "from collections import deque\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "\n", "def grade_one(qid, raw):\n", " q = QIDX[qid]\n", " val, st = parse(q, raw)\n", " return st, (st == \"์ •์ƒ\" and grade_fuzzy(q, val))\n", "\n", "\n", "@functools.lru_cache(maxsize=50000)\n", "def grade_cached(qid, raw):\n", " \"\"\"๊ฐ™์€ (๋ฌธํ•ญ, ์›๋ณธ ์‘๋‹ต)์€ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค(๋Œ€๊ทœ๋ชจ ์‘์‹œ์—์„œ ๊ฐ™์€ ๋‹ต์ด ๋ฐ˜๋ณต๋จ).\"\"\"\n", " return grade_one(qid, raw)\n", "\n", "\n", "def corrupt(df, rng, rate):\n", " \"\"\"์†์ƒ ๋‹ต์•ˆ ์ฃผ์ž…: ์ดˆ์žฅ๋ฌธ, ์ œ์–ด๋ฌธ์ž, ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ฌธํ•ญ, None.\"\"\"\n", " df = df.copy()\n", " idx = rng.choice(len(df), int(len(df) * rate), replace=False)\n", " kinds = rng.integers(0, 4, len(idx))\n", " for i, k in zip(idx, kinds):\n", " df.at[df.index[i], \"raw\"] = [\"๊ฐ€\" * 20000, \"\\x00\\x07์‚ผ๊ฐ์ต\", None, \"1\"][k]\n", " if k == 3:\n", " df.at[df.index[i], \"qid\"] = \"Q99\"\n", " return df\n", "\n", "\n", "def run(df, fn, fail_rate=0.0, seed=0, cap=2000):\n", " \"\"\"์ œ์ถœ ํ์—์„œ cap๊ฑด์”ฉ ๊บผ๋‚ด ์ฑ„์ . ์ฑ„์  ์ค‘ ์˜ˆ์™ธยท์žฅ์• ๊ฐ€ ๋‚˜๋ฉด '์ฑ„์  ๋ณด๋ฅ˜' ํ๋กœ ์˜ฎ๊ธฐ๊ณ  ๊ณ„์† ์ฒ˜๋ฆฌํ•œ๋‹ค(์•ˆ์ „ ์‹คํŒจ).\"\"\"\n", " rng = np.random.default_rng(seed)\n", " inbox, result, hold = deque(df[[\"rid\", \"qid\", \"raw\"]].itertuples(index=False)), {}, {}\n", " t0, crashes, max_q = time.perf_counter(), 0, len(inbox)\n", " while inbox:\n", " batch = [inbox.popleft() for _ in range(min(cap, len(inbox)))]\n", " for rid, qid, raw in batch:\n", " try:\n", " if qid not in QIDX:\n", " raise KeyError(\"๋ฌธํ•ญ ์—†์Œ\")\n", " if raw is not None and len(str(raw)) > 200:\n", " raise ValueError(\"์‘๋‹ต ๊ธธ์ด ์ดˆ๊ณผ\")\n", " if rng.random() < fail_rate:\n", " raise TimeoutError(\"์ฑ„์ ๊ธฐ ์‘๋‹ต ์—†์Œ\")\n", " result[rid] = fn(qid, \"\" if raw is None else str(raw).replace(\"\\x00\", \"\").replace(\"\\x07\", \"\"))\n", " except Exception as e: # ์‹œ์Šคํ…œ์€ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๋ณด๋ฅ˜ ํ์— ์›๋ณธ์„ ๋ณด์กด\n", " hold[rid] = type(e).__name__\n", " sec = time.perf_counter() - t0\n", " lost = len(df) - len(result) - len(hold)\n", " return {\"๊ฑด์ˆ˜\": len(df), \"์ฒ˜๋ฆฌ(์ดˆ)\": round(sec, 2), \"์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ)\": int(len(df) / sec), \"์ฑ„์  ์™„๋ฃŒ\": len(result),\n", " \"๋ณด๋ฅ˜\": len(hold), \"์œ ์‹ค\": lost, \"๋‹ค์šด\": crashes, \"์ตœ๋Œ€ ๋Œ€๊ธฐ์—ด\": max_q}, hold\n", "\n", "\n", "base = make_responses(40, seed=7) # ํ‰์‹œ: 120๋ช… x 32๋ฌธํ•ญ\n", "rng = np.random.default_rng(1)\n", "rows, holds = [], {}\n", "for mult, fail in [(1, 0.0), (3, 0.0), (6, 0.0), (6, 0.02), (11, 0.02)]:\n", " df = pd.concat([base.assign(rid=base[\"rid\"] + f\"-{k}\") for k in range(mult)], ignore_index=True)\n", " df = corrupt(df, rng, 0.003)\n", " grade_cached.cache_clear()\n", " tracemalloc.start()\n", " r, hold = run(df, grade_cached, fail_rate=fail, seed=mult)\n", " peak = tracemalloc.get_traced_memory()[1] / 1e6; tracemalloc.stop()\n", " rows.append({\"๋ถ€ํ•˜\": f\"ํ‰์‹œ x{mult}\" + (\" +์žฅ์• 2%\" if fail else \"\"), **r, \"๋ฉ”๋ชจ๋ฆฌ ์ตœ๋Œ€(MB)\": round(peak, 1)})\n", " holds[rows[-1][\"๋ถ€ํ•˜\"]] = hold\n", "T = pd.DataFrame(rows)\n", "print(T.drop(columns=[\"๋‹ค์šด\", \"์ตœ๋Œ€ ๋Œ€๊ธฐ์—ด\"]).to_string(index=False))\n", "print(\"(์œ„ ํ‘œ์˜ ์ฒ˜๋ฆฌ๋Ÿ‰์€ ๋ฉ”๋ชจ๋ฆฌ ์ถ”์ ์„ ์ผ  ์ƒํƒœ๋ผ ์‹ค์ œ๋ณด๋‹ค ๋‚ฎ๊ฒŒ ๋‚˜์˜จ๋‹ค)\")\n", "w = T[T[\"๋ถ€ํ•˜\"] == \"ํ‰์‹œ x6\"].iloc[0]\n", "print(f\"\\n๋™์‹œ ์‘์‹œ 500% ์ฆ๊ฐ€(ํ‰์‹œ x6): ์‹œ์Šคํ…œ ๋‹ค์šด {int(T['๋‹ค์šด'].sum())}๊ฑด, ๋ฐ์ดํ„ฐ ์œ ์‹ค {int(T['์œ ์‹ค'].sum())}๊ฑด \"\n", " f\"(๋ชฉํ‘œ ๋‹ค์šด 0๊ฑดยท์œ ์‹ค๋ฅ  0%: {'๋‹ฌ์„ฑ' if T['๋‹ค์šด'].sum() == 0 and T['์œ ์‹ค'].sum() == 0 else '๋ฏธ๋‹ฌ'})\")\n", "h = pd.Series(holds[\"ํ‰์‹œ x11 +์žฅ์• 2%\"]).value_counts()\n", "print(\"๋ณด๋ฅ˜ ์‚ฌ์œ (ํ‰์‹œ x11 +์žฅ์• 2%):\", {k: int(v) for k, v in h.items()}, \"-> ์›๋ณธ ๋ณด์กด, ์žฅ์•  ๋ณต๊ตฌ ํ›„ ์žฌ์ฑ„์  ๋˜๋Š” ๊ต๊ด€ ํ™•์ธ\")\n", "\n", "# ๋ณ‘๋ชฉ ํ”„๋กœํŒŒ์ผ๋ง๊ณผ ์ตœ์ ํ™”: ์บ์‹œ ์—†์Œ vs ์บ์‹œ\n", "df6 = pd.concat([base.assign(rid=base[\"rid\"] + f\"-{k}\") for k in range(6)], ignore_index=True)\n", "prof = {}\n", "t0 = time.perf_counter(); [parse(QIDX[q], r) for q, r in zip(df6[\"qid\"], df6[\"raw\"])]; prof[\"ํŒŒ์‹ฑ\"] = time.perf_counter() - t0\n", "sa = df6[df6[\"qid\"].map(lambda q: QIDX[q][\"type\"]) == \"SA\"]\n", "t0 = time.perf_counter(); [grade_fuzzy(QIDX[q], parse(QIDX[q], r)[0]) for q, r in zip(sa[\"qid\"], sa[\"raw\"])]; prof[\"๋‹จ๋‹ต ์œ ์‚ฌ๋„ ์ฑ„์ \"] = time.perf_counter() - t0\n", "print(\"\\n[๋ณ‘๋ชฉ ํ”„๋กœํŒŒ์ผ] \" + \", \".join(f\"{k} {v * 1000:.0f} ms\" for k, v in prof.items()) + f\" (๋‹จ๋‹ตํ˜•์€ ์ „์ฒด์˜ {len(sa) / len(df6):.0%})\")\n", "r0, _ = run(df6, grade_one)\n", "grade_cached.cache_clear(); r1, _ = run(df6, grade_cached)\n", "print(f\"์ตœ์ ํ™” ์ „ {r0['์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ)']:,}๊ฑด/์ดˆ -> ์บ์‹œ ์ ์šฉ ํ›„ {r1['์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ)']:,}๊ฑด/์ดˆ ({r1['์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ)'] / r0['์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ)']:.1f}๋ฐฐ)\")\n", "print(f\"๊ธฐ๋Šฅ ์ถ•์†Œ ์šด์šฉ: ์žฅ์• ์œจ์ด 5%๋ฅผ ๋„˜์œผ๋ฉด ๋‹จ๋‹ตํ˜• ์œ ์‚ฌ๋„ ์ฑ„์ ์„ ๋„๊ณ  ์ •ํ™• ์ผ์น˜๋งŒ ์ฑ„์ , ๋‚˜๋จธ์ง€๋Š” ๊ต๊ด€ ๊ฒ€ํ† ๋กœ ๋„˜๊ธด๋‹ค.\")\n", "\n", "plt.figure(figsize=(8, 4))\n", "plt.bar(T[\"๋ถ€ํ•˜\"].str.replace(\"ํ‰์‹œ\", \"base\").str.replace(\"+์žฅ์• 2%\", \"+fail2%\"), T[\"์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ)\"])\n", "plt.ylabel(\"responses / sec\"); plt.title(\"Grading throughput under load (cached)\"); plt.xticks(rotation=15)\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 08 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", " ๋ถ€ํ•˜ ๊ฑด์ˆ˜ ์ฒ˜๋ฆฌ(์ดˆ) ์ฒ˜๋ฆฌ๋Ÿ‰(๊ฑด/์ดˆ) ์ฑ„์  ์™„๋ฃŒ ๋ณด๋ฅ˜ ์œ ์‹ค ๋ฉ”๋ชจ๋ฆฌ ์ตœ๋Œ€(MB)\n", " ํ‰์‹œ x1 3840 0.11 33625 3834 6 0 0.5\n", " ํ‰์‹œ x3 11520 0.19 59703 11501 19 0 1.1\n", " ํ‰์‹œ x6 23040 0.27 86855 23004 36 0 2.0\n", " ํ‰์‹œ x6 +์žฅ์• 2% 23040 0.24 96047 22558 482 0 2.0\n", "ํ‰์‹œ x11 +์žฅ์• 2% 42240 0.46 91783 41326 914 0 3.6\n", "(์œ„ ํ‘œ์˜ ์ฒ˜๋ฆฌ๋Ÿ‰์€ ๋ฉ”๋ชจ๋ฆฌ ์ถ”์ ์„ ์ผ  ์ƒํƒœ๋ผ ์‹ค์ œ๋ณด๋‹ค ๋‚ฎ๊ฒŒ ๋‚˜์˜จ๋‹ค)\n", "\n", "๋™์‹œ ์‘์‹œ 500% ์ฆ๊ฐ€(ํ‰์‹œ x6): ์‹œ์Šคํ…œ ๋‹ค์šด 0๊ฑด, ๋ฐ์ดํ„ฐ ์œ ์‹ค 0๊ฑด (๋ชฉํ‘œ ๋‹ค์šด 0๊ฑดยท์œ ์‹ค๋ฅ  0%: ๋‹ฌ์„ฑ)\n", "๋ณด๋ฅ˜ ์‚ฌ์œ (ํ‰์‹œ x11 +์žฅ์• 2%): {'TimeoutError': 857, 'ValueError': 30, 'KeyError': 27} -> ์›๋ณธ ๋ณด์กด, ์žฅ์•  ๋ณต๊ตฌ ํ›„ ์žฌ์ฑ„์  ๋˜๋Š” ๊ต๊ด€ ํ™•์ธ\n", "\n", "[๋ณ‘๋ชฉ ํ”„๋กœํŒŒ์ผ] ํŒŒ์‹ฑ 52 ms, ๋‹จ๋‹ต ์œ ์‚ฌ๋„ ์ฑ„์  150 ms (๋‹จ๋‹ตํ˜•์€ ์ „์ฒด์˜ 25%)\n", "์ตœ์ ํ™” ์ „ 89,083๊ฑด/์ดˆ -> ์บ์‹œ ์ ์šฉ ํ›„ 498,682๊ฑด/์ดˆ (5.6๋ฐฐ)\n", "๊ธฐ๋Šฅ ์ถ•์†Œ ์šด์šฉ: ์žฅ์• ์œจ์ด 5%๋ฅผ ๋„˜์œผ๋ฉด ๋‹จ๋‹ตํ˜• ์œ ์‚ฌ๋„ ์ฑ„์ ์„ ๋„๊ณ  ์ •ํ™• ์ผ์น˜๋งŒ ์ฑ„์ , ๋‚˜๋จธ์ง€๋Š” ๊ต๊ด€ ๊ฒ€ํ† ๋กœ ๋„˜๊ธด๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 09. AAR ๊ธฐ๋ฐ˜ ๋ฌธํ•ญ ๋ณ€๋ณ„๋ ฅ ๋ถ„์„ ๋ฐ ๋‚œ์ด๋„๋ณ„ ๊ฐ€์ค‘์น˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์žฌ๋ณด์ •\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "AAR ๊ธฐ๋ฐ˜ ๋ฌธํ•ญ ๋ณ€๋ณ„๋ ฅ ๋ถ„์„ ๋ฐ ๋‚œ์ด๋„ ๊ฐ€์ค‘์น˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์žฌ๋ณด์ •ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์‹œํ–‰ ํ›„ ๊ฒ€ํ† (AAR)๋กœ ๋ฌธํ•ญ์˜ ๋ณ€๋ณ„๋ ฅ๊ณผ ์ •๋‹ต ํ‚ค ์˜ค๋ฅ˜๋ฅผ ์ฐพ์•„๋‚ด๊ณ , ๊ฒฐํ•จ ๋ฌธํ•ญ ์กฐ์น˜์™€ ๋ฐฐ์  ๊ฐ€์ค‘์น˜ ์žฌ๋ณด์ •์œผ๋กœ ํ•ฉ๊ฒฉ ํŒ์ •์˜ ์ •ํ™•๋„๋ฅผ ๋†’์ธ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **๋ฌธํ•ญ๋ณ„ ์ •๋‹ต๋ฅ , ์ ์ด์—ฐ ์ƒ๊ด€(๋ณ€๋ณ„๋„), ์˜ค๋‹ต ๋ณด๊ธฐ ์„ ํƒ์ž์˜ ํ‰๊ท  ์ ์ˆ˜๋ฅผ ๋ถ„์„ํ•ด ์ •๋‹ต ํ‚ค ์ž…๋ ฅ ์˜ค๋ฅ˜์™€ ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ ๋ฌธํ•ญ์„ ์ฐพ๋Š”๋‹ค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `docs/trn02_aar_report.md ๋ฐ configs/trn02_tuned_params.yaml` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์˜ค๋ฅ˜ ์›์ธ ๊ทœ๋ช…์˜ ๊ฐ๊ด€์„ฑ ๋ฐ ๊ฐœ์„  ์กฐ์น˜์˜ ๋…ผ๋ฆฌ์  ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: AAR ํ”ผ๋“œ๋ฐฑ ์ ์šฉ ํ›„ ์ข…ํ•ฉ ์ •ํ™•๋„ 10%p ์ด์ƒ ํ–ฅ์ƒ, ์žฌ๋ฐœ ์˜ค๋ฅ˜ 0๊ฑด\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: AAR ๋ณด๊ณ ์„œ ์ž‘์„ฑ ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ์ „ํ›„ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ํšŒ๊ท€ ํ…Œ์ŠคํŠธ(Regression Test) ์Šค์œ„ํŠธ ์ „์ฒด ์ž๋™ ์‹คํ–‰ ๋ฐ ๋น„๊ต\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์‹œํ–‰ ํ›„ ๊ฒ€ํ† (AAR)๋กœ ๋ฌธํ•ญ๋ณ„ ์ •๋‹ต๋ฅ ยท๋ณ€๋ณ„๋„(์ ์ด์—ฐ ์ƒ๊ด€)ยท์˜ค๋‹ต ๋ณด๊ธฐ ์„ ํƒ ๋ถ„ํฌ๋ฅผ ๋ถ„์„ํ•ด ์ •๋‹ต ํ‚ค ์˜ค๋ฅ˜์™€ ๋ณ€๋ณ„๋ ฅ ์—†๋Š” ๋ฌธํ•ญ์„ ์ฐพ์•„๋‚ด๊ณ , ๋‚œ์ด๋„ยท๋ณ€๋ณ„๋„ ๊ฐ€์ค‘์น˜๋ฅผ ์žฌ๋ณด์ •ํ•œ๋‹ค. ์‹ค์ œ ์ˆ™๋‹ฌ๋„ ๋Œ€๋น„ ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„์˜ ๊ฐœ์„ ๊ณผ ์ƒˆ ์‘์‹œ ์ง‘๋‹จ์—์„œ์˜ ์žฌ๋ฐœ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•˜๊ณ  ์žฌ๋ณด์ • ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ YAML๋กœ ์ถœ๋ ฅํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "KEY_REG = {\"Q11\": 3} # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฌธํ•ญ ๋“ฑ๋ก ์‹œ ์ •๋‹ต ํ‚ค๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ•œ ๋ฌธํ•ญ(์ •๋‹ต์€ 2)\n", "AMBIG = \"Q08\" # ๋ณด๊ธฐ 2ยท3์ด ๋ชจ๋‘ ๊ทธ๋Ÿด๋“ฏํ•ด ์‹ค๋ ฅ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฐˆ๋ฆฌ๋Š” ๋ฌธํ•ญ\n", "\n", "\n", "def field_data(seed):\n", " \"\"\"ํ˜„์žฅ ์‹œํ–‰ ๋ฐ์ดํ„ฐ: Q08์€ ์‹ค๋ ฅ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ 2ยท3์ด ๋ฐ˜๋ฐ˜, ๋‚˜๋จธ์ง€๋Š” ์ƒ์„ฑ๊ธฐ ๊ทธ๋Œ€๋กœ.\"\"\"\n", " df = make_responses(40, seed=seed)\n", " rng = np.random.default_rng(seed + 100)\n", " m = df[\"qid\"] == AMBIG\n", " df.loc[m, \"raw\"] = rng.choice([\"2\", \"3\"], m.sum())\n", " df[\"val\"] = [parse(QIDX[q], r)[0] for q, r in zip(df[\"qid\"], df[\"raw\"])]\n", " return df\n", "\n", "\n", "def grade(df, key_fix=False, drop=(), weights=None):\n", " def ok(q, v):\n", " if q[\"qid\"] in KEY_REG and not key_fix:\n", " return v == str(KEY_REG[q[\"qid\"]])\n", " return grade_fuzzy(q, v)\n", " d = df[~df[\"qid\"].isin(drop)].copy()\n", " d[\"correct\"] = [ok(QIDX[q], v) for q, v in zip(d[\"qid\"], d[\"val\"])]\n", " w = weights or {}\n", " d[\"pts\"] = [w.get(q, POINTS[QIDX[q][\"diff\"]]) for q in d[\"qid\"]]\n", " d[\"score\"] = d[\"pts\"] * d[\"correct\"]\n", " tot = d.groupby(\"sid\")[[\"score\", \"pts\"]].sum()\n", " return d, tot[\"score\"] / tot[\"pts\"]\n", "\n", "\n", "def item_analysis(d, key_fix=False):\n", " \"\"\"์ •๋‹ต๋ฅ (p), ์ ์ด์—ฐ ์ƒ๊ด€(ํ•ด๋‹น ๋ฌธํ•ญ ์ œ์™ธ ์ด์ ๊ณผ์˜ ์ƒ๊ด€), ์˜ค๋‹ต ๋ณด๊ธฐ ์„ ํƒ์ž์˜ ํ‰๊ท  ์ด์ ์ด ์ •๋‹ต ์„ ํƒ์ž๋ณด๋‹ค ๋†’์€์ง€(ํ‚ค ์˜ค๋ฅ˜ ์˜์‹ฌ).\"\"\"\n", " tot = d.groupby(\"sid\")[\"correct\"].mean()\n", " rows = []\n", " for qid, g in d.groupby(\"qid\"):\n", " rest = tot[g[\"sid\"]].values - g[\"correct\"].values / len(BANK)\n", " r = np.corrcoef(g[\"correct\"].astype(float), rest)[0, 1] if g[\"correct\"].std() > 0 else 0.0\n", " key_sus = False\n", " if QIDX[qid][\"type\"] == \"MC\":\n", " by = pd.Series(rest, index=g[\"val\"].values).groupby(level=0).mean()\n", " key = KEY_REG[qid] if qid in KEY_REG and not key_fix else QIDX[qid][\"answer\"]\n", " right = by.get(str(key), np.nan)\n", " key_sus = bool(by.drop(index=[k for k in by.index if k not in \"1234\" or k == \"\"], errors=\"ignore\").max() > right + 0.08)\n", " rows.append({\"qid\": qid, \"p\": round(g[\"correct\"].mean(), 2), \"r_pb\": round(r, 2), \"key_suspect\": key_sus})\n", " t = pd.DataFrame(rows).set_index(\"qid\")\n", " t[\"flag\"] = np.where(t[\"key_suspect\"], \"์ •๋‹ต ํ‚ค ์˜ค๋ฅ˜ ์˜์‹ฌ\", np.where(t[\"r_pb\"] < 0.1, \"๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ\", \"\"))\n", " return t\n", "\n", "\n", "def pass_accuracy(df, ratio, cut=0.7):\n", " \"\"\"ํ•ฉ๊ฒฉ ํŒ์ •(์ ์ˆ˜ ๋น„์œจ cut ์ด์ƒ)๊ณผ ๊ฐ€์ƒ ์‹ค์ œ ์ˆ™๋‹ฌ๋„(๊ฒฐํ•จ ์—†๋Š” ์‹œํ—˜์—์„œ์˜ ๊ธฐ๋Œ€ ๋“์ ๋ฅ  0.7 ์ด์ƒ)์˜ ์ผ์น˜์œจ.\"\"\"\n", " sol = {s[\"sid\"]: s for s in make_soldiers(40, SEED_OF[id(df)])}\n", " pts = np.array([POINTS[q[\"diff\"]] for q in BANK])\n", " exp = {s: float(np.dot(pts, [p_correct(sol[s][\"theta\"][q[\"area\"]], q[\"diff\"]) for q in BANK]) / pts.sum()) for s in ratio.index}\n", " truth = pd.Series(exp) >= 0.7\n", " return float(((ratio >= cut) == truth[ratio.index]).mean())\n", "\n", "\n", "SEED_OF = {}\n", "A = field_data(7); SEED_OF[id(A)] = 7\n", "d0, r0 = grade(A)\n", "acc0 = pass_accuracy(A, r0)\n", "ia = item_analysis(d0)\n", "print(\"[AAR 1. ๋ฌธํ•ญ ๋ถ„์„ - ๊ฒฝ๊ณ  ๋ฌธํ•ญ]\")\n", "print(ia[ia[\"flag\"] != \"\"].to_string())\n", "print(f\"์ „์ฒด ๋ฌธํ•ญ ๋ณ€๋ณ„๋„ ์ค‘์•™๊ฐ’ {ia['r_pb'].median():.2f}, ์ •๋‹ต๋ฅ  0.9 ์ดˆ๊ณผ(๋„ˆ๋ฌด ์‰ฌ์›€) {int((ia['p'] > 0.9).sum())}๊ฐœ, 0.3 ๋ฏธ๋งŒ(๋„ˆ๋ฌด ์–ด๋ ค์›€) {int((ia['p'] < 0.3).sum())}๊ฐœ\")\n", "print(\"์›์ธ ํŒ์ •: Q11์€ ์ƒ์œ„๊ถŒ์ด ๊ณ ๋ฅธ ๋ณด๊ธฐ(2)๊ฐ€ ๋“ฑ๋ก ํ‚ค(3)์™€ ๋‹ฌ๋ผ ํ‚ค ์ž…๋ ฅ ์˜ค๋ฅ˜, Q08์€ ๋ณด๊ธฐ 2ยท3์ด ์‹ค๋ ฅ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฐˆ๋ ค ๋ฌธํ•ญ ์ž์ฒด ๊ฒฐํ•จ.\")\n", "\n", "# AAR 2. ์žฌ๋ณด์ •: ํ‚ค ์ˆ˜์ • -> ๊ฒฐํ•จ ๋ฌธํ•ญ ์ œ์™ธ -> ๋ฐฐ์  ๊ฐ€์ค‘์น˜ ํƒ์ƒ‰\n", "flagged = list(ia[ia[\"flag\"] == \"๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ\"].index)\n", "rows = [{\"๋‹จ๊ณ„\": \"์žฌ๋ณด์ • ์ „\", \"ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\": acc0}]\n", "d1, r1 = grade(A, key_fix=True)\n", "rows.append({\"๋‹จ๊ณ„\": \"1) ์ •๋‹ต ํ‚ค ์ˆ˜์ •\", \"ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\": pass_accuracy(A, r1)})\n", "d2, r2 = grade(A, key_fix=True, drop=flagged)\n", "rows.append({\"๋‹จ๊ณ„\": \"2) ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ ๋ฌธํ•ญ ์ œ์™ธ\", \"ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\": pass_accuracy(A, r2)})\n", "ia2 = item_analysis(d2, key_fix=True)\n", "best = None\n", "for base in [(2, 3, 5), (1, 2, 3), (3, 3, 3)]:\n", " for alpha in [0.0, 0.5, 1.0, 2.0]:\n", " w = {q: base[QIDX[q][\"diff\"] - 1] * (1 + alpha * max(ia2.loc[q, \"r_pb\"], 0)) for q in ia2.index}\n", " _, r = grade(A, key_fix=True, drop=flagged, weights=w)\n", " a = pass_accuracy(A, r) # ํ•ฉ๊ฒฉ์„  70%๋Š” ์ •์ฑ…๊ฐ’์ด๋ผ ํŠœ๋‹ํ•˜์ง€ ์•Š๋Š”๋‹ค\n", " if best is None or a > best[0] + 1e-9:\n", " best = (a, base, alpha, 0.7, w)\n", "acc3, base, alpha, cut, W = best\n", "rows.append({\"๋‹จ๊ณ„\": f\"3) ๋ฐฐ์ {base}ยท๋ณ€๋ณ„๋„ ๊ฐ€์ค‘ {alpha}\", \"ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\": acc3})\n", "S = pd.DataFrame(rows); S[\"ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\"] = S[\"ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\"].round(3)\n", "print(\"\\n[AAR 2. ์žฌ๋ณด์ • ๋‹จ๊ณ„๋ณ„ ํšจ๊ณผ(์‘์‹œ ์ง‘๋‹จ A)]\")\n", "print(S.to_string(index=False))\n", "gain = (acc3 - acc0) * 100\n", "print(f\"์ข…ํ•ฉ ์ •ํ™•๋„ {acc0:.1%} -> {acc3:.1%} (+{gain:.1f}%p, ๋ชฉํ‘œ 10%p ์ด์ƒ: {'๋‹ฌ์„ฑ' if gain >= 10 else '๋ฏธ๋‹ฌ'})\")\n", "print(\"-> ๊ฒฐํ•จ ๋ฌธํ•ญ์ด 32๊ฐœ ์ค‘ 2๊ฐœ๋ผ ๊ฐœ์„  ํญ์ด ์ œํ•œ๋œ๋‹ค. ๋‚˜๋จธ์ง€ ๋ถˆ์ผ์น˜๋Š” ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ ์‘์‹œ์ž์˜ ์ธก์ • ์˜ค์ฐจ๋กœ, ๋ฌธํ•ญ ์ˆ˜๋ฅผ ๋Š˜๋ ค์•ผ ์ค„์–ด๋“ ๋‹ค.\")\n", "\n", "# AAR 3. ์ƒˆ ์‘์‹œ ์ง‘๋‹จ B(๋‹ค๋ฅธ ์ธ์›)์—์„œ ๊ฒ€์ฆยท์žฌ๋ฐœ ํ™•์ธ\n", "B = field_data(21); SEED_OF[id(B)] = 21\n", "_, rb0 = grade(B)\n", "dB, rb = grade(B, key_fix=True, drop=flagged, weights=W)\n", "accB0, accB = pass_accuracy(B, rb0), pass_accuracy(B, rb, cut)\n", "iaB = item_analysis(dB, key_fix=True)\n", "rec = iaB[iaB[\"key_suspect\"] | iaB.index.isin(flagged + list(KEY_REG))]\n", "rec = rec[rec[\"flag\"] != \"\"]\n", "new = iaB[(iaB[\"flag\"] != \"\") & ~iaB.index.isin(rec.index)]\n", "print(f\"\\n[AAR 3. ์‘์‹œ ์ง‘๋‹จ B ๊ฒ€์ฆ] ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„ {accB0:.1%} -> {accB:.1%} (+{(accB - accB0) * 100:.1f}%p)\")\n", "print(f\"์žฌ๋ฐœ ์˜ค๋ฅ˜(์กฐ์น˜ํ•œ ํ‚ค ์˜ค๋ฅ˜ยท๊ฒฐํ•จ ๋ฌธํ•ญ์˜ ์žฌ๊ฒ€์ถœ) {len(rec)}๊ฑด (๋ชฉํ‘œ 0๊ฑด: {'๋‹ฌ์„ฑ' if len(rec) == 0 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"์‹ ๊ทœ ์ ๊ฒ€ ๋Œ€์ƒ {len(new)}๊ฑด: {list(new.index)} -> ๋‹ค์Œ AAR์—์„œ ๋ฌธํ•ญ ๊ฐœ์ • ์—ฌ๋ถ€ ํŒ๋‹จ\")\n", "\n", "yaml = \"\\n\".join([\"# configs/trn02_tuned_params.yaml [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\", \"answer_key_fix:\", \" Q11: 2\", \"excluded_items:\"]\n", " + [f\" - {q} # ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ, ๋ฌธํ•ญ ๊ฐœ์ • ํ›„ ์žฌํˆฌ์ž…\" for q in flagged]\n", " + [f\"points_by_difficulty: {{1: {base[0]}, 2: {base[1]}, 3: {base[2]}}}\", f\"discrimination_alpha: {alpha}\",\n", " f\"pass_ratio: {cut}\", \"review: ๋‹ค์Œ ์‹œํ–‰ ํ›„ AAR์—์„œ ๋ณ€๋ณ„๋„ 0.1 ๋ฏธ๋งŒ ๋ฌธํ•ญ ์žฌ์ ๊ฒ€\"])\n", "print(\"\\n\" + yaml)\n", "\n", "plt.figure(figsize=(7, 4))\n", "c = [\"tab:red\" if f else \"tab:blue\" for f in ia[\"flag\"]]\n", "plt.scatter(ia[\"p\"], ia[\"r_pb\"], c=c)\n", "for q in ia[ia[\"flag\"] != \"\"].index:\n", " plt.annotate(q, (ia.loc[q, \"p\"], ia.loc[q, \"r_pb\"]))\n", "plt.axhline(0.1, ls=\"--\", color=\"gray\"); plt.xlabel(\"item difficulty p (accuracy)\"); plt.ylabel(\"discrimination r_pb\")\n", "plt.title(\"AAR item analysis (red = flagged)\"); plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 09 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[AAR 1. ๋ฌธํ•ญ ๋ถ„์„ - ๊ฒฝ๊ณ  ๋ฌธํ•ญ]\n", " p r_pb key_suspect flag\n", "qid \n", "Q08 0.56 -0.10 False ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ\n", "Q11 0.08 -0.26 True ์ •๋‹ต ํ‚ค ์˜ค๋ฅ˜ ์˜์‹ฌ\n", "Q27 0.89 0.08 False ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ\n", "Q28 0.87 0.07 False ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ\n", "์ „์ฒด ๋ฌธํ•ญ ๋ณ€๋ณ„๋„ ์ค‘์•™๊ฐ’ 0.30, ์ •๋‹ต๋ฅ  0.9 ์ดˆ๊ณผ(๋„ˆ๋ฌด ์‰ฌ์›€) 2๊ฐœ, 0.3 ๋ฏธ๋งŒ(๋„ˆ๋ฌด ์–ด๋ ค์›€) 1๊ฐœ\n", "์›์ธ ํŒ์ •: Q11์€ ์ƒ์œ„๊ถŒ์ด ๊ณ ๋ฅธ ๋ณด๊ธฐ(2)๊ฐ€ ๋“ฑ๋ก ํ‚ค(3)์™€ ๋‹ฌ๋ผ ํ‚ค ์ž…๋ ฅ ์˜ค๋ฅ˜, Q08์€ ๋ณด๊ธฐ 2ยท3์ด ์‹ค๋ ฅ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฐˆ๋ ค ๋ฌธํ•ญ ์ž์ฒด ๊ฒฐํ•จ.\n", "\n", "[AAR 2. ์žฌ๋ณด์ • ๋‹จ๊ณ„๋ณ„ ํšจ๊ณผ(์‘์‹œ ์ง‘๋‹จ A)]\n", " ๋‹จ๊ณ„ ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„\n", " ์žฌ๋ณด์ • ์ „ 0.833\n", " 1) ์ •๋‹ต ํ‚ค ์ˆ˜์ • 0.858\n", " 2) ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ ๋ฌธํ•ญ ์ œ์™ธ 0.875\n", "3) ๋ฐฐ์ (2, 3, 5)ยท๋ณ€๋ณ„๋„ ๊ฐ€์ค‘ 0.0 0.875\n", "์ข…ํ•ฉ ์ •ํ™•๋„ 83.3% -> 87.5% (+4.2%p, ๋ชฉํ‘œ 10%p ์ด์ƒ: ๋ฏธ๋‹ฌ)\n", "-> ๊ฒฐํ•จ ๋ฌธํ•ญ์ด 32๊ฐœ ์ค‘ 2๊ฐœ๋ผ ๊ฐœ์„  ํญ์ด ์ œํ•œ๋œ๋‹ค. ๋‚˜๋จธ์ง€ ๋ถˆ์ผ์น˜๋Š” ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ ์‘์‹œ์ž์˜ ์ธก์ • ์˜ค์ฐจ๋กœ, ๋ฌธํ•ญ ์ˆ˜๋ฅผ ๋Š˜๋ ค์•ผ ์ค„์–ด๋“ ๋‹ค.\n", "\n", "[AAR 3. ์‘์‹œ ์ง‘๋‹จ B ๊ฒ€์ฆ] ํ•ฉ๊ฒฉ ํŒ์ • ์ •ํ™•๋„ 75.8% -> 77.5% (+1.7%p)\n", "์žฌ๋ฐœ ์˜ค๋ฅ˜(์กฐ์น˜ํ•œ ํ‚ค ์˜ค๋ฅ˜ยท๊ฒฐํ•จ ๋ฌธํ•ญ์˜ ์žฌ๊ฒ€์ถœ) 0๊ฑด (๋ชฉํ‘œ 0๊ฑด: ๋‹ฌ์„ฑ)\n", "์‹ ๊ทœ ์ ๊ฒ€ ๋Œ€์ƒ 3๊ฑด: ['Q12', 'Q13', 'Q21'] -> ๋‹ค์Œ AAR์—์„œ ๋ฌธํ•ญ ๊ฐœ์ • ์—ฌ๋ถ€ ํŒ๋‹จ\n", "\n", "# configs/trn02_tuned_params.yaml [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\n", "answer_key_fix:\n", " Q11: 2\n", "excluded_items:\n", " - Q08 # ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ, ๋ฌธํ•ญ ๊ฐœ์ • ํ›„ ์žฌํˆฌ์ž…\n", " - Q27 # ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ, ๋ฌธํ•ญ ๊ฐœ์ • ํ›„ ์žฌํˆฌ์ž…\n", " - Q28 # ๋ณ€๋ณ„๋ ฅ ๋ถ€์กฑ, ๋ฌธํ•ญ ๊ฐœ์ • ํ›„ ์žฌํˆฌ์ž…\n", "points_by_difficulty: {1: 2, 2: 3, 3: 5}\n", "discrimination_alpha: 0.0\n", "pass_ratio: 0.7\n", "review: ๋‹ค์Œ ์‹œํ–‰ ํ›„ AAR์—์„œ ๋ณ€๋ณ„๋„ 0.1 ๋ฏธ๋งŒ ๋ฌธํ•ญ ์žฌ์ ๊ฒ€\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 10. ๋ฐฉ๊ณต ํ€ด์ฆˆ ์ž๋™์ฑ„์  ๋ฐ ์˜ค๋‹ต๋ถ„์„ ์‹œ์Šคํ…œ ํ†ตํ•ฉ ์ตœ์ข… ์šด์˜ ํŒจํ‚ค์ง€ ์™„์„ฑ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋ฐฉ๊ณต ํ€ด์ฆˆ ์ž๋™์ฑ„์  ๋ฐ ์˜ค๋‹ต๋ถ„์„ ์‹œ์Šคํ…œ ํ†ตํ•ฉ ์ตœ์ข… ์šด์˜ ํŒจํ‚ค์ง€๋ฅผ ์™„์„ฑํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋ฌธํ•ญ ๊ฒ€์ฆ๋ถ€ํ„ฐ ์ •์ œ, ์ฑ„์ , ์˜ค๋‹ต ๋ถ„๋ฅ˜, ์ทจ์•ฝ ์˜์—ญ ์ถ”์ฒœ, ๊ต๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ์—ด๊นŒ์ง€ ์ „์ฒด ์‹œ์Šคํ…œ์„ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉํ•˜๊ณ  ์šด์˜ ํŒจํ‚ค์ง€๋กœ ์™„์„ฑํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์žฌ๋ณด์ • ์„ค์ •(CONFIG)์„ ์ ์šฉํ•œ ํ†ตํ•ฉ ํŒŒ์ดํ”„๋ผ์ธ์„ ์—ฌ๋Ÿฌ ์‘์‹œ ํšŒ์ฐจ์— ์‹คํ–‰ํ•ด End-to-End ์„ฑ๊ณต๋ฅ  ํ™•์ธํ•˜๊ณ  ์œ ํšจ์„ฑ์„ ์ ๊ฒ€ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/main_trn02.py, README.md, docs/trn02_final_report.pdf(md)` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ ์‹ค๋ฌด ์ฆ‰์‹œ ๋„์ž… ๊ฐ€๋Šฅ์„ฑ, ์šด์šฉ ์‹ ๋ขฐ๋„ ๋ฐ ์ „๋žต์  ๊ธฐ๋Œ€ํšจ๊ณผ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: End-to-End ํ†ตํ•ฉ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ์„ฑ๊ณต๋ฅ  100%, ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ 85% ์ด์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์›ํด๋ฆญ ์‹คํ–‰ ์Šคํฌ๋ฆฝํŠธ ๋™์ž‘ ํ™•์ธ ๋ฐ ์ตœ์ข… ๋ฐœํ‘œ ์‚ฐ์ถœ๋ฌผ ์ž‘์„ฑ ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์ตœ์ข… ๊ฒ€์ฆ ์œ„์›ํšŒ(ํ‰๊ฐ€๊ด€) ๋ชจ์˜ ์‹œ์—ฐ ๋ฐ ์ „์ฒด ๊ธฐ๋Šฅ ํ†ตํ•ฉ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"๋ฌธํ•ญ DB ๊ฒ€์ฆ, ์‘๋‹ต ์ •์ œ, ์ž๋™ ์ฑ„์ , ์˜ค๋‹ต ์œ ํ˜• ๋ถ„๋ฅ˜, ์ทจ์•ฝ ์˜์—ญ ์ถ”์ฒœ, ๊ต๊ด€ ๊ฒ€ํ† (HITL) ๋Œ€๊ธฐ์—ด, AAR ์žฌ๋ณด์ • ํŒŒ๋ผ๋ฏธํ„ฐ ์ ์šฉ๊นŒ์ง€ ๋ฐฉ๊ณต ํ€ด์ฆˆ ์ฑ„์ ยท์˜ค๋‹ต๋ถ„์„ ์‹œ์Šคํ…œ ์ „์ฒด๋ฅผ ํ•œ ํŒŒ์ผ๋กœ ํ†ตํ•ฉ ์‹คํ–‰ํ•˜๊ณ , ์ž์ฒด ์ ๊ฒ€(์‹คํ–‰ ์„ฑ๊ณต๋ฅ ยทํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€)๊ณผ ๊ต์œกํ›ˆ๋ จ ๋‹ด๋‹น๊ด€ ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ์„ ์ถœ๋ ฅํ•œ๋‹ค.\"\"\"\n", "import difflib\n", "import hashlib\n", "import inspect\n", "import json\n", "import re\n", "import sys\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.pipeline import make_pipeline\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๋ฐฉ๊ณตยท๋“œ๋ก  ์‹๋ณ„ ํ€ด์ฆˆ ๋ฌธํ•ญ ์€ํ–‰๊ณผ ๊ฐ€์ƒ ์‘์‹œ์ž ์‘๋‹ต ์ƒ์„ฑ๊ธฐ\n", "# ๋ฌธํ•ญ์€ ์ผ๋ฐ˜ ์›์น™ ์ˆ˜์ค€์˜ ๊ฐ€์ƒ ๋ฌธํ•ญ์ด๋ฉฐ ์‹ค์ œ ๊ต๋ฒ”ยท๊ต์ „๊ทœ์น™ ์›๋ฌธ์ด ์•„๋‹ˆ๋‹ค. ์‘์‹œ์ž๋Š” ๊ฐ€๋ช…(S001~)์ด๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "AREAS = [\"ํ˜•์ƒ์‹๋ณ„\", \"ํ”ผ์•„์‹๋ณ„\", \"๊ฒฝ๋ณด๋ณด๊ณ \", \"๊ต์ „ํ†ต์ œ\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\"]\n", "ETYPES = [\"๊ฐœ๋… ํ˜ผ๋™\", \"ํ”ผ์•„ ์˜ค์ธ\", \"์ ˆ์ฐจ ๋ˆ„๋ฝ\"]\n", "POINTS = {1: 2, 2: 3, 3: 5}\n", "C, F, P = ETYPES\n", "\n", "\n", "def _mc(qid, area, diff, stem, opts, ans, dist):\n", " return {\"qid\": qid, \"type\": \"MC\", \"area\": area, \"diff\": diff, \"stem\": stem, \"options\": opts, \"answer\": ans,\n", " \"dist\": dict(zip([i for i in range(1, 5) if i != ans], dist))}\n", "\n", "\n", "def _sa(qid, area, diff, stem, accept, wrong):\n", " return {\"qid\": qid, \"type\": \"SA\", \"area\": area, \"diff\": diff, \"stem\": stem, \"accept\": accept, \"wrong\": wrong}\n", "\n", "\n", "def _order(qid, area, diff, stem):\n", " return {\"qid\": qid, \"type\": \"ORDER\", \"area\": area, \"diff\": diff, \"stem\": stem, \"answer\": \"ABCD\"}\n", "\n", "\n", "BANK = [\n", " _mc(\"Q01\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†์ด ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ๋ฅผ ๊ฐ€์ง„ ์†Œํ˜• ๊ณ ์ •์ต ์‹ค๋ฃจ์—ฃ์€?\", [\"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์‚ผ๊ฐ์ต ๊ณ ์ •์ตํ˜•\", \"ํšŒ์ „์ต ํ—ฌ๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 2, [C, C, F]),\n", " _mc(\"Q02\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๊ฐ€ ์‹ญ์ž๋กœ ๋ฐฐ์น˜๋œ ์‹ค๋ฃจ์—ฃ์€?\", [\"๊ณ ์ •์ต ๋“œ๋ก ํ˜•\", \"์ฟผ๋“œ์ฝฅํ„ฐํ˜•\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\"], 2, [C, C, C]),\n", " _mc(\"Q03\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ฐ€๋Š˜๊ณ  ๊ธด ๋™์ฒด ์ค‘๊ฐ„์— ์ž‘์€ ๋‚ ๊ฐœ๊ฐ€ ๋ถ™์€ ์‹ค๋ฃจ์—ฃ์€?\", [\"์ˆœํ•ญ๋ฏธ์‚ฌ์ผํ˜•\", \"ํƒ„๋„๋ฏธ์‚ฌ์ผํ˜•\", \"๋ฏผ๊ฐ„ ๊ฒฝ๋น„ํ–‰๊ธฐํ˜•\", \"์กฐ๋ฅ˜\"], 1, [C, F, F]),\n", " _mc(\"Q04\", \"ํ˜•์ƒ์‹๋ณ„\", 3, \"์›๊ฑฐ๋ฆฌ๋ผ ์‹ค๋ฃจ์—ฃ ์‹๋ณ„์ด ์–ด๋ ค์šธ ๋•Œ ๋จผ์ € ํ•  ํ–‰๋™์€?\", [\"์ถ”์ • ์œ ํ˜•์œผ๋กœ ํ™•์ • ๋ณด๊ณ \", \"๊ด€์ธก ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•˜๊ณ  ์ถ”๊ฐ€ ๊ด€์ธก\", \"๊ด€์ธก ์ค‘๋‹จ\", \"๋ฏผ๊ฐ„๊ธฐ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _mc(\"Q05\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์‹๋ณ„ ์‹ ํ˜ธ ์‘๋‹ต์ด ์—†๋Š” ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํŒ๋‹จ์€?\", [\"์ฆ‰์‹œ ์ ๊ธฐ๋กœ ํ™•์ •\", \"๋ฏธํ™•์ธ ํ•ญ๊ณต๊ธฐ๋กœ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ถ”๊ฐ€ ํ™•์ธ\", \"์•„๊ตฐ๊ธฐ๋กœ ๊ฐ„์ฃผ\", \"๋ฌด์‹œ\"], 2, [F, F, P]),\n", " _mc(\"Q06\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด์™€ ์ผ์น˜ํ•˜๋Š” ์ €์† ๋น„ํ–‰์ฒด์˜ ๋ถ„๋ฅ˜๋Š”?\", [\"์  ๋“œ๋ก \", \"๋ฏผ๊ฐ„๊ธฐ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ, ์ถ”๊ฐ€ ํ™•์ธ\", \"์ˆœํ•ญ๋ฏธ์‚ฌ์ผ\", \"์•„๊ตฐ ์ •์ฐฐ๊ธฐ๋กœ ํ™•์ •\"], 2, [F, C, F]),\n", " _mc(\"Q07\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์•„๊ตฐ ๋“œ๋ก  ์šด์šฉ ๊ณต์ง€๊ฐ€ ์žˆ๋Š” ๊ณต์—ญ์—์„œ ๋ฏธํ™•์ธ ๋“œ๋ก ์„ ๋ณด๋ฉด ์šฐ์„  ํ™•์ธํ•  ๊ฒƒ์€?\", [\"๊ธฐ์ƒ ์ •๋ณด\", \"์•„๊ตฐ ์šด์šฉ ๊ณ„ํš๊ณผ ์‹๋ณ„ ์‹ ํ˜ธ\", \"๋Œ€์‘ ์ค€๋น„ ์ƒํƒœ\", \"์ฃผ๋ณ€ ์ง€ํ˜•\"], 2, [C, P, C]),\n", " _mc(\"Q08\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"์กฐ๋ฅ˜ ๋ฌด๋ฆฌ์™€ ์†Œํ˜• ๋“œ๋ก  ๊ตฐ์ง‘ ๊ตฌ๋ถ„์— ๊ฐ€์žฅ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด๋Š”?\", [\"ํฌ๊ธฐ๋งŒ ๋น„๊ต\", \"๊ถค์ ์˜ ๊ทœ์น™์„ฑ๊ณผ ์—ด ์‹ ํ˜ธ\", \"์ƒ‰์ƒ\", \"๊ด€์ธก ์‹œ๊ฐ\"], 2, [C, C, C]),\n", " _mc(\"Q09\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๊ณต์ค‘ ์œ„ํ˜‘ ์ตœ์ดˆ ๋ณด๊ณ ์— ํฌํ•จํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์€?\", [\"๊ด€์ธก ์‹œ๊ฐ\", \"๋ฐฉํ–ฅ๊ณผ ๊ฑฐ๋ฆฌ\", \"์ถ”์ • ์œ ํ˜•\", \"๊ด€์ธก์ž ๊ฐœ์ธ ์—ฐ๋ฝ์ฒ˜\"], 4, [P, P, P]),\n", " _mc(\"Q10\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๊ฒฝ๋ณด ์ „ํŒŒ ํ›„ ์ƒํ™ฉ์ด ๋ฐ”๋€Œ๋ฉด?\", [\"์ตœ์ดˆ ๋ณด๊ณ  ์œ ์ง€\", \"๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ถ”๊ฐ€ ๋ณด๊ณ \", \"์ƒํ™ฉ ์ข…๋ฃŒ ํ›„ ์ผ๊ด„ ๋ณด๊ณ \", \"๊ฐœ์ธ ํŒ๋‹จ์œผ๋กœ ๊ฒฝ๋ณด ํ•ด์ œ\"], 2, [P, P, P]),\n", " _mc(\"Q11\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ณด๊ณ  ๋‚ด์šฉ์ด ๋ถˆํ™•์‹คํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ํ‘œํ˜„์€?\", [\"ํ™•์ • ํ‘œํ˜„์œผ๋กœ ๋ณด๊ณ \", \"'์ถ”์ •'์ž„์„ ๋ฐํžˆ๊ณ  ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ๋ณด๊ณ \", \"๋ณด๊ณ  ๋ณด๋ฅ˜\", \"๋‹ค๋ฅธ ๊ด€์ธก์ž ๋ณด๊ณ ๋ฅผ ๋ณต์‚ฌ\"], 2, [C, P, P]),\n", " _mc(\"Q12\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋“œ๋ก  ํƒ์ง€ ํ›„ ๋ณด๊ณ  ๊ฒฝ๋กœ๋กœ ์•Œ๋งž์€ ๊ฒƒ์€?\", [\"์ธ์ ‘ ๋ถ€๋Œ€์—๋งŒ ์ „ํŒŒ\", \"์ง€ํœ˜๊ณ„ํ†ต์„ ๋”ฐ๋ผ ๋ณด๊ณ \", \"๊ฐœ์ธ ๋ฉ”์‹ ์ €๋กœ ๊ณต์œ \", \"๋ณด๊ณ  ์—†์ด ๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q13\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ๋Œ€์‘์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?\", [\"๊ฐ€๋Šฅํ•˜๋‹ค\", \"์ธ๊ฐ„ ์ง€ํœ˜๊ด€์˜ ์Šน์ธ์ด ํ•„์š”ํ•˜๋‹ค\", \"์‹ ๋ขฐ๋„ 90% ์ด์ƒ์ด๋ฉด ๊ฐ€๋Šฅ\", \"์•ผ๊ฐ„์—๋งŒ ๊ฐ€๋Šฅ\"], 2, [C, C, C]),\n", " _mc(\"Q14\", \"๊ต์ „ํ†ต์ œ\", 2, \"๋ฏผ๊ฐ„ ํ”ผํ•ด ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์ธ๋˜๋ฉด?\", [\"๊ทธ๋Œ€๋กœ ์ง„ํ–‰\", \"์ฆ‰์‹œ ๋ณด๊ณ ํ•˜๊ณ  ์ง€ํœ˜๊ด€ ํŒ๋‹จ์„ ๋ฐ›๋Š”๋‹ค\", \"์กฐ์น˜ ํ›„ ๋ณด๊ณ \", \"๋ฌด์‹œ\"], 2, [P, P, F]),\n", " _mc(\"Q15\", \"๊ต์ „ํ†ต์ œ\", 3, \"์‹๋ณ„์ด ๋ถˆํ™•์‹คํ•œ ํ‘œ์ ์— ๋Œ€ํ•œ ์›์น™์€?\", [\"์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผํ•ด ์ฆ‰์‹œ ๋Œ€์‘\", \"์‹๋ณ„ ํ™•์ธ ์ „๊นŒ์ง€ ์Šน์ธ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค\", \"๋ฌด์กฐ๊ฑด ๋ฌด์‹œ\", \"๋‹ค์ˆ˜๊ฒฐ๋กœ ๊ฒฐ์ •\"], 2, [F, F, C]),\n", " _mc(\"Q16\", \"๊ต์ „ํ†ต์ œ\", 2, \"ํŒ๋‹จ ๊ทผ๊ฑฐ๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”?\", [\"ํ˜•์‹์ƒ ํ•„์š”\", \"์‚ฌํ›„ ๊ฒ€ํ† ์™€ ์ฑ…์ž„ ์ถ”์ \", \"๋ณด๊ณ  ์ƒ๋žต\", \"๊ธฐ๋ก ๋ถˆํ•„์š”\"], 2, [C, P, P]),\n", " _mc(\"Q17\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"๋Œ€๋“œ๋ก  ์ฒด๊ณ„์˜ ์ผ๋ฐ˜์  ์ฒ˜๋ฆฌ ์ˆœ์„œ๋Š”?\", [\"๋Œ€์‘-ํƒ์ง€-์‹๋ณ„\", \"ํƒ์ง€-์‹๋ณ„-๋ณด๊ณ ยทํŒ๋‹จ-๋Œ€์‘\", \"์‹๋ณ„-๋Œ€์‘-ํƒ์ง€\", \"๋ณด๊ณ -ํƒ์ง€-๋Œ€์‘\"], 2, [P, P, P]),\n", " _mc(\"Q18\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 3, \"๋ ˆ์ด๋”๋งŒ์œผ๋กœ ์กฐ๋ฅ˜์™€ ๋“œ๋ก  ๊ตฌ๋ถ„์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?\", [\"๋ฐ˜์‚ฌ ์‹ ํ˜ธ ํฌ๊ธฐ๊ฐ€ ๋น„์Šทํ•  ์ˆ˜ ์žˆ์–ด์„œ\", \"๋ ˆ์ด๋”๋Š” ์•ผ๊ฐ„์— ๋ชป ์จ์„œ\", \"๋“œ๋ก ์—๋Š” ๊ธˆ์†์ด ์—†์–ด์„œ\", \"์กฐ๋ฅ˜๋Š” ํƒ์ง€๋˜์ง€ ์•Š์•„์„œ\"], 1, [C, C, C]),\n", " _mc(\"Q19\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ „์ž๊ด‘ํ•™(EO) ์„ผ์„œ์˜ ์•ฝ์ ์ด ์ปค์ง€๋Š” ์กฐ๊ฑด์€?\", [\"๋ง‘์€ ์ฃผ๊ฐ„\", \"์•ˆ๊ฐœยท์•ผ๊ฐ„\", \"์ €์† ํ‘œ์ \", \"๊ทผ๊ฑฐ๋ฆฌ\"], 2, [C, C, C]),\n", " _mc(\"Q20\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"๊ฒฝ๋ณด๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ์˜ค๊ฒฝ๋ณด๋กœ ํŒ๋ช…๋˜๋ฉด?\", [\"๊ฒฝ๋ณด ์žฅ์น˜๋ฅผ ๋ˆ๋‹ค\", \"์‚ฌ๋ก€๋ฅผ ๊ธฐ๋กยท๋ณด๊ณ ํ•˜๊ณ  ๊ธฐ์ค€ ์žฌ๊ฒ€ํ†  ์š”์ฒญ\", \"๋ฌด์‹œํ•œ๋‹ค\", \"๋ชจ๋‘ ์‹ค์ œ ์œ„ํ˜‘์œผ๋กœ ๊ฐ„์ฃผ\"], 2, [P, P, F]),\n", " _sa(\"Q21\", \"ํ”ผ์•„์‹๋ณ„\", 1, \"์•„๊ตฐยท์ ยท๋ฏผ๊ฐ„์„ ๊ตฌ๋ถ„ํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"ํ”ผ์•„์‹๋ณ„\", \"IFF\"], {\"ํ‘œ์ ํš๋“\": C, \"์ ๊ธฐํŒ์ •\": F, \"๊ฒฝ๊ณ„\": C}),\n", " _sa(\"Q22\", \"ํ˜•์ƒ์‹๋ณ„\", 2, \"๊ผฌ๋ฆฌ๋‚ ๊ฐœ ์—†๋Š” ์‚ผ๊ฐํ˜• ๋‚ ๊ฐœ ํ˜•ํƒœ๋ฅผ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์‚ผ๊ฐ์ต\", \"๋ธํƒ€์ต\", \"delta\"], {\"์ง์„ ์ต\": C, \"ํ›„ํ‡ด์ต\": C, \"์ฟผ๋“œ์ฝฅํ„ฐ\": C}),\n", " _sa(\"Q23\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"๋ถˆํ™•์‹คํ•œ ๊ด€์ธก์„ ๋ณด๊ณ ํ•  ๋•Œ ๋ถ™์ด๋Š” ํ‘œํ˜„์€?\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C, \"ํ™•์ธ\": C, \"์ƒ๋žต\": P}),\n", " _sa(\"Q24\", \"๊ต์ „ํ†ต์ œ\", 1, \"AI ํƒ์ง€ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ตœ์ข… ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ์ฃผ์ฒด๋Š”?\", [\"์ง€ํœ˜๊ด€\"], {\"AI\": C, \"์šด์šฉ๋ณ‘\": P, \"๋ ˆ์ด๋”\": C}),\n", " _sa(\"Q25\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 1, \"์กฐ๋ฅ˜๋ฅผ ๋“œ๋ก ์œผ๋กœ ์ž˜๋ชป ๊ฒฝ๋ณดํ•˜๋Š” ๊ฒƒ์„ ๋ฌด์—‡์ด๋ผ ํ•˜๋Š”๊ฐ€?\", [\"์˜ค๊ฒฝ๋ณด\", \"์˜คํƒ\", \"false alarm\"], {\"๋ฏธํƒ\": C, \"์žฌ๊ฒฝ๋ณด\": C, \"์ ๊ธฐ\": F}),\n", " _sa(\"Q26\", \"ํ”ผ์•„์‹๋ณ„\", 2, \"์šดํ•ญ ์ •๋ณด๊ฐ€ ํ™•์ธ๋œ ์—ฌ๊ฐ๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๋Š”๊ฐ€?\", [\"๋ฏผ๊ฐ„๊ธฐ\", \"๋ฏผํ•ญ๊ธฐ\"], {\"์ ๊ธฐ\": F, \"๋ฏธํ™•์ธ๊ธฐ\": P, \"์•„๊ตฐ๊ธฐ\": F}),\n", " _sa(\"Q27\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"ํšŒ์ „๋‚ ๊ฐœ 4๊ฐœ๋ฅผ ๊ฐ€์ง„ ๋“œ๋ก  ํ˜•ํƒœ๋Š”?\", [\"์ฟผ๋“œ์ฝฅํ„ฐ\", \"quadcopter\"], {\"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\": C, \"๊ณ ์ •์ต\": C, \"ํ—ฌ๊ธฐ\": F}),\n", " _sa(\"Q28\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 1, \"๋ณด๊ณ ๋Š” ๋ฌด์—‡์„ ๋”ฐ๋ผ ์˜ฌ๋ผ๊ฐ€๋Š”๊ฐ€?\", [\"์ง€ํœ˜๊ณ„ํ†ต\"], {\"์ธ์ ‘๋ถ€๋Œ€\": P, \"๊ฐœ์ธ๋ฉ”์‹ ์ €\": P, \"ํ†ต์‹ ๋ง\": C}),\n", " _order(\"Q29\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"์ตœ์ดˆ ๋ณด๊ณ  ์ˆœ์„œ: A ๊ด€์ธก ์‹œ๊ฐ, B ๋ฐฉํ–ฅยท๊ฑฐ๋ฆฌ, C ์ถ”์ • ์œ ํ˜•, D ๊ด€์ธก์ž ์กฐ์น˜\"),\n", " _order(\"Q30\", \"๋Œ€๋“œ๋ก ์ผ๋ฐ˜\", 2, \"์ฒ˜๋ฆฌ ์ˆœ์„œ: A ํƒ์ง€, B ์‹๋ณ„, C ๋ณด๊ณ ยทํŒ๋‹จ, D ๋Œ€์‘\"),\n", " _order(\"Q31\", \"๊ต์ „ํ†ต์ œ\", 3, \"ํ†ต์ œ ์ˆœ์„œ: A ์‹๋ณ„ ํ™•์ธ, B ์ƒ๊ธ‰ ์Šน์ธ ์š”์ฒญ, C ์Šน์ธ ์ˆ˜๋ น, D ์กฐ์น˜ ๊ธฐ๋ก\"),\n", " _order(\"Q32\", \"ํ”ผ์•„์‹๋ณ„\", 3, \"ํ™•์ธ ์ˆœ์„œ: A ์šดํ•ญ์ •๋ณด ๋Œ€์กฐ, B ์‹๋ณ„ ์‹ ํ˜ธ ํ™•์ธ, C ๋ฏธํ™•์ธ ๋ถ„๋ฅ˜, D ๋ณด๊ณ \"),\n", "]\n", "QIDX = {q[\"qid\"]: q for q in BANK}\n", "UNITS = [\"๊ฐ€์ƒ1์ค‘๋Œ€\", \"๊ฐ€์ƒ2์ค‘๋Œ€\", \"๊ฐ€์ƒ3์ค‘๋Œ€\"]\n", "_CIRC = \"โ‘ โ‘กโ‘ขโ‘ฃ\"\n", "PARA = {\"Q21\": \"์•„๊ตฐ์ ๊ตฐ ๊ตฌ๋ถ„\", \"Q22\": \"์„ธ๋ชจ ๋‚ ๊ฐœ\", \"Q23\": \"์ถ”์ •๋จ์œผ๋กœ ํ‘œ๊ธฐ\", \"Q24\": \"์ค‘๋Œ€์žฅ(์ง€ํœ˜๊ด€)\", \"Q25\": \"์ž˜๋ชป๋œ ๊ฒฝ๋ณด\",\n", " \"Q26\": \"๋ฏผ๊ฐ„ ์—ฌ๊ฐ๊ธฐ\", \"Q27\": \"4๋ฐœ ๋“œ๋ก \", \"Q28\": \"์ง€ํœ˜ ๋ผ์ธ\"} # ๋œป์€ ๋งž์ง€๋งŒ ์ธ์ • ๋‹ต์•ˆ ๋ชฉ๋ก์— ์—†๋Š” ํ‘œํ˜„\n", "\n", "\n", "def make_soldiers(n_per_unit, seed):\n", " rng = np.random.default_rng(seed)\n", " out = []\n", " for u, unit in enumerate(UNITS):\n", " base = [0.3, -0.1, 0.0][u]\n", " for i in range(n_per_unit):\n", " off = rng.normal(0, 0.5, len(AREAS)) + (np.array([0, 0, 0, -0.6, 0]) if u == 1 else 0)\n", " out.append({\"sid\": f\"S{len(out) + 1:03d}\", \"unit\": unit, \"theta\": dict(zip(AREAS, base + rng.normal(0, 0.6) + off)),\n", " \"tend\": dict(zip(ETYPES, rng.dirichlet([1.2, 1.2, 1.2])))})\n", " return out\n", "\n", "\n", "def p_correct(theta, diff):\n", " return 1 / (1 + np.exp(-1.7 * (theta - (diff - 2) * 0.9 + 0.9)))\n", "\n", "\n", "def _typo(s, rng):\n", " if len(s) < 2:\n", " return s\n", " i = int(rng.integers(len(s) - 1))\n", " return s[:i] + s[i + 1] + s[i] + s[i + 2:] if len(s) <= 3 or rng.random() < 0.5 else s[:i] + s[i + 1:]\n", "\n", "\n", "def _fmt_mc(k, rng):\n", " r = rng.random()\n", " if r < 0.55:\n", " return str(k)\n", " return rng.choice([_CIRC[k - 1], f\"{k}๋ฒˆ\", \"ABCD\"[k - 1], \"abcd\"[k - 1] + \")\", f\" {k} \", chr(0xFF10 + k), f\"{k}.\"])\n", "\n", "\n", "def _fmt_sa(s, rng):\n", " r = rng.random()\n", " if r < 0.45:\n", " return s\n", " if r < 0.6:\n", " return \" \".join(s) if len(s) <= 4 else s[:2] + \" \" + s[2:]\n", " if r < 0.72:\n", " return s + rng.choice([\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \".\", \"์ž„\"])\n", " if r < 0.8:\n", " return s.upper() if s.isascii() else \"\".join(chr(ord(ch) + 0xFEE0) if \"!\" <= ch <= \"~\" else ch for ch in s) + \" \"\n", " return _typo(s, rng)\n", "\n", "\n", "def answer(q, sol, rng):\n", " \"\"\"ํ•œ ๋ฌธํ•ญ ์‘๋‹ต: (์›๋ณธ ์‘๋‹ต ๋ฌธ์ž์—ด, ์˜๋„ํ•œ ์ •๋‹ต ์—ฌ๋ถ€, ์˜๋„ํ•œ ๋‹ต, ์˜ค๋‹ต ์œ ํ˜•).\"\"\"\n", " ok = rng.random() < p_correct(sol[\"theta\"][q[\"area\"]], q[\"diff\"])\n", " if q[\"type\"] == \"MC\":\n", " if ok:\n", " k, et = q[\"answer\"], None\n", " else:\n", " ks = list(q[\"dist\"]); w = np.array([sol[\"tend\"][q[\"dist\"][k]] for k in ks])\n", " k = int(rng.choice(ks, p=w / w.sum())); et = q[\"dist\"][k]\n", " return _fmt_mc(k, rng), ok, str(k), et\n", " if q[\"type\"] == \"SA\":\n", " if ok and rng.random() < 0.06:\n", " return PARA[q[\"qid\"]], ok, PARA[q[\"qid\"]], None\n", " if ok:\n", " s, et = str(rng.choice(q[\"accept\"])), None\n", " else:\n", " ws = list(q[\"wrong\"]); w = np.array([sol[\"tend\"][q[\"wrong\"][x]] for x in ws])\n", " s = str(rng.choice(ws, p=w / w.sum())); et = q[\"wrong\"][s]\n", " return _fmt_sa(s, rng), ok, s, et\n", " if ok:\n", " s = \"ABCD\"\n", " elif rng.random() < 0.5:\n", " s = \"ABCD\".replace(\"ABCD\"[rng.integers(1, 4)], \"\") # ๋‹จ๊ณ„ ํ•˜๋‚˜ ๋น ๋œจ๋ฆผ\n", " else:\n", " i = int(rng.integers(0, 3)); s = list(\"ABCD\"); s[i], s[i + 1] = s[i + 1], s[i]; s = \"\".join(s) # ์ˆœ์„œ ๋’ค๋ฐ”๋€œ\n", " raw = s if rng.random() < 0.6 else rng.choice([\"-\".join(s), \" \".join(s), s.lower(), \",\".join(s)])\n", " return raw, ok, s, (None if ok else P)\n", "\n", "\n", "def make_responses(n_per_unit=40, seed=7, blank=0.02):\n", " \"\"\"์‘์‹œ์ž x ๋ฌธํ•ญ ์‘๋‹ตํ‘œ(DataFrame)๋ฅผ ๋งŒ๋“ ๋‹ค. truth_* ์—ด์€ ๊ต๊ด€ ๊ธฐ์ค€ ์ •๋‹ต(ํ‰๊ฐ€์šฉ)์ด๋‹ค.\"\"\"\n", " rng = np.random.default_rng(seed)\n", " rows = []\n", " for sol in make_soldiers(n_per_unit, seed):\n", " for q in BANK:\n", " raw, ok, intended, et = answer(q, sol, rng)\n", " if rng.random() < blank:\n", " raw, ok, intended, et = \"\", False, \"\", None\n", " rows.append({\"rid\": f\"R{len(rows):05d}\", \"sid\": sol[\"sid\"], \"unit\": sol[\"unit\"], \"qid\": q[\"qid\"], \"raw\": raw,\n", " \"truth_correct\": ok, \"truth_answer\": intended, \"truth_etype\": et})\n", " return pd.DataFrame(rows)\n", "# ---------------------------------------------------------------------------\n", "\n", "# ---- ์‘๋‹ต ์ •์ œยทํŒŒ์‹ฑยท์ฑ„์  ๊ณตํ†ต ํ•จ์ˆ˜ ----\n", "TAILS = (\"์ž…๋‹ˆ๋‹ค\", \"์ด๋‹ค\", \"์ž„\")\n", "\n", "\n", "def norm_text(s):\n", " \"\"\"์ „๊ฐ->๋ฐ˜๊ฐ(NFKC), ์†Œ๋ฌธ์ž, ๊ณต๋ฐฑยท๋ฌธ์žฅ๋ถ€ํ˜ธ ์ œ๊ฑฐ, ๋๋งบ์Œ ์–ด๋ฏธ ์ œ๊ฑฐ.\"\"\"\n", " s = unicodedata.normalize(\"NFKC\", str(s)).strip().lower()\n", " s = re.sub(r\"[\\s\\.\\,\\-\\(\\)ยท]\", \"\", s)\n", " for t in TAILS:\n", " if s.endswith(t) and len(s) > len(t) + 1:\n", " s = s[:-len(t)]\n", " return s\n", "\n", "\n", "def parse(q, raw):\n", " \"\"\"์›๋ณธ ์‘๋‹ต -> (์ •์ œ๊ฐ’, ์ƒํƒœ). ์ƒํƒœ: ์ •์ƒ / ๋ฏธ์‘๋‹ต / ํ˜•์‹์˜ค๋ฅ˜\"\"\"\n", " if raw is None or str(raw).strip() == \"\":\n", " return \"\", \"๋ฏธ์‘๋‹ต\"\n", " s = unicodedata.normalize(\"NFKC\", str(raw)).strip()\n", " if q[\"type\"] == \"MC\":\n", " t = re.sub(r\"[\\s๋ฒˆ\\.\\)]\", \"\", s).lower()\n", " t = {\"a\": \"1\", \"b\": \"2\", \"c\": \"3\", \"d\": \"4\"}.get(t, t)\n", " return (t, \"์ •์ƒ\") if t in (\"1\", \"2\", \"3\", \"4\") else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"ORDER\":\n", " t = re.sub(r\"[^A-Za-z]\", \"\", s).upper()\n", " return (t, \"์ •์ƒ\") if t and set(t) <= set(\"ABCD\") and len(set(t)) == len(t) else (t, \"ํ˜•์‹์˜ค๋ฅ˜\")\n", " return norm_text(s), \"์ •์ƒ\"\n", "\n", "\n", "def grade_exact(q, val):\n", " if q[\"type\"] == \"MC\":\n", " return val == str(q[\"answer\"])\n", " if q[\"type\"] == \"ORDER\":\n", " return val == q[\"answer\"]\n", " return val in {norm_text(a) for a in q[\"accept\"]}\n", "\n", "\n", "def grade_fuzzy(q, val, cut=0.75):\n", " \"\"\"๋‹จ๋‹ตํ˜•์€ ์˜คํƒˆ์ž(๊ธ€์ž ์ˆœ์„œ ๋ฐ”๋€œยทํ•œ ๊ธ€์ž ๋ˆ„๋ฝ)๋ฅผ ํ—ˆ์šฉ(์œ ์‚ฌ๋„ cut ์ด์ƒ)ํ•˜๋˜, ์•Œ๋ ค์ง„ ์˜ค๋‹ต๊ณผ ๋” ๊ฐ€๊นŒ์šฐ๋ฉด ์˜ค๋‹ต์œผ๋กœ ๋ณธ๋‹ค.\"\"\"\n", " if grade_exact(q, val) or q[\"type\"] != \"SA\" or len(val) < 2:\n", " return grade_exact(q, val)\n", " best = max(1.0 if sorted(val) == sorted(norm_text(a)) else difflib.SequenceMatcher(None, val, norm_text(a)).ratio() for a in q[\"accept\"])\n", " worst = max(difflib.SequenceMatcher(None, val, norm_text(w)).ratio() for w in q[\"wrong\"])\n", " return best >= cut and best > worst\n", "\n", "\n", "def score_of(q, correct):\n", " return POINTS[q[\"diff\"]] if correct else 0\n", "\n", "# ---- ์˜ค๋‹ต ์œ ํ˜• ๋ถ„๋ฅ˜๊ธฐ(๊ทœ์น™ + ๋ฌธ์ž n-gram ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€) ----\n", "SEED = {\"ํ”ผ์•„ ์˜ค์ธ\": [\"์ ๊ธฐ\", \"์  ๋“œ๋ก \", \"์•„๊ตฐ๊ธฐ\", \"์  ํ—ฌ๊ธฐ\", \"์ ๊ธฐํŒ์ •\", \"ํ—ฌ๊ธฐ\", \"์  ํ•ญ๊ณต๊ธฐ ํ™•์ •\", \"์•„๊ตฐ ๋“œ๋ก \"],\n", " \"์ ˆ์ฐจ ๋ˆ„๋ฝ\": [\"์ƒ๋žต\", \"์ธ์ ‘๋ถ€๋Œ€\", \"๊ฐœ์ธ๋ฉ”์‹ ์ €\", \"๋ณด๊ณ  ์•ˆ ํ•จ\", \"์šด์šฉ๋ณ‘\", \"๋ฏธํ™•์ธ๊ธฐ\", \"๋‹จ๊ณ„ ๊ฑด๋„ˆ๋œ€\", \"์ง์ ‘ ์ „ํŒŒ\"],\n", " \"๊ฐœ๋… ํ˜ผ๋™\": [\"์ง์„ ์ต\", \"ํ›„ํ‡ด์ต\", \"ํ—ฅ์‚ฌ์ฝฅํ„ฐ\", \"๋ฏธํƒ\", \"์žฌ๊ฒฝ๋ณด\", \"ํ™•์ •\", \"ํ™•์ธ\", \"๋ ˆ์ด๋”\", \"AI\", \"ํ‘œ์ ํš๋“\", \"๊ฒฝ๊ณ„\", \"ํ†ต์‹ ๋ง\", \"๊ณ ์ •์ต\"]}\n", "\n", "\n", "def build_text_model():\n", " texts, labels = [], []\n", " for q in BANK:\n", " if q[\"type\"] == \"SA\":\n", " for w, et in q[\"wrong\"].items():\n", " texts.append(w); labels.append(et)\n", " for et, ws in SEED.items():\n", " texts += ws; labels += [et] * len(ws)\n", " m = make_pipeline(TfidfVectorizer(analyzer=\"char_wb\", ngram_range=(1, 3)), LogisticRegression(max_iter=2000, C=5))\n", " return m.fit([norm_text(t) for t in texts], labels)\n", "\n", "\n", "def classify_error(q, val, text_model):\n", " \"\"\"์˜ค๋‹ต 1๊ฑด์˜ ์œ ํ˜•๊ณผ ํŒ๋‹จ ๊ทผ๊ฑฐ(rule/text)๋ฅผ ๋Œ๋ ค์ค€๋‹ค.\"\"\"\n", " if q[\"type\"] == \"MC\":\n", " return q[\"dist\"].get(int(val), \"๊ฐœ๋… ํ˜ผ๋™\") if val in \"1234\" and val else \"๊ฐœ๋… ํ˜ผ๋™\", \"๋ณด๊ธฐ ๋งคํ•‘\"\n", " if q[\"type\"] == \"ORDER\":\n", " return \"์ ˆ์ฐจ ๋ˆ„๋ฝ\", (\"๋‹จ๊ณ„ ๋ˆ„๋ฝ\" if len(val) < 4 else \"์ˆœ์„œ ๋’ค๋ฐ”๋€œ\")\n", " known = {norm_text(w): et for w, et in q[\"wrong\"].items()}\n", " if val in known:\n", " return known[val], \"์˜ค๋‹ต ๋ชฉ๋ก\"\n", " near = max(known, key=lambda w: difflib.SequenceMatcher(None, val, w).ratio())\n", " if difflib.SequenceMatcher(None, val, near).ratio() >= 0.75 or sorted(val) == sorted(near):\n", " return known[near], \"์˜ค๋‹ต ๋ชฉ๋ก(์˜คํƒˆ์ž)\"\n", " return str(text_model.predict([val])[0]), \"๋ฌธ์ž ๋ชจ๋ธ\"\n", "\n", "CONFIG = {\"pass_ratio\": 0.7, \"fuzzy_cut\": 0.75, \"excluded\": [], \"weak_area\": 0.6} # ๋ฏธ์…˜ 9 ์žฌ๋ณด์ • ๊ฒฐ๊ณผ ํ˜•์‹\n", "\n", "\n", "def validate_bank(bank):\n", " errs = []\n", " for q in bank:\n", " if q[\"area\"] not in AREAS or q[\"diff\"] not in POINTS:\n", " errs.append(f\"{q['qid']}: ์˜์—ญ/๋‚œ์ด๋„ ์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"MC\" and (q[\"answer\"] not in range(1, 5) or len(q[\"dist\"]) != 3):\n", " errs.append(f\"{q['qid']}: ์ •๋‹ต ํ‚ค/์˜ค๋‹ต ๋งคํ•‘ ์˜ค๋ฅ˜\")\n", " if q[\"type\"] == \"SA\" and not q[\"accept\"]:\n", " errs.append(f\"{q['qid']}: ์ธ์ • ๋‹ต์•ˆ ์—†์Œ\")\n", " return errs\n", "\n", "\n", "def dedup(df):\n", " return df.drop_duplicates([\"sid\", \"qid\"], keep=\"last\")\n", "\n", "\n", "def grade_all(df, tm):\n", " d = df[~df[\"qid\"].isin(CONFIG[\"excluded\"])].copy()\n", " pv = [parse(QIDX[q], r) for q, r in zip(d[\"qid\"], d[\"raw\"])]\n", " d[\"val\"], d[\"status\"] = [p[0] for p in pv], [p[1] for p in pv]\n", " d[\"correct\"] = [s == \"์ •์ƒ\" and grade_fuzzy(QIDX[q], v, CONFIG[\"fuzzy_cut\"]) for q, v, s in zip(d[\"qid\"], d[\"val\"], d[\"status\"])]\n", " d[\"score\"] = [score_of(QIDX[q], c) for q, c in zip(d[\"qid\"], d[\"correct\"])]\n", " d[\"etype\"] = [None if c or s != \"์ •์ƒ\" else classify_error(QIDX[q], v, tm)[0] for q, v, c, s in zip(d[\"qid\"], d[\"val\"], d[\"correct\"], d[\"status\"])]\n", " d[\"area\"] = d[\"qid\"].map(lambda q: QIDX[q][\"area\"])\n", " return d\n", "\n", "\n", "def weak_areas(d):\n", " t = d.groupby([\"sid\", \"area\"])[\"correct\"].mean().unstack()\n", " return {s: list(r[r < CONFIG[\"weak_area\"]].index) for s, r in t.iterrows()}\n", "\n", "\n", "class Review:\n", " def __init__(self):\n", " self.items, self.log = [], []\n", "\n", " def submit(self, iid, reason):\n", " prev = self.log[-1][\"hash\"] if self.log else \"GENESIS\"\n", " body = {\"iid\": iid, \"event\": \"SUBMIT\", \"reason\": reason}\n", " self.log.append({**body, \"hash\": hashlib.sha256((prev + json.dumps(body, ensure_ascii=False)).encode()).hexdigest()})\n", " self.items.append({\"iid\": iid, \"status\": \"PENDING\"})\n", "\n", "\n", "def pipeline(seed):\n", " t0 = time.perf_counter()\n", " assert not validate_bank(BANK), \"๋ฌธํ•ญ DB ์˜ค๋ฅ˜\"\n", " tm = build_text_model()\n", " raw = make_responses(40, seed=seed)\n", " raw = pd.concat([raw, raw.sample(50, random_state=seed)]) # ์žฌ์ „์†ก ์ค‘๋ณต 50๊ฑด\n", " d = grade_all(dedup(raw), tm)\n", " full = sum(POINTS[QIDX[q][\"diff\"]] for q in d[\"qid\"].unique())\n", " person = d.groupby([\"sid\", \"unit\"])[\"score\"].sum().reset_index()\n", " person[\"pass\"] = person[\"score\"] >= full * CONFIG[\"pass_ratio\"]\n", " rv = Review()\n", " for _, r in d[(d[\"status\"] == \"์ •์ƒ\") & ~d[\"correct\"] & d[\"truth_correct\"]].iterrows():\n", " rv.submit(r[\"rid\"], \"์ด์˜ ์ œ๊ธฐ ์˜ˆ์ƒ(๋œป์€ ๋งž์„ ์ˆ˜ ์žˆ์Œ)\")\n", " for _, r in person[(person[\"score\"] - full * CONFIG[\"pass_ratio\"]).abs() <= 3].iterrows():\n", " rv.submit(r[\"sid\"], \"ํ•ฉ๊ฒฉ์„  ๊ทผ์ฒ˜ ํ™•์ธ\")\n", " return {\"d\": d, \"person\": person, \"weak\": weak_areas(d), \"review\": rv, \"sec\": time.perf_counter() - t0, \"n_raw\": len(raw)}\n", "\n", "\n", "# ---------------- 1. ํ†ตํ•ฉ ์‹คํ–‰(3๊ฐœ ์‘์‹œ ํšŒ์ฐจ) ----------------\n", "runs, ok = [], 0\n", "for seed in (7, 21, 33):\n", " try:\n", " out = pipeline(seed); runs.append(out); ok += 1\n", " acc = (out[\"d\"][\"correct\"] == out[\"d\"][\"truth_correct\"]).mean()\n", " print(f\"ํšŒ์ฐจ seed={seed}: ์›๋ณธ {out['n_raw']:,}๊ฑด -> ์ฑ„์  {len(out['d']):,}๊ฑด, ์ฑ„์  ์ •ํ™•๋„ {acc:.1%}, \"\n", " f\"ํ•ฉ๊ฒฉ๋ฅ  {out['person']['pass'].mean():.1%}, ๊ต๊ด€ ๊ฒ€ํ†  {len(out['review'].items)}๊ฑด, {out['sec']:.1f}์ดˆ\")\n", " except Exception as e:\n", " print(f\"ํšŒ์ฐจ seed={seed} ์‹คํŒจ: {e}\")\n", "print(f\"End-to-End ์‹คํ–‰ ์„ฑ๊ณต๋ฅ  {ok / 3:.0%} (๋ชฉํ‘œ 100%: {'๋‹ฌ์„ฑ' if ok == 3 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# ---------------- 2. ์ž์ฒด ์ ๊ฒ€(๋‹จ์œ„ ํ…Œ์ŠคํŠธ + ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€) ----------------\n", "called = set()\n", "own = {n for n, f in list(globals().items()) if inspect.isfunction(f) and f.__module__ == \"__main__\"}\n", "sys.setprofile(lambda fr, ev, arg: called.add(fr.f_code.co_name) if ev == \"call\" else None)\n", "tm = build_text_model()\n", "tests = {\n", " \"๋ฌธํ•ญ DB ๊ฒ€์ฆ ํ†ต๊ณผ\": validate_bank(BANK) == [],\n", " \"์ „๊ฐ ์ˆซ์ž ํŒŒ์‹ฑ\": parse(QIDX[\"Q01\"], \"๏ผ’\")[0] == \"2\",\n", " \"์›๋ฌธ์ž ํŒŒ์‹ฑ\": parse(QIDX[\"Q01\"], \"โ‘ก\")[0] == \"2\",\n", " \"๋‹จ๋‹ต ์–ด๋ฏธ ์ œ๊ฑฐ ์ฑ„์ \": grade_fuzzy(QIDX[\"Q21\"], norm_text(\"ํ”ผ์•„์‹๋ณ„์ž…๋‹ˆ๋‹ค\")),\n", " \"๋‹จ๋‹ต ์˜คํƒˆ์ž ํ—ˆ์šฉ\": grade_fuzzy(QIDX[\"Q27\"], norm_text(\"์ฟผ๋“œ์ฝฅํƒ€\")),\n", " \"์ˆœ์„œํ˜• ๋‹จ๊ณ„ ๋ˆ„๋ฝ = ์ ˆ์ฐจ ๋ˆ„๋ฝ\": classify_error(QIDX[\"Q30\"], \"ACD\", tm)[0] == \"์ ˆ์ฐจ ๋ˆ„๋ฝ\",\n", " \"์„ ๋‹คํ˜• ์˜ค๋‹ต ์œ ํ˜• ๋งคํ•‘\": classify_error(QIDX[\"Q05\"], \"1\", tm)[0] == \"ํ”ผ์•„ ์˜ค์ธ\",\n", " \"๋ฏธ์‘๋‹ต ๋ณด์กด\": parse(QIDX[\"Q01\"], \"\")[1] == \"๋ฏธ์‘๋‹ต\",\n", " \"์ค‘๋ณต ์ œ๊ฑฐ\": len(dedup(pd.DataFrame({\"sid\": [\"S1\", \"S1\"], \"qid\": [\"Q01\", \"Q01\"]}))) == 1,\n", " \"์ ์ˆ˜ ๋ฐฐ์ \": score_of(QIDX[\"Q04\"], True) == 5,\n", " \"์ทจ์•ฝ ์˜์—ญ ์‚ฐ์ถœ\": weak_areas(pd.DataFrame({\"sid\": [\"S1\"] * 2, \"area\": [\"๊ต์ „ํ†ต์ œ\"] * 2, \"correct\": [False, True]})) == {\"S1\": [\"๊ต์ „ํ†ต์ œ\"]},\n", " \"๊ฒ€ํ†  ๋Œ€๊ธฐ์—ด ๊ธฐ๋ก\": len((lambda r: (r.submit(\"X\", \"t\"), r.log)[1])(Review())) == 1,\n", " \"ํ™•๋ฅ  ํ•จ์ˆ˜ ๋‹จ์กฐ์„ฑ\": p_correct(1.0, 2) > p_correct(0.0, 2),\n", "}\n", "mini = make_responses(2, seed=1).head(150)\n", "tests[\"์†Œ๊ทœ๋ชจ ํŒŒ์ดํ”„๋ผ์ธ\"] = len(grade_all(dedup(mini), tm)) == 150\n", "tests[\"์ƒˆ ๋ฌธํ•ญ ์ƒ์„ฑยท๊ฒ€์ฆ\"] = validate_bank([_mc(\"T1\", \"ํ˜•์ƒ์‹๋ณ„\", 1, \"s\", list(\"abcd\"), 1, [C, C, F]), _sa(\"T2\", \"๊ฒฝ๋ณด๋ณด๊ณ \", 2, \"s\", [\"์ถ”์ •\"], {\"ํ™•์ •\": C}),\n", " _order(\"T3\", \"๊ต์ „ํ†ต์ œ\", 3, \"s\")]) == []\n", "tests[\"ํ†ตํ•ฉ ํŒŒ์ดํ”„๋ผ์ธ ์žฌ์‹คํ–‰\"] = pipeline(5)[\"person\"].shape[0] == 120\n", "make_soldiers(1, 0); _fmt_mc(1, np.random.default_rng(0)); _typo(\"abc\", np.random.default_rng(0))\n", "sys.setprofile(None)\n", "cov = len(own & called) / len(own)\n", "print(f\"\\n์ž์ฒด ์ ๊ฒ€ {sum(tests.values())}/{len(tests)} ํ†ต๊ณผ, ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ {cov:.0%} ({len(own & called)}/{len(own)}) (๋ชฉํ‘œ 85% ์ด์ƒ: {'๋‹ฌ์„ฑ' if cov >= 0.85 else '๋ฏธ๋‹ฌ'})\")\n", "for k, v in tests.items():\n", " if not v:\n", " print(\" ์‹คํŒจ:\", k)\n", "miss = sorted(own - called)\n", "if miss:\n", " print(\"ํ…Œ์ŠคํŠธ ๋ฏธ์‹คํ–‰ ํ•จ์ˆ˜:\", miss)\n", "\n", "# ---------------- 3. ๊ฒฐ๊ณผ ์š”์•ฝยท๋ธŒ๋ฆฌํ•‘ ----------------\n", "R = runs[0]\n", "d, person = R[\"d\"], R[\"person\"]\n", "area = d.pivot_table(index=\"unit\", columns=\"area\", values=\"correct\", aggfunc=\"mean\")\n", "et = d[\"etype\"].value_counts()\n", "low = area.stack().sort_values().head(3)\n", "fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", "area.T.plot.bar(ax=ax[0], rot=0); ax[0].set_xticks(range(len(area.columns)), [\"Shape\", \"IFF\", \"Report\", \"Control\", \"C-UAS\"][:len(area.columns)])\n", "ax[0].set_title(\"Accuracy by area\"); ax[0].legend([\"Co.1\", \"Co.2\", \"Co.3\"])\n", "ax[1].bar([\"Concept\", \"IFF error\", \"Procedure\"], [et.get(k, 0) for k in ETYPES]); ax[1].set_title(\"Error types\")\n", "plt.tight_layout(); plt.show()\n", "\n", "print(\"\\n[๊ต์œกํ›ˆ๋ จ ๋‹ด๋‹น๊ด€ ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ] [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\")\n", "print(f\"1. ์‘์‹œ {len(person)}๋ช…, ํ‰๊ท  {person['score'].mean():.1f}์ , ํ•ฉ๊ฒฉ๋ฅ  {person['pass'].mean():.1%}\")\n", "print(f\"2. ์˜ค๋‹ต ์œ ํ˜•: \" + \", \".join(f\"{k} {int(et.get(k, 0))}๊ฑด\" for k in ETYPES) + f\" -> ๊ฐ€์žฅ ๋งŽ์€ ์œ ํ˜• '{et.idxmax()}' ์ค‘์‹ฌ ๋ณด์ถฉ ๊ต์œก\")\n", "print(\"3. ์ทจ์•ฝ ์ค‘๋Œ€ยท์˜์—ญ: \" + \", \".join(f\"{u} {a}({v:.2f})\" for (u, a), v in low.items()))\n", "print(f\"4. ๊ฐœ์ธ ๋ณด์ถฉ ํ•™์Šต ๋Œ€์ƒ {sum(bool(v) for v in R['weak'].values())}๋ช…(ํ•™์Šต ๋ณด์กฐ ์ž๋ฃŒ, ์ธ์‚ฌ ํ‰๊ฐ€ ๋ฏธ์‚ฌ์šฉ)\")\n", "print(f\"5. ๊ต๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ {len(R['review'].items)}๊ฑด: ์ž๋™ ํ™•์ • 0๊ฑด, ๊ต๊ด€ ๊ฒฐ์ • ํ›„ ์ ์ˆ˜ ํ™•์ •\")\n", "print(\"6. ํ–ฅํ›„: ์ธ์ ‘ ๋ถ€๋Œ€ ๊ต์œก์ฒด๊ณ„์™€ ์‘๋‹ตยท๊ฒฐ๊ณผ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค(API) ์ •์˜, ํšŒ์ฐจ๋ณ„ AAR๋กœ ๋ฌธํ•ญ ๊ฐœ์ •\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 10 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "ํšŒ์ฐจ seed=7: ์›๋ณธ 3,890๊ฑด -> ์ฑ„์  3,840๊ฑด, ์ฑ„์  ์ •ํ™•๋„ 99.0%, ํ•ฉ๊ฒฉ๋ฅ  50.8%, ๊ต๊ด€ ๊ฒ€ํ†  49๊ฑด, 0.3์ดˆ\n", "ํšŒ์ฐจ seed=21: ์›๋ณธ 3,890๊ฑด -> ์ฑ„์  3,840๊ฑด, ์ฑ„์  ์ •ํ™•๋„ 98.8%, ํ•ฉ๊ฒฉ๋ฅ  49.2%, ๊ต๊ด€ ๊ฒ€ํ†  63๊ฑด, 0.3์ดˆ\n", "ํšŒ์ฐจ seed=33: ์›๋ณธ 3,890๊ฑด -> ์ฑ„์  3,840๊ฑด, ์ฑ„์  ์ •ํ™•๋„ 99.0%, ํ•ฉ๊ฒฉ๋ฅ  56.7%, ๊ต๊ด€ ๊ฒ€ํ†  51๊ฑด, 0.2์ดˆ\n", "End-to-End ์‹คํ–‰ ์„ฑ๊ณต๋ฅ  100% (๋ชฉํ‘œ 100%: ๋‹ฌ์„ฑ)\n", "\n", "์ž์ฒด ์ ๊ฒ€ 16/16 ํ†ต๊ณผ, ํ•จ์ˆ˜ ์ปค๋ฒ„๋ฆฌ์ง€ 100% (22/22) (๋ชฉํ‘œ 85% ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "\n", "[๊ต์œกํ›ˆ๋ จ ๋‹ด๋‹น๊ด€ ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ] [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\n", "1. ์‘์‹œ 120๋ช…, ํ‰๊ท  67.4์ , ํ•ฉ๊ฒฉ๋ฅ  50.8%\n", "2. ์˜ค๋‹ต ์œ ํ˜•: ๊ฐœ๋… ํ˜ผ๋™ 367๊ฑด, ํ”ผ์•„ ์˜ค์ธ 186๊ฑด, ์ ˆ์ฐจ ๋ˆ„๋ฝ 419๊ฑด -> ๊ฐ€์žฅ ๋งŽ์€ ์œ ํ˜• '์ ˆ์ฐจ ๋ˆ„๋ฝ' ์ค‘์‹ฌ ๋ณด์ถฉ ๊ต์œก\n", "3. ์ทจ์•ฝ ์ค‘๋Œ€ยท์˜์—ญ: ๊ฐ€์ƒ2์ค‘๋Œ€ ๊ต์ „ํ†ต์ œ(0.50), ๊ฐ€์ƒ2์ค‘๋Œ€ ํ”ผ์•„์‹๋ณ„(0.57), ๊ฐ€์ƒ3์ค‘๋Œ€ ํ”ผ์•„์‹๋ณ„(0.65)\n", "4. ๊ฐœ์ธ ๋ณด์ถฉ ํ•™์Šต ๋Œ€์ƒ 85๋ช…(ํ•™์Šต ๋ณด์กฐ ์ž๋ฃŒ, ์ธ์‚ฌ ํ‰๊ฐ€ ๋ฏธ์‚ฌ์šฉ)\n", "5. ๊ต๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ 49๊ฑด: ์ž๋™ ํ™•์ • 0๊ฑด, ๊ต๊ด€ ๊ฒฐ์ • ํ›„ ์ ์ˆ˜ ํ™•์ •\n", "6. ํ–ฅํ›„: ์ธ์ ‘ ๋ถ€๋Œ€ ๊ต์œก์ฒด๊ณ„์™€ ์‘๋‹ตยท๊ฒฐ๊ณผ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค(API) ์ •์˜, ํšŒ์ฐจ๋ณ„ AAR๋กœ ๋ฌธํ•ญ ๊ฐœ์ •\n", "```\n", "
\n" ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 }