Spaces:
Running
Running
| # RM-DETECT โ ๊ธฐ์ ์ํคํ ์ฒ ์ค๋ช ์ | |
| > AI ์์ฑ ํ ์คํธ ํ๋ณ๊ธฐ์ ๋ด๋ถ ๊ตฌ์กฐ, ๋ฐ์ดํฐ ํ๋ฆ, ํผ์ฒ ์ฌ์, ๋ชจ๋ธ, API, ๋ฐฐํฌ๋ฅผ ์ ๋ฆฌํ ๊ธฐ์ ๋ฌธ์์ ๋๋ค. | |
| > ๊ฐ์๋ [README](README.md)๋ฅผ, ์๋๋ ๊ตฌํ ์ธ๋ถ๋ฅผ ๋ค๋ฃน๋๋ค. | |
|  | |
| --- | |
| ## 1. ๊ฐ์ | |
| RM-DETECT๋ ์ ๋ ฅ ํ ์คํธ๊ฐ ๋๊ท๋ชจ ์ธ์ด๋ชจ๋ธ(LLM)๋ก ์์ฑ๋์ ๊ฐ๋ฅ์ฑ์ ์ถ์ ํ๋ **๋ ๋ฆฝ์ ์ผ๋ก ๊ตฌํํ ํ๋ณ๊ธฐ**์ ๋๋ค. ๋ ๊ฐ์ง ์ํธ ๋ณด์์ ์ ํธ๋ฅผ ํ๋์ ๋ถ๋ฅ๊ธฐ๋ก ๊ฒฐํฉํฉ๋๋ค: | |
| - **ํ ํฐ ์์ธก ๊ฐ๋ฅ์ฑ(perplexity)** โ LLM์ด ๋ง๋ ๊ธ์ ๋ "์์ธก ๊ฐ๋ฅ"ํ๋ค๋, AI ํ ์คํธ ํ์ง ๋ฌธํ(GLTR, DetectGPT ๋ฑ)์์ ํ๋ฆฝ๋ ์๋ฆฌ | |
| - **๋ฌธ์ฒดยทํต๊ณ ๋ถ์(stylometry)** โ ์ธ์ด๋ชจ๋ธ ์์ด ๊ณ์ฐํ๋ ๋ฌธ์ฅ ๋ฆฌ๋ฌยท์ดํยท๋ฌธ์ฅ๋ถํธยท์ด๋ฏธ ํน์ง | |
| ํ๊ตญ์ด์ ์์ด๋ฅผ ๋ชจ๋ ์ง์ํ๋ฉฐ, ๋ฌธ์ ์ ์ฒด ์ ์์ **๋ฌธ๋จ๋ณ ์ ์**๋ฅผ ๋์์ ์ฐ์ถํฉ๋๋ค. ๋ชจ๋ ์ถ๋ก ์ด ์๋ฒ ๋ด๋ถ์์ ์คํ๋์ด **์ธ๋ถ ์ ๋ฃ API๋ฅผ ํธ์ถํ์ง ์์ต๋๋ค.** | |
| --- | |
| ## 2. ์ปดํฌ๋ํธ ๊ตฌ์กฐ | |
| ``` | |
| rmdetect/ | |
| โโโ app.py # FastAPI ๋ฐฑ์๋ + RMDetector ์ค์ผ์คํธ๋ ์ด์ | |
| โโโ aidetect/ # ํ์ง ์์ง (ํ๋ ์์ํฌ ๋ ๋ฆฝ) | |
| โ โโโ perplexity.py # โ ํ ํฐ ์์ธก ๊ฐ๋ฅ์ฑ (GPT-2 / KoGPT2) | |
| โ โโโ stylometry.py # โก ๋ฌธ์ฒดยทํต๊ณ ํผ์ฒ (model-free) | |
| โ โโโ langid.py # ์ธ์ด ๊ฐ์ง (Hangul-ratio router) | |
| โ โโโ features.py # โ +โก ๋ณํฉ โ 28์ฐจ์ ๋ฒกํฐ | |
| โ โโโ __init__.py | |
| โโโ ai_detector_model.joblib # ํ์ต๋ ๋ถ๋ฅ๊ธฐ (StandardScaler + LogReg) | |
| โโโ static/ # ๋จ์ผ ํ์ด์ง ์น UI | |
| โ โโโ index.html | |
| โ โโโ app.js | |
| โ โโโ style.css | |
| โโโ requirements.txt | |
| โโโ run.sh # ๋ก์ปฌ ์คํ ์คํฌ๋ฆฝํธ | |
| โโโ Dockerfile # ์ปจํ ์ด๋/HF Spaces ๋ฐฐํฌ | |
| ``` | |
| ๊ณ์ธต์ ์ธ ๊ฒน์ ๋๋ค: **UI(static) โ API(app.py) โ ์์ง(aidetect)**. ์์ง์ ์น ํ๋ ์์ํฌ์ ์์กดํ์ง ์์ผ๋ฏ๋ก CLIยท๋ฐฐ์นยท๋ค๋ฅธ ์๋น์ค์์๋ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. | |
| --- | |
| ## 3. ์์ฒญ ์ฒ๋ฆฌ ํ๋ฆ (`POST /detect`) | |
| 1. **์ธ์ด ๊ฐ์ง** โ `langid.detect_lang()` ๊ฐ ํ๊ธ/๋ผํด ๋ฌธ์ ๋น์จ๋ก `ko`/`en` ํ์ (์์ฒญ์ `lang`์ด ๋ช ์๋๋ฉด ๊ทธ๋๋ก ์ฌ์ฉ). | |
| 2. **๋ฌธ๋จ ๋ถํ ** โ ๋น ์ค ๊ธฐ์ค์ผ๋ก ๋ฌธ๋จ์ ๋๋๋, ๊ฐ ๋ฌธ๋จ์ **์๋ฌธ ๋ฌธ์ ์คํ์ `(start, end)`** ์ ๋ณด์กด. ์ด ์คํ์ ๋๋ถ์ UI๊ฐ ์๋ฌธ์ ์ ํํ ๊ตฌ๊ฐ์ ํ์ด๋ผ์ดํธํ ์ ์์. | |
| 3. **ํผ์ฒ ์ถ์ถ** โ ๋ฌธ์ ์ ์ฒด์ ๊ฐ ๋ฌธ๋จ์ ๋ํด `features.full_features()` ๋ฅผ ํธ์ถํด 28์ฐจ์ ๋ฒกํฐ๋ฅผ ๋ง๋ฆ. | |
| 4. **๋ถ๋ฅ** โ `StandardScaler โ LogisticRegression` ํ์ดํ๋ผ์ธ์ด AI ํ๋ฅ ์ ์ฐ์ถํ๊ณ , ํ์ ๋ฐด๋์ ๋งคํ. | |
| 5. **๊ธฐ์ฌ ์ ํธ ๊ณ์ฐ** โ ๊ฐ ํผ์ฒ์ ํ์คํ ๊ฐ ร ํ๊ท ๊ณ์๋ก "์ด ํ์ ์ AI/์ธ๊ฐ ์ชฝ์ผ๋ก ๋ฏผ ์์ ์ ํธ"๋ฅผ ์ถ์ถ. | |
| 6. **์๋ต ์ง๋ ฌํ** โ ๋ฌธ์ ์ ์ + ๋ฌธ๋จ ๋ฐฐ์ด(์คํ์ ยทํ๋ฅ ยทํ์ ยทflaggedยท์์ ์ ํธ)์ JSON์ผ๋ก ๋ฐํ. | |
| ๋ฌธ๋จ์ด `paragraph_min_chars`(๊ธฐ๋ณธ 30์)๋ณด๋ค ์งง์ผ๋ฉด `low_confidence=true`๋ก ํ์ํ๊ณ flagged ๋์์์ ์ ์ธํฉ๋๋ค. | |
| --- | |
| ## 4. ์ ํธ ๊ณ์ด โ โ ํ ํฐ ์์ธก ๊ฐ๋ฅ์ฑ (`perplexity.py`) | |
| ์์ causal LM์ ์ธ์ด๋ณ๋ก ๋ก๋ํฉ๋๋ค: ์์ด **GPT-2**, ํ๊ตญ์ด **KoGPT2(skt/kogpt2-base-v2)**, ๊ฐ๊ฐ ~125M ํ๋ผ๋ฏธํฐ. | |
| - **์๋ฆฌ** โ LLM์ ๋ค์ ํ ํฐ์ผ๋ก ๋์ ํ๋ฅ ์ ํ ํฐ์ ๊ณ ๋ฅด๋ฏ๋ก, ๊ธฐ๊ณ ์์ฑ ํ ์คํธ๋ perplexity๊ฐ ๋ฎ๊ณ ๋ชจ๋ธ์ด ์์๋ก ๊ผฝ์์ ํ ํฐ์ ๋น์จ(top-k ์ ์ค๋ฅ )์ด ๋์. ์ธ๊ฐ ๊ธ์ "burstier"ํด์ ๋ฎ์ ํ๋ฅ ์ ๋จ์ด๋ฅผ ๋ ์์ฃผ ์. | |
| - **์ฐ์ถ ํผ์ฒ** (6๊ฐ): `ppl`, `log_ppl`, `mean_logprob`, `std_logprob`, `median_logprob`, `topk_hit_rate` | |
| - **๊ธด ๋ฌธ์ ์ฒ๋ฆฌ** โ ์ปจํ ์คํธ ์ฐฝ(โค1024 ํ ํฐ)์ ๋๋ ํ ์คํธ๋ **sliding window**๋ก ์ฒ๋ฆฌํ๊ณ , ๊ฒน์น๋ ๊ตฌ๊ฐ์ ๋ง์คํนํด ์ค๋ณต ์ง๊ณ๋ฅผ ๋ฐฉ์ง. | |
| - **๋ชจ๋ธ ๋ก๋ฉ** โ ๋ก์ปฌ `models/gpt2`ยท`models/kogpt2`๊ฐ ์์ผ๋ฉด ์คํ๋ผ์ธ์ผ๋ก ๋ก๋, ์์ผ๋ฉด HuggingFace ํ๋ธ์์ ์๋ ๋ค์ด๋ก๋(์: HF Spaces ์ฒซ ์คํ). | |
| --- | |
| ## 5. ์ ํธ ๊ณ์ด โก โ ๋ฌธ์ฒดยทํต๊ณ (`stylometry.py`) | |
| ์ธ์ด๋ชจ๋ธ์ด ํ์ ์๋ 22๊ฐ ํผ์ฒ. ํ๊ตญ์ดยท์์ด ๋ชจ๋์ ๋์ํ๋ ์ ๊ท์ ๊ธฐ๋ฐ ๋ฌธ์ฅ ๋ถํ ์ ์ฌ์ฉํฉ๋๋ค. | |
| | ๊ทธ๋ฃน | ํผ์ฒ | ์ ํธ ๋ฐฉํฅ | | |
| |---|---|---| | |
| | ๋ฌธ์ฅ ๋ฆฌ๋ฌ | `burstiness`, `cv_sent_len`, `std_sent_len`, `mean_sent_len`, `mean_succ_diff`, `n_sents` | ์ธ๊ฐ์ ๋ฌธ์ฅ ๊ธธ์ด ๋ณ๋์ด ํผ; AI๋ ๊ท ์ผ | | |
| | ์ดํ | `type_token_ratio`, `rep_bigram_rate`, `word_entropy`, `avg_word_len` | AI๋ ์ ์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ณต | | |
| | ๋ฌธ์ฅ๋ถํธ | `excl_per100`, `ques_per100`, `ellipsis_per100`, `comma_per100`, `quote_per100`, `emoji_per100`, `punct_variety` | ์ธ๊ฐ์ `!!`, `โฆ`, ์ด๋ชจ์ง ์ฌ์ฉ โ | | |
| | ๊ตฌ์ด์ฒด | `informal_per100` (ใ ใ , lol, ์ง์งโฆ) | ์ธ๊ฐ ์ ํธ | | |
| | ์ ์์ด | `connective_per100` (์ข ํฉ์ ์ผ๋ก, ๋ฐ๋ผ์, furthermoreโฆ) | AI ์ ํธ | | |
| | ํ๊ตญ์ด ์ด๋ฏธ | `ko_formal_per100`, `ko_casual_per100`, `ko_formal_minus_casual` | ๊ฒฉ์์ฒด(ํ์์ต๋๋ค)๋ AI ์ชฝ, ๊ตฌ์ด์ฒด(~ํ์)๋ ์ธ๊ฐ ์ชฝ | | |
| --- | |
| ## 6. ๋ถ๋ฅ๊ธฐ (`ai_detector_model.joblib`) | |
| - **๊ตฌ์กฐ** โ scikit-learn `Pipeline([StandardScaler, LogisticRegression(class_weight="balanced")])` | |
| - **์ ๋ ฅ** โ `FEATURE_ORDER`๋ก ๊ณ ์ ๋ **28์ฐจ์** ๋ฒกํฐ (perplexity 6 + stylometry 22) | |
| - **์ฑ๋ฅ** โ ๋ณด์ ๋ฐ์ดํฐ์ ์์ **5-fold ๊ต์ฐจ๊ฒ์ฆ ์ ํ๋ 97.2%, AUC 0.994** (ํ๊ตญ์ด 100%, ์์ด 93.8%) | |
| - **์ค๋ช ๊ฐ๋ฅ์ฑ** โ ์ ํ ๋ชจ๋ธ์ด๋ผ ๊ฐ ํผ์ฒ์ (ํ์คํ ๊ฐ ร ๊ณ์) ๊ธฐ์ฌ๋๋ฅผ ๊ทธ๋๋ก ๋ ธ์ถ. ์ธก์ ๋ ์์ ์ ํธ: ์ธ๊ฐ ์ชฝ์ ๊ตฌ์ด์ฒดยท์ธ์ฉ๋ถํธยท๋ง์ค์ํ ๋ฐ๋์ ๋ฌธ์ฅ burstiness, AI ์ชฝ์ ํ๊ตญ์ด ๊ฒฉ์ ์ด๋ฏธยท๊ธด ๋จ์ดยท๊ท ์ผํ ๋ฌธ์ฅ ๋ฆฌ๋ฌ. | |
| --- | |
| ## 7. ํ์ ๋ฐด๋ | |
| | AI ํ๋ฅ | verdict | ๋ฌธ๋จ flagged | | |
| |---|---|---| | |
| | โฅ 0.85 | AI-generated | โ | | |
| | โฅ 0.60 | Likely AI | โ | | |
| | โฅ 0.40 | Mixed | | | |
| | โฅ 0.15 | Likely human | | | |
| | < 0.15 | Human-written | | | |
| ๋ฌธ๋จ์ `ai_probability โฅ 0.60` ์ด๊ณ `low_confidence`๊ฐ ์๋ ๋ **flagged**(์์ฌ ์์ญ)๋ก ํ์๋ฉ๋๋ค. | |
| --- | |
| ## 8. API ๋ ํผ๋ฐ์ค | |
| ### `GET /health` | |
| ```json | |
| { "status": "ok", "model_loaded": true, "load_error": null, "uptime_s": 159.9, "max_chars": 20000 } | |
| ``` | |
| ### `POST /detect` | |
| ์์ฒญ: | |
| ```json | |
| { "text": "๊ฒ์ฌํ ๊ธ ...", "lang": "ko" } | |
| ``` | |
| `lang`์ ์ ํ(`"ko"` | `"en"` | ์๋ต ์ ์๋ ๊ฐ์ง). ์๋ต(์์ฝ): | |
| ```jsonc | |
| { | |
| "language": "ko", | |
| "overall_ai_probability": 0.50, | |
| "verdict": "Mixed", | |
| "n_paragraphs": 3, | |
| "n_flagged": 1, | |
| "top_features": [ {"feature": "informal_per100", "contribution": -1.17, "direction": "human"} ], | |
| "paragraphs": [ | |
| { "index": 0, "start": 0, "end": 40, "text": "...", | |
| "ai_probability": 0.35, "verdict": "Likely human", | |
| "flagged": false, "low_confidence": false, "top_features": [ ... ] } | |
| ], | |
| "elapsed_ms": 2640.3 | |
| } | |
| ``` | |
| **์ค๋ฅ ์๋ต**: ๋น ์ ๋ ฅ `400` ยท ์๋ชป๋ `lang` `400` ยท 20000์ ์ด๊ณผ `413` ยท ๋ชจ๋ธ ๋ฏธ๋ก๋ฉ `503`. | |
| --- | |
| ## 9. ์น UI (`static/`) | |
| ์์กด์ฑ ์๋ ์์ HTML/CSS/๋ฐ๋๋ผ JS. ํ ์คํธ ์ ๋ ฅ โ `fetch("/detect")` โ ๊ฒฐ๊ณผ ๋ ๋: | |
| - ์ ์ฒด AI ํ๋ฅ ์ SVG ๋๋ ๊ฒ์ด์ง + verdict๋ก ํ์ | |
| - ๋ฌธ์ ์์ค ์์ ๊ธฐ์ฌ ์ ํธ๋ฅผ ์นฉ์ผ๋ก ํ์ | |
| - ๋ฌธ๋จ์ ํ๋ฅ ์ ๋ฐ๋ผ ์ด๋กโ๋ ธ๋โ๋นจ๊ฐ์ผ๋ก ์์ ๋ฑ๊ธํํ ์นด๋๋ก ์ฌํ, ์นด๋๋ฅผ ํด๋ฆญํ๋ฉด ๊ทธ ๋ฌธ๋จ์ ์์ ์ ํธ๊ฐ ํผ์ณ์ง | |
| - ํ๊ตญ์ดยท์์ด ๋ผ๋ฒจ ๋ณ๊ธฐ, Cmd/Ctrl+Enter ๋จ์ถํค | |
| --- | |
| ## 10. ๋ฐฐํฌ | |
| ### ๋ก์ปฌ | |
| ```bash | |
| pip install -r requirements.txt | |
| ./run.sh # http://127.0.0.1:8000 | |
| ``` | |
| ๋ก์ปฌ `models/`๊ฐ ์์ผ๋ฉด ์คํ๋ผ์ธ์ผ๋ก ์ฆ์ ๋ก๋, ์์ผ๋ฉด ํ๋ธ์์ ๋ชจ๋ธ์ ๋ฐ์. | |
| ### Docker / Hugging Face Spaces | |
| `Dockerfile`์ HF Spaces ๊ท๊ฒฉ(๋น๋ฃจํธ UID 1000, ํฌํธ 7860, HF ์บ์ ๊ฒฝ๋ก)์ ๋ง์ถฐ์ ธ ์์ต๋๋ค. `README.md` ์๋จ์ YAML ํค๋(`sdk: docker`, `app_port: 7860`)๋ก Spaces๊ฐ ์ค์ ์ ์ฝ์ต๋๋ค. ์ปจํ ์ด๋์๋ ์ฝ๋๋ง ๋ด๊ณ , ์ธ์ด๋ชจ๋ธ์ ์ฒซ ์คํ ์ ํ๋ธ์์ ๋ฐ์ ์บ์ํฉ๋๋ค. | |
| --- | |
| ## 11. ํ๊ณ | |
| - **์์ ๋ณด์ ์ (n=36)** ๊ณผ **LLM์ด ๋ชจ์ฌํ "์ธ๊ฐ" ์ํ** โ ํ์์ ์ธ ์ค์ ์ธ๊ฐ ๊ธ(๋ ผ๋ฌธ, ์ ์ ๋ ์๊ธฐ์๊ฐ์)์ ์ค์ ๋ณด๋ค ๋ ์์ฃผ ์คํ๋ ์ ์์. | |
| - **์์ ์ฐธ์กฐ LM(GPT-2/KoGPT2, ~125M)** โ ๋ ํฌ๊ฑฐ๋ instruction-tuned ๋ชจ๋ธ์ด๋ฉด perplexity ์ ํธ๊ฐ ๋ ์ ๋ช ํด์ง. | |
| - **ํจ๋ฌํ๋ ์ด์ฆ์ ์ทจ์ฝ** โ burstinessยท๊ตฌ์ด์ฒด์ ๋ฐ์ํ๋ฏ๋ก, ๊ตฌ์ด์ฒด๋ฅผ ์๊ณ ๋ฌธ์ฅ ๊ธธ์ด๋ฅผ ํฉ๋จ๋ฆฌ๋ "ํด๋จธ๋์ด์ง" ํธ์ง์ด AI ์ ์๋ฅผ ๋ฎ์ถค. | |
| - **CPU ์ง์ฐ** โ ๋ฉํฐ์ฝ์ด์์ ์์ฒญ๋น 1~2์ด, ๊ณต์ ๋ฌด๋ฃ CPU์์๋ ๋ ๋๋ฆผ. | |
| RM-DETECT๋ **์ฐ๊ตฌ์ฉ ํ๋กํ ํ์ **์ด๋ฉฐ ํ๋ฅ ์ ์ถ์ ์ ๋๋ค. ํ๋ฌธ์ ๋ถ์ ํ์ยท์ฑ์ฉ ๋ฑ ์ค๋ํ ๊ฒฐ์ ์ ๋จ๋ ๊ทผ๊ฑฐ๋ก ์ฌ์ฉํ์ง ๋ง๊ณ , ์ฌ๋ฌ ์ ํธ ์ค ํ๋๋ก๋ง ํ์ฉํ์ธ์. | |