ChatGPTで用いられるGPT-3.5系のモデルをAPIから利用してみた

## はじめに

こちらは「[Applibot Advent Calendar 2022](https://qiita.com/advent-calendar/2022/applibot)」4日目の記事になります。

2022年11月30日に、OpenAIが対話型AIモデル「ChatGPT」をリリースしました。こちらは[OpenAIのChatページ](https://chat.openai.com/chat) (要アカウント登録&ログイン)から利用でき、AIと対話形式で様々な会話の推論を非常に高い精度で行うことができるものになります。

このモデルはGPT3~GPT4の間に当たるとしてGPT-3.5系のモデルと呼ばれており、「text-davinci-003」というモデルがAPIから利用可能なモデルとしてほぼ同時期に追加されたため、今回そちらのモデルを使って簡単なコマンドライン上で動く対話ツールを実装してみました。

## 準備

### API_KEYの発行

APIを叩くためには鍵が必要なため、OpenAIのサイトからアカウントの発行と、鍵の発行を行う必要があります。

1. OpenAIのページのSignUpからアカウント登録を行います。

https://openai.com/api/

2. 下記ページの`Create new secret key` ボタンを実行し、シークレットキーを発行します。

https://beta.openai.com/account/api-keys

### インストール

本実装ではopenaiパッケージを利用してapiを叩きます。
下記コマンドを実行し、openaiパッケージをインストールします。

```bash
pip install openai
```

## 実装

### コード

指定した文言をopenaiの対話モデル「text-davinci-003」に対してAPIを投げるプログラムが下記になります。

```python:chat.py
import os, argparse
import openai

HISTORY_PATH='history.log'
openai.api_key = 'OPENAI_API_KEY'

parser = argparse.ArgumentParser(description='会話内容を入力してください')
parser.add_argument('input')
parser.add_argument('-r', '--reset', action='store_true',help='会話履歴(history.log)を初期化して実行する')
args = parser.parse_args()

history=''
# リセットフラグが立っていない&ファイルが存在する場合履歴を読み込む
if os.path.isfile(HISTORY_PATH) and not args.reset:
  with open(HISTORY_PATH, 'r') as f:
    history = f.read()

print(args.input)
# 会話として推論するため、会話ログを付け足してリクエストする
prompt = history + args.input

response = openai.Completion.create(
  model='text-davinci-003',
  prompt=prompt,
  temperature=0, # ランダム性の制御[0-1]
  max_tokens=1000, # 返ってくるレスポンストークンの最大数
  top_p=1.0, # 多様性の制御[0-1]
  frequency_penalty=0.0, # 周波数制御[0-2]：高いと同じ話題を繰り返さなくなる
  presence_penalty=0.0 # 新規トピック制御[0-2]：高いと新規のトピックが出現しやすくなる
)

texts = ''.join([choice['text'] for choice in response.choices])
print(texts)

# 会話履歴の保存
with open(HISTORY_PATH, 'w') as f:
  f.write(prompt)
  f.write(texts)
  # 文の切れ目に改行を追加
  f.write('\n\n')
```

モデル自体は会話の推論をしているだけであり、トレーニングされているわけではないため、過去の会話ログを追記してリクエストを送る必要があります。
ここではAPIの会話ログをhistory.logとして保存しており、そちらを新規入力の先頭に付与することで過去の会話ログを保持させています。
また、「-r, --reset」をつけることで、会話ログを初期化できるようにしました。

## 実行

それでは実際に利用してみましょう。
ここでは「エラトステネスの篩」をpythonで実装することを目的に、会話していきたいと思います。
まずは「エラトステネスの篩」について聞いてみます。

```zsh
python chat.py エラトステネスの篩について教えて。
```
```
エラトステネスの篩とは、素数を列挙するアルゴリズムのことです。

アルゴリズムの手順は以下の通りです。

1. 2から始めて、数列を作成します。

2. 2を素数としてマークします。

3. 2の倍数をすべてマークします。

4. 次に素数としてマークされていない最小の数を素数としてマークします。

5. その数の倍数をすべてマークします。

6. 4から5を繰り返します。

7. 繰り返しが終わったら、マークされていない数が素数です。
```

わかりやすいですね！では、どのようなプログラムになるか聞いてみます。
```zsh
python chat.py pythonのプログラムに変換して。
```
```python
def eratosthenes(n):
    prime_list = []
    for i in range(2, n+1):
        if all(i % j != 0 for j in range(2, i)):
            prime_list.append(i)
    return prime_list

print(eratosthenes(100))
```

eratosthenes関数を定義して、100までの素数を表示してくれるようになりました！
こちら実際に実行したら動くプラグラムになっています。
100が決め打ちなのが気になるので、コマンドラインから指定できるようにお願いしてみます。

```zsh
python chat.py nをコマンドラインから渡せるようにして。
```
```python
import sys

def eratosthenes(n):
    prime_list = []
    for i in range(2, n+1):
        if all(i % j != 0 for j in range(2, i)):
            prime_list.append(i)
    return prime_list

n = int(sys.argv[1])
print(eratosthenes(n))
```

コマンドラインから引いてくれるようになりました！
最後にわかりやすいようにコメントの追加もお願いしてみます。

```zsh
python chat.py わかりやすいようにコメントを追加して。
```
```python
import sys

# エラトステネスの篩を実装する関数
def eratosthenes(n):
    # 素数を格納するリスト
    prime_list = []
    # 2からnまでの数を調べる
    for i in range(2, n+1):
        # iが素数かどうかを判定する
        # 2からi-1までの数で割り切れなければ素数
        if all(i % j != 0 for j in range(2, i)):
            # 素数ならリストに追加
            prime_list.append(i)
    # 素数のリストを返す
    return prime_list

# コマンドライン引数からnを取得
n = int(sys.argv[1])
# 素数のリストを表示
print(eratosthenes(n))
```

上記をeratosthenes.pyとして保存して下記を実行すると、無事素数が取得できました。
```zsh
python eratosthenes.py 100
```
```
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
```

エラトステネスの篩をpythonで実装することができました！

## ChatGPTとの比較・感想

今回「text-davinci-003」に対して行った対話を、ChatGPTに対しても同様に行ってみたところ、下記のようなやり取りになりました。

![chatgpt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21249/c59ac584-dc4a-3fc6-a975-2b00ad1144cf.png)

何回か試して見ましたが、ChatGPTのほうが「text-davinci-003」によりもブレが大きく、文章の内容についても補足など広く返してくれる傾向がありました。
今回「temperature」などのハイパーパラメータはコード生成のサンプルのままにしていたため、その点が影響を受けた可能性が考えられます。
ChatGPTのハイパーパラメータと同様のパラメータであればだいたい近似するのかもしれません。
コード生成においては今回のパラメータのほうがブレが少ないためやりやすそうと感じました。

## まとめ

ChatGPTを提供しているopenaiで公開された「text-davinci-003」を用いたapi利用してみました。
試したところChatGPTと遜色ないレベルのレスポンスが確認できました。
また今回記事には記載しませんでしたが、「golandで実装して。」などや、「英語に翻訳して。」などの会話で、実際にgolandで動くプログラムや、英語の説明などに切り替えてくれたりなど非常に可能性を感じました。

openaiに登録すると簡単にAPIを叩くことができるため、今後アプリとの各種連携が大いに期待できると思いました。
注意点としては登録時に18ドルの無料クレジットがもらえるため、個人で試しに使う分には問題ないですが、それ以上はお金がかかるため、大規模に利用する場合は予算との相談になるため、GPT3系の公開されているモデルなどを考慮に入れる必要がありそうです。

以上、「[Applibot Advent Calendar 2022](https://qiita.com/advent-calendar/2022/applibot)」 4日目の記事でした。