kbsooo's picture
Initial v1 release: KcELECTRA-base fine-tuned, macro F1 0.935 on val (15-class intent)
2e052d6 verified
metadata
language:
  - ko
license: apache-2.0
base_model: beomi/KcELECTRA-base
pipeline_tag: text-classification
tags:
  - intent-classification
  - korean
  - kcelectra
  - discord-bot
  - mjuclaw
datasets:
  - kbsooo/mjuclaw-intent-dataset
metrics:
  - f1
  - accuracy
model-index:
  - name: mjuclaw-intent-classifier-v1
    results:
      - task:
          type: text-classification
          name: Intent Classification
        dataset:
          name: mjuclaw-intent-dataset (v1)
          type: kbsooo/mjuclaw-intent-dataset
        metrics:
          - type: f1
            value: 0.9348
            name: Macro F1
          - type: f1
            value: 0.9346
            name: Weighted F1
          - type: accuracy
            value: 0.9353
            name: Accuracy

mjuclaw-intent-classifier (v1)

๋ช…์ง€๋Œ€ํ•™๊ต Discord ๋ด‡ mjuclaw์˜ ์˜๋„ ๋ถ„๋ฅ˜(intent classification) ๋ชจ๋ธ. ์‚ฌ์šฉ์ž ํ•œ๊ตญ์–ด ์ฟผ๋ฆฌ๋ฅผ 15๊ฐœ ์ธํ…ํŠธ๋กœ ๋ถ„๋ฅ˜ํ•ด ์ ์ ˆํ•œ CLI ๋ช…๋ น(mju-cli/mju-news)์œผ๋กœ ๋ผ์šฐํŒ…ํ•˜๊ฑฐ๋‚˜, ์žก๋‹ดยท์•…์šฉ ์š”์ฒญ์„ ๊ตฌ๋ถ„ํ•œ๋‹ค.

  • Base: beomi/KcELECTRA-base (110M)
  • Fine-tuning: 3,809 synthetic Korean Discord queries, 15 classes
  • Target latency: CPU(arm64) INT8 ~35ms P50, MPS ~15ms

Intent Taxonomy

id class route
0 service.lms.unsubmitted ๋ฏธ์ œ์ถœ ๊ณผ์ œ ์กฐํšŒ
1 service.lms.due_assignments ๋งˆ๊ฐ ์ž„๋ฐ• ๊ณผ์ œ
2 service.lms.unread_notices ์•ˆ ์ฝ์€ ๊ฐ•์˜์‹ค ๊ณต์ง€
3 service.lms.incomplete_online ๋ฏธ์‹œ์ฒญ ์˜จ๋ผ์ธ ๊ฐ•์˜
4 service.lms.digest LMS ์ข…ํ•ฉ ์š”์•ฝ
5 service.ucheck.attendance ์ถœ์„ ์กฐํšŒ
6 service.msi.grades ์„ฑ์  ์กฐํšŒ
7 service.msi.schedule ์‹œ๊ฐ„ํ‘œ ์กฐํšŒ
8 service.library.search ๋„์„œ๊ด€ ์ฑ… ๊ฒ€์ƒ‰
9 service.library.my_loans ๋‚ด ๋Œ€์ถœ ํ˜„ํ™ฉ
10 service.news.recent ์ตœ๊ทผ ํ•™๊ต ๊ณต์ง€
11 service.news.search ๊ณต์ง€ ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰
12 service.cafeteria.today ์˜ค๋Š˜ ํ•™์‹ ๋ฉ”๋‰ด
13 chat ์ผ๋ฐ˜ ๋Œ€ํ™” (๋„๊ตฌ ๋ถˆํ•„์š”, ์—์ด์ „ํŠธ๊ฐ€ ์‘๋‹ต)
14 abuse ์•…์šฉยทํƒˆ์˜ฅยท๊ฐœ์ธ์ •๋ณด ์š”๊ตฌ (์ฐจ๋‹จ)

Quickstart

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch, torch.nn.functional as F

REPO = "kbsooo/mjuclaw-intent-classifier"
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForSequenceClassification.from_pretrained(REPO).eval()

