evex-ft-2
Qwen3-0.6B-Base に、ある Discord サーバーの過去ログを追加学習したもの。
evex-ft-1 の第2世代で、
ベースも
学習方法も同じ。変えたのはコーパスだけ。
ゼロから学習した兄弟もある —
evex-1 /
evex-2 (5.87M / tokenizer もログから作った)。
⚠️ 表示名が重みに入っている
話者ラベルが実在の人の表示名そのものです (あかり: にょ: たこ:)。
つまりこのモデルは特定の人になりきって書けます。
- 出力をその人が書いたものとして扱ってはいけない
- 学習データは同意を明示的に取っていない会話。ログそのものと Discord の
user ID は公開していない
- 使える 147 個のラベルは
speakers.json にある (表示名と発言数だけ)
ft-1 の何を直したか
ft-1 は口調は移ったが話を受けて返さなかった。
A: git rebase と merge の違いって何
B: まじ?
原因はコーパスの分布。598,908 発言のうち 45.3% が10字以下 / 76.6% が20字以下で、
「聞かれたら答える」信号がほぼ無い。忘却ではない — 学習に入っていない普通の
日本語の perplexity は base の 1.12倍で、窓を 4096 に伸ばしても変わらない。
その形を選ぶ確率が低いだけだった。
sft-v5 で2つ足した。外部データは使っていない (混ぜると「借り物の口調」が入る)。
| 件数 | train に占める割合 |
|---|
| 噛み合った箇所 | 23,111 | 12.3% |
| 長い発言 | 32,593 | 25.6% |
- 噛み合った箇所 … 直前が疑問で別人が答えた / 直前の人に
@名前 で宛てた、
応答が12字以上のもの。直前2行から切り出す
- 長い発言 … 40字以上の発言を直前の文脈つきで切り出す
長い発言の 91% は名前を持つ人のものなので、ここを重くすると「長く喋る」と
「なりきり度」が同時に上がる。トレードオフにならない。
切り出しの一部は話者を匿名の役 (A/B/C) に付け替えてある (噛み合いは半分、
長い発言は4本に1本)。bot が /as 未指定のとき匿名の役で喋るのに、
「疑問→20字以上の答え」の 88.1% が名前持ちで、既定の経路に信号が届いていなかった。
効果 (実測 / 匿名の役 B: / n=50)
同じプロンプト・同じ乱数で、このサーバー自身の val を同じ尺度で測った値を
地の値として並べる。
| evex-ft-1 | evex-ft-2 | 地の値 |
|---|
| 噛み合い (質問の語が返答に出る) | 20.0% | 34.0% | — |
| 平均の長さ | 32字 | 49字 | 17字 |
| 敬体で終わる (借り物の口調) | 5.0% | 0.0% | 3.0% |
| 未知語 (コーパスに無い語) | 14.3% | 13.8% | 14.8% |
| 逐語コピー | — | 0.0% | — |
名前を指定した場合 (あかり:) は噛み合い 55% で ft-1 と同じ、平均 24→30字、
敬体は 20% → 10% に下がった。
epoch の選び方 — val では決まらない
epoch 1 val 2.5980
epoch 2 val 2.6079 ← こちらを推奨
val は epoch 1 の方が良く、ft-1 (2.5415) より両方悪い。val 集合は完全に同じ
なので比較できるが、切り出しが train の 38% を占めて分布がずれたため。
val は「口調が移ったか」しか測っていない。
生成で測ると逆になる (匿名の役 / n=50):
| epoch 1 | epoch 2 |
|---|
| 噛み合い | 42.0% | 34.0% |
| 敬体 | 18.0% | 0.0% |
| 未知語 | 8.5% | 13.8% |
| 逐語コピー | 4.0% | 0.0% |
epoch 2 は借り物の口調がゼロ、語彙の手触りが本物とほぼ同じ、逐語コピーもゼロ。
噛み合いだけ 8pt 負けるが誤差寄り (17 vs 21 / 50)。
使い方
素の transformers で動く。独自トークンは1つも足していない。
1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3
4path = "tako080614/evex-ft-2"
5tok = AutoTokenizer.from_pretrained(path, subfolder="epoch-2")
6model = AutoModelForCausalLM.from_pretrained(path, subfolder="epoch-2", dtype=torch.float32)
7model.eval()
8
9prompt = "#ch2\nA: git rebase と merge の違いって何\nB:"
10
11ids = tok(prompt, return_tensors="pt")
12out = model.generate(**ids, max_new_tokens=80, do_sample=True, temperature=0.9,
13 top_k=40, min_p=0.05, repetition_penalty=1.1,
14 pad_token_id=tok.eos_token_id)
15print(tok.decode(out[0], skip_special_tokens=True)[len(prompt):])
プロンプトの形
#ch2 ← #ch0..#ch15 か #other (上位16で全体の 97%)
たこ: これバグってる? ← 147人は表示名 (speakers.json)
A: どこで止まってる? ← それ以外は A..H / Z の相対の役
あかり: ← 末尾に置いた人として続きを書く
同じ人が続けて喋ることがある (学習データの話者の塊のうち 27.4% が2連続以上)。
素の日本語形式には終端トークンが無いので、放っておくと max_new_tokens を使い切る。
次の 名前: で切ること。
[画像] [動画] などは添付だけの発言で、返答としては出さないよう損失から外してある。
数字
| |
|---|
| ベース | Qwen/Qwen3-0.6B-Base (596,049,920 params) |
| 学習トークン | 15.85M (ft-1 は 10.09M) / 73,782 会話 |
| 窓 | 1024 (会話を EOS で継いで詰める / パディングしない) |
| 学習 | 2 epoch / lr 5e-5 / cosine / AdamW 8bit / fp16 + fp32 master |
| ハードウェア | Kaggle T4 / 峰 12.8GB / 約 6.8 時間 |
できること / できないこと
できる: そのサーバーの口調、147人の読み分け、話を受けて返すこと、
名前を持たない人の口調 (例を渡せば)
できない: 事実、計算、翻訳、指示に従うこと
内容は当てにならない。 これは ft-1 から変わっていないし、変えられない —
Qwen3-0.6B は instruct 版ですら「日本の首都」を大阪と答える。0.6B で狙えるのは
「話を受けて、それらしい長さで、このサーバーの口調で返す」まで。
ライセンス
ベースは Qwen3-0.6B-Base (Apache-2.0) の派生。学習データは非公開の私的な会話なので、
再配布・商用利用は想定していない。上の「表示名が重みに入っている」を必ず読むこと。