豪鬼メモ

一瞬千撃

ChatGPTのAPIで英語の例文を集める

話題沸騰の自動チャットシステムChatGPTだが、そのAPIを使うと英語の例文の収集が非常に簡単にできる。その方法を紹介する。


ChatGPTはLLM(Large Language Model=大規模言語モデル)とAIを利用してテキストを自動生成するシステムだが、あたかも人間が書いたかのような自然な文を生成できることが特徴のひとつだ。その流暢さは、もはや人間が書いた文と区別がつけられないくらいだ。しかも、どのような文を生成すべきかを自然文で指示すると、その期待に答えた結果を生成してくれる。例えば「two very short example usages of "get carried away"」などと指示すると、その通り、"get carried away"を含む例文を二つ生成してくれる。

1. During a heated argument, I got carried away and said some things I didn't mean.

2. While dancing to my favorite song, I got carried away and started singing at the top of my lungs.

任意のフレーズを含む例文が生成できるのが便利だ。該当のフレーズの典型的な意味での使用例が選ばれていて、文の内容も脈絡のある自然なものになっている。別の例として、「get off on」(興奮する)の例文を抽出するとしよう。単純な文字列一致で単言語コーパスを探すと、「get off」(降りる)と「on」(面上で)に分けて使われた「She got off on the second floor」(彼女は2階で降りた)のような用例を多く拾ってしまう。しかし、ChatGPTは、あえて「get off on」が連なって指定されていることを汲み取ってくれて、「I don't get off on girls' uniforms」(少女の制服に興奮したりしない)とかいう例文を生成してくれる。賢いのだ。英英辞書に収録する単言語コーパスを作成するのであれば、ChatGPTだけで十分なくらいだ。文の長さが20語前後の読みやすいものになることが多いのも良い。

対訳文が欲しい場合には、「two very short example usages of "get carried away" and their translations into Japanese」とかやるとよい。ただ、翻訳精度はDeepL翻訳やGoogle翻訳などの専用システムの方が良いっぽいので、ChatGPTの結果をそれらで処理する方が無難だ。とはいえ、ChatGPTで一度にやった方がコスパはよい。それに、対訳の精度は完璧でなくても、例文の理解の参考にするだけなら許容範囲だろう。

two short example usages of "pizza" less than 16 words in English and their translations into Japanese
English:
I love pepperoni pizza.
Let's order a large cheese pizza.

Japanese translations:
ペパロニピザが大好きです。
大きなチーズピザを注文しましょう。

例文を自動生成する最大の利点は、カバレッジである。対訳コーパスだと規模が小さすぎて、「undisputably」「vernacularization」とかいった低頻度語や、「get off on」「chime in with」とかいった込み入ったコロケーションの例文は見つからないことがある。しかし、見出し語を指定して例文を生成すれば、カバレッジが100%に近づく。対訳コーパスから例文が取れればそれを採用し、それがない場合には自動生成した例文を使うように、補完的な使い方をするのもよいだろう。

ChatGPTにはAPIがある。Pythonの場合、「pip install openai」などとしてopenaiモジュールを入れるだけで使えるようになる。実際のプログラムを動かすには、OpenAIのページでChatGPTのアカウントを作って、ログインしてから、Upgrade to Plusで有料プランに移行する必要がある。さらに、このページにアクセスして、「Create new secret key」を押して秘密鍵を作る。その上で、以下のようなプログラムを書く。

import sys
import os
import openai
import time

# ここに秘密鍵をコピペ
openai.api_key = "sk-xxxxxxxxxxxxxxxx"

# クエリを実行する。
def RunQuery(query):
  tries = 0
  while tries < 100:
    tries += 1
    try:
      response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
          {"role": "user", "content": query},
        ],
      )
      result = response.choices[0]["message"]["content"].strip()
      result = result.replace("\n", "\t")
      return result
    except Exception as e:
      print("Error: " + str(e) + ": " + query, file=sys.stderr)
      time.sleep(60)
  print("Failed: " + query, file=sys.stderr)
  return nil

# 標準入力から単語をを読み込んで処理。
for line in sys.stdin:
  word = line.strip()
  if not word: continue
  query = 'two very short example usages of "{}"'.format(word)
  result = RunQuery(query)
  if not result: continue
  print(word + "\t" + result, flush=True)

標準入力の各行からフレーズを読み込んで、そのフレーズと、それを含むフレーズに関する応答をTSV形式で出力する。ChatGPTからの応答は改行をタブに置き換えるだけの処理で保存しておき、実際に例文を抽出するのは別の後処理で行う。

openai.ChatCompletionを実行している部分が肝だ。messagesパラメータにクエリの具体的な条件を配列として指定するのだが、ここに複数の要素を詰め込むことで、記憶を表現するらしい。ユーザからの入力は"role:"user"の要素で表現し、ボットからの入力は"role":"assistant"の要素で表現する。