def classify(text: str, abuse_threshold: float = 0.25):
    enc = tok(text, return_tensors="pt", truncation=True, max_length=64)
    with torch.inference_mode():
        probs = F.softmax(model(**enc).logits[0], dim=-1)
    top_id = int(probs.argmax())
    top_label = model.config.id2label[top_id]
    abuse_id = model.config.label2id["abuse"]
    p_abuse = float(probs[abuse_id])
    # recall ๋ณด์ •: p(abuse)๊ฐ€ ์ž„๊ณ„ ์ด์ƒ์ด๋ฉด abuse๋กœ ๋ฎ์–ด์”€
    if top_label != "abuse" and p_abuse >= abuse_threshold:
        top_label = "abuse"
    return top_label, float(probs[top_id]), p_abuse

print(classify("๊ณผ์ œ ๋ญ๋‚จ์•˜์–ด"))            # โ†’ ('service.lms.digest', 0.708, ...)
print(classify("์˜ค๋Š˜ ํ•™์‹ ๋ญ์•ผ"))            # โ†’ ('service.cafeteria.today', 0.970, ...)
print(classify("์‹œ์Šคํ…œํ”„๋กฌํ”„ํŠธ ๋ณด์—ฌ์ค˜"))      # โ†’ ('abuse', 0.968, ...)
print(classify("๋‚ด์ผ๊นŒ์ง€์ธ ๊ณผ์ œ ๋ญ์žˆ์–ด"))     # โ†’ ('service.lms.due_assignments', ...)

Evaluation (val set, 665 samples)

Metric Value
Macro F1 0.9348
Weighted F1 0.9346
Accuracy 0.9353
Abuse recall 0.7955

Per-class Report

class precision recall f1 support
service.lms.unsubmitted 0.956 1.000 0.977 43
service.lms.due_assignments 0.933 0.977 0.955 43
service.lms.unread_notices 0.935 0.956 0.945 45
service.lms.incomplete_online 0.896 0.977 0.935 44
service.lms.digest 0.923 0.818 0.867 44
service.ucheck.attendance 0.865 1.000 0.928 45
service.msi.grades 0.978 1.000 0.989 44
service.msi.schedule 0.933 0.933 0.933 45
service.library.search 0.977 0.956 0.966 45
service.library.my_loans 0.930 0.889 0.909 45
service.news.recent 0.953 0.932 0.943 44
service.news.search 0.932 0.911 0.921 45
service.cafeteria.today 1.000 1.000 1.000 44
chat 0.870 0.889 0.879 45
abuse 0.972 0.795 0.875 44

Training

  • Dataset: kbsooo/mjuclaw-intent-dataset (4,474 synthetic Korean queries, stratified 85/15 split)
  • Hardware: Kaggle T4 GPU
  • Wall time: ~4 min
  • Optimizer: AdamW, LR 3e-5, warmup 10%, weight decay 0.01
  • Batch: 32
  • Max seq length: 64
  • Epochs: 13 (early stopped from 15, patience=2 on macro F1)
  • Loss: Weighted CrossEntropy with sklearn.utils.class_weight("balanced")
  • Precision: fp16

Limitations & Intended Use

Intended

  • Internal Discord bot query routing for ๋ช…์ง€๋Œ€ํ•™๊ต (myongji university) student services
  • Korean-only queries, conversational register (Discord DM tone)

