evex-3.5

ある Discord サーバーの過去ログを主材料に、tokenizer からゼロで作った 18.9M パラメータの言語モデル。事前学習済みの重みは1つも使っていない。

evex-3(15.74M)から変えたのは 2 つだけ:

  1. 語彙を 4096 → 12288 にした
  2. 公開のなりきり掲示板で会話の土台を作ってから、このサーバーのログで仕上げた

同じ系統の兄弟 — evex-1 / evex-2(5.87M)/ evex-3(15.74M)。

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

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

  • 出力をその人が書いたものとして扱ってはいけない
  • 学習データは同意を明示的に取っていない会話。ログそのものと Discord の user ID・表示名は公開していない(speakers.json は順位と発言数だけ)
  • 外部データには話者トークンを一切使っていない。<|sN|> が指すのは このサーバーの人だけで、なりきり掲示板の投稿者ではない

二段で学習している

データ 目的
段1 公開のなりきり掲示板 78% + このサーバー 22% 会話の交代・話の受け方
段2 このサーバーだけ 口調と 147 人

混合比だけで「このサーバーらしさ」を守ろうとすると、外部が数倍あるぶん永久に 薄まる。最後がこのサーバーなら分布は戻り、土台の恩恵だけ取れる。

外部データには相対の役 <|a|>..<|h|> しか使わない。 「会話とはこう進むもの」だけを外から教え、誰が誰かはこのサーバーだけが持つ

二段は効いたのか(対照を回して確かめた)

同じコーパス・同じ語彙で、段2 をゼロからも回した。

段2 val (epoch 1) val (epoch 3) 噛み合い
段1 から 5.0638 4.9405 38.3%
ゼロから(対照) 5.6111 5.1017 30.0%

段1 からの方が全 epoch で上。しかも段1 からの epoch 1 が対照の epoch 3 より 良いので、土台があると 3 分の 1 の学習で追い越せる

効果(実測 / n=150)

このモデル自身の val を同じ尺度で測った値を地の値として並べる。

匿名の役 <|b|>(bot の既定経路)

evex-3 evex-3.5 地の値
噛み合い(質問の語が返答に出る) 37.3% 45.3%
20字以上で返す 55.3% 60.0% 31.2%
平均の長さ 30字 32字 19字
未知語 9.9% 10.0% 14.9%
逐語コピー(20字以上の完全一致) 0.0% 0.0%

名前持ち <|s0|>(なりきり)

evex-3 evex-3.5
噛み合い 24.0% 34.0%
20字以上 64.7% 48.7%
平均の長さ 41字 29字

両方の経路で噛み合いが大きく伸びた(+8pt / +10pt)。なりきり時は短くなったが、 噛み合いが 24% → 34% なので、中身の薄い長文が減って的を射た短文になった形。

なりきり掲示板の口調は漏れていない。地の文((微笑み のような括弧)を 含む返答は 0.7% で、このサーバー自身の val(0.9%)を下回る。 元データは本文の 74% が地の文だったが、ビルド時に落としてある。

逐語コピーの数え方について

20字以上の完全一致だけを数える。閾値なしで測ると (学習に1877回) それはそ(114回)のような誰でも言う相槌が「丸写し」として数えられ、 なりきりが 25.3% と出る。中身を見ると 14 件すべてが 10 字以下で、 21字以上の一致は 0 件だった。短い相槌を返しやすい経路ほど高く出るだけの 指標になっていたので、下限を入れている。

数字

パラメータ 18,880,896(埋め込み 4.72M + 8層 14.2M)
d_model 384 / 8層 / 6 head / context 1024 / RoPE + RMSNorm + SwiGLU + weight tying
語彙 12,288(ログから作った sentencepiece BPE)
学習 段1 3 epoch (lr 1e-3) → 段2 6 epoch (lr 3e-4 → 1.5e-4)
val loss 4.9299(記号を外した尺度)/ 素の val 5.0206
ハードウェア HF Jobs t4-small / 55,493 tok/s / 約60分

val は evex-3(4.0741)と比べられない。語彙が 3 倍になると 1 トークンあたりの 情報量が増えて loss は上がる。比べられるのは同じ語彙で回した対照との間だけ。

語彙 12288 を選んだ理由

語彙 日本語 英字 英字トークン docker compose up -d
4096(evex-3) 1.56字 1.94字 641 11 tok
12288 2.06字 2.62字 3,058 7 tok
16384 2.21字 3.08字 4,218 7 tok

evex-3 は「100回以上出る語 9,342 に対しマージ枠 3,670」で、英字は 2,663 語に対し トークン 641 しかなかった。出力に出ていた Cloberlop PHTHPAPAPA は、 この断片を組み立て損ねた形。

他の設定は全部いまの値が最善だった(総トークン数で比較): coverage 0.9995 は +0.8%、coverage 1.0 は +5.8%、split_digits は +2.3%、 unigram は +1.4% で、いずれも悪化。珍しい文字に3トークン払う方が、 頻出語のマージ枠を失うより安い。

コーパス

段1 23,267,670 トークン(外部 78%)
段2 19,280,662 トークン(このサーバーだけ)
話者 147人(200件以上 / 発言の 96.6%)+ 相対の役 8 + 溢れ 1

外部は OmniAICreator/Japanese-Roleplay-Dialogues(Apache-2.0)。 なりきりチャット掲示板 4,324 スレッド / 606,492 投稿。 中の人の連絡(OOC)と地の文を落とし、露骨な表現を含む投稿を除いてある。

日本語の Discord データは公開されていない。discord を名前に含む 116 の データセットに language: ja は 0 件で、92GB の多言語ダンプ 2,863 ギルドを 抽出調査しても日本語が 5% を超えるものは 0 件だった。

使い方

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.5")
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人の読み分け、会話の交代、話を受けて返すこと

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

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

ライセンス

重みは MIT。学習データのうち外部ぶんは Japanese-Roleplay-Dialogues(Apache-2.0)、 残りは非公開の私的な会話。上の「147人の話者が重みに入っている」を必ず読むこと。

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

Dataset used to train tako080614/evex-3.5