ChatGPT API検証 max_tokens について

# 概要
ChatGPTのAPIを利用時に`max_tokens`というオプション設定ができる.  
これについて解説・実験してみる.  

# 前提

 - ChatGPTをAPI経由で使用したことがある人
 - WebAPIをJSONで扱っている
 - トークンの量と文字量の違いが分かる人
 - トークンを数えるツールは[こちら](https://platform.openai.com/tokenizer)

## 前提知識(トークンについて)
トークンとは,ChatGPTに聞いてみた.

```
ChatGPTにおける「トークン」とは、
テキストを処理する際に使用される基本的なデータ単位です。
トークンは単語、句読点、スペース、特殊文字などを表すことができ、
テキストを機械が解析しやすい形に分割するために用いられます。

例えば、「こんにちは、世界！」という文は、
「こんにちは」「、」「世界」「！」というトークンに分割されます。
このトークン化により、ChatGPTはテキストを理解し、
適切な応答を生成することができます。
また、トークンは、ChatGPTが生成するテキストの長さを計算する際にも使用されます。
```

こう返ってきますが,単語で分割されているというのは嘘です.

基本的に英語ベースの場合の処理だと,綺麗に単語単位での分割になります.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/fed0f3cf-6678-a67c-6f25-915de7d91799.png)

日本語での「こんにちは、世界！」は以下のように分割され,「世界」という単語は2トークンで「世」,1トークンで「界」を表現します.
一部の漢字が1トークン扱いでない理由は,漢字単体に複数の読みや意味があり,文脈でそれらが変わるためとされています.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/48cd94e7-0bd8-bfd4-8e4c-2b0d3ab59073.png)

また,英語の場合でも,1単語が長い場合その限りではありません.
例として,医学用語「Pseudopseudohypoparathyroidism」は1トークンではありません.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/9e609ec8-01c1-43be-8dbb-e890453659ba.png)

また,生成系AIモデル(GPT-3やGPT-3.5など)毎にトークンの分け方が異なる場合や,アップデート,バックエンドシステムの改修で変更される場合がありますので,
**トークン数を厳密に制御することは困難**と考えて活用するのがいいかと思います.  

だいたいこれぐらいのトークン数だよね,という使い方が適切とおもわれます.

## 前提知識(max_tokensについて)
以下は,`max_tokens`についての解説になります.

https://qiita.com/flyaway2525/items/4549a8c9931854db5eb0#任意オプションmax_tokens

簡単に言うと,ChatGPTの回答に文字数(正確にはトークン数)制限を付ける機能です.
APIはこの文字数(トークン)で料金が変わります.
10文字ぐらいで答えてほしいのに,毎回10万文字で返答されて,その分料金が上がってしまったらユーザーもお怒りです.
今回は,`max_tokens`について検証します.

# 機能解説
`max_tokens`は,ChatGPTのレスポンスのトークン量を制限する機能になります.  

```JSON
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "太陽はなぜ明るいのか？"}
  ],
  "max_tokens": 800
}
```
例えば上記の入力に対して,下記のように返答があります.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/7ebaaaf1-d1b6-3bad-5460-71a9ec14003a.png)
返答のトークン数が,設定値以下になります.([tokenizerを使用](https://platform.openai.com/tokenizer))

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/afbf400b-3978-d7ce-59ee-21df7e02c707.png)

800トークン以内という制限は満たしていますが,説明をこのトークン数分までしてくれるわけではなさそうです.


# 実験 ① とても短く設定してみる.

`max_tokens`を少なく設定することで強制的に短い答えを作れるか検証します.
そのため,単語で答えられる質問をします.

入力をした質問 : 赤と青を混ぜたら何色ですか？
予想される回答 : 紫 (2トークン)

```JSON
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "赤と青を混ぜたら何色ですか？"}
  ],
  "max_tokens": 2
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/95fb786d-089a-3e3f-32b1-9659d105faa2.png)

いや,間違えとるがな
でもこれ,`赤と青を混ぜたら紫になります.`と言おうとしたら文章がブちぎれた可能性があるので,`max_token`を3にしてみます.

```JSON
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "赤と青を混ぜたら何色ですか？"}
  ],
  "max_tokens": 3
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/d6dd02ee-d3c4-6ada-7109-34b7756b3b2e.png)
どうやら予想は当たっていそうです.
ちょっとSeed値を固定して,1ずつ増やしてみます.

```JSON
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "user", "content": "赤と青を混ぜたら何色ですか？"}
  ],
  "seed" : 27 // 適当な数,固定しておくことで何度やっても回答が同じになる.
  "max_tokens": N // 増やしていく
}
```
N = 5の時 (青は2トークンなので,N=3とN=4の時は同じ)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/161abebf-d478-22ff-9e10-a0c41e773e9c.png)
N = 6の時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/e127cd05-5655-d029-cd48-2d23cae2ed3f.png)
N = 17の時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/9c199c2a-e189-105a-5c16-18349a52ad95.png)
N = 21の時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/988d579d-9e51-1916-3731-079084357a1a.png)

# 実験 ② とても長く設定してみる.
N = 10000の時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/adae0e81-a5c5-759e-6ecd-357c4661f29a.png)

上記より`max_tokens`に関わらず,文章を作ろうとしてます.  


# 結論

:::note info
**`max_tokens`
生成する文章の質に対しては影響を与えず,分量のみを制御する.**
:::

# 余談,返答文章の減らし方

上記の例のように,質問に対して端的にこたえてほしいものです.

なので注文に`簡単に答えて`などと加えます.

```JSON
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {"role": "system", "content": "一言で回答すること"},
    {"role": "user", "content": "赤と青を混ぜたら何色ですか？"}
  ],
  "seed" : 27 // 上記と同じ
  "max_tokens": 10
}
```
一言で回答してくれました.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1505183/a4accdb8-9416-4c16-7112-3eb07964f8a5.png)