生成 AI は次にどの単語を選ぶ？ Gemini の logprobs 機能で確率を見てみよう！

# はじめに
生成 AI 特に大規模言語モデル ( LLM ) がテキストを生成するときは、文章を一気に作るのではなく、「次の単語」「その次の単語」というように、ステップごとに生成しています。
(その単語はトークンと呼ばれています。)
そして、そのトークンは確率によって選ばれます。
正確には、確率を対数（log）で表現したものとなっています。
Gemini API では、その情報を出力することが可能でしたので試してみました。

https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#logprobs


# Gemini の生成候補を見てみる
## ライブラリ
```bash
pip install google-genai
```

## 実行
Gemini の config の `GenerateContentConfig` に引数として指定します。

- response_logprobs
  - boolean
  - 各ステップでモデルによって選択されたトークンのログ確率を返す
- logprobs
  - int
  - 各生成ステップで、最上位の候補トークンのログ確率を返す

`logprobs`の説明として公式では以下となっています。
>各生成ステップで、最上位の候補トークンのログ確率を返します。
>モデルが選択したトークンとログ確率は、各ステップで常に返されますが、上位候補のリストに含まれない場合があります。
>1～5 の範囲の整数値を使用して、返される候補の数を指定します。

:::note info
モデルが選択したトークンとログ確率は、各ステップで常に返されますが、上位候補のリストに含まれない場合があります。

常に一番確率が高いものだけを選ぶと、文章が非常に単調で予測しやすいものになってしまいます。
なので、`temperature`、`topP`、`tooK`を調整することで創造性や多様性を出しているためだと思われます。
:::

https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference?hl=ja#logprobs


```python
from google import genai

client = genai.Client(
  vertexai=True, project=PROJECT_ID, location=LOCATION
)
response = client.models.generate_content(
  model="gemini-2.5-flash",
  contents='日本で一番高い山は？',
  config=genai.types.GenerateContentConfig(
      response_logprobs=True,
      logprobs=3
  )
)

print(response.model_dump_json(indent=2))
```

出力結果として、通常の回答も出力されます。
```json
"text": "日本で一番高い山は**富士山（ふじさん）**です。\n\n*   **標高:** 3,776メートル\n*   **場所:** 静岡県と山梨県にまたがっています。\n*   **特徴:** 日本の象徴的
```

そして、`logprobs_result`フィールドが追加されています。
ここでは選択されたトークンが出力されます。
```json
      "logprobs_result": {
        "chosen_candidates": [
          {
            "log_probability": -0.0006595747,
            "token": "日本",
            "token_id": 10956
          },
          {
            "log_probability": -0.0008520805,
            "token": "で",
            "token_id": 237007
          },
          {
            "log_probability": -0.0008368316,
            "token": "一番",
            "token_id": 58412
          },
          {
            "log_probability": -0.000024436236,
            "token": "高い",
            "token_id": 42459
          },
          {
            "log_probability": -0.00001251619,
            "token": "山",
            "token_id": 237845
          },
          {
            "log_probability": -0.00006818941,
            "token": "は",
            "token_id": 237048
          },
          {
            "log_probability": -1.3368748,
            "token": "**",
            "token_id": 1018
          },
          {
            "log_probability": -0.00011181257,
            "token": "富士",
            "token_id": 110581
          },
```

そして、`top_candidates`フィールドに候補のトークンが出力されています。
`logprobs`は 3 と指定したので、ステップ毎に3つの候補があります。
```json
        "top_candidates": [
          {
            "candidates": [
              {
                "log_probability": -0.0006595747,
                "token": "日本",
                "token_id": 10956
              },
              {
                "log_probability": -8.169859,
                "token": "日本の",
                "token_id": 76444
              },
              {
                "log_probability": -8.394783,
                "token": "はい",
                "token_id": 45621
              }
            ]
          },
          {
            "candidates": [
              {
                "log_probability": -0.0008520805,
                "token": "で",
                "token_id": 237007
              },
              {
                "log_probability": -7.1236033,
                "token": "（",
                "token_id": 237221
              },
              {
                "log_probability": -10.61703,
                "token": "(",
                "token_id": 236769
              }
            ]
          },
          {
            "candidates": [
              {
                "log_probability": -0.0008368316,
                "token": "一番",
                "token_id": 58412
              },
              {
                "log_probability": -7.4343524,
                "token": "**",
                "token_id": 1018
              },
              {
                "log_probability": -8.440848,
                "token": "最も",
                "token_id": 80428
              }
            ]
          },
          {
            "candidates": [
              {
                "log_probability": -0.000024436236,
                "token": "高い",
                "token_id": 42459
              },
              {
                "log_probability": -11.121072,
                "token": "標",
                "token_id": 239188
              },
              {
                "log_probability": -12.555941,
                "token": "（",
                "token_id": 237221
              }
            ]
          },

```

これを見ると、最初に「はい」と返事をする可能性もあることが分かります。
(生成 AI 使ってると、確かに最初に「はい」って言ってくる時もあるなと思いました)
そして、「日本で」の次として「一番」や「最も」が候補としてあり、似たような単語の意味が候補として挙がっているのが分かりました。


# まとめ
今回は、Gemini API の `logprobs` 機能を使って、LLM がテキストを生成する際の裏側、つまりトークンごとの選択候補とその確率を覗いてみました。
`response_logprobs` と `logprobs` パラメータを設定するだけで、AI が選んだ単語だけでなく、他にどんな選択肢をどのくらいの確率で検討していたのかを具体的に知ることができます。

これにより、
- AIの挙動の解釈
  - なぜこの出力になったのか、根拠を持って推測できる
- デバッグやチューニング
  - 予期しない出力の原因を探ったり、モデルの振る舞いを調整するヒントが得られる

といったメリットが得られるかなと思います。
AI の生成過程を深く理解することで、効果的に使いこなせるようになるのではないかと思いました！