--- license: apache-2.0 base_model: Qwen/Qwen3.8-27B language: - ko tags: - korean - vocabulary-pruning - language-confusion - code-switching - recipe --- # Qwen3.8-27B — Korean CJK-Output Suppression (M2) 한국어로 답할 때 문장 중간에 한자·가나가 끼어드는 현상을 **가중치 수준에서** 줄입니다. `lm_head` 의 CJK 토큰 54,902행만 교체합니다. ``` 인기가极高的 해양 생물 테마파크 ← base 오후 6시前后的(경계) 시간대는 ← base 파일 버전号和 마지막 수정 시각을 ← base ``` ## ⚠️ 이 저장소에는 가중치가 없습니다 — 레시피입니다 바뀌는 텐서가 **`lm_head.weight` 하나**뿐이고, 32개 파일 55.6GB 중 **31개가 원본과 바이트 동일**합니다. 55GB 사본을 하나 더 만드느니 원본을 받아 여기 스크립트를 돌리는 편이 낫습니다 — 베이스가 갱신되면 그대로 따라가고, 무엇이 달라졌는지도 diff 로 확인됩니다. ```bash hf download Qwen/Qwen3.8-27B --local-dir ./Qwen3.8-27B python apply_mask.py --model-dir ./Qwen3.8-27B --mask mask_m2-medium.json ``` CPU 로 수 분이면 끝납니다(GPU 불필요). 스크립트는 쓰기 **전에** 마진을 검증하고, 쓰고 나서 재로드해 의도 외 텐서가 안 바뀌었는지 assert 합니다. `--dry-run` 으로 측정만 할 수도 있습니다. | 파일 | 무엇 | |---|---| | `apply_mask.py` | 원본에 적용하는 스크립트 (측정 → 검증 → 샤드 1개 패치 → 재로드 검증) | | `mask_m2-medium.json` | **채택된 마스크** — 54,902 토큰 | | `mask_m1-conservative.json` | 보수적 마스크 — 34,187 토큰 (가나 + 간체전용) | | `mask_m3-full.json` | 전면 마스크 — 58,888 토큰 (⛔ 한자 병기가 무너집니다, 아래 참조) | | `build_masks.py` | 마스크 3종을 토크나이저에서 다시 만드는 스크립트 | ## 무엇이 달라지나 프롬프트 3,369건, 같은 엔진에서 짝지어 측정 (`temperature=0.0`, `max_tokens=400`). | | 오염률 | 실오류* | |---|---|---| | `Qwen/Qwen3.8-27B` | 2.55% | 1.81% | | M1 적용 | 1.51% | 0.83% | | **M2 적용 (권장)** | **0.68%** | **0.18%** | | M3 적용 | 0.06% | — | \* 정상적인 한국어 한자 병기(`개항(開港)`)를 제외한 실제 오류. McNemar p < 0.0001, 런간 노이즈 바닥 0.33pp 대비 5.7배. **한국어 한자 병기는 보존됩니다.** 한자를 명시적으로 요구한 12개 프롬프트에서 M1·M2 는 base 와 동일하게 12/12 생성합니다. CJK **입력**도 그대로 읽습니다 — `embed_tokens` 와 토크나이저는 건드리지 않습니다. `coding`(HumanEval) base 92.19% = M2 92.19%, delta 0.0pp (n=64). ⚠️ n=64 의 최소검출차가 13.29pp 라 형식적으로는 `INSUFFICIENT_N` 입니다 — "회귀 없음"이 아니라 "검출하지 못했다"로 읽어 주세요. ## 방법 `lm_head.weight` 의 대상 행을 은닉상태 평균 방향의 큰 음수 배수로 대체합니다. ``` W_i := -alpha * mu_h / ||mu_h||^2 (alpha = 200) ``` `lm_head` 에 bias 가 없어 행을 0 으로 만들면 로짓이 0 이 될 뿐 −inf 가 아닙니다. 다른 후보가 전부 음수인 순간 0 이 argmax 가 됩니다. `mu_h` 는 한국어 6문장의 최종 은닉상태 평균으로 **측정**합니다(`apply_mask.py` 의 `PROBE_TEXTS`, 두 번의 독립 측정에서 `||mu_h||²` 가 9845.958 / 9887.059 로 0.4% 안에서 재현). 결과 로짓은 **−217.0 대 최대 16.5** 입니다. ⛔ "한글 토큰의 W 행 평균"을 `mu_h` 프록시로 쓰려던 시도는 기각됐습니다 (cos(mu_h, mu_W) = 0.048, 사실상 직교). forward pass 가 필요합니다. ### 무엇을 자르고 무엇을 남겼나 오염을 문자로 보면 72.3%가 한국어도 쓰는 번체/공용 한자라 "간체만 자르기"로는 부족합니다. 그런데 **토큰**으로 보면 오염 498회 중 355회가 `您的`·`贵公司`·`具体时间` 같은 **중국어 단어 통짜**입니다. 한국어 한자 병기는 낱글자로 쪼개집니다(`개항(開港)` → `開`,`港`). 그래서 M2 는 **가나 + 간체전용 + 2자 이상 순수한자 토큰**(54,902개)을 자르고 단일 한자 토큰은 남깁니다. 대가는 `목적`·`사고` 처럼 통짜 토큰인 한국 한자어입니다. M3(전면, 58,888개)은 오염을 0.06%까지 낮추지만 한자 병기가 12/12 → 3/12 로 무너지고, 남은 3건도 **틀린 한자**를 냅니다(`債權`→`倖`, `새옹지마`→`壺齋`). 비교용으로 올려 두었을 뿐 권장하지 않습니다. ## 다른 모델에 쓰려면 토큰 id 는 토크나이저에 종속됩니다. 다른 체크포인트라면 마스크를 다시 만드세요. ```bash python build_masks.py --model # transformers, opencc 필요 ``` ## 한계 - ⚠️ **`temperature` 를 낮게 쓰세요.** 온도가 오염률을 5배 좌우합니다 — 같은 모델·같은 프롬프트에서 `T=1.0` 9.33% vs `T=0.0` 1.92%. 이 카드의 모든 수치는 `T=0.0` 기준입니다. - **가지치기의 하한은 0 이 아닙니다.** 전면 마스킹에서도 2/3,369 가 남았고 정체는 `鄕`·`蕩` 이었습니다. 희귀 한자는 어휘에 단독 토큰이 없어 **바이트 조각으로 조립**되고 (`鄕` → `[98248, 243]`), 그 바이트 토큰은 자를 수 없습니다 — 자르면 임의 UTF-8 처리가 깨집니다. - 능력 검증은 **coding 축만** 했습니다. `longctx`·`kmmlu`·`english` 는 재지 않았습니다. - 한자 대가 프로브는 n=12 로 신뢰구간이 넓습니다([75.75, 100]). - 같은 레시피를 **0.8B 에 적용한 결과는 재현에 실패했습니다**(프록시 0.20% vs 실가중치 1.33%, 런간 바닥의 3.4배). 누출은 한국어 문맥이 아니라 코드 문맥의 `・` 였습니다. 27B 은 같은 코드 문맥 프로브에서 오염 0건이지만, **작은 모델에서 그대로 성립한다고 가정하지 마세요.** 왜 0.8B 만 뚫리는지는 아직 모릅니다. - **이것은 위생 처리이지 문체 개선이 아닙니다.** 사람다움·한국어 문체 축은 대상이 아닙니다. ## 선행 연구 ⛔ **접근 자체는 새롭지 않습니다.** [`dnotitia/smoothie-qwen`](https://github.com/dnotitia/smoothie-qwen) 이 같은 방향(Qwen `lm_head` 조정으로 중국어 억제)으로 Qwen3 0.6B~235B 사전조정본을 이미 배포하고 있습니다. 이 저장소가 더한 것은 **한국어 한자 보존을 설계축으로 삼은 M1/M2/M3 마스크 곡선**과, 위에 적은 세 관측(온도가 5배를 좌우한다 · 바이트 조립 때문에 하한이 0 이 아니다 · 같은 레시피가 0.8B 에서 재현되지 않는다)입니다. 관련: [SASFT (ICLR 2026)](https://arxiv.org/abs/2507.14894) · [Korean token pruning](https://arxiv.org/abs/2604.16235) · [TLPO (ACL 2026)](https://arxiv.org/abs/2604.26553) ## 다른 언어 같은 기법을 타깃 언어별로 다시 설계한 저장소들입니다. 2026-09-01 에 나머지 다섯 언어도 측정했습니다(각 300 프롬프트, T=0/1.0 짝지은 측정). | 언어 | 저장소 | 판정 기준 | 측정 | |---|---|---|---| | 한국어 | (이 저장소) | 간체·가나 + 2자↑ 순수한자 | ✅ 3,369 프롬프트 | | 일본어 | [ja](https://huggingface.co/ThakiCloud/Qwen3.8-27B-ja-cjk-suppressed) | Shift-JIS 레퍼토리 | △ 5.0→0.33% (경계) | | 번체 중국어 | [zh-TW](https://huggingface.co/ThakiCloud/Qwen3.8-27B-zhTW-cjk-suppressed) | Big5 레퍼토리 | ✅ 40.0→1.33% (T=1.0) | | 광둥어 | [yue](https://huggingface.co/ThakiCloud/Qwen3.8-27B-yue-cjk-suppressed) | Big5-HKSCS 레퍼토리 | ✅ 20.33→0.67% (T=1.0) | | 베트남어 | [vi](https://huggingface.co/ThakiCloud/Qwen3.8-27B-vi-cjk-suppressed) | CJK 전면 (정당 사용 0) | △ 2.0→0.0% (경계) | | 아랍어 | [ar](https://huggingface.co/ThakiCloud/Qwen3.8-27B-ar-script-suppressed) | iso8859-6 + 타슈킬·숫자 예외 | ⛔ 오염 미관측 (널) | ⛔ **이 저장소의 마스크를 다른 언어에 그대로 쓰지 마세요.** 일본어 상용어 12개 중 11개가 여기 M2 에 잘립니다 — "2자 이상 순수한자 = 중국어 단어" 규칙은 한국어 정서법에서만 성립합니다. `build_masks_multi.py` 가 언어별 규칙을 담고 있습니다. ## 라이선스 `apache-2.0` — 베이스 모델 `Qwen/Qwen3.8-27B` 와 동일합니다. 이 저장소는 마스크·스크립트만 배포하며 가중치를 재배포하지 않습니다.