こんにちは、ソリューションアーキテクトの内田(ニックネーム: jet)です。
7/5(水)・7/6(木)に開催したIoTカンファレンス「SORACOM Discovery 2023」はお楽しみいただけましたでしょうか?7/6(木)は久しぶりのオフライン開催となり、多くの方に会場にお越しいただき、実際にIoTデバイスを見て触れていただいておりました。ありがとうございました。
このブログでは「SORACOM Discovery 2023」で、私が担当した以下のセッションについての内容を少しフォローしたいと思います。
IoTが産み出すデータを「生成系AI」で活かすテクニック
7/5(水)のオンラインセッションの内容は、5/30(火)に開催した、IoTや周辺技術の知見をエンジニア向けに共有するイベント「IoT-Tech Meetup 第1回【ChatGPT × IoT】」でお話ししたものをブラッシュアップした内容です。
7/5(水)のオンラインセッションでは、セッションの中で時系列データを扱う時にはトークンの上限に注意が必要なことと、チャットの回答結果を例えばJSON形式にしたいという場合は、プロンプトを工夫すれば指定した出力形式になる場合があるが、ダメな時はダメなのでシステム連携で使う場合には、さらにひと工夫が必要というまとめを行っていました。
ChatGPT の機能アップデート
ここまでで、普段からChatGPTを使っている方はご存知かもしれませんが、以下のような機能アップデートがありました。
アップデート内容をピックアップしてみると
new function calling capability in the Chat Completions API
Chat Completions API に関数呼び出し機能が追加された
updated and more steerable versions of gpt-4 and gpt-3.5-turbo
gpt-4 と gpt-3.5-turbo のモデルに新しいバージョンが追加された
new 16k context version of gpt-3.5-turbo (vs the standard 4k version)
トークンの上限が今までの4倍になった
などが、大きな変更点かと感じました。
モデルのアップデート
ドキュメント にも記載があるように、GPT-4、GTP-3.5に 0613
が付いたバージョンが追加されています。
加えて、GTP-3.5には 16k
が付いたバージョンも追加されています。これはトークンの上限がこれまでの4倍になったバージョンです。
LATEST MODEL | MAX TOKENS |
---|---|
gpt-3.5-turbo-0613 | 4,096 tokens |
gpt-3.5-turbo-16k-0613 | 16,384 tokens |
上限が大きくなったので、使える幅が増えるのは嬉しいのですが同時に料金が別設定になっているので、本格的に利用する前には確認を行なってください。
Function calling
0613
が付いたバージョン以降で利用できる 機能 で、自分で定義した関数呼び出しができる機能です。関数が定義してあると、AIへの問いかけで、AIが判断して関数呼び出しを行うための返答をしてくれます。Web版のplugin機能とは違い、関数を自動で呼び出してくれるわけではない点は認識する必要があります。
アップデートされた機能を試してみる
というわけで、本題へ。これまではプロンプトの工夫やSystem role を利用してAIに制約を付けることで、API連携に必要なJSON形式の回答を生成していました。
Function calling 機能を使うことで、連携先のJSON形式へ対応できるのではないか?という部分を実際に試してみようと思います。
Function を定義する
Chat Completions API を呼び出す引数に、定義した関数の情報を渡す必要があります。関数の定義は、ドキュメント にあるように JSON Schema での定義が必要です。
今回は、過去の時系列データから1日後の同じ時間帯のデータを予測することを試してみました。JSONの入れ子や配列などを定義しています。
AIはこれを関数呼び出しのために必要な情報だと認識するので、関数へのインプット情報として指定したJSON形式のデータが得られるかどうかを実際に試してみます。
functions = [ { "name": "predict_data", "description": "添付された元データを使って今後の未来予測を実行する関数", "parameters": { "type": "object", "required": [ "predictions", "summary", "chat_type" ], "properties": { "predictions": { "type": "array", "description": "未来の予測時間, 未来の予測温度, 未来の予測湿度がそれぞれ有効な値で記載されていること。それが複数個のリストとになっている。例: predictions:[{time:予測値,temp:予測値,humi:予測値}]", "items": { "type": "object", "required": [ "time", "temp", "humi" ], "properties": { "time": { "type": "string", "description": "未来の予測日時の文字列。AIが予測日時を記載する。例: 2023-06-17 15:52:15", }, "temp": { "type": "number", "description": "未来の予測温度の数値。AIが予測温度を記載する。例: 24.5", }, "humi": { "type": "number", "description": "未来の予測湿度の数値。AIが予測湿度を記載する。例: 65.5", }, }, }, }, "summary": { "type": "object", "description": "予測をした理由, 予測についてコメントや補足を含む。例: {reason:AIの返答,note:AIの返答}", "required": [ "reason", "note" ], "properties": { "reason": { "type": "string", "description": "予測した経緯や理由をAIが記載する。例: 添付されたデータを利用して未来を予測しました。", }, "note": { "type": "string", "description": "予測した際にコメントや補足があればAIが記載する。例: 季節や場所の内容を考慮していますが、予測が正しいとは限りません。", }, }, }, "chat_type": { "type": "string", "description": "予測を実行したことの識別子。例: prediction", "enum": [ "prediction" ], }, }, }, } ]
Function calling を設定する
ChatGPTの 呼び出しには Python ライブラリの openai / openai-python を利用しています。openai / openai-python は MIT ライセンス で提供されていています。
実際にの呼び出し部分は以下の通りで、変更点は定義した関数を functions=functions
で渡しているところと、 関数呼び出しの function_call="auto"
が追加されているだけです。関数呼び出しは、コメントアウトしてあるように関数名を直接していして呼び出すこともできます。
response = openai.ChatCompletion.create( model=selected_model, messages=[ {"role": "user", "content": user_content}, ], functions=functions, function_call="auto", # function_call={"name": "predict_data"}, )
あとは、結果が見えるようにして実際に実行してみます。
message = response["choices"][0]["message"] pprint.pprint(message) if message.get("function_call"): function_name = message["function_call"]["name"] arguments = json.loads(message["function_call"]["arguments"]) pprint.pprint(arguments) else: print('# 🤖 reply = ', message.content.strip())
ChatGTPに与えているのは、以下のグラフの時系列データです。データは GPS マルチユニット SORACOM Edition を利用して収集した、日時・温度・湿度の1時間のデータです。
Function calling を設定して ChatGPT を呼び出す
プロンプトには以下のように入力しました。今回は user role
のみを利用しました。
- 添付したデータから既存の関数を呼び出して今後の未来予測を実行する
- 予測の `間隔` は `添付したデータと同じ間隔` で必ず予測すること
- 予測する期間は、`1日後` の `同一の時間帯` とする
- 予測期間のすべての予測データを欠けることなく必ず回答すること
- 予測には`時間`,`温度`,`湿度`を有効な値で必ず含めること
- 予測の`温度`,`湿度` は季節と場所を考慮した値であること
- 予測の内容を導き出した理由を必ず含めること
- その他のコメントがある場合は含めること
- 現在の季節は `夏`
- 場所は `日本` の `東京`
- `季節` と `場所` の情報を必ず加味して予測すること
実際に呼び出してみたところ、ChatGPT からの返答は function を呼び出すための引数として、定義した JSON形式での返答を受け取ることができました。
出力された JSON データの内容を確認すると、ちょっと気温が高すぎるきがしますが、これは ChatGPT が不得意な分野であるためです。実際にはこの予測も含めて自分の関数や他の特化型 AI と連携するような仕組みにしてあげるのが良いかと感じました。
{'chat_type': 'prediction', 'predictions': [{'humi': 64.0, 'temp': 33.0, 'time': '2023-07-05 18:47:31'}, {'humi': 67.0, 'temp': 33.1, 'time': '2023-07-05 18:46:36'}, {'humi': 63.0, 'temp': 33.2, 'time': '2023-07-05 18:45:30'}, {'humi': 64.0, 'temp': 33.3, 'time': '2023-07-05 18:44:30'}, {'humi': 62.0, 'temp': 33.4, 'time': '2023-07-05 18:43:30'}, {'humi': 63.0, 'temp': 33.5, 'time': '2023-07-05 18:42:30'}, {'humi': 61.0, 'temp': 33.6, 'time': '2023-07-05 18:41:35'}, {'humi': 64.0, 'temp': 33.7, 'time': '2023-07-05 18:40:30'}, {'humi': 62.0, 'temp': 33.8, 'time': '2023-07-05 18:39:30'}, {'humi': 63.0, 'temp': 33.9, 'time': '2023-07-05 18:38:30'}, {'humi': 65.0, 'temp': 34.0, 'time': '2023-07-05 18:37:31'}, {'humi': 61.0, 'temp': 34.1, 'time': '2023-07-05 18:36:29'}, {'humi': 64.0, 'temp': 34.2, 'time': '2023-07-05 18:35:29'}, {'humi': 62.0, 'temp': 34.3, 'time': '2023-07-05 18:34:29'}, {'humi': 63.0, 'temp': 34.4, 'time': '2023-07-05 18:33:29'}, {'humi': 65.0, 'temp': 34.5, 'time': '2023-07-05 18:32:29'}, {'humi': 62.0, 'temp': 34.6, 'time': '2023-07-05 18:31:34'}, {'humi': 64.0, 'temp': 34.7, 'time': '2023-07-05 18:30:29'}, {'humi': 63.0, 'temp': 34.8, 'time': '2023-07-05 18:29:28'}, {'humi': 65.0, 'temp': 34.9, 'time': '2023-07-05 18:28:28'}, {'humi': 62.0, 'temp': 35.0, 'time': '2023-07-05 18:27:28'}, {'humi': 64.0, 'temp': 35.1, 'time': '2023-07-05 18:26:33'}, {'humi': 63.0, 'temp': 35.2, 'time': '2023-07-05 18:25:28'}, {'humi': 62.0, 'temp': 35.3, 'time': '2023-07-05 18:24:28'}, {'humi': 64.0, 'temp': 35.4, 'time': '2023-07-05 18:23:28'}, {'humi': 61.0, 'temp': 35.5, 'time': '2023-07-05 18:22:28'}, {'humi': 63.0, 'temp': 35.6, 'time': '2023-07-05 18:21:32'}, {'humi': 59.0, 'temp': 35.7, 'time': '2023-07-05 18:20:27'}, {'humi': 62.0, 'temp': 35.8, 'time': '2023-07-05 18:19:27'}, {'humi': 61.0, 'temp': 35.9, 'time': '2023-07-05 18:18:27'}, {'humi': 64.0, 'temp': 36.0, 'time': '2023-07-05 18:17:27'}, {'humi': 60.0, 'temp': 36.1, 'time': '2023-07-05 18:16:32'}, {'humi': 63.0, 'temp': 36.2, 'time': '2023-07-05 18:15:28'}, {'humi': 62.0, 'temp': 36.3, 'time': '2023-07-05 18:14:26'}, {'humi': 65.0, 'temp': 36.4, 'time': '2023-07-05 18:13:26'}, {'humi': 60.0, 'temp': 36.5, 'time': '2023-07-05 18:12:26'}, {'humi': 63.0, 'temp': 36.6, 'time': '2023-07-05 18:11:32'}, {'humi': 62.0, 'temp': 36.7, 'time': '2023-07-05 18:10:27'}, {'humi': 65.0, 'temp': 36.8, 'time': '2023-07-05 18:09:27'}, {'humi': 61.0, 'temp': 36.9, 'time': '2023-07-05 18:08:26'}, {'humi': 64.0, 'temp': 37.0, 'time': '2023-07-05 18:07:26'}, {'humi': 62.0, 'temp': 37.1, 'time': '2023-07-05 18:06:31'}, {'humi': 63.0, 'temp': 37.2, 'time': '2023-07-05 18:05:26'}, {'humi': 65.0, 'temp': 37.3, 'time': '2023-07-05 18:04:26'}, {'humi': 61.0, 'temp': 37.4, 'time': '2023-07-05 18:03:25'}, {'humi': 64.0, 'temp': 37.5, 'time': '2023-07-05 18:02:24'}, {'humi': 63.0, 'temp': 37.6, 'time': '2023-07-05 18:01:29'}, {'humi': 62.0, 'temp': 37.7, 'time': '2023-07-05 18:00:24'}, {'humi': 65.0, 'temp': 37.8, 'time': '2023-07-05 17:59:24'}, {'humi': 61.0, 'temp': 37.9, 'time': '2023-07-05 17:58:24'}, {'humi': 64.0, 'temp': 38.0, 'time': '2023-07-05 17:57:24'}, {'humi': 63.0, 'temp': 38.1, 'time': '2023-07-05 17:56:28'}, {'humi': 65.0, 'temp': 38.2, 'time': '2023-07-05 17:55:23'}, {'humi': 62.0, 'temp': 38.3, 'time': '2023-07-05 17:54:23'}, {'humi': 64.0, 'temp': 38.4, 'time': '2023-07-05 17:53:23'}, {'humi': 61.0, 'temp': 38.5, 'time': '2023-07-05 17:52:23'}, {'humi': 63.0, 'temp': 38.6, 'time': '2023-07-05 17:51:23'}, {'humi': 62.0, 'temp': 38.7, 'time': '2023-07-05 17:50:23'}, {'humi': 65.0, 'temp': 38.8, 'time': '2023-07-05 17:49:23'}, {'humi': 61.0, 'temp': 38.9, 'time': '2023-07-05 17:48:22'}], 'summary': {'note': '夏の季節として予測', 'reason': '気温と湿度の上昇傾向'}}
更なるアップデート
ブログを書いている間に、更なるアップデートがありました。アップデートのスピードが早いので、気がついたら機能や仕組みが追加されている感じですね。こちらも普段からChatGPTを使っている方はご存知かもしれませんが、内容は GTP-4 が GA になりました。詳細は以下をご確認ください。
また、プラグインの Code Interpreter
がベータ提供ではありますが、ChatGPT Plus 利用者に開放されました。
Code interpreter とは
Code interpreter は ChatGPT で利用できるプラグインのひとつで、Webブラウザで利用できる ChatGPT 上で、コードの実行やデータをファイルとしてアップロードして、データの分析などが実行できます。
利用するには、設定から Code interpreter を ON にし、プラグインを利用するために GPT-4 を選択してプラグインとして Code interpreter をチェックしてください。
Code interpreter 試してみる
SORACOM Harvest Data に保存されているデータを CSV形式でダウンロード して、そのCSVファイルをChatGPT にアップロードして、Code interpreter を試してみます。
今回は GPS マルチユニット SORACOM Edition を利用しているので、ChatGTPにはインプットするデータの データフォーマット も一緒にインプットします。現時点では Code interpreter はインターネットアクセスが行えないようなので、データフォーマットはURLではなく、文字列としてインプットしています。
CSV ファイルとデータフォーマットを受け取ったあとは、ChatGPT によりデータ内容の確認が行われました。
Code interpreter の便利なところのひとつは、実際に実行されたコードや実行結果を確認できるところです。コードがあれば、ローカル環境でいろいろ試したい時に活用できそうです。
その後は、ChatGPT と会話をしながらデータについて分析や集計を行えます。グラフを描写することもできるので、可視化をしながら色々試していくのも便利かもしれません。
JSON 形式での出力を指定してみる
最後にこれまでと同じように出力を JSON 形式でお願いしてみました。分析した結果が JSON 形式に適さない部分は変更して出力してくれたようです。
Code interpreter を利用することで、手軽にデータを分析して可視化することができました。また他のサービスや API にインプットするための JSON 形式の結果を作成することもできました。まだまだベータということなので制約もあるのですが、手軽に使えるのは魅力ですし、ChatGPT の Web で完結できるようなデータ分析については利用してみると手軽に新しい発見が得られるかもしれませんね。
まとめ
ChatGPT の 様々なアップデートによって、より手軽に API への連携ができるようになってきました。また、トークン数の上限も大きくなり、これまで普通に渡せなかったデータや文章が渡せるようになってきました。
それでも、トークン数には上限があるため引き続き大きすぎるデータや文章を渡すための工夫や、Function calling 利用時に想定していない返答がある可能性はゼロではないので、そういった部分にうまく折り合いをつけ ながら、AI が得意な部分で利用していくことが必要となってきます。
また、Code interpreter のような高機能なプラグインを利用することで、データの分析や可視化も簡単に行えるようになっています。
どちらの場合も、利用する場合には ChatGPT の有償ライセンスが必要となりますので、手軽にIoTの時系列データと AI の組み合わせを体験したい人は、今回発表された「SORACOM Harvest Data Intelligence」をまずはお試しいただければと思います。
まだまだこれからもアップデートや新機能の追加は続きそうなので、楽しみに待ちつつ、IoT と AI の新しい活用方法にチャレンジしてもらえるきっかけのひとつになれば嬉しいです。
― ソラコム内田 (jet) @uchimanajet7