妙高市の方言チャットボットVerUp:実験スクリプトを Google Colab で動かしてみる
実験スクリプトを Google Colab で動かしてみる
前回の記事では、フェーズ1 の実験スクリプト(`experiment.py`)を実行し、10 フレーズの翻訳結果とトークン数を確認しました。
今回は、そのスクリプトのコアロジックを Google Colab で動かしてみます。コードをセルに分割して少しずつ実行できるので、各関数が何をしているかを手を動かしながら確認するのにちょうどいい方法です。
ノートブックはこちらから開けます。
Google Colab で動かすメリット
ローカル環境でスクリプトを実行するには、仮想環境・依存パッケージ・`secrets.toml` の用意が必要でした。Colab を使うと、ブラウザだけで試せます。
ライブラリのインストールは `!pip install openai` 一行で済む
API キーは Colab のシークレット機能で安全に管理できる
セルを分割して実行できるので、動作を段階的に確認しやすい
ノートブックの構成
ノートブックは、`experiment.py` のロジックをセルに分解した構成になっています。
ライブラリのインポート
from openai import OpenAI`experiment.py` では `tomli` も使っていましたが、Colab では不要です。API キーの読み込み方法が変わるためです。
API キーを読み込む
Colab では `.streamlit/secrets.toml` が使えないので、Colab のシークレット機能(`userdata`)を使います。
from google.colab import userdata
API_KEY = userdata.get("OPENAI_API_KEY")左サイドバーの「シークレット」に `OPENAI_API_KEY` を登録しておけば、`userdata.get()` で取り出せます。`secrets.toml` と同じ役割を Colab の仕組みで果たしています。
ファイル名とモデルの定義
PHRASES_FILE = "experiment_phrases.txt"
OLD_DICT_FILE = "dialect_dict_old.txt"
NEW_DICT_FILE = "dialect_dict.txt"
RESULT_FILE = "experiment_result.md"
MODEL = "gpt-5.4-mini"
TRANSLATION_PROMPT_TEMPLATE = """\
あなたは、標準語を妙高市の方言に翻訳するシステムです。
入力された標準語テキストを妙高市の方言に翻訳してください。
翻訳結果のみを出力し、説明は不要です。
### 辞書 ###
{dict_content}"""ローカル環境では `Path(file).parent.parent` でパスを組み立てていましたが、Colab ではファイルをアップロードしてセッションのルートに置くので、ファイル名だけで参照できます。
Colab にファイルをアップロードする
ノートブックを実行する前に、次の 2 ファイルを Colab にアップロードします。
私の GitHub からダウンロードできますし、ご自身で作ってアップロードしても面白いと思います。
$$
\begin{array}{|l|l|}
\hline
\text{ファイル} & \text{説明} \\
\hline
\texttt{experiment\_phrases.txt} & \text{翻訳する標準語フレーズ集(10 フレーズ)} \\
\texttt{dialect\_dict.txt} & \text{方言辞書} \\
\hline
\end{array}
$$
アップロードの手順:
Colab の左サイドバーにあるフォルダアイコン(「ファイル」タブ)をクリック
「セッションストレージにアップロード」ボタンを押して、2 つのファイルを選択
アップロードしたファイルは `/content/` ディレクトリに置かれます。ファイル名だけで `open()` できるのは、Colab のカレントディレクトリが `/content/` になっているためです。
注意: Colab のセッションストレージはセッションを閉じると消えます。次回使うときは再アップロードが必要です。
フレーズ読み込み関数を段階的に確認する
`load_phrases()` の動作を、段階的に確認しました。
まず、ハイフン除去込みで正しく読み込まれているか確認します。
def load_phrases():
phrases = []
with open(PHRASES_FILE, encoding="utf-8") as f:
for line in f:
line = line.strip()
if line.startswith("- "):
phrases.append(line[2:])
elif line:
phrases.append(line)
return phrases
print(load_phrases())出力:
['こんにちは。今日はいい天気ですね。', 'そうですね。だいぶ暖かくなってきました。', ...]次に、ファイルをそのままプリントして元データを確認します。
def load_phrases():
with open(PHRASES_FILE, encoding="utf-8") as f:
for line in f:
line = line.strip()
print(line)
return
load_phrases()出力:
- こんにちは。今日はいい天気ですね。
- そうですね。だいぶ暖かくなってきました。
- 体調はどうですか?しばらく顔を見ませんでしたが。
...`with open(...) as f` の `f` の正体も確認してみました。
with open(PHRASES_FILE, encoding="utf-8") as f:
print(f)出力:
<_io.TextIOWrapper name='experiment_phrases.txt' mode='r' encoding='utf-8'>`f` はファイルオブジェクトと呼ばれる Python のオブジェクトで、ファイルの内容を読む窓口のようなものです。`for line in f:` と書くと、1行ずつ取り出せます。
辞書読み込み関数
def load_dict(path):
with open(path, encoding="utf-8") as f:
return f.read()辞書ファイルの先頭 200 文字を表示して内容を確認しました。
dict_content = load_dict(NEW_DICT_FILE)
print(dict_content[:200])出力:
標準語 => 方言
朝早いうちに => あさげ
朝早いうちに大事な仕事を終わらせませんか => あさげのうちに大事な仕事おやさんかね
あらしまった => あっさーさ
...翻訳関数を実行する
`run_experiment()` はノートブックで定義しています。引数として OpenAI クライアント・フレーズリスト・辞書文字列・ラベルを渡すと、全フレーズを順に翻訳してトークン数とともに返します。
client = OpenAI(api_key=API_KEY)
phrases = load_phrases()
dict_content = load_dict(NEW_DICT_FILE)
label = "辞書"
run_experiment(client, phrases, dict_content, label)実行結果:
[辞書] 1/10 入力:3539(キャッシュ:0) 出力:15
こんにちは。今日はいい天気ですね。
→ こんにちは。今日はええ天気だねや。
[辞書] 2/10 入力:3545(キャッシュ:3328) 出力:23
そうですね。だいぶ暖かくなってきました。
→ そうだねや。だいぶあったかくなってきたねや。
[辞書] 3/10 入力:3549(キャッシュ:3328) 出力:21
体調はどうですか?しばらく顔を見ませんでしたが。
→ なじょだね?しばらく顔みんかったけど。
[辞書] 4/10 入力:3566(キャッシュ:3328) 出力:38
気温が上がったり下がったりしたせいか、風邪をひいて寝込んでいました。だいぶ元気になりました。
→ 気温が上がったり下がったりしたせいか、風邪をひいてねぶってました。だいぶ元気になりました。
...
[辞書] 9/10 入力:3548(キャッシュ:3328) 出力:24
温泉に入って、ゆったりするのが大好きなんですよ。
→ 温泉入って、じょんのびすんの大好きなんだでね。
[辞書] 10/10 入力:3560(キャッシュ:3328) 出力:37
温かいお湯に浸かって、美味しいごちそうを食べるのは天国みたいですね。
→ あったけぇよに浸かって、うんめぇごっつぉ食うのは天国みてぇだねや。2 フレーズ目からキャッシュが効いているのは、前回のローカル実行と同じ結果です。
前回のローカル実行と翻訳内容を見比べると、一部のフレーズで結果が微妙に異なります。例えば 3 番は「しばらく顔めーねかったでも」→「しばらく顔みんかったけど」と変わっています。同じモデル・同じ辞書でも、毎回まったく同じ訳が出るとは限らないのです。これは生成 AI の性質上、自然なことです。
API レスポンス全体を観察する
ノートブックでは、API の返答オブジェクト全体を表示するセルも用意しました。`system_prompt` は `run_experiment()` 内と同様に、`TRANSLATION_PROMPT_TEMPLATE` を辞書で展開したものです。
import json
response = client.responses.create(
model=MODEL,
instructions=system_prompt,
input=phrases[1],
max_output_tokens=200,
)
print(json.dumps(response.model_dump(), indent=2, ensure_ascii=False))`response` は翻訳結果だけでなく、モデル名・トークン数・完了理由(`stop_reason`)・レスポンス ID など、たくさんの情報を持ったオブジェクトです。`model_dump()` で Python の辞書に変換し、`json.dumps()` で見やすく整形して表示しています。`response.output_text` や `response.usage.input_tokens` といった属性でアクセスしているデータがどこにあるのかが分かります。
まとめ
Google Colab でコアロジックを動かし、各関数の動作を段階的に確認しました。
`load_phrases()` ── ファイルを 1 行ずつ読み、ハイフンを除去してリストに格納する
`load_dict()` ── ファイル全体をひとつの文字列として読み込む
`run_experiment()` ── 辞書をシステムプロンプトに埋め込み、各フレーズを API に送って翻訳する
キャッシュは Colab でも同様に 2 フレーズ目から有効になる
次回はフェーズ2 に入ります。辞書(`dialect_dict.txt`)を新しい書式に書き直し、実験スクリプトを再実行します。旧辞書と新辞書の翻訳結果・トークン数を並べて比較できるようになります。
続く
開発中ブランチへのリンクです。
書いている人について
最後まで読んでいただき、ありがとうございます。
私のnoteの紹介記事です。
よろしければ、お読みいただければと思います。
この記事の連載マガジンです。
私の生まれ故郷の歴史
最後に
この記事が気に入ったら、スキをいただけると励みになります。
