3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ラン画像から抽出したテキストをChatGPTのプロンプトで解析してみた

Last updated at Posted at 2024-03-24

前書き

この記事は連作で、ラン記録をシェアし、メンバーの走行記録を集計する活動を、LINE Botで自動化しようとしている取り組みです。

Cloud Vision APIでテキスト検出できることから、目で読んで手で打つという手間を省けないかと取り組み始めました。

image.png

クリティカルな課題はそのまま未解決ながら、日々ラン画像が投稿され、集計に活躍しています。

タイムや距離がうまく読み取られなかった場合も「修正」に誘導する機能を作り、利用者が自らLINEのチャット内で修正してくれており、ときどきパターンマッチングの処理を追加修正して、新たに検出されたエラーに対応していました。

プルリクのほとんどがパターンマッチ修正

ChatGPTのプロンプトで解析

2023年に入ってChatGPTのAPIも公開され、さまざまな組み込みも行われています。そして最近バズった記事で、なるほどChatGPTで解析できそうだなと今更ながら気づきました。

あるデータを元に、プロンプトを与えてテキストを生成する流れは、前職でも見ていたので、GASで試しに組んでみようというのが今回の記事になります。

GASでの組み込み

以下の記事を参考に、呼び出し部分は作成しました。

openaiのapikey取得は、以前に参加したオンラインのWorkshopで作成したことがあったので、ログインしてキーを追加しました。

GASのプロジェクトにもプロパティを追加して、これを読むようにします。
image.png

chatgpt.gs
var OPENAI_API_KEY = PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY");

プロンプトの文字列を定義

chatgpt.gs
var prompt = `以下は、画像からGoogle Cloud Visionで抽出されたテキストで、スマホアプリのスクリーンショットやランニングウォッチの表示をスマホで撮影したものになります。
このテキストの中から、走った距離と時間を抽出してください。
json形式で、distanceとdurationという値を返してください。
距離は数値を文字列で返してください。kmは不要です。
距離がマイルで検出された場合はkmに換算してください。
タイムは、日本語であっても0:00:00形式にしてください。分秒で時がない場合もです。
スマホのスクリーンショットはOSの時計表示が先頭に入ることもあり、注意が必要です。
距離やタイムに関連するラベルが近くに配置されていることも目印になります。
タイムの時分秒のうち、時が上付数字(¹)で認識される分とつながって見えるケースがあります。
`;

これまでせっせと、正規表現とGoogle Apps Scriptで書いていた内容を日本語の指示で書いている感じですね。

そして、API呼び出し部分です。

chatgpt.gs
// ChatGPTにプロンプトとOCRされたテキストを与えて距離とタイムを取得
function detectTimeAndDistanceByGPT(text) {
  //ChatGPTのAPIのエンドポイントを設定
  const apiUrl = 'https://api.openai.com/v1/chat/completions';
  //ChatGPTに投げるメッセージを定義(ユーザーロールの投稿文のみ)
  const messages = [{'role': 'user', 'content': prompt + text}];
  //OpenAIのAPIリクエストに必要なヘッダー情報を設定
  const headers = {
    'Authorization':'Bearer '+ OPENAI_API_KEY,
    'Content-type': 'application/json',
    'X-Slack-No-Retry': 1
  };
  //ChatGPTモデルやトークン上限、プロンプトをオプションに設定
  const options = {
    'muteHttpExceptions' : true,
    'headers': headers, 
    'method': 'POST',
    'payload': JSON.stringify({
      //'model': 'gpt-3.5-turbo',
      'model': 'gpt-4',
      'max_tokens' : 1024,
      'temperature' : 0.9,
      'messages': messages})
  };
  //OpenAIのChatGPTにAPIリクエストを送り、結果を変数に格納
  const response = JSON.parse(UrlFetchApp.fetch(apiUrl, options).getContentText());
  return response.choices[0].message.content;
}

これでChatGPTのAPIからは、以下のようなjsonが得られます。

API Result
{ "distance": "5.04", "duration": "00:24:39" } 

return response.choices[0].message.content;

チャットの最初の応答の中身ですね。

いざ、検証!

これまで数々のパターンマッチ修正をするたびに、テキストと期待する距離・タイムの組み合わせをテストとして残していました。数にして42パターン。

chatgpt.gs
function detectTestGpt(name, result, duration, distance) {
  analized = detectTimeAndDistanceByGPT(result);
  try{
    td = JSON.parse(analized);
    dt = td.duration;
    dd = td.distance;
    console.log(name + ': ' + dd + ', ' + dt + ' result: ' + (dd == distance && dt == duration ? 'OK' : 'NG'));
    return (dd == distance && dt == duration ? 'o' : 'x');
  }
  catch{
    console.log(name + ': ' + analized + ' result: NG');
    return ('x');
  }
}

ChatGPTが以下のような応答をすることがあるため、JSONがどうかにより処理を分岐しています。

このテキストからは、走った距離と時間が明確に抽出できません。テキストには複数の距離が記載されていますが、それらがどのランニングに対応するのか、また時間についても「23:47」や「18:34」など複数の時間が記載されていますが、これが走行時間を示しているのかは不明です。より具体的な情報が必要です。

この場合もjsonで返すようにプロンプトで指示してもよいですね。

ChatGPT APIのモデルとパラメータ

modelとtemptureという二つのパラメータについて、組み合わせで検証してみました。42パターンのうち失敗した数になります。

model/ tempture 0.0 0.9
gpt-3.5-turbo 24 26
gpt-4-0619 22 27

temperature (オプション): モデルの応答の多様性を制御します。値が低いほど、より決定論的な応答が生成されます(0.0に近づくほど決定的になります)、高いほどランダムな応答が生成されます(1.0に近づくほどランダムになります)。※参照元

失敗しているパターンは、Vision AIの読み取り段階で数字が逆さまに認識されているケースや時分秒の時が上付文字で認識されているケースなどで、プロンプトでどうにかするより入力ソースをもっと整えた方がいいような気もしました。

他にも、既存のパターンマッチの返す検出された時間のフォーマットが揃っていなくて、テストコードの比較処理でエラーになっているものもあり、プロンプトで検出できたものはもう少し多いです。
今回はAPI呼び出しをGASでやってみることと、使えそうかどうかを見てみるというのが目的でしたので、これ以上検証コードの調整は行いません。

gpt-4では連続呼び出しに引っかかった

既存のテストパターンを全て実行するテストコードを走らせたところ、途中(29件目とか33件目)でエラーがでました。

{ error:
{ message: 'Rate limit reached for gpt-4 in organization org-fh5PfF4U7Nih5NuphBQZoJZ3 on tokens per min (TPM): Limit 10000, Used 8934, Requested 1316. Please try again in 1.5s. Visit https://platform.openai.com/account/rate-limits to learn more.',
type: 'tokens',
param: null,
code: 'rate_limit_exceeded' } }

エラーメッセージをみると、連続呼び出しによるものでした。

まとめ

GASからChatGPTのAPIを呼び出して、プロンプトを与えて解析処理をすることができました。

また、テキスト生成の用途ではGASで組んでいるアプリでもさまざま使えそうです。

GeminiやClaude 3など、新たな生成AIで直接画像を読ませて解析してもらうこともできそうです。

本プロジェクトとしては、パターンマッチが失敗した時に走らせてみて、結果が取れれば採用してそのことを開発チームがわかるようにすると、ひょっとしたらバックアップになるかもしれないなと思いました。

また時間のあるときに、テストデータの定義を見直してテストコードを共通化し、検出に失敗した時の処理を加えてみたいと思います。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?