Copilot ChatとOpenAI APIとPandas

アウトプットをサボりすぎた。

書き残しておきたい事はそれなりにあったのだが、生来の筆無精がこのところ加速してしまい、様々なことをやりっぱなしにしてしまっていた。

そんなわけで今日はテーマが非常に雑多である。 それぞれゆるく繋がってはいるが、いかんせんとりとめが無く申し訳ない。

本来は話題を一つだけに精選し、丁寧なチュートリアルの形でお届けしようかとも考えたが、そんなことはどこかの誰かがとうの昔にやっていそうなので、今回は純粋に「僕は今こんなことをやっています」という実況中継にしたい。

Copilot Chatとのかいこう

最近、遅ればせながらGitHub Copilot Chat(以下Copilot Chat)との会話にはまっている。

ただのCopilotは前々から知っていたが、Copilot “Chat”の方は「ChatGPTとの棲み分けが分からん」という意味不明な理由で食指が動かなかった。

しかし、いざ使ってみるとこれがなかなかどうして楽しい。 下図のようにVisual Studio Code拡張機能として動作するので、開発中に別種のアプリを複数立ち上げて気を散らせてしまうこともない。

このチャットフォームに技術的な質問をすると、対話的に Copilot が手順やコーディングサンプルなどのソリューションを即座に提示してくれる。
たとえば ──

tercel-s:
指定したS3バケットの特定のオブジェクトの署名付きURLを発行するLambda関数を書いて

── という漠然とした指示だけで、Copilot Chat は良きにはからって以下のような回答文を生成する。

GitHub Copilot:
以下は、指定したS3バケットの特定のオブジェクトの署名付きURLを発行するLambda関数の例です。この例では、AWS SDK for Python (Boto3)を使用しています。

import boto3

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    # S3バケット名とオブジェクトキーを取得する
    bucket_name = 'バケット名'
    object_key = 'オブジェクトキー'

    # 署名付きURLを発行する
    url = s3_client.generate_presigned_url(
        'get_object',
        Params={
            'Bucket': bucket_name,
            'Key': object_key
        },
        ExpiresIn=3600  # URLの有効期限を1時間に設定する
    )

    # 署名付きURLを返す
    return {
        'statusCode': 200,
        'body': url
    }

ご覧のように、細かい指示を多少端折っても、AI がイイ感じに我が意を汲んでくれる。

これしきの事は ChatGPT でもお茶の子さいさいではあるが、Copilot Chatの場合はさらに、エディタで開いているソースを参照した上で回答を生成してくれるのだ。

つまり、すでに書きかけのソースがある場合は、Copilotはそのコードのクセを考慮して回答を生成する。 大変賢い。

CopilotやCopilot Chatのよいところは、初見のライブラリの調査に要する時間を大幅に短縮してくれる点である。 大抵、多機能のライブラリは使えるようになるまでの道のりが長く、往々にしてサンプルコードも解析しづらかったりする。

真にミニマルなサンプルコードをCopilotに生成させ、それを足がかりにしてライブラリの仕様を掘り下げる方が認知負荷も軽いし、地球にも優しい。

──僕はこのCopiot(そしてCopilot Chat)を知ったとき、会社としてこの技術を積極的に活用させてもらえるベンダーとそうでないベンダーの能力差が、今後決定的に二極化しそうだと思った。 さらに、プログラミング自体の価値は早晩暴落するのでは……と危惧もしている。

ただ、開発が高速化したりプログラムが高品質化したりするのはよいことなので、余裕が生まれた分、より高次元のところで新たな価値を生めるようになりたいと思う次第である。

OpenAI APIたわむれる

おもしろそうな本(Python / JavaScriptによるOpen AIプログラミング)を手に入れたので、これまた遅ればせながらOpenAI APIで遊ぶことにした。

Completion API

まずは、一問一答で応答文を生成してくれる Completion APIを試す。 コード自体かなり簡単だが、さらにCopilotの補完が効くので、写経も非常にラクである。

import openai

api_key = '████████████████████'

def access_openai(prompt_value):
  openai.api_key = api_key
  response = openai.Completion.create(model='text-davinci-003',
                                      prompt=prompt_value,
                                      max_tokens=100)
  result = response.choices[0].text.strip()
  print(result)

if __name__ == '__main__':
  input_text = input('テキストを入力してください: ')
  access_openai(input_text)

このコードは、ユーザのテキスト入力に基づいて返答する(APIを呼ぶだけの)代物である。 動かすと下図のようにイイ感じの応答文が表示される。

ちなみにプロンプト冒頭の固定文は、@fladdict氏のHackである。

安易な発想だが、OpenAIのAPIを基幹業務システムと連携させたら面白そうだと常々思う。

現状、基幹に蓄積されているデータを人間が充分に利活用できているかというと必ずしもそうではない。

