# Qwen3.5 4L Headless Backbone (40k EN/KO) > 이 문서는 [영문 모델 카드](README.md)의 한국어 번역입니다. 수치·표·파일 경로는 원문과 동일합니다. > 원문과 해석이 어긋나는 부분이 있으면 영문 카드가 기준입니다. Qwen3.5-0.8B에서 증류한 **헤드 없는 4계층 텍스트 백본**이며, 어휘는 영어와 한국어로 잘라냈습니다. 언어 모델 헤드도, 분류 헤드도, 레이블도, 임계값도 없습니다. 헤드를 붙여서 직접 학습시켜야 합니다. 이 카드는 이것이 어떻게 만들어졌는지의 기록입니다. 원본 모델에 프롬프트를 넣고, 그것을 분류기로 바꾸고, 계층을 제거하고, 정밀도를 바꾸고, 어휘를 자르고, 규칙으로 다시 자르는 — 경로 전체를 따라가며 각 단계가 무엇을 측정했는지 보여줍니다. 우리는 이 모델이 무엇보다 낫다고 주장하는 것이 아닙니다. 우리는 이것을 만들었고, 다른 모델들 옆에 놓고 측정했으며, 어떤 수치가 왜 움직였는지에 대한 짐작이 있는 곳에서는 그것이 짐작이라고 말합니다. | | | |---|---| | **루트 모델** | 4 계층, hidden 1024, 어휘 항목 **39,866**개 | | **파라미터** | 120,639,808 | | **BF16 가중치** | 241,285,160 바이트 (230.1 MiB) | | **Q8_0 GGUF** | 124 MiB | | **헤드** | 없음 — `score.weight`가 존재하지 않는다 | | **업스트림** | [`Qwen/Qwen3.5-0.8B`](https://huggingface.co/Qwen/Qwen3.5-0.8B), 24 계층, 248,320행 | | **증류 데이터** | 레이블 없는 WikiText-103 4,096행, 태스크 레이블 없음 | | **언어 범위** | 영어와 한국어. 한자는 바이트 토큰으로 폴백한다. | > **2026-09-20에 루트가 바뀌었습니다.** 이전에는 128,000항목, 210.9M 파라미터, 402.29 MiB > 모델이었습니다. 이 카드에서 "128k"로 표시된 모든 것은 그 이전 루트를 설명하며, 그 루트는 해당 > 커밋 직전의 git 리비전에 그대로 남아 있습니다. 오늘 다운로드하는 것은 위 표의 39,866항목 > 모델입니다. 5절과 6절이 왜 바뀌었는지 설명합니다. > > **저장소 이름 또한 2026-09-20에 변경되었습니다**, `qwen35-standalone4l-classification-base`에서 > `qwen3.5-4l-vocab40k-en-ko-headless`로. 옛 URL은 리다이렉트되지만, 옛 id에 대한 > `from_pretrained`는 그 리다이렉트를 따라가지 않는다고 보고되어 있으므로 고정된 코드는 갱신해야 > 합니다. 옛 id에는 이제 여기를 가리키는 가중치 없는 스텁이 들어 있습니다. `vocab40k`는 > 39,866항목을 어림한 것이며, 공개하지 않은 세 개의 그리드 형제는 `vocab15k`, `vocab24k`, > `vocab72k`가 될 것입니다. --- ## 어떻게 비교되는가 아래의 모든 arm은 하나의 고정된 프로토콜 아래 하나의 분할에서 학습되고 캘리브레이션되고 프로파일링되었으므로, 열들은 동등 조건입니다. 그 수는 65개입니다: 열 개 계열에 걸친 인코더 arm 46개, 이전 Qwen arm 15개, 그리고 태스크 블라인드 어휘 그리드 arm 네 개이며, 이 루트가 그중 하나입니다. ![모든 지표에서의 루트](benchmark/figures/10_root_on_every_metric.png) | 지표 | 이 루트 | 순위 | 필드 최고 | 필드 중앙값 | |---|---:|---:|---:|---:| | macro F1 (시드 3개) | **0.5721 ± 0.0087** | **3 / 65** | 0.5781 | 0.5121 | | 문서 p50 | 30.4 ms | **52 / 65** | 5.7 ms | 11.2 ms | | 최대 프로세스 트리 RSS | 1,937 MiB | 40 / 65 | 1,601 MiB | 1,880 MiB | | BF16 가중치 파일 | 230 MiB | 31 / 65 | 22 MiB | 238 MiB | | 문서당 GPU 줄 | 5.01 | **52 / 65** | 0.81 | 2.14 | 함께 읽으면: 품질에서는 맨 위에 있고 시간과 에너지에서는 하위 3분의 1에 있습니다. 이 측정의 어떤 것도 품질 열의 최상단을 해소하지 못합니다 — 여섯 개 arm이 최고로부터 시드 SD 중앙값(0.0117) 하나 안에 들어오고 이 루트도 그중 하나이므로, 그 3위는 승리가 아니라 동률입니다. 비용 열은 동률이 아닙니다: 필드에서 가장 빠른 arm은 문서 한 건을 5.7 ms에 답하는 데 비해 이 루트는 30.4 ms이고, 0.81 J를 쓰는 데 비해 5.01 J를 씁니다. 그 여섯 arm의 동률 안에서는 비교가 더 좁고 더 유리합니다. `roberta-base`는 11.1 ms와 238 MiB에서 0.5524에 이릅니다; 가장 좋은 인코더는 모든 비용 축에서 더 싸고 품질은 0.020 낮으며, SD는 0.0143과 0.0087입니다. 동률인 arm들 중에서 `qwen35-taskfree-base8l-v248k`는 73.8 ms, 802 MiB, 11.31 J로 그 품질에 맞먹습니다 — 이 루트는 30.4 ms, 230 MiB, 5.01 J로 같은 대역에 도달합니다. *우리가 읽기로는:* 이것은 262,144 위치 디코더 스택을 256토큰 윈도를 읽는 데 쓰고 있는 것이고, 시간에서 이 루트가 지는 arm 대부분은 바로 그 형태를 위해 만들어진 인코더입니다. 우리는 시간을 비용으로 치르게 하는 것이 어휘 절단이나 깊이가 아니라 아키텍처라고 생각합니다 — 하지만 그것을 분리해 내지는 않았으므로, 그것은 짐작으로 남습니다. 절단과 깊이가 실제로 움직인 것은 파일과 에너지이며, 그것은 위의 패널에 보입니다. 이 표의 모든 값은 열어 본 캘리브레이션 분할입니다. 고정 프로토콜 테스트 평가는 2026-09-23에 한 번, 이 프로젝트가 전에 사용한 적 있는 재분할을 대상으로 수행했으며 — 한 번도 본 적 없는 홀드아웃이 아닙니다 — 내려간 값까지 포함해 [**최종 테스트**](#최종-테스트)에 전부 보고했습니다. --- ## 컨테이너로 실행하기 두 개의 스택이며, 둘 다 CPU 전용이고, 둘 다 전용 no-KV 런타임을 이미지 안에서 컴파일하며 실행 시점에 가져오는 것은 아무것도 없습니다. **파일 받기.** 가중치와 런타임 압축 파일 두 개는 Git LFS로 저장되어 있습니다. git-lfs 없이 `git clone`을 하면(맥의 기본 상태가 이렇습니다) 그 자리에 130바이트 정도의 포인터 파일만 받아지고, 두 컨테이너 모두 그 사실을 알리며 멈춥니다. git-lfs가 필요 없는 Hub CLI로 받으시거나: ```bash hf download mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless --local-dir qwen3.5-4l ``` git-lfs를 먼저 설치하신 뒤(맥 `brew install git-lfs`, Debian/Ubuntu `apt install git-lfs`, Git for Windows에는 이미 들어 있습니다) `git lfs install`을 실행하고 클론하시면 됩니다. 이미 받은 사본은 그 자리에서 `git lfs pull`로 고칠 수 있습니다. **[`docker/`](docker/) — 지금 바로 돌아가는 것을 보기.** ```bash tar -xzf bundle/runtime_source.tar.gz -C bundle/ docker compose up --build # then http://127.0.0.1:8787 ``` 그 안에 무엇이 들어 있는지 분명히 해 둡니다: **이 루트가 아니라**, 14레이블 SemEval-2020 Task 11 헤드를 얹은 이 루트의 백본입니다 — 같은 39,866항목 토크나이저이며, 어휘와 머지와 추가 토큰이 바이트 단위로 동일합니다. 이것은 헤드가 붙었을 때 루트가 무엇이 되는지 보여주는 실례이고, 실제 문서를 판정하는 데 쓸 모델이 아닙니다. 거기서 측정한 값: 어텐션 KV 할당 0 MiB, 네이티브 RSS 173 MiB, 28토큰 윈도에서 연산 56.4 ms, 스레드 4개. **[`docker-train/`](docker-train/) — 같은 발상을 자기 레이블로.** 자기 데이터를 넣으면 Q8 분류기와 서빙 컨테이너가 나옵니다. ```bash python make_base.py # assembles base/ from this repository's own weights tar -xzf vendor/runtime_source.tar.gz -C vendor/ docker compose run --rm validate docker compose run --rm train-cpu docker compose run --rm export docker compose up model ``` 윈도우에서는 Docker Desktop을 켜고 PowerShell에서 같은 줄을 그대로 실행하시면 됩니다. Git for Windows로 클론해도 괜찮습니다. 이 저장소의 `.gitattributes`가 줄바꿈 변환을 꺼두어서 파일이 바이트 그대로 받아지고, 컨테이너의 해시 검사도 통과합니다. 2026-09-22 이전에 클론한 사본은 줄바꿈이 CRLF로 바뀌어 있으니 다시 클론하셔야 합니다. 함께 들어 있는 예제 문서 네 건에서는 그것이 몇 분 만에 돌아가고 `"Please refund this purchase and return the order."` → `refund` 0.999, `"배송 상태를 알려주세요."` → `shipping` 0.998로 끝나는데, 어휘가 한글을 담은 토큰을 모두 남기기 때문입니다. 모든 내보내기는 자기 자신의 특화 대 일반 출력 패리티를 검사합니다; 그 실행에서는 로짓 0.0, 확률 0.0, 레이블 불일치 0이었습니다. 어느 컨테이너도 공개된 루트를 그대로 실행하지 않습니다 — 루트는 헤드가 없으므로, 둘 다 그 위에 헤드를 붙인 상태로 동작합니다. --- ## 사용법 ```python from transformers import AutoModel, AutoTokenizer model_id = "mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless" tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=False) backbone = AutoModel.from_pretrained(model_id, trust_remote_code=False) ``` 가중치 파일에는 `score.weight`가 없습니다. 출력은 은닉 상태이며, 헤드를 학습시키기 전까지는 클래스 의미를 갖지 않습니다. ### 먼저 토크나이저를 자기 텍스트에 대고 확인하라 어휘가 잘려 있으므로 토큰 수가 업스트림 모델과 다르고, 그 차이는 언어별로 고르지 않습니다. 각각 한 문장으로 측정했습니다: | | 업스트림 248k | 이 루트 | 비율 | |---|---:|---:|---:| | 영어 | 9 | 12 | 1.33x | | 한국어 | 16 | 16 | **1.00x** | | 중국어 | 8 | 58 | **7.25x** | | 일본어 | 10 | 44 | **4.40x** | 한국어가 그대로인 것은 절단 규칙이 id와 무관하게 한글을 담은 토큰을 모두 남기기 때문입니다. 업스트림 어휘의 한자 토큰은 모두 id 65,536 위에 있고 하나도 살아남지 않으므로, 중국어와 일본어는 바이트로 인코딩됩니다. 연산량은 토큰 수에 비례하므로, 텍스트가 CJK라면 이 루트는 적절한 루트가 아닙니다. ![절단이 언어별로 치르는 비용](benchmark/figures/09_tokenizer_cost.png) ```python len(tokenizer("a representative document from your data")["input_ids"]) ``` ### 헤드 학습하기 ```bash git clone https://huggingface.co/mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless cd qwen3.5-4l-vocab40k-en-ko-headless/distillation python -m pip install -e . qwen35-distill finetune \ --checkpoint .. \ --train examples/multilabel_train.jsonl \ --labels examples/labels.json \ --mode multilabel \ --output my-classifier-4l ``` 함께 제공되는 플랫폼에는 여섯 개 명령 — `validate-dataset`, `extract-text`, `materialize`, `materialize-classifier`, `finalize-base`, `distill` — 이 있고, **그중 어느 것도 어휘를 자르지 않습니다**. 업스트림 교사 모델에서 래더를 돌리면 깊이 축소는 재현되고 248,320행 전체 임베딩을 가진 4계층 모델이 나오는데, 이는 이 루트와는 다른 산출물입니다. 이 어휘를 원한다면 이 루트에서 시작하십시오. --- ### 헤드를 학습시키신다면, 임계값은 직접 고르셔야 합니다 벤치마크 자신의 표가 분명히 보여 주는데 이 카드가 말하지 않은 것이 하나 있습니다. 프로토콜이 고르는 결정 임계값은 불안정하고, 무엇에 안착하든 헤드는 과다예측합니다. ![임계값과 과다예측](benchmark/figures/22_threshold_and_overprediction.png) 한 arm의 세 시드에서 동결된 18점 그리드는 0.10, 0.15, 0.30을 골랐고, 다른 arm에서는 0.05, 0.20, 0.20을 골랐습니다. 한 모델, 한 분할에서 오직 시드만 달랐는데 3배와 4배의 범위입니다. task-blind arm들도 같습니다. 최종 테스트의 task-blind 실행 열세 개에서 선택된 임계값은 0.05에서 0.45까지 퍼져 있습니다. 그리고 모든 실행이 데이터에 있는 것보다 더 많은 양성을 예측합니다. (문서, 레이블) 쌍의 34.4%가 실제 양성인데, 선택된 임계값들은 예측을 41%에서 71%에 놓습니다. 한 대조 실행은 참 양성 하나당 거짓 양성 1.34개를 돌려줍니다. 드문 레이블이 있는 14개 레이블 문제에서 macro F1을 최대화하도록 고른 임계값이 하는 일이 이것입니다 — macro F1은 드문 레이블에서 *무언가*를 맞히는 것을 보상하고, 그 가장 싼 방법은 더 자주 예라고 말하는 것입니다. 이 중 어느 것도 백본의 결함이 아니라 동결된 프로토콜의 선택 규칙과 이 분할의 레이블 희소성의 성질입니다. 이것을 여기 적는 이유는, 여러분이 자신의 헤드를 붙이실 때 이 수치들 중 가장 놀라기 쉬운 부분이기 때문입니다. **이 임계값들을 그대로 물려받지 마시고, 여러분의 응용이 원하는 정밀도/재현율 균형에 맞게 여러분의 데이터에서 캘리브레이션하십시오.** ## 아키텍처 ``` Qwen3.5-0.8B, 24 layers upstream └─ 24→8 knowledge distillation layers (0,4,6,11,13,16,20,23) └─ 8→6 layers (0,1,3,4,6,7) └─ 6→4 layers (0,2,3,5) └─ vocabulary cut 248,320 → 39,866 rows └─ this root 4L · hidden 1024 · headless ``` | | | |---|---| | 계층 종류 | `linear_attention → full_attention → linear_attention → full_attention` | | Hidden / FFN | 1,024 / 3,584 | | 런타임 클래스 | `Qwen3_5TextModel`, Transformers 5.13.0 | | 증류 목표 | 은닉 상태 및 인터페이스 표현 정합; 로짓 KD 없음, 교차 엔트로피 없음, 레이블 없음 | | 재귀 스칼라 | KD 동안 192개 고정; 나머지 파라미터는 학습 | **계층 맵을 어떻게 골랐는지, 있는 대로 밝힙니다.** 레이블이 붙은 SemEval-2020 Task 11 데이터에서 후보 맵들의 순위를 매겨 선택했습니다 — 6→4 맵은 6개 중 4개를 고르는 열다섯 개 후보 전체에서 학습 macro F1 기준 1위입니다. 이 저장소의 예전 `config.json` 파일들은 `selection_basis: "predeclared_architecture_only_default"`로 기록하는데, 그것은 틀렸습니다; 현재 루트는 `semeval_ranked_inherited`로 기록합니다. 증류 경계 목표가 맵의 함수이므로, 그 선택은 가중치 안에 들어 있습니다. 어휘를 잘라도 그것은 제거되지 않습니다. --- ### 파라미터는 실제로 어디에 있는가 두 번의 절단이 그 순서여야 했던 이유는 판단이 아니라 산수입니다. ![파라미터가 있는 곳](benchmark/figures/20_where_the_parameters_live.png) 24계층에서 임베딩 테이블은 모델의 3분의 1입니다. 4계층으로 자르면 계층 스택의 84% — 498M에서 80M으로 — 가 사라지지만 임베딩은 전혀 건드려지지 않으므로, 모델은 752M에서 334M으로만 줄고 임베딩의 비중은 33.8%에서 **76.1%**로 올라갑니다. 깊이를 자르고 나니 남은 것의 4분의 3이 이 모델이 결코 쓰지 않을 248,320개 항목의 참조표였고, 그래서 다음에 자를 가치가 있는 것이 어휘였습니다. 39,866으로 자르면 임베딩은 모델의 33.8%로 돌아오는데, 이는 24계층 교사가 가지고 있던 것과 같은 균형이며, 모델은 120.6M이 됩니다. 이 루트 아래의 두 그리드 포인트는 더 내려갑니다. 15,380 항목에서는 임베딩이 16%이고 남은 것의 거의 전부가 계층 스택입니다. ### 사다리 자체는 무엇을 치렀는가 증류는 세 단계이고, 이 저장소의 표가 각 단계의 손실을 기록하고 있습니다. ![사다리의 손실](benchmark/figures/21_ladder_loss.png) 총합으로 읽으면 순서가 단조롭지 않습니다. 24→8은 열여섯 계층을 0.176에, 8→6은 두 계층을 0.034에, 그리고 6→4는 두 계층을 0.047에 — 같은 계층 수에 대해 바로 앞 단계보다 39% 더 — 떨어뜨립니다. 제거된 계층당으로 읽으면 단조롭게 올라갑니다. 0.0110, 0.0169, 0.0235. 마지막 두 계층은 처음 열여섯 계층이 치른 값의 계층당 2.1배를 치릅니다. 여기에서 기전을 읽어 내지는 않습니다. 단계당 한 번의 실행이라 산포가 없고, 레이블을 한 번도 보지 않는 정합 목적함수이며, 모든 단계에서 인터페이스 손실이 최종 손실보다 큰데 이는 정합 대상이 되는 경계가 제약일 때 예상되는 모습입니다. ## 경로, 단계별로 아래의 모든 것은 측정된 값입니다. 각 절은 **우리가 가정한 상황**이며, 그 상황을 세우기 위해 사용한 데이터와 하드웨어를 밝힙니다. 우리는 특정 태스크를 위해 만들고 있던 것이 아니었으므로, 경로 전체에 걸쳐 하나의 데이터셋을 고정해 두지 않았습니다: 1절은 짧은 문서에 응답하는 엣지 보드를 가정하고 그를 위해 3클래스 내부 세트를 사용합니다; 2절부터는 다중 레이블 문서 작업을 하는 데스크톱 GPU를 가정하고 14레이블 SemEval 파생 세트를 사용합니다. 각 절을 그 자체의 시나리오로 읽으십시오 — 한 절 안의 arm들은 데이터, 프로토콜, 호스트를 공유하며, 그것이 그 절 안의 비교를 의미 있게 만드는 것입니다. ![경로 전체를 한 그림으로](benchmark/figures/06_stage_path.png) ### 1. 원본 모델에 프롬프트 넣기 **가정한 상황:** 엄격한 메모리 상한 아래에서 짧은 문서를 분류하는 엣지 보드. **그를 위해 사용한 데이터:** legacy37, 3 클래스, 37개 문서, 내부 레이블, 사람이 만든 정답이 아닙니다. **하드웨어:** UNO Q 엣지 보드. **2026-07-15 측정.** | arm | 정확도 | macro F1 | 산출물 | 문서 한 건 | 메모리 | |---|---:|---:|---:|---:|---| | **P0** 프롬프트, Q4_0 생성 | 62.16% | **0.2556** | 507.2 MB | warm p50 8.57 s | ≥2.441 GiB, **요청 31에서 OOM** | | **C24** 24L 백본 + 3로짓 헤드, Q6_K | 94.59% | 0.9571 | 629.7 MB | cold 21.32 s | 1,454.7 MiB | | **C8Q** 8L 증류 + 헤드, F16/Q6_K | 97.30% | 0.9781 | 395.3 MB | cold 11.60 s | 1,192.0 MiB | P0은 37개 문서 전부에 대해 유효한 레이블을 반환했고, 그 37개 전부에 대해 `scam_attempt`를 반환했습니다. 이는 클래스들에 걸쳐 점수가 낮게 퍼진 것이 아니라 클래스 붕괴입니다: 이 세트는 `scam_attempt` 23개 / `normal` 11개 / `scam_news_edu` 3개이므로, 62.16%는 정확히 다수 클래스 비율입니다. P0은 또한 연속 31회 요청 후 2.441 GiB cgroup 한계에 도달해 프로덕션 러너를 함께 다운시켰습니다. 같은 런타임에서 C8Q 대 C24: 37행 벽시계 시간 421.24 s → 145.07 s, 콜드 1건 21.32 s → 11.60 s. 품질 차이에 대한 대응 부트스트랩 구간이 0을 포함하므로, 우리는 이를 정확도상의 승리가 아니라 압축 아래에서 품질이 붕괴하지 않은 것으로 읽습니다. *수치가 무엇을 기준으로 측정된 것인지:* P0의 메모리는 라우터와 모델 서버를 포함한 컨테이너 cgroup이고, 분류기 수치는 단일 포그라운드 프로세스의 최대치입니다. 분류기의 행당 벽시계 시간은 모델 로드를 포함한, 균등 분배된 배치 값이며 상주 HTTP p50이 아닙니다. 계층이 다르므로 메모리 열은 동등 조건의 차이가 아니라 "보드가 무엇을 담고 있어야 했는지"로 읽으십시오. *우리가 읽기로는:* 생성은 요청마다 시스템 프롬프트와 디코드 루프를 비용으로 치르며, 이 상황에서는 정확도가 문제가 되기 전에 그것이 구속 조건이었습니다. 이 절 이후의 모든 것은 분류기입니다. ### 2. 분류기로서: 24 계층, 248k 어휘 **가정한 상황:** 긴 문서에 여러 레이블을 붙이는 데스크톱 GPU. **그를 위해 사용한 데이터:** SemEval-2020 Task 11, 14 레이블, 56개 문서의 열린 캘리브레이션 분할. **하드웨어:** RTX 5070 Ti. **프로토콜:** 5 에폭, 윈도 256 스트라이드 128, 배치 1에 32스텝 누적, AdamW lr 2e-5, BF16, 레이블별 최대값 집계, 18포인트 임계값 그리드. **시드 세 개(41/42/43), SD는 3표본 SD입니다.** | | macro F1 | p50 | 최대 RSS | 가중치 파일 | GPU J/doc | |---|---:|---:|---:|---:|---:| | 24L, 248k, BF16 | 0.5487 ± 0.0228 | 221.3 ms | 2,037 MiB | 1,435 MiB | 33.37 | 이것이 아래 모든 것의 출발점입니다: 전체 깊이, 전체 어휘 백본에 새로 초기화한 14레이블 헤드를 붙인 것입니다. ### 3. 계층 제거 같은 어휘, 같은 프로토콜, 같은 시드. 깊이만 달라집니다. | 깊이 | macro F1 | p50 | 가중치 파일 | GPU J/doc | |---:|---:|---:|---:|---:| | 24L | 0.5487 ± 0.0228 | 221.3 ms | 1,435 MiB | 33.37 | | 8L | 0.5686 ± 0.0212 | 73.8 ms | 802 MiB | 11.31 | | 6L | 0.5627 ± 0.0241 | 52.0 ms | 720 MiB | 7.98 | | 4L | 0.5602 ± 0.0391 | **29.7 ms** | 637 MiB | **4.71** | 네 깊이에 걸친 품질은 0.0199 범위에 걸쳐 있습니다. SD는 0.0212에서 0.0391에 걸쳐 있습니다. 이 폭은 실행 간 변동 안에 있으므로, 우리는 여기서 순위를 읽지 않습니다. 같은 범위에서 지연은 7.5x, 문서당 에너지는 7.1x 움직입니다. *우리가 읽기로는:* 이 태스크에서 우리가 제거한 깊이는 이 측정이 볼 수 있는 품질을 담고 있지 않았습니다. 더 어려운 태스크에서도 그런지는 우리는 모릅니다; 우리는 이 하나만 측정했습니다. ### 4. 정밀도 변경 4 계층, 248k 어휘, 같은 헤드. BF16은 CUDA에서 돌아가고, 두 GGUF 정밀도는 스레드 4개로 CPU에서 돌아갑니다 — 서로 다른 두 배포 상황입니다. 따라서 지연 열은 열을 따라 내려가며 읽는 것이 아니라 각 행 자신의 호스트 안에서 읽어야 하고, 파일과 품질 열은 열을 따라 내려가며 읽습니다. | 정밀도 | macro F1 | p50 | 최대 RSS | 가중치 파일 | |---|---:|---:|---:|---:| | BF16 (CUDA) | 0.5602 ± 0.0391 | 29.7 ms | 2,082 MiB | 637 MiB | | F16 GGUF (CPU) | 0.5592 ± 0.0398 | 783.8 ms | 2,075 MiB | 648 MiB | | Q8_0 GGUF (CPU) | 0.5612 ± 0.0356 | 696.6 ms | **1,781 MiB** | **349 MiB** | Q8_0은 BF16에 비해 파일을 절반으로 줄이고 품질을 +0.0010 움직이는데, 이는 SD 안쪽으로 한참 들어갑니다. *우리가 읽기로는:* 이 백본의 8비트 양자화는 우리가 검출할 수 있는 어떤 비용도 치르지 않았습니다. 이후 어휘를 자르면 양자화 비용이 조금 더 든다는 단서를 보았습니다 — 잘라낸 arm 세 개에서 0.0015에서 0.0068, 전체 어휘 arm 두 개에서는 대략 0 — 하지만 시드 세 개에서는 이것도 분리되지 않으므로, 우리는 이를 발견이 아니라 관찰로 기록합니다. ### 5. 임베딩을 128k로 자르기 4계층 모델에서는 임베딩이 지배적입니다: 334,096,704 파라미터 중 254,279,680, **76.11%**. 깊이 축소는 언제나 나머지 24%만 건드렸습니다. | | macro F1 | p50 | Q8_0 파일 | |---|---:|---:|---:| | 4L, 248k | 0.5602 ± 0.0391 | 29.7 ms | 349 MiB | | 4L, 128k | 0.5637 ± 0.0321 | 29.5 ms | **218 MiB** | 계층 가중치는 바뀌지 않았습니다; 임베딩 행만 재매핑되었습니다. **그 128,000항목이 어디서 왔는가.** `v128k_remap_manifest.json`은 `models/semeval-propaganda`를 가리키는 `source_128k_classifier_tokenizer`를 기록합니다. 어휘는 SemEval 프로파간다 분류기의 토크나이저에서 토큰 문자열 기준으로 가져온 것이며, 그 128,000항목을 고른 규칙은 이 저장소 어디에도 문서화되어 있지 않습니다. 따라서 태스크와 무관하다고 서술된 루트가 태스크를 염두에 두고 선택된 어휘를 갖고 있었습니다. 그래서 6절이 있습니다. 그것은 한국어에도 비용을 치렀습니다. 한국어를 담을 수 있는 토큰이 10,927개에서 2,137개로 줄었고, 같은 한국어 텍스트가 128k 어휘에서는 토큰을 **+92.17%** 더 씁니다. ### 6. 원본 임베딩을 규칙으로 자르기: 영어와 한국어 이것이 현재 루트입니다. 규칙은 무엇을 만들기 전에 적어 두고 고정했으며, 어떤 어휘 크기도 태스크 결과를 근거로 선택되지 않았습니다 — 네 가지 크기를 만들고, 네 가지 모두 측정했으며, 비용을 근거로 하나를 공개합니다. **규칙.** 업스트림 BPE id 기준 상위 *N*개 항목, 여기에 한글을 담은 모든 토큰(유니코드 스크립트 속성 기준, 그리고 바이트가 전부 한글 범위에 들어가는 바이트 조각 포함), 여기에 256바이트 알파벳, 여기에 추가 토큰 33개를 남깁니다. *N* = 32,768에서 이는 39,866항목입니다. 24→8→6→4 래더 전체를 그리드 포인트마다 한 번씩 업스트림 교사 모델에서 다시 증류했으므로, 이들은 나중에 잘라낸 것이 아니라 자기 어휘에서 학습된 것입니다. | N | 항목 | macro F1 | BF16 p50 | Q8_0 파일 | GPU J/doc | |---:|---:|---:|---:|---:|---:| | 8,192 | 15,380 | 0.5699 ± 0.0160 | 37.2 ms | 98 MiB | 6.00 | | 16,384 | 23,551 | 0.5781 ± 0.0032 | 33.8 ms | 106 MiB | 5.40 | | **32,768** | **39,866** | **0.5721 ± 0.0087** | **30.4 ms** | **124 MiB** | **5.01** | | 65,536 | 72,455 | 0.5778 ± 0.0079 | 30.0 ms | 159 MiB | 4.73 | 넷은 품질에서 0.0081 범위에 걸쳐 있고, 가장 큰 SD는 0.0160입니다. 우리는 순위를 읽지 않습니다. 무관한 결함 때문에 폐기한 같은 그리드의 이전 빌드는 이들을 다른 순서로 놓았는데, 이것이 그 순서가 잡음이라는, 우리가 가진 가장 분명한 증거입니다. 비용은 단조롭고 꺾이는 지점이 있습니다. 64k→32k는 BF16 지연 1.2%를 내고 파일의 22.0%를 얻습니다; 32k→16k는 11.2%를 내고 14.1%를 얻습니다; 16k→8k는 10.2%와 에너지 11.1% 증가를 내고 8.2%를 얻습니다. 우리가 32k를 공개한 것은 파일 절감이 그 대가보다 큰 마지막 단계이기 때문이며, 점수가 가장 좋아서가 아닙니다 — 가장 좋지 않았습니다. ![그리드](benchmark/figures/05_taskblind_grid.png) **어휘를 자르면 추론 비용은 줄어드는 것이 아니라 늘어납니다.** 거꾸로 읽히는 말인데, 기전은 토크나이저입니다. 항목이 적을수록 같은 텍스트가 더 많고 더 짧은 토큰으로 적히고, 프로토콜은 문서를 고정된 256토큰 윈도로 읽으므로, 토큰이 많아지면 윈도가 많아지고 순전파가 많아집니다. 같은 문서 56개를 각 그리드 포인트 자신의 토크나이저로 재어 보았습니다. | N | 항목 | 토큰/문서 | 윈도/문서 | BF16 p50 | GPU J | Q8_0 p50 | Q8_0 파일 | |---:|---:|---:|---:|---:|---:|---:|---:| | 8,192 | 15,380 | 1,629 | 12.27 | 1.239배 | 1.269배 | 1.346배 | **98 MiB** | | 16,384 | 23,551 | 1,462 | 10.89 | 1.124배 | 1.143배 | 1.154배 | 106 MiB | | **32,768** | **39,866** | **1,345** | **10.04** | **1.012배** | **1.060배** | **1.103배** | **124 MiB** | | 65,536 | 72,455 | 1,264 | 9.36 | 1.000배 | 1.000배 | 1.000배 | 159 MiB | 가장 큰 그리드 포인트에서 가장 작은 쪽으로 가면 윈도 수가 1.311배 늘고, 측정된 세 가지 비용은 각각 1.239배, 1.269배, 1.346배로 나옵니다. 같은 백본, 같은 깊이, 같은 프로토콜에서 어휘만 다른데, 윈도 수가 세 지표를 모두 몇 퍼센트 오차 안에서 예측합니다. 그러니까 절단은 파일 크기를 사고 그 값을 연산으로 치릅니다. 가장 작은 그리드 포인트는 이 가족에서 가장 작은 파일이면서 동시에 가장 느리고 가장 전기를 많이 먹는 구성원입니다. 위 표의 32k 모서리는 그 두 곡선이 교차하는 지점입니다. ![절단이 추론에서 치르는 값](benchmark/figures/18_vocabulary_cost_inversion.png) *범위:* 영어 뉴스 문서 기준입니다. 이 비율은 여러분의 텍스트가 살아남은 항목들로 얼마나 잘 덮이는지에 달려 있으므로, 한국어, 특히 CJK는 같은 방식으로 움직이지 않습니다 — [사용법](#먼저-토크나이저를-자기-텍스트에-대고-확인하라)의 토크나이저 표를 봐 주십시오. --- ## 다른 모델들 옆에 놓고 61개 arm, 고정된 프로토콜 하나, 183회 학습, 549회 캘리브레이션, 549회 격리 추론 프로파일, 2026-09-19 완료. 계열: Qwen3.5 (13), Qwen2.5 (2), 그리고 열 개 인코더 계열 (46) — BERT, RoBERTa, XLM-R, DeBERTa-v3, mDeBERTa-v3, ALBERT, ELECTRA, MPNet, ModernBERT, DistilBERT, mBERT, multilingual MiniLM. 6절의 어휘 그리드 arm 네 개는 이후에 같은 프로토콜, 분할, 시드 아래에서 학습되었고, 그에 따라 필드는 65개가 됩니다. BF16 3시드 평균 기준 상위 열두 개: | # | arm | macro F1 | SD | p50 ms | 파일 MiB | J/doc | |---|---|---:|---:|---:|---:|---:| | 1 | `qwen35-taskblind-base4l-N16384` (23,551) | 0.5781 | ±0.0032 | 33.8 | 198 | 5.40 | | 2 | `qwen35-taskblind-base4l-N65536` (72,455) | 0.5778 | ±0.0079 | 30.0 | 294 | 4.73 | | 3 | **`qwen35-taskblind-base4l-N32768` (39,866) — 이 루트** | **0.5721** | ±0.0087 | 30.4 | 230 | 5.01 | | 4 | `qwen35-taskblind-base4l-N8192` (15,380) | 0.5699 | ±0.0160 | 37.2 | 182 | 6.00 | | 5 | `qwen35-taskfree-base8l-v248k` | 0.5686 | ±0.0212 | 73.8 | 802 | 11.31 | | 6 | `qwen35-specialized4l-v128k` | 0.5685 | ±0.0133 | 29.8 | 402 | 4.67 | | 7 | `qwen35-taskfree-base4l-v128k` | 0.5637 | ±0.0321 | 29.5 | 402 | 4.61 | | 8 | `qwen35-taskfree-base6l-v248k` | 0.5627 | ±0.0241 | 52.0 | 720 | 7.98 | | 9 | `qwen35-taskfree-base8l-v128k` | 0.5611 | ±0.0050 | 74.3 | 567 | 11.21 | | 10 | `qwen35-taskfree-base4l-v248k` | 0.5602 | ±0.0391 | 29.7 | 637 | 4.71 | | 11 | `qwen35-taskfree-base6l-v128k` | 0.5568 | ±0.0245 | 51.7 | 485 | 8.16 | | 12 | `roberta-base` | 0.5524 | ±0.0143 | **11.1** | **238** | **2.24** | 앞의 네 행이 어휘 그리드이며, 그들의 0.0081 폭은 그 안에서 가장 큰 SD보다 작습니다. 65개 arm 전체의 3시드 SD 중앙값은 0.0117이고 여섯 개 arm이 최상위로부터 그런 SD 하나 안에 들어오므로, 이 순서는 이 측정으로 결정되지 않습니다. 같은 필드에 걸쳐 macro F1은 0.093 범위인 데 비해 문서 p50은 40x(5.7 ms에서 228.9 ms), 가중치 파일은 64x(22 MiB에서 1,435 MiB), GPU 에너지는 43x(0.81 J에서 34.76 J) 범위입니다. ![모든 지표에서의 루트](benchmark/figures/10_root_on_every_metric.png) 세 개 arm — `mdeberta-v3-4l`, `deberta-v3-base-6l`, `deberta-v3-base-8l` — 은 정확히 0.475627을 기록하는데, 이는 이 분할에서 전부 양성으로 예측하는 예측기의 macro F1입니다. 이들은 공유 프로토콜 아래에서 학습에 실패했습니다. 우리는 이들을 버리지 않고 보고합니다. 비용에서는 이 깊이에서 인코더들이 우리보다 앞서 있습니다: `roberta-base-6l`은 7.4 ms와 157 MiB로 돌아가고, 그 표를 만든 시점의 우리 값은 29.5 ms와 402 MiB이며, 품질 차이는 0.021, SD는 0.014와 0.032입니다. 전체 표: [`benchmark/full/61_arm_bf16.csv`](benchmark/full/61_arm_bf16.csv). ![품질 대 지연](benchmark/figures/01_quality_vs_latency.png) ![상위 arm들이 겹친다](benchmark/figures/02_top_arms_overlap.png) ![깊이](benchmark/figures/03_depth_quality_and_cost.png) ### 전체 판이 어떻게 생겼는가 ![65개 arm의 품질 대 비용](benchmark/figures/14_field_quality_vs_cost.png) 두 패널은 같은 65개 arm을 담고 있으며, 비용 축만 다릅니다. 점선은 파레토 경계 — 품질과 비용을 동시에 이기는 다른 arm이 없는 arm들 — 입니다. 지연 기준으로는 일곱 개가, 에너지 기준으로는 여덟 개가 경계를 이루고, 두 경계의 싼 쪽 절반은 모두 RoBERTa 계열이 차지합니다. 이 루트는 품질 축의 위쪽에 있고, 그 자리에 있기 위해 `roberta-base`의 약 2.7배 지연을 지불합니다. ![판에서 실제로 변하는 것](benchmark/figures/15_field_metric_spans.png) 나란히 놓으면 이 폭 자체가 결과입니다. 판 전체에서 macro F1은 **1.2배** — 0.4756에서 0.5781까지 — 움직이는 동안, 가중치 파일은 **64.4배**, GPU 에너지는 **42.9배**, 문서 지연은 **40.3배** 움직입니다. 품질의 폭은 그 숫자가 보여 주는 것보다도 좁습니다. 그 아래 끝이 정확히 전부 양성으로 답하는 예측기이므로, 판 전체가 "무엇이든 예"라고 답하는 모델 위 0.093의 유효 범위 안에 들어갑니다. peak RSS는 거의 움직이지 않는데, 이는 가중치가 아니라 하네스가 그 값을 지배하기 때문입니다. ![인코더 계열별 깊이 효과](benchmark/figures/16_field_depth_effect.png) 모든 인코더 계열을 같은 프로토콜로 4, 6, 8 계층으로 잘랐고, 각 계열의 전층 체크포인트를 그 옆에 그대로 두었습니다. **12개 계열 중 7개에서 가장 좋은 arm은 가장 깊은 것이 아닙니다.** `bert-base`, `mbert`, `electra-base`는 4계층에서, `minilm-multilingual`은 6계층에서, `mpnet-base`와 `mdeberta-v3`는 8계층에서 정점을 찍습니다. 깊이가 값을 하는 다섯 계열 — roberta, xlmr, deberta-v3, albert, modernbert — 은 그 대가를 지연과 파일 크기로 치릅니다. 세 개의 arm은 공유 프로토콜 아래에서 아예 학습되지 않았고 정확히 바닥에 앉아 있습니다. 이것은 이 과제를, 이 해상도에서 관찰한 결과입니다. 깊이가 중요하지 않다는 주장이 아니며, 이 루트가 네 계층인 이유도 아닙니다. 그 이유는 3절에 있습니다. ![65개 arm 전부를 한 축 위에](benchmark/figures/17_field_resolution_floor.png) 마지막 그림이 이 순위에 대한 정직한 요약입니다. 판 전체의 3시드 SD 중앙값은 0.0116이고, 여섯 개의 arm이 최고점으로부터 그 한 개 SD 안에 들어 있습니다. 몇몇 SD 막대는 상위 열 개 arm 사이의 간격 전체보다도 넓습니다. 이 순위는 측정했기 때문에 보고하는 것이지, 측정이 그것을 가려냈기 때문이 아닙니다. --- ## 최종 테스트 2026-09-23에 SemEval-2020 Task 11 **v2 테스트 재분할**을 대상으로 고정 프로토콜 평가를 한 번 수행했습니다. 사전등록 문서는 테스트 바이트를 한 개도 읽기 전에 동결되었습니다. **이것은 한 번도 본 적 없는 홀드아웃이 아닙니다.** 이 프로젝트는 v2 분할을 전에 사용한 적이 있고, 동결된 문서는 이전 테스트 노출을 `true`로 기록하고 있습니다. 공식 리더보드 테스트가 아니며, 보안 하네스 증명도 없고, 여기서 한국어 품질이나 일반적인 의도 분류 성능이 확립되지도 않습니다. 사전등록 초안은 이 분할이 한 번도 열린 적 없다고 잘못 적었는데, 그 초안은 삭제하지 않고 수정본 옆에 그대로 보관하고 있습니다. 동결이 미리 고정한 것은 다음과 같습니다. 열한 개 arm의 명단, arm·시드·정밀도별 결정 임계값 — 각각 그 arm의 기존 캘리브레이션 보고서에서 읽어 온 값이며 테스트에서 다시 계산하지 않았습니다 — 집계 규칙, 지표 집합, 그리고 어떤 결과가 나오든 전부 공개한다는 규칙입니다. 보관된 페이로드는 추출 전에 SHA-256 `ed224269dc…`와 일치해야 했고, 일치했습니다. 31개 작업 중 31개가 완료되었으며, 문서 55개, 레이블 14개, 실패 0건, 학습 없음, 임계값 탐색 없음입니다. 이후 31개 실행의 지표 전부를 저장된 확률로부터 별도의 표준 라이브러리 구현으로 다시 계산했고, 평가기와 일치했습니다. ![캘리브레이션에서 봉인 테스트로](benchmark/figures/11_final_test_calibration_to_test.png) | arm | 정밀도 | 캘리브레이션 | 테스트 | Δ | |---|---|---:|---:|---:| | **task-blind 4L N=32,768 — 이 루트의 분류기** | BF16 | 0.5719 ± 0.0092 | **0.5988 ± 0.0103** | **+0.0269** | | task-blind 4L N=32,768 | Q8_0 | 0.5849 (시드 41) | 0.5862 (시드 41) | +0.0013 | | task-blind 4L N=65,536 | BF16 | 0.5775 ± 0.0076 | 0.5832 ± 0.0047 | +0.0057 | | task-blind 4L N=16,384 | BF16 | 0.5787 ± 0.0021 | 0.5379 ± 0.0496 | −0.0408 | | task-blind 4L N=8,192 | BF16 | 0.5699 ± 0.0160 | 0.5555 ± 0.0070 | −0.0144 | | 과거 4L v128k | BF16 | 0.5637 ± 0.0321 | 0.5747 ± 0.0183 | +0.0110 | | 과거 8L v248k | BF16 | 0.5686 ± 0.0212 | 0.5718 ± 0.0213 | +0.0032 | | 과거 4L v248k | BF16 | 0.5602 ± 0.0391 | 0.5707 ± 0.0081 | +0.0105 | | 과거 24L v248k | BF16 | 0.5487 ± 0.0228 | 0.5612 ± 0.0102 | +0.0125 | | roberta-base 12L | BF16 | 0.5524 ± 0.0143 | 0.5883 ± 0.0109 | +0.0359 | | roberta-base 6L | BF16 | 0.5427 ± 0.0139 | 0.5839 ± 0.0152 | +0.0411 | | 전부 양성 예측기 | — | 0.4756 | **0.4866** | — | 양자화는 테스트에서 macro F1 0.0013을 치렀습니다. 4절이 캘리브레이션에서 본 "측정되지 않는 정도"와 같은 값이며, 이번에는 임계값을 맞추지 않은 분할에서 본 것입니다. **+0.0269은 이 모델에 관한 결과가 아닙니다.** 열한 개 arm 중 아홉 개가 올랐고, 열한 개의 평균 이동은 +0.0085이며, 가장 많이 오른 것은 RoBERTa 레퍼런스 *둘*입니다. 이 테스트 분할이 캘리브레이션 분할보다 높게 나오게 만드는 무언가는 판 전체에 적용되므로, 이 이동은 어느 arm의 성질이 아니라 분할 쌍의 성질입니다. 어느 arm도 다른 arm과 분리되지 않습니다. 이 루트의 평균 ± SD 구간은 `roberta-base`(0.5883 ± 0.0109), `roberta-base-6l`(0.5839 ± 0.0152), 그리고 과거 4L arm 둘과 겹칩니다. 구간을 벗어나는 경우에도 N=65,536과는 0.0006, N=16,384와는 0.0010 차이인데, 이는 그 값을 만들어 낸 설계의 산포보다 한두 자릿수 작은 간격입니다. 내려간 arm도 둘 있습니다. N=8,192는 0.0144, N=16,384는 0.0408 떨어졌고, 후자는 표에서 가장 넓은 산포를 함께 지고 있습니다. ![봉인 테스트의 시드 산포](benchmark/figures/13_final_test_seed_spread.png) 모든 arm의 모든 시드가 문서 55개 위에서 폭 0.103의 띠 안에 들어옵니다. 그리고 대부분의 arm에서는 자기 시드 세 개 사이의 거리가 이웃 arm까지의 거리보다 큽니다. N=16,384가 극단적인 사례로, 한 시드는 0.5951인데 나머지 둘은 0.51 부근입니다. ![게시된 루트의 레이블별 결과](benchmark/figures/12_final_test_per_label.png) 레이블별로 보면 F1은 거의 단조롭게 support를 따라갑니다. 지지 문서가 45개인 `Loaded_Language`는 0.918이고, 14개 이하인 여섯 레이블은 0.446에서 0.481 사이에 놓입니다. 그리고 열네 개 레이블 전부에서 recall이 precision을 넘습니다. 0.05에서 0.3에 걸친 동결 임계값 아래에서 이 헤드는 어디서나 과다예측합니다. 실제 배포를 위해 학습하는 헤드라면 다르게 캘리브레이션하실 것입니다. v2 분할은 이 프로토콜 아래에서 튜닝과 모델 선택 용도로 이제 은퇴했습니다. v1은 열지 않았으며, v1이 한 번도 쓰이지 않았다고 주장하지는 않습니다. 증거: `REPORT.md` 1–89행, `RESULTS.json` 1–742행(SHA-256 `0d2da5e2e8b250671bf3b19be0e7bce49f14fe492f9c71ce75717b0eee02dec7`), `PER_LABEL.csv`, 그리고 동결된 사전등록 문서(SHA-256 `85ec92a0be…`). 여섯 건 모두 이 프로젝트의 리서치 스토어에 등록되어 있고, 이 실행은 그곳에 `artifact_final_test_20260923_v1`로 기록되어 있습니다. ## 해석을 바꿀 수 있었기에 확인한 두 가지 ### 호스트가 영향을 주는가? 처음에 낸 절단 대 원본 지연 비율은 벤치마크의 고정된 행을 분모로 썼고, 그래서 모든 절단이 더 느려 보이게 만들었습니다. 같은 실행에서 같은 호스트에 원본 arm을 다시 측정하니 호스트 계수가 1.0525 (BF16), 1.0448 (F16), 1.1059 (Q8_0)로 나왔고, RSS 비율은 0.99였습니다. 교정된 동일 호스트 기준에서는 비율이 뒤집혔습니다: 64k 절단은 정밀도에 따라 원본 p50의 0.966x에서 0.991x로 돌아가고, BF16 문서당 에너지는 그것의 0.977x입니다. | 절단 | 원본 대비 Q8_0 p50 | 원본 대비 Q8_0 RSS | |---|---:|---:| | v64k | **0.966x** | 0.680x | | v32k | 1.036x | 0.623x | | v16k | 1.167x | 0.598x | ![어휘 절단](benchmark/figures/04_vocabulary_cut_curve.png) ### 262k 컨텍스트가 도움이 되는가? 프로토콜은 모든 문서를 256토큰 윈도로 읽는데, 이는 262,144 위치 디코더를 512 위치 인코더와 같은 윈도로 묶어 버립니다. 캘리브레이션 문서의 중앙값은 1,034 토큰이고 96%가 4,096 안에 들어가므로, 우리는 이를 두 번 시험했습니다. 더 긴 윈도로 읽기, 같은 학습된 헤드, 시드 41: | 윈도 | 이 계열 (262k ctx) | ModernBERT (8,192) | RoBERTa (514) | DeBERTa-v3 (512) | |---:|---:|---:|---:|---:| | 256 | **0.5816** | 0.5126 | 0.5381 | 0.5330 | | 512 | 0.5633 | 0.4904 | **0.5509** | **0.5376** | | 1,024 | 0.5722 | 0.4855 | — | — | | 2,048 | 0.5408 | 0.4795 | — | — | | 4,096 | 0.5480 | 0.4788 | — | — | 8,192 컨텍스트를 가진 인코더는 256→4,096에서 0.0338을 잃고, 262k 모델은 0.0336을 잃습니다. ![윈도 스윕](benchmark/figures/08_long_context_window.png) 헤드를 256이 아니라 윈도 4,096에서 학습, 시드 세 개: | 학습 윈도 | macro F1 | |---|---:| | 256 / 스트라이드 128 | 0.5721 ± 0.0087 | | 4,096 / 스트라이드 2,048 | 0.5446 ± 0.0062 | *우리가 해결하지 못한 교란:* 4,096 윈도는 학습 윈도 280개를 내는데 256은 수천 개를 내므로, 50% 겹침 윈도잉이 증강 역할도 하고 있었습니다. 이 비교는 컨텍스트와 학습 신호의 양을 동시에 바꿉니다. *우리가 읽기로는:* 이 태스크에서 수치를 움직이는 것이 컨텍스트 길이라고 우리는 생각하지 않는데, 주된 이유는 8,192 인코더가 262k 모델을 그렇게 가깝게 따라가기 때문입니다. 우리는 긴 컨텍스트가 일반적으로 쓸모없다고 주장하는 것이 아닙니다. #### 그러면 더 넓은 윈도는 무엇을 치르는가? 품질이 하나의 질문이었다면 시간은 또 다른 질문인데, 이 카드에는 그것이 없었습니다. 그래서 나중에 재었습니다. 백본 순전파만, BF16, RTX 5070 Ti 한 대, 한 문서의 윈도 전부를 한 배치로 제출, 토큰 길이는 같은 문서 56개의 실제 값입니다. | 윈도 | 윈도/문서 | 밀어 넣은 위치 수 | 패딩 | ms/문서 | |---:|---:|---:|---:|---:| | **256** | 10.04 | 143,872 | 2.6% | **15.90** | | 512 | 4.80 | 137,728 | 5.7% | 15.76 | | 1,024 | 2.29 | 131,072 | 14.4% | 15.94 | | 2,048 | 1.30 | 149,504 | 38.0% | 18.61 | | 4,096 | 1.04 | 237,568 | 66.6% | 29.84 | **더 넓은 윈도는 속도를 사 주지 않습니다.** 256에서 1,024로 가면 윈도의 77%가 사라지는데 시간은 똑같이 15.9 ms입니다. 50% 보폭이면 윈도가 무엇이든 문서 토큰의 약 두 배가 모델을 통과하기 때문입니다 — 일의 양은 보존되고 모양만 바뀝니다. 1,024를 넘으면 좋아지는 대신 나빠집니다. 문서 중앙값이 1,034 토큰이므로 4,096에서는 순전파의 3분의 2가 패딩이고, 청구서는 거의 두 배가 됩니다. 위 품질 표와 나란히 놓으면 이 과제에 대한 답은 이것으로 끝납니다. 더 넓은 윈도는 정확도를 치르고, 1,024를 넘으면 시간까지 치릅니다. 둘 중 어느 것도 긴 컨텍스트 일반에 대한 주장이 아닙니다 — 262k 위치 디코더에게 1,000토큰짜리 뉴스 기사를 읽으라고 시켰을 때 벌어지는 일입니다. ![더 넓은 윈도가 치르는 값](benchmark/figures/19_window_cost.png) *이것은 2026-09-24에 동결된 61-arm 프로토콜 밖에서 수행한 백본 순전파 마이크로 벤치마크입니다.* 절대 밀리초 값은 벤치마크의 문서 p50이 아니며 — 헤드도, 토크나이즈도, 데이터 적재도 포함하지 않습니다 — 위의 표들과 비교하시면 안 됩니다. 측정의 내용은 윈도 크기 사이의 비율입니다. 스크립트와 결과: [`benchmark/cost_probe/`](benchmark/cost_probe/). --- ## 전용 런타임 모델과 함께, 그것만을 돌리기 위해 만든 런타임이 있습니다. 이 루트를 쓰는 데 필요한 것은 아닙니다 — 이 카드의 모든 것은 그것 없이 측정했습니다 — 하지만 이 작업의 나머지 절반이므로, 그것이 무엇인지 적습니다. ### 그것이 무엇인가 런타임은 **llama.cpp의 벤더링 복사본** ([ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp))이며, 준비 시점에 `prepare_runtime.py`가 고정된 패치 세트를 적용합니다. 모든 패치는 정확한 소스 문자열에 고정되어 있고 앵커가 움직였으면 준비 도구가 예외를 던지므로, 빌드는 정확히 재현되거나 요란하게 실패합니다. 트리는 업스트림 커밋에 고정되는 대신 1,389개 파일 번들 매니페스트에 파일별 SHA-256으로 벤더링되어 있으므로, 바이트 단위로 다시 빌드되지만 업스트림 리비전을 지명하지는 않습니다. 이것은 정확히 하나의 바이너리 `qwen35-classifier`를 빌드하며, 생성기에 필요한 모든 것은 꺼져 있습니다: 서버 없음, 예제 없음, 도구 없음, CUDA·BLAS·Metal·Vulkan·OpenMP 없음, 백엔드 로딩 없음. x86 타깃은 AVX2, FMA, F16C, BMI2, SSE4.2와 AVX-512 F/CD/VL/DQ/BW/**VNNI**를 요구합니다; AVX-VNNI만 있는 CPU는 지원되지 않습니다. **그중 실제로 받을 수 있는 것.** v3 트리 전체는 연구 저장소의 `deployment/` 디렉터리에 있고, 그것은 어디에도 공개되어 있지 않습니다. 따라서 이 카드에서 `deployment/`로 시작하는 경로는 링크가 아니라 어떤 것이 어디서 측정되었는지를 가리키는 표지로 받아들이십시오. 공개된 것은 쓸 수 있는 부분입니다: [`runtime/`](runtime/)의 완화된 두 소스, 그리고 [`docker-train/vendor/`](docker-train/vendor/) 안의, 패치된 트리를 완전히 빌드할 수 있는 복사본이며, 그 스택은 이것으로 자기 네이티브 바이너리를 컴파일합니다. ### 애초에 왜 별도의 런타임인가 분류기는 생성기가 아니며, 디코딩 런타임이 하는 일 대부분은 분류기에서는 낭비입니다. 샘플링 루프가 없고, 두 번째 토큰이 없고, 호출 사이에 이어갈 것이 없고, 마지막 은닉 상태의 한 행만이 헤드에 도달합니다. 여기서 네 가지가 따라오며, 이는 특화 레벨 2 이상 전 구간에서 성립합니다: - **KV 캐시를 할당하지 않습니다.** 어텐션 KV 할당은 **0 MiB**입니다. 윈도는 한 번 읽습니다. - **14레이블 헤드가 그래프 안에서 돌아갑니다.** 로짓은 C++ 쪽에서 나오며, 요청 경로에 Python이나 torch 왕복이 없습니다. - **가중치는 첫 호출 때 한 번 패킹되어** 부호 오프셋 레이아웃으로 들어가고 그대로 유지됩니다: 상주 **99,696,640 바이트**, 최대 입력 스크래치 **2,064,384 바이트**. - **유휴 상태는 공짜입니다.** 스레드 풀은 `poll=0`으로 생성되므로, 로드된 유휴 서비스는 **반 초 동안 CPU 틱 0회**를 기록합니다. ### 각 특화 레벨이 무엇을 켜는가 `Q35_SPECIALIZE_LEVEL`이 전체를 단계로 나누며, 0이 기본 경로이고 12가 x86 기본값입니다. 모든 단계는 실제 스위치이므로, 각각을 바로 아래 단계와 비교해 측정할 수 있습니다. | 레벨 | 무엇을 더하는가 | |---:|---| | 1 | 분류기의 `[tokens+3, 6144]` 입력을 위해 `concat`을 `ne2` 단위가 아니라 채널 단위로 분할 | | 2 | **특화된 꼬리**: `inp_out_ids`를 아예 만들지 않고, 마지막 블록은 최종 4개 토큰 행만 나르며, 헤드는 `ggml_get_rows` 대신 뷰로 한 행을 가져온다 | | 3 | 마지막 계층의 Q / gate / 출력 투영을 **쿼리 토큰 4개에 대해서만** 계산; K와 V는 윈도 전체를 유지하고, 인과 마스크는 그에 맞게 잘리며, RoPE 위치는 건드리지 않는다 | | 4 | 직접 작성한 4x4 Q8 GEMM | | 5 | 패킹된 Q8 커널 | | 6 | **v2 참조 경로**: 패킹된 가중치를 부호 오프셋 형태로 나른다 | | 7 | aarch64 NEON Q8 커널 (소스에는 있으나 여기서 빌드하거나 시험하지 않았다) | | 8 | 4의 배수뿐 아니라 임의의 열 수에서의 Q8 타일 | | 9 | 8 + 요청 사이에 잠드는 상주 스레드 풀 | | 10 | 9 + 감쇠, 델타, 갱신, 출력을 거쳐 행 단위로 뜨겁게 유지되는 재귀 상태 | | 11 | 10 + 4x8 ZMM Q8 커널. **더 느리게 측정되어 채택하지 않았다** | | 12 | 10 + 128폭 재귀 상태를 위한 전용 AVX-512 루틴 | ### 왜 정확한 상태로 남는가 이 중 어느 것도 근사가 아닙니다. 패킹 커널은 부호 없는 값에서 부호 있는 값으로의 시프트를 행렬별 보정 하나로 접어 넣고 `_mm512_dpbusd_epi32`를 사용하는데, 이는 참조 경로의 int32 레인 여덟 개와 부동소수점 누산 순서를 보존합니다. 꼬리 좁히기는 헤드가 결코 읽지 않음이 증명되는 행들을 버립니다. 재귀 부분 재작성은 모든 벡터 리덕션을 원래 순서대로 유지합니다. 중요한 확인: **문서 56개와 윈도 511개**에 걸쳐, 레벨 6 참조에 대한 레벨 12는 최대 절대 로짓 차이 **0.0**, 최대 절대 확률 차이 **0.0**, 윈도 단위·문서 단위 레이블 불일치 **0**을 냈습니다. 재로드, A/B/A, 스레드 수 1/3/4, 라이브 서비스 경로도 모두 일치했습니다. ### 특화가 무엇을 얻어 주는가 128k 모델에서 레벨 6에 대한 레벨 12 — 같은 바이너리, 같은 가중치, 같은 입력: | 윈도의 토큰 수 | 레벨 6 | 레벨 12 | 변화 | |---:|---:|---:|---:| | 33 | 15.76 ms | 12.29 ms | −22.1% | | 64 | 20.96 ms | 19.85 ms | −5.3% | | 129 | 50.84 ms | 37.44 ms | −26.3% | | 233 | 92.14 ms | 70.47 ms | −23.5% | | 256 | 77.33 ms | 73.36 ms | −5.1% | 작은 두 행은 토큰 수가 4폭 타일에 대해 나쁘게 놓이는 경우입니다; 256 토큰이 233보다 싸게 나오는 것은 같은 효과를 반대쪽에서 읽은 것입니다. ### 잘라낸 어휘를 받아들이게 만들기 출하된 상태로는 128,000이 아닌 어떤 어휘도 두 곳에서 거부했습니다: | 위치 | 검사 | 효과 | |---|---|---| | `qwen35-classifier.cpp:47` | `llama_vocab_n_tokens(...) != 128000` | "this runtime supports only Qwen3.5 4L / 128k / 14-label classification"를 던진다 | | `src/models/qwen35.cpp` | `GGML_ASSERT(n_layer == 4 && n_embd == 1024 && tok_embd->ne[1] == 128000)` | 레벨 >= 2에서 `sched_reserve()` 안에서 중단된다 | 둘 다 계산상의 의존이 아니라 모델 동일성 가드입니다. 특화된 꼬리는 `n_embd`, `n_cls_out`, `n_seqs`, `n_tokens`를 읽고 임베딩 행 수는 결코 읽지 않으며, 직접 작성한 Q8 커널은 hidden 1024에서 동작합니다. 그래서 이식 작업은 소스 트리의 **복사본**에서 어휘 동등성만 완화했습니다(`tok_embd->ne[1] == 128000`이 `> 0`이 된다); 아키텍처, 깊이, 폭은 여전히 강제되며, 고정된 v3 빌드의 바이너리 다섯 개는 모두 기록된 SHA-256과 여전히 일치합니다. 완화된 두 소스는 [`runtime/`](runtime/)에 있습니다. 이식 후, 세 절단 모두 완전 특화 상태로 로드되며 일반 경로와 **비트 단위로 동일한** 출력을 냅니다: | 절단 | 프로브의 토큰 수 | 레벨 0 | 레벨 12 | 변화 | |---|---:|---:|---:|---:| | v16k | 33 | 14.94 ms | 10.68 ms | −28.5% | | v32k | 25 | 11.47 ms | 7.36 ms | −35.8% | | v64k | 23 | 10.32 ms | 7.26 ms | −29.7% | ![런타임이 얻어 주는 것](benchmark/figures/07_runtime_specialization.png) ### 실행하기 [`docker/`](docker/)의 컨테이너가 가장 짧은 경로이고, [`docker-train/`](docker-train/)은 바로 이 패치된 트리에서 자기 네이티브 바이너리를 빌드합니다. 둘 다 이 카드 위쪽에서 설명합니다. ### 손을 대기 전에 - **분류 헤드가 있는 GGUF가 필요합니다.** 이 루트는 헤드가 없으므로, 먼저 헤드를 학습시키고 그 체크포인트를 변환하십시오. 런타임은 공개된 루트를 그대로 실행할 수 없습니다. - **이 이식은 개발용 이식입니다.** v3 검증 스크립트(`validate_runtime.py`, `validate_reload.py`, `validate_threads_service.py`)를 거치지 않았습니다; 위의 비트 단위 동일성은 그 스위트가 아니라 이식 자체의 패리티 검사입니다. - 실질적으로 **x86 AVX-512 VNNI 전용**입니다. NEON 경로는 소스에 있으나 빌드하거나 시험하지 않았고, aarch64 / A53 변종은 측정되지 않았습니다. - **여기의 시간 측정치는 단일 문장 네이티브 연산 중앙값**이며, 데스크톱 세션이 돌고 있는 호스트에서 잰 것이고, 벤치마크의 격리된 문서 단위 프로토콜이 아닙니다. 같은 문장이라도 절단마다 토큰 수가 다르므로, 행을 넘나들며 비교하지 말고 행 안에서 비교하십시오. - **이 카드의 다른 어떤 곳의 수치도 이 런타임에서 나오지 않았습니다.** 그것들은 모두 벤치마크 자체의 `profile_inference.py`에서 나왔습니다. --- ## 이것이 말해 주지 않는 것 - **단 한 번의 테스트 평가는 홀드아웃이 아닙니다.** 위 2026-09-23 실행은 v2 재분할을 사용했고, 이 프로젝트는 그 분할을 전에 사용한 적이 있으며, 동결된 사전등록 문서가 그 이전 노출을 기록하고 있습니다. 이 카드의 다른 모든 수치는 열어 본 56개 문서 캘리브레이션 분할이고, 이 저장소에는 `test.jsonl`이 없습니다. - **평가가 작습니다.** 문서 56개, 레이블 14개, 그리고 가장 드문 레이블은 그중 6개에 나타나므로, macro F1의 14분의 1이 표본 여섯 개에 걸려 있습니다. 이것이 0.005–0.039 시드 SD의 가장 유력한 원인입니다. - **시나리오는 우리 것이고, 그 수가 적습니다.** 2–6절의 수치는 한 상황 — 데스크톱 GPU에서의 영어 SemEval 파생 프로파간다 기법 검출 — 에서 나오고, 1절은 또 다른 한 상황에서 나옵니다. 이 루트는 그 둘 중 어느 쪽을 위해 만든 것도 아닙니다; 그것들은 압축을 지켜보기 위해 우리가 마련하게 된 상황일 뿐입니다. 한국어는 토큰화 동등성으로만 등장하며, 여기 어디에도 한국어 평가는 없습니다. - **시드 세 개는 실행 간 변동이며, 신뢰 구간이 아닙니다.** - **계층 맵은 레이블이 붙은 데이터에서 선택되었습니다.** 위에 공개했으며, 고치지는 않았습니다. - **품질 수치는 새 헤드 전이 체크포인트의 것이며**, 이 헤드 없는 루트의 것이 아닙니다. 헤드를 학습시키기 전까지 이 루트의 다운스트림 품질은 NR입니다. - 긴 컨텍스트, 캘리브레이션, 견고성, 공정성, 프로덕션 안전성은 확립되지 않았습니다. - 이것을 사실 검증기, 안전성 오라클, 자율 의사결정자로 사용하지 마십시오. ## 파일 | 경로 | 무엇인가 | |---|---| | root | 39,866항목 헤드 없는 백본 | | [`root_manifest.json`](root_manifest.json) | 그 어휘 규칙, 계층 맵, 해시, 왜 이 그리드 포인트인지 | | [`benchmark/full/`](benchmark/full/) | 61개 arm 표, 그리드, 동일 호스트 절단 | | [`benchmark/cost_probe/`](benchmark/cost_probe/) | 토크나이즈 비용과 윈도 비용 측정 스크립트와 결과 | | [`benchmark/figures/`](benchmark/figures/) | 위의 스물두 개 그림 | | [`runtime/`](runtime/) | 전용 런타임의 완화된 두 소스와 그 자신의 README | | [`docker/`](docker/) | 헤드를 얹은 백본을 돌리는 컨테이너, CPU 전용 | | [`docker-train/`](docker-train/) | 자기 데이터로 분류기를 학습시키고, 변환하고, 서빙 | | [`models/semeval-propaganda/`](models/semeval-propaganda/) | 별개의 계보이며, 이 루트의 자식이 아니다 | | [`distillation/`](distillation/) | 재사용 가능한 래더 플랫폼 | | `v128k_remap_manifest.json` | 대체된 128k 루트에 대해 살아남은 단 하나의 기록 | | `SHA256SUMS` | 이 저장소 모든 파일의 sha256 | ## 라이선스와 출처 표시 공개된 코드와 모델 산출물은 Apache-2.0이며, 업스트림 모델 및 데이터 약관을 따릅니다. Qwen3.5의 출처는 Qwen입니다. Transformers, PyTorch, Hugging Face Hub, WikiText, SemEval은 각 저작자의 작업물로 남습니다. WikiText나 SemEval 원본 레코드는 재배포하지 않습니다. WikiText 데이터셋 페이지에는 라이선스 문구 불일치가 있습니다 — 메타데이터는 CC BY-SA 3.0과 GFDL을 적고 있는데 본문은 CC BY-SA 4.0이라고 합니다. [`Salesforce/wikitext`](https://huggingface.co/datasets/Salesforce/wikitext)를 직접 확인하십시오.