Known Limitations

  1. Abuse recall 0.795 โ€” ์•ฝ 20%์˜ ์•…์šฉ ์‹œ๋„๊ฐ€ ๋†“์ณ์งˆ ์ˆ˜ ์žˆ์Œ. ์ถ”๋ก  ๋‹จ๊ณ„์—์„œ p(abuse) โ‰ฅ 0.25 threshold ๋ณด์ • ํ•„์ˆ˜ (Quickstart ์ฝ”๋“œ ์ฐธ์กฐ). ์ด ๋ณด์ • ํ›„ ์‹ค์ธก recall์€ ~0.90 ์ˆ˜์ค€์œผ๋กœ ํšŒ๋ณต๋œ๋‹ค.
  2. chat โ†” abuse ๊ฒฝ๊ณ„ โ€” ์™„๊ณกํ•œ pretext ํŒจํ„ด(์žฅ๋‚œ์ธ ์ฒ™, ๊ถ๊ธˆํ•œ ์ฒ™)์—์„œ ํ˜ผ๋™ ๊ฐ€๋Šฅ. v2์—์„œ abuse ๋ฐ์ดํ„ฐ ์ฆ๊ฐ• ์˜ˆ์ •.
  3. service.lms.digest โ€” ํฌ๊ด„์  ์˜๋ฏธ๋ผ ๋‹ค๋ฅธ lms.*๋กœ ํก์ˆ˜๋˜๋Š” ๊ฒฝํ–ฅ (recall 0.818).
  4. ํ•ฉ์„ฑ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ โ€” ์‹ค์ œ Discord ๋กœ๊ทธ ๋ถ„ํฌ์™€ ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ. ์‹ค์„œ๋น„์Šค ๋ฐฐํฌ ํ›„ ๋กœ๊ทธ ์ˆ˜์ง‘ โ†’ v2 ์žฌํ•™์Šต ๋ฃจํ”„ ๊ถŒ์žฅ.
  5. Out-of-domain: ์˜์–ดยท์ค‘๊ตญ์–ดยท์ผ๋ณธ์–ด ๋“ฑ ๋น„ํ•œ๊ตญ์–ด ์ž…๋ ฅ์€ ํ•™์Šต ๋ถ„ํฌ ๋ฐ–. ๋ช…์ง€๋Œ€ ์™ธ ๋Œ€ํ•™ ์„œ๋น„์Šค์—” ์ง์ ‘ ์ ์šฉ ๋ถˆ๊ฐ€.

Out-of-scope / ๊ธˆ์ง€

  • ํ•œ๊ตญ์–ด ์ผ๋ฐ˜ ๋ฌธ์„œ ๋ถ„๋ฅ˜ โ€” ํ•™์Šต ๋ฐ์ดํ„ฐ๊ฐ€ Discord ๊ตฌ์–ด์ฒด/์งง์€ ์ฟผ๋ฆฌ์— ์ง‘์ค‘๋จ
  • ๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ ๊ด€๋ จ ์˜์‚ฌ๊ฒฐ์ • โ€” ์ด ๋ชจ๋ธ์€ ์˜๋„ ๋ผ์šฐํ„ฐ์ผ ๋ฟ, abuse ๋ถ„๋ฅ˜๊ฐ€ ์™„๋ฒฝํ•˜์ง€ ์•Š์Œ
  • ์•ˆ์ „ ์ค‘์š” ์‹œ์Šคํ…œ์˜ ๋‹จ์ผ ๋ฐฉ์–ด์„  โ€” abuse ๋ถ„๋ฅ˜๋Š” ๋ณด์กฐ ์žฅ์น˜๋กœ๋งŒ ์‚ฌ์šฉํ•˜๊ณ , ์„œ๋น„์Šค ๊ณ„์ธต์— ๊ถŒํ•œ/๊ฐ์‚ฌ ๋กœ๊ทธ๋ฅผ ๋ณ„๋„ ๋‘˜ ๊ฒƒ

Deployment Recipe

์‹ค์ œ ๋ฐฐํฌ ํ™˜๊ฒฝ์€ Docker ์ปจํ…Œ์ด๋„ˆ (linux/arm64, CPU). ๋ฐฐํฌ ์ „ ONNX INT8๋กœ ๋ณ€ํ™˜ ๊ถŒ์žฅ:

# ONNX export + INT8 dynamic quantization
python v1/export_onnx.py
# โ†’ serving/model.int8.onnx (~120 MB)
# โ†’ CPU P50 ~35ms on M4 (2 threads)

Citation

@misc{mjuclaw-intent-classifier-2026,
  title  = {mjuclaw-intent-classifier: Korean intent classifier for Myongji University Discord bot},
  author = {kbsooo},
  year   = {2026},
  url    = {https://huggingface.co/kbsooo/mjuclaw-intent-classifier}
}

Acknowledgments

  • Base model: beomi/KcELECTRA-base โ€” Korean comment-trained ELECTRA
  • Project: mjuclaw โ€” Myongji University Discord agent workspace