たとえば「予算達成のためにはいつまでにどの程度の規模の案件がどれくらい必要で、そのためにどんなアクションが必要か云々」みたいな施策をいつまでも人間が考えて、システムはただデータを愚直に溜めておくだけというのは、あまりにも不便ではあるまいか──。

無論、センシティブな業績データ等の扱いには細心の注意を要するが、基幹システムがただのデータ置き場になっているとしたら、それはあまりに勿体無いと思ってしまう。

Completion Chat API

さらに、会話を連鎖させる用のCompletion Chat APIなるものも試した。

簡単なチャットボットが30行らずで書けてしまう*1こういうのでいいんだよ

ステートレスなAPIの筈なのに、どうやって過去の会話の文脈を理解しているのか不思議であったが、なんのことはない、会話のやりとり全てを配列に記録しておき、それを毎回APIに渡せばよいのであった。

至極単純だが、なるほどたしかに回帰リカレント的なAPIデザインである。

Image Generation

OpenAI APIはテキスト生成だけでなく、画像生成もイケるようだ。

まだ日本語の指示だととんでもない画像が生成されるので、事前に英語に翻訳しておかねばならない。

名探偵コナンっぽいものを描かせようとしたが、いまいち思った通りにならなかった。

低解像度で恐縮だが、非実在少年探偵が生成された。

同時上映: Pandas

これも随分前に手に入れた本(Pythonによる実務で役立つデータサイエンス練習問題200+ (1): アナリティクスの基礎・可視化と実践的テクニック)だが、内容が面白そうだったので手を動かして問題に取り組んでみた。

1巻では、データ解析用のライブラリPandasでCSVねくり回したり、可視化ライブラリMatplotlibでグラフを描いたりする演習が収録されている。

ポケモンのデータを用いた演習も用意されており、楽しみながら学ぶことができる。

ピボット集計

2次元データからピボットテーブルを作る問題は、非常に実用性が高く応用しやすい。

たとえば、ソフトウェア品質を見る際に、担当者別・機能別・原因工程別にバグの数を集計し、定量比較する営みは日常業務のはんちゅうである。

いかにもPandasが大活躍しそうである。

import pandas as pd
import numpy as np

"""
ポケモンデータ https://logopt.com/data/Pokemon.csv に対して、
メインタイプ (Type 1) と世代 (Generation) ごとの
平均攻撃力 (Attack) と守備力 (Defense) の平均値を求めてください。
"""

poke = pd.read_csv(
  'https://logopt.com/data/Pokemon.csv',
  index_col=0)

print(poke.head())
print()

pivot = pd.pivot_table(
  poke, values=['Attack', 'Defense'],
  index=['Type 1', 'Generation'],
  aggfunc=np.mean)

print(pivot.head())
print(pivot.tail())

このコードは、(コメントにもある通り)ポケモンCSVデータを読み込んで、メインタイプ (Type 1) と世代 (Generation) ごとの攻撃力 (Attack) と守備力 (Defense) の平均値を求めている。

Excelでは、マウスでちまちまと設定が必要だったピボット集計が、Pandasによっていとも簡単にできてしまった。 もうこれでよいではないか。

見える化

続いて、グラフ描画のお稽古である。

僕が学生の時分は、Gnuplotというツールが主流だったが、使い方にクセがありすぎて難儀したものである。

令和の世の中であればMatplotlibSeabornこそが有力な選択肢だろう。

import pandas as pd
import matplotlib.pyplot as plt

"""
ポケモンデータ https://logopt.com/data/poke.csv のデータフレームに対して、
攻撃力 (Attack)、守備力 (Defense) のヒストグラムを描画せよ。
"""

poke = pd.read_csv(
  'https://logopt.com/data/poke.csv',
  index_col=0)

print(poke.head())
print()

poke.hist(bins=20, column=['Attack', 'Defense'])
plt.show()

オーソドックスなヒストグラムのプロットであれば、たったこれだけである。

大量の定型コードを書く必要もない。 これならば、グラフの出来栄えの微調整に手こずってレポートの締め切りをオーバーしてしまう心配もないだろう(その代わり、人間が真に頭を使うべきところに時間的リソースを集中しやすくなる)。

ヒストグラム

散布図

並行座標図

ご覧の通り、少ないコード量でそこそこ綺麗なグラフが描けるとテンションが上がるものである。

まとめ

Copilotを使うようになって、初見のライブラリに挑戦する際の調査コストがかなり下がった。

また、コードを書いているそばから、まるで心を読むかのような的確な提案をしてくるあたりも感心した。

そんなわけで、Copilotの力を借りてOpenAI APIとPandasに挑戦した夏であった。

*1:適切なエラーハンドリングが1つも入っていないので、売り物に組み込むにはもう少しだけ固める必要がある。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.