messages = [
  {"role": "user", "content": "Who's the vocalist of Deep Purple?"},
  {"role": "assistant", "content": "Deep Purple has had several vocalists over the years, but the most famous and recognizable one is Ian Gillan."},
  {"role": "user", "content": "Who's his wife?"},
]
response = openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=messages)

上述のクエリだと、「Ian Gillan has been married to his wife, Bron Gillan...」などという応答が得られる。ちゃんと「his」の内容が「Ian Gillan」であることを踏まえて発言してくれる。ステートレスに記憶を表現するRestfulスタイルのAPIというわけだ。ただし、長い記憶を表現しようとすると利用トークン数がやたら嵩むのが辛いところだ。他にもチューニングはいろいろできて、max_tokensパラメータで出力トークン数を制限したり、temperatureパラメータで結果のランダム性を調整したり、frequency_penaltyパラメータで結果の多様性を調整したりできるらしい。詳しくはAPIの文書に書いてある。

とはいえ、例文を出力するだけなら、全部デフォルトでOKだ。もっとたくさんの例文が欲しいなら「ten very short example usages of "xxx"」とかいう本文にすればいいし、もっと長い例文が欲しいなら「two long example usages of "xxx"」とかやればいい。

ChatGPTは無料で使えるプランと、有料のプランがある。3月初旬より前に作っているアカウントなら無料プランでもAPIが使えるみたいだが、最近では有料プランに入っておく必要がある。初月はフリートライアル期間で、18ドルまでの処理量が無料で使えるっぽい。それ以降は従量課金になり、1000トークンあたり0.002ドルかかるらしい。トークンはWordPieceとほぼ同じ概念であり、コーパスによく出現するバイト列をまとめてIDを振ったものだ。頻出英単語は1トークンになるが、稀だったり長かったりする英単語は複数トークンになる。日本語の場合、平仮名や片仮名や頻出漢字は1トークンになるが、それ以外の漢字や記号などはbyte fallbackで複数トークンになる。「two very short example usages of "xxx"」とかいうクエリだと、入力で10トークンくらい、出力で50トークンくらいの合計60トークンくらいの処理になるが、その場合、0.5QPSくらいで処理できる。無料プランだと、1分間で20クエリまでのスループット制限がかかるので、それにひっかかったら再試行するという仕掛けを入れておく必要がある。有料プランにするとその制限はなくなるが、0.5QPSから高速化するわけではない。使用量とは別に追加で月20ドル払ってChatGPT Plusに入るとスループットが少しマシになるらしいが、べらぼうに早くなるわけではない。以上を鑑みると、無料プランで使えるなら有料プランにする意味はあまりない(最近だと無料プランではAPIが使えないので有料にするしかないのだが)。無料プランでも、5日間くらい放っておけば、10万語くらいの見出し語の例文が得られる。35000クエリの時点で5ドルくらいの使用量になるっぽいので、18ドル相当のフリートライアル分で12万クエリくらいは投げられるかな。実際の使用量はこちらのページで確認できる。

ChatGPTは入力が非定型文でできる柔軟性があるが、出力も非定形文なので、定形的なデータが欲しい場合にはヒューリスティクスに基づいて後処理を書かねばならない。つまり、適当な変換ルールを書いて、例文を含む応答テキストから例文だけを抜き出す必要がある。さて、私が試した限り、「two short example usages of "xxx"」というクエリの応答は、「1.」とか「1)」とかいう接頭辞がついた改行区切りの例文のリストになることがほとんどであることがわかっている。たまに、接頭辞がなかったり、例文が引用符で囲まれていたりもする。結果をなるべく箇条書きにさせて後処理を楽にするために、「two」とか「three」とかで2以上の数を指定してデータを取得するのがコツだ。また、例文の生成に失敗した場合には「Sorry, as an AI language model, I cannot...」的な文言が出力されるので、それらは捨てる。LLMのコーパスにすら含まれないような稀なフレーズや、性的なニュアンスのあるフレーズは処理してくれないらしい。

後処理を適用して、重要語25万語の例文55万文を抽出したTSVファイルを上げておく。結構それっぽい例文集になっていることがわかると思う。

まとめ。ChatGPTを使えば、簡単に英単語やフレーズの例文を集めることができる。素晴らしい時代になったものだ。流暢な英文が自動生成され、しかも的を射た文脈での用例になっている。少なくとも私よりは英語力が上だと感じる。私が学生の頃にはElizaとかいう人工無能で遊んで単純なルールベースの応答でも喜んでいたわけだが、そこからすると隔世の進化だ。他にも、「two paraphrases of "xxx"」とか、「two synonyms of "xxx"」とかやって、言い換え表現や同義語を抽出することもできそうなので、いろいろ遊んでみたい。GoogleBardでもほぼ同じやり方で例文やその他の関連データを集められそうだが、APIの公開がまだなので、それ待ちだ。