はじめに
「このLINEのトークで話してる人、AIで再現できないかな?」と思ったことはありませんか?
実は、LINEのトーク履歴さえあれば、その人の話し方・口調を学習したAIを個人のGPU(RTX 3060などで十分!)で作れます。
この記事では、LINEのトーク履歴(.txtエクスポート)を使って Qwen2.5-3B-Instruct をQLoRAでファインチューニングし、特定の人物を模倣するAIを作る手順を、コードと一緒に丁寧に解説します。
動作環境
GPU: NVIDIA RTX 3060 12GB / CUDA 12.4 / Python 3.12 / Poetry
モデルは4bit量子化(QLoRA)のため、12GB VRAM でも動きます。
目次
- 全体の流れ
- 環境構築
- Step 1: チャットログのパース
- Step 2: 話者の選択
- Step 3: 学習コーパスの構築
- Step 4: QLoRAファインチューニング
- Step 5: 推論・対話
- ハマりポイントと対処法
- まとめ
全体の流れ
パイプラインは以下の5ステップで、各ステップが独立したPythonファイルになっています。
LINE .txt → parse_chatlog.py → select_speaker.py → build_corpus.py → train.py → inference.py
| ファイル | 役割 | 入力 | 出力 |
|---|---|---|---|
parse_chatlog.py |
LINEログをパース | .txt(LINEエクスポート) | list[Message] |
select_speaker.py |
話者を対話的に選択 | パース済みメッセージ | 話者名 + 統合メッセージ |
build_corpus.py |
JSONL学習データを生成 | メッセージ一覧・話者名 | corpus/*.jsonl |
train.py |
QLoRAでファインチューニング | .jsonl | LoRAアダプタ |
inference.py |
対話型CLI | LoRAアダプタ | ターミナル上の会話 |
環境構築
ディレクトリ構成
/
├── chatlog/ # LINEの .txt を置く
├── corpus/ # 生成されるJSONL(自動作成)
├── models/ # 学習後のLoRAアダプタ(自動作成)
├── parse_chatlog.py
├── select_speaker.py
├── build_corpus.py
├── train.py
├── inference.py
└── pyproject.toml
Poetryで仮想環境を作る
# Poetry がなければまずインストール
pip install poetry
# 依存パッケージをインストール
poetry install
# CUDAありのPyTorchを別途インストール(cu124 の場合)
poetry run pip install torch --index-url https://download.pytorch.org/whl/cu124
なぜ PyTorch だけ別インストールなの?
HuggingFace等のライブラリはPyPI経由でインストールできますが、PyTorch のCUDAビルドは download.pytorch.org/whl/cu124 というカスタムインデックスにあります。pyproject.toml の [[tool.poetry.source]] に設定してありますが、念のため上記コマンドでも明示インストールするのが確実です。
CUDA の確認
poetry run python -c "import torch; print(torch.cuda.is_available(), torch.version.cuda)"
# True 12.4 と表示されればOK
Step 1: チャットログのパース(parse_chatlog.py)
LINEのエクスポート形式について
LINEのトーク履歴は「設定 → トーク → トーク履歴をバックアップ」からテキスト(.txt)でエクスポートできます。形式はiOS/Android・言語設定によって2種類あります。
| 形式 | サンプル | 区切り文字 |
|---|---|---|
| 日本語 (A) | 23:47 Aさん わ! |
スペース |
| 英語 (B) | 20:07\tAさん\t"去年の春とった!" |
タブ |
parse_chatlog.py は冒頭数行を見てフォーマットを自動判定します。どちらの形式でも同じ関数 parse_file() を使えばOKです。
スキップするメッセージ
スタンプ・写真・動画・通話履歴などはAIの学習に使えないため自動スキップされます。
SKIP_KEYWORDS = [
"スタンプ", "写真", "動画", "通話",
"[Sticker]", "[Photo]", "[Video]", ...
]
動作確認
poetry run python parse_chatlog.py chatlog/トーク履歴.txt
# 出力例
フォーマット: 日本語
話者: ['Aさん', '友人B']
メッセージ数: 3842
[2025.12.30 火曜日] 23:47 Aさん: わ!
[2025.12.30 火曜日] 23:47 Aさん: 見ます!
...
複数行メッセージの扱い
LINEでは改行を含むメッセージを送れます。エクスポートすると後続行にタイムスタンプがつきません。タイムスタンプのない行は「継続行」として直前のメッセージに自動で連結されます。
Step 2: 話者の選択(select_speaker.py)
複数人のトーク履歴から、模倣したい人物を対話的に選びます。複数ファイルにまたがる同一人物の発言を収集する機能も持っています。
poetry run python select_speaker.py
=== チャットログ一覧 ===
[0] friend_a.txt
[1] friend_b.txt
使用するログの番号を入力してください: 0
=== 話者一覧 ===
[0] Aさん
[1] 友人B
模倣したい話者の番号を入力してください: 0
話者「Aさん」を選択しました(発言数: 1203件)
他のチャットログから「Aさん」を追加で収集できます。
--- friend_b.txt ---
このファイルから「Aさん」を抽出しますか? [y/n]: n
別名での登録に対応
同一人物でもファイルによって表示名が異なることがあります(例: 「Aさん(フルネーム)」と「Aさん」)。同名が見つからない場合は別名として手動マッピングでき、speaker フィールドが正規化されます。
Step 3: 学習コーパスの構築(build_corpus.py)
出力形式(JSONL)
OpenAIやHuggingFaceの学習フォーマットに合わせた Chat Messages 形式 で出力します。
{
"messages": [
{"role": "system", "content": "あなたはAさんです。..."},
{"role": "user", "content": "えー!それはひどい笑"},
{"role": "assistant", "content": "ほんとにね笑笑"}
]
}
セッション分割とは?
「朝9時のAさんの発言」と「夜10時のBさんの発言」は無関係なのに、そのまま使うと「朝のAさんの発言への返答が夜のBさんのメッセージ」という意味のないペアが学習データになってしまいます。
そこで、指定した時間ギャップ(デフォルト60分)以上空いたら別の会話(セッション)として分割してからサンプルを生成します。
# デフォルト設定で実行(対話的に選択)
poetry run python build_corpus.py
# ファイルと話者を直接指定する場合
poetry run python build_corpus.py \
--chatlog chatlog/friend_a.txt \
--speaker 友人A \
--context-turns 3 \
--max-gap-minutes 60
主なオプション
| オプション | 説明 | デフォルト |
|---|---|---|
--context-turns |
返答直前に含める会話ターン数 | 3 |
--max-gap-minutes |
セッション分割の閾値(分) | 60 |
--output |
出力JSONLのパス | corpus/<話者名>_corpus.jsonl |
Step 4: QLoRAファインチューニング(train.py)
QLoRAとは?
QLoRAは4bit量子化 + LoRAの組み合わせで、大規模LLM(3Bパラメータ)を民生用GPU(12GB VRAM)でファインチューニングする手法です。
- 4bit NF4量子化でモデルのメモリ使用量を約75%削減
- **LoRA(Low-Rank Adaptation)**でAttentionレイヤの差分だけを学習
- 学習するパラメータ数は全体の約0.5〜2%程度なのでVRAMに優しい
LoRAの設定
LoraConfig(
r=16, # LoRAランク(大きいほど表現力↑、メモリ↑)
lora_alpha=32, # スケーリング係数(通常 r の2倍)
lora_dropout=0.05,
target_modules=[ # Qwen2.5の全Attentionレイヤ + FFN
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
)
学習の実行
poetry run python train.py \
--corpus corpus/Aさん_corpus.jsonl \
--epochs 3 \
--batch-size 2 \
--grad-accum 8
実効バッチサイズは batch_size × grad_accum = 16 です。VRAM不足の場合は --batch-size 1 にしてみてください。
サンプル数が少ない場合はオーバーフィットに注意
サンプルが数十件しかない場合、エポックを増やしすぎるとモデルが学習データをそのまま暗記してしまいます。まず3エポックで試して、応答が自然かどうか確認しましょう。
Step 5: 推論・対話(inference.py)
学習が終わったら、対話型CLIで実際に話しかけてみましょう。
poetry run python inference.py \
--model-dir models/ \
--speaker Aさん
モデルを読み込み中: models/
ベースモデルを自動検出しました: Qwen/Qwen2.5-3B-Instruct
--- Aさんとの会話を開始します(終了: 'exit' または Ctrl+C)---
あなた: 最近どう?
Aさん: まあまあかな〜 なんか眠いけど笑
あなた: 何かあった?
Aさん: いや別に!ただの寝不足
推論パラメータ
| パラメータ | デフォルト | 説明 |
|---|---|---|
temperature |
0.8 | 高いほどランダムな応答になる |
top_p |
0.9 | 確率上位 90% のトークンからサンプリング |
repetition_penalty |
1.1 | 同じ表現の繰り返しを抑制 |
--max-history |
6 | 保持する会話ターン数(VRAMを節約) |
ハマりポイントと対処法
CUDA が認識されない
# ドライババージョンとCUDAのバージョンを確認
nvidia-smi
poetry run python -c "import torch; print(torch.version.cuda)"
torch.cuda.is_available() が False の場合、PyTorchのインデックスURLが合っていないことが多いです。CUDA 12.4なら --index-url https://download.pytorch.org/whl/cu124 を使ってください。
VRAM 不足(OOM)
以下を試してください。
-
--batch-size 1に下げる -
--max-seq-length 512に短くする -
--grad-accum 16に増やして実効バッチサイズを保つ
LINEのエクスポートファイルが文字化けする
LINEのエクスポートはUTF-8です。Windowsのメモ帳などで開くとBOM付きになることがあります。コードは encoding="utf-8" で読んでいるので、BOM付きの場合は encoding="utf-8-sig" に変更してください。
継続行が正しく結合されない
LINEの英語エクスポート形式(B)では、改行を含むメッセージが "..." で囲まれます。このパーサはその形式に対応していますが、まれに引用符が崩れているファイルがあります。その場合は手動で確認してください。
まとめ
LINEのトーク履歴から「その人っぽいAI」を作るパイプラインを解説しました。やってみてわかった重要なポイントをまとめます。
- データ量: 数百サンプル以上あると自然な応答になりやすい。少なすぎるとオーバーフィットしやすい。
- セッション分割: 時間的に離れた発言を無関係につなげないことがコーパスの品質に直結する。
- QLoRA: RTX 3060 12GB でも7Bモデルの学習が可能。量子化による品質低下は会話タスクでは許容範囲内。
- システムプロンプト: ファインチューニング後の口調再現の主役は LoRAの重み であり、システムプロンプトではない。全サンプルに同一のプロンプトが入っているため、モデルはプロンプトに「従う」のではなく、そのフォーマットごと話者のスタイルを学習する。システムプロンプトは学習時と同じフォーマットを推論時に再現するために必要であり、文章の内容を変えても応答がほぼ変わらないことが多い。「システムプロンプトが口調を制御している」という感覚はベースモデル(ファインチューニング前)での話に近い。
今後の改善アイデア
- 絵文字・顔文字の扱いを改善する
- グループトークにも対応する
- 複数話者を同時に学習して、話し相手によって口調を切り替える
- Mergekit で LoRA をベースモデルにマージして推論を高速化する
- Gradio で Web UI を作る