1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要

生成AIの回答の確信具合を数値で評価したい。
OpenAI系のAPIのレスポンスは"logprobs"の設定で確信具合を取得できる。
今回はレスポンスからわかる分析データも一緒に表示するチャットシステムを以下の内容をベースに構築する。

logprobsについて

logprobs(log probabilities、対数確率)は、モデルが各トークン(単語や記号など)をどれくらいの確信で予測したかを数値的に示すもの。
Logprobs が高い(0 に近い)ほど、モデルはそのトークンを「自信を持って選んだ」といえる。
各トークンのlogprobsを集計して出力全体の確率を出す。

今回のコードでできること

チャットをすると右側に確信度の分析結果が一緒に出力される。
exp(合計 logprob)が今回の出力が生成される確率。ただし、これは小さくなりすぎるので、比較するなら平均確率で十分だと思われる。
image.png

動機

  • プロンプトの効果をlogprobsの値を元に分析・比較したい

ソースコード(Pythonファイル)

import gradio as gr
import requests
import json

import math

def chatgpt_chat(message, history, system_prompt):
    chatgpt_history = []

    if system_prompt and system_prompt.strip():
        chatgpt_history.append({"role": "system", "content": system_prompt})

    for row in history:
        chatgpt_history.append({"role": row["role"], "content": row["content"]})

    chatgpt_history.append({"role": "user", "content": message})

    try:
        url = "http://127.0.0.1:8000/v1/chat/completions"
        data = {
            "model": "openai/gpt-oss-20b",
            "messages": chatgpt_history,
            "logprobs": True,
            "top_logprobs": 5,
            "temperature": 0.7,
            "max_tokens": 512,
        }

        response = requests.post(
            url, headers={"Content-Type": "application/json"}, data=json.dumps(data)
        )

        if response.status_code == 200:
            res_json = response.json()
            print(json.dumps(res_json, ensure_ascii=False, indent=2))  # デバッグ出力

            answer = (
                res_json.get("choices", [{}])[0]
                .get("message", {})
                .get("content", "レスポンスにテキストがありません。")
            )

            # --- Log probability 情報 ---
            logprob_data = res_json.get("choices", [{}])[0].get("logprobs", {})

            if logprob_data and "content" in logprob_data:
                # 各トークンのlogprobを合計
                logprobs_list = [
                    t["logprob"]
                    for t in logprob_data["content"]
                    if "logprob" in t
                ]

                if logprobs_list:
                    total_logprob = sum(logprobs_list)
                    avg_logprob = total_logprob / len(logprobs_list)
                    final_prob = math.exp(total_logprob)  # 全体確率(かなり小さくなる)
                    avg_prob = math.exp(avg_logprob)      # 平均確率(1トークンあたり)

                    logprob_text = (
                        f"🧮 トークン数: {len(logprobs_list)}\n"
                        f"🔢 合計 logprob: {total_logprob:.4f}\n"
                        f"📉 平均 logprob: {avg_logprob:.4f}\n"
                        f"🎯 平均確率: {avg_prob:.6f}\n"
                        f"(exp(合計 logprob) = {final_prob:.6e}\n\n"
                        f"---\n詳細:\n{json.dumps(logprob_data, ensure_ascii=False, indent=2)}"
                    )
                else:
                    logprob_text = "logprob が含まれていません。"
            else:
                logprob_text = "(Log probabilities 情報はありません)"

        else:
            answer = f"Error: {response.status_code}"
            logprob_text = response.text

    except Exception as e:
        answer = "エラーが発生しました。"
        logprob_text = str(e)

    return answer, logprob_text

# --- UI構築 ---
with gr.Blocks() as demo:
    gr.Markdown("## 🤖 ChatGPT ローカルサーバー (/v1/chat/completions 対応)")

    system_prompt = gr.Textbox(
        label="System Prompt",
        value="あなたは親切なAIです。",
        interactive=True,
    )

    with gr.Row():
        with gr.Column(scale=3):
            chatbot = gr.Chatbot(label="💬 Chat", type="messages", height=500)
            user_input = gr.Textbox(label="メッセージを入力", placeholder="入力してEnter...")
            send_btn = gr.Button("送信", variant="primary")
        with gr.Column(scale=2):
            logprob_box = gr.Textbox(
                label="🧠 Log Probabilities",
                interactive=False,
                lines=25,
                placeholder="モデルの確率出力がここに表示されます。",
            )

    # メッセージ処理
    def process_message(message, history, system_prompt):
        answer, logprobs = chatgpt_chat(message, history, system_prompt)
        history = history + [{"role": "user", "content": message}, {"role": "assistant", "content": answer}]
        return history, "", logprobs

    # ボタンおよびEnterで実行
    send_btn.click(
        process_message,
        inputs=[user_input, chatbot, system_prompt],
        outputs=[chatbot, user_input, logprob_box],
    )
    user_input.submit(
        process_message,
        inputs=[user_input, chatbot, system_prompt],
        outputs=[chatbot, user_input, logprob_box],
    )

demo.launch(debug=True, share=False, inbrowser=False)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?