evex-3

ある Discord サーバーの過去ログ 94万件だけで、tokenizer からゼロで作った 15.74M パラメータの言語モデル。事前学習済みの重みは1つも使っていない。

同じ系統の兄弟 — evex-1 / evex-2(どちらも 5.87M)。 Qwen3-0.6B に同じログを追加学習した別系統もある (evex-ft-1 / ft-2 / ft-3)。

⚠ 147人の話者が重みに入っている

<|s0|> から <|s146|>実在の人ひとりひとりに対応する。 つまりこのモデルは特定の人になりきって書ける

  • 出力をその人が書いたものとして扱ってはいけない
  • 学習データは同意を明示的に取っていない会話。ログそのものと Discord の user ID・表示名は公開していない(speakers.json は順位と発言数だけ)
  • evex-1 は 48人だった。147人に増やしたので、なりきれる範囲が広がっている

evex-2 から変えたこと

evex-2 のコーパスには、evex-ft-2 / ft-3 で測って効いた工夫がひとつも入っていなかった。 あちらは全部コーパス側の話なので、記号に翻訳してそのまま持ち込んだ。

evex-2 evex-3
話者 48人(発言の 85.3%) 147人(96.6%)
返信 <|re|> の真偽だけ(相手を捨てていた <|re|><|sM|> で相手も
15分 / 20件 / 1200字 60分 / 60件 / 3600字 × 3通りの切り方
噛み合いの重み付け なし train の 12.5%
長い発言の重み付け なし train の 24.5%
context 512 1024
学習トークン 6,479,221 23,950,617(3.70倍)
パラメータ 5,865,984 15,735,168

返信先を残したのが一番大きい。 113,001 件の返信のうち 94,729 件で相手が分かる。 賑やかなチャンネルでは「誰に答えたか」が噛み合いの信号そのもので、evex-2 は そこを捨てていた。

窓の切り方を3通りにして 2.08 倍に増やしてある。新しい情報は増えない (同じ発言が違う位置・違う文脈長で出るだけ)ので、1 epoch が evex-2 の約2 epoch にあたる。開始位置をずらす方式は効かなかった(36,647 窓のうち 35,858 が同じ本文。 窓は主に沈黙時間で切れるので、読み始める位置を変えても境界が動かない)。

効果と代償(実測)

同じコーパスで 5.87M も回してある(evex-2 と同じ形)。良くなった分がコーパス由来か 大きさ由来かを分けるため。このモデル自身の val を同じ尺度で測った値を地の値にする。

匿名の役 <|b|>(bot の既定経路 / n=60)

対照 5.87M evex-3 15.74M 地の値
噛み合い(質問の語が返答に出る) 38.3% 41.7%
20字以上で返す 65.0% 58.3% 31.2%
平均の長さ 39字 29字 19字
未知語 19.6% 9.8% 14.9%
逐語コピー 3.3% 10.0%
敬体 3.3% 5.0% 3.0%

名前持ち <|s0|>(n=60): 噛み合い 23.3% / 20字以上 53.3% / 平均 35字 / 未知語 6.6% / 逐語コピー 15.0%

**記号だけの返答は 0%**(evex-2 と同じ。<url><file> を損失から外してある)。

実際に出てくるもの(samples.txt に epoch 5 のぶんが入っている):

<|s0|>rebase 疲れた<|s3|> → <|re|><|s5|>codexのレートリミット来たから
                             codexに課金してclaude codeのレートリミット来たから
<|a|>これバグってる?<|a|> → いやこれ普通にtry catchするからtry catchでエラー起きそうで
<|a|>TCP と UDP どっちが速いん<|b|> → TCPとかSPAの関係で外部環境を動かそうとしたら
                                      Chromebookが起動しなくて良いよ...

返信先を自分で書く。<|re|><|s5|> のように「誰に返しているか」を出力に含める。 読む側は <|re|> と続く話者トークンを落としてから本文を取ること (相手のトークンを「次の話者」と読むと本文が丸ごと落ちる)。

大きくした効果はほとんど無かった

噛み合いは +3.4pt だが、逐語コピーが 3 倍になった。24M トークンに対する Chinchilla 最適は 1.21M パラメータなので、5.87M で既に 5 倍・15.74M で 13 倍過剰。 効いたのはコーパスであってサイズではない。

参考: 同じ質問・同じ匿名の役で Qwen3-0.6B 追加学習の系統は ft-1 20% / ft-2 34% / ft-3 30%。 38倍小さいこのモデルの方が噛み合っている(ただし事実の正しさは別の話で、 どちらも当てにならない)。

数字

パラメータ 15,735,168(埋め込み 1.57M + 8層)
d_model 384 / 8層 / 6 head / context 1024 / RoPE + RMSNorm + SwiGLU + weight tying
語彙 4,096(ログから作った sentencepiece BPE)
学習トークン 23,950,617 / 145,202 会話
学習 5 epoch / lr 1e-3 / cosine / AdamW / fp16 + fp32 master
val loss 4.0741(記号を外した尺度)/ 素の val 4.1557
ハードウェア Kaggle T4 / 54,556 tok/s / 約 37 分

epoch ごとの val: 4.4115 → 4.1712 → 4.0927 → 4.0769 → 4.0741。 epoch 3 は逐語コピーが 11.7% と低いが、既定経路の噛み合いが 31.7% しか無いので epoch 5 を採った。

val は evex-1 / evex-2 と直接比べられない。コーパスも tokenizer も違う。 比べられるのは同じ v4 で回した 5.87M との間だけ。

コーパス v4

外部データは使っていない。全部このサーバーのログ。

読んだ発言 945,009(うち bot 357,376 を除外)
会話 18,806 → 窓の切り方3通りで 38,106(同じ本文 17,373 は落とした)
噛み合った箇所 23,414 を ×2
長い発言(40字以上) 30,134 を ×2
語彙 4,096(制御12 + 話者147 + 役8 + バイト等259 / 2.29 文字per token)
val 最後の14日 436 会話。train と本文が同じものは外してある

切り出しの割合は evex-ft-2 / ft-3 で測って効いた値(12.3% / 25.6%)に合わせて 周回数を自動で決めている。多すぎると窓の中身が3〜4行の短い会話ばかりになる。

使い方

transformers では動かない。素の PyTorch で読む。

import json, torch, sentencepiece as spm
from safetensors.torch import load_file
from huggingface_hub import snapshot_download

path = snapshot_download("tako080614/evex-3")
cfg = json.load(open(f"{path}/config.json"))
sp = spm.SentencePieceProcessor(model_file=f"{path}/tok.model")

state = load_file(f"{path}/model.safetensors")
state["head.weight"] = state["embed.weight"]      # tie_word_embeddings

# model.py は https://github.com/tako0614/sakana の scripts/llm/model.py
from model import Config, MicroLM
model = MicroLM(Config(**{k: cfg[k] for k in
    ["vocab_size", "n_layers", "d_model", "n_heads", "context", "dropout", "attn_dropout"]}))
model.load_state_dict(state)
model.eval()

prompt = "<|conv|><|a|>これバグってる?<|s0|>"
ids = torch.tensor([sp.encode(prompt, out_type=int)])
out = model.generate(ids, max_new_tokens=80, temperature=0.9, top_k=40,
                     stop_id=sp.piece_to_id("<|end|>"))
print(sp.decode(out[0].tolist())[len(prompt):])

プロンプトの形

<|conv|><|s0|>これバグってる?<|s3|><|re|><|s0|>どこで止まってる?<|s0|>
         ↑147人の誰か        ↑別の人  ↑<|s0|> への返信          ↑ここの続きを書かせる
  • 話者は <|s0|>..<|s146|>(固有)と <|a|>..<|h|> / <|z|>(会話ごとの相対)
  • 名前を持たない人だけに相対の役を振る。混ぜ方を学習と変えてはいけない
  • 返信は <|re|> のあとに相手のトークンを置く。相手が会話に居ないときは <|re|> だけにする(学習でもそうしてある)
  • <|end|> で会話が終わる。<url> <file> <mention> <channel> <time> は 正規化が作った記号で、損失から外してあるので自分では書かない

できること / できないこと

できる: このサーバーの口調、147人の読み分け、会話の交代

できない: 事実、計算、翻訳、指示に従うこと

15.74M しか無いので内容は当てにならない。狙えるのは「話を受けて、それらしい形で、 このサーバーの口調で返す」まで。

ライセンス

重みは MIT。学習データは非公開の私的な会話なので、上の「147人の話者が重みに 入っている」を必ず読むこと。

Downloads last month
122
Safetensors
Model size
15.7M params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support