第4回:ChatGPTで2万件のレシピを「時短版」に自動変換する

LLMが変えたもの

コサイン類似度で栄養バランスを計算し、LSTMで献立の時系列パターンを予測する。Part 1からPart 3まで、当時の機械学習技術を駆使して家庭の献立問題に取り組んできました。

数年が経ち、世界は変わりました。ChatGPTの登場です。

「あの2万件のレシピデータ、ChatGPTで何かできないだろうか」

ふとそう思い、同じプロジェクトをLLMの視点から再訪することにしました。


新たな課題:レシピが「立派すぎる」問題

改めてデータベースに蓄積された2万件のレシピを見直したとき、一つの問題に気づきました。

これらのレシピの多くは、料理本やレシピサイトから収集したものです。見栄えが良く、手順も丁寧に書かれています。しかし、平日の夕食に作るには手間がかかりすぎるものが多いのです。

共働き世帯や育児中の主婦にとって、「鶏もも肉を30分マリネしてからオーブンで40分焼く」というレシピは現実的ではありません。仕事や家事を終えて帰宅してから調理を始める日常の中では、「15分で作れる」「材料が少ない」「工程がシンプル」といった条件が求められます。

しかし、レシピの「簡略化」は単純な作業ではありません。調理手順を短くするだけでなく、味が大きく損なわれないように代替手法を提案し、材料の分量も調整する必要があります。これは人間がやると1件あたり数分かかる作業で、2万件を手作業で処理するのは非現実的です。

「レシピの内容を理解して、より簡単なバージョンに書き換える」——これはまさにLLMが得意とする仕事ではないか。


ChatGPT APIによるレシピ変換

OpenAIのChat Completions APIを使い、レシピを一括で「時短版」に変換するスクリプトを実装しました。

from openai import OpenAI
import json
import pandas as pd

client = OpenAI(api_key="your-api-key")

df = pd.read_csv('dataset/recipe_master.csv')
df = df[(df['recipe_id'] >= start_recipe_id) & (df['recipe_id'] <= end_recipe_id)]

df['name_new'] = ''
df['recipe_new'] = ''
df['ingredients'] = ''

for index, row in df.iterrows():
    try:
        chat_completion = client.chat.completions.create(
            messages=[{
                "role": "user",
                "content": f"元のレシピ:{row['recipe']}\nを基に、より簡単に作れるレシピにアレンジしてください。"
                           f"さらに、この簡単なレシピにぴったりなキャッチーな名前を考えてください。"
                           f"そして、材料を二人分で次のようなJSONフォーマットでリストアップしてください:\n\n"
                           f"{{'鶏もも肉': '200g', 'サツマイモ(中サイズ)': '1本', ...}}\n\n"
                           f"応答は 'recipe_new'、'name_new'、'ingredients' のキーを持つ"
                           f"JSONオブジェクトとしてフォーマットしてください。"
            }],
            model="gpt-3.5-turbo",
        )

        response = chat_completion.choices[0].message.content
        response_data = json.loads(response)
        df.at[index, 'name_new'] = response_data['name_new']
        df.at[index, 'recipe_new'] = response_data['recipe_new']
        df.at[index, 'ingredients'] = response_data['ingredients']
    except json.JSONDecodeError:
        print(f"JSON format error at recipe_id {row['recipe_id']}")

df.to_csv('dataset/recipe_master_updated.csv', index=False)

1回のAPIコールで、元のレシピから3つの情報を同時に生成しています。

  1. recipe_new: 簡略化されたレシピ手順
  2. name_new: 時短版にふさわしいキャッチーな料理名
  3. ingredients: 二人分の材料リスト(JSON形式)

プロンプト設計のポイント

このプロンプトには、いくつかの設計上の判断があります。

なぜJSON形式で出力させるか

自由形式のテキストで返答させると、後処理が大変になります。「材料:鶏もも肉 200g、サツマイモ 1本…」のようなテキストから材料名と分量を正規表現で抽出するのは、表記ゆれへの対応だけでも相当な手間です。

JSON形式を明示的に指定することで、json.loads() で一発パースできます。構造化データとして扱えるため、後から「特定の材料を含むレシピ」を検索したり、買い物リストを自動生成したりといった応用が容易になります。

二人分を指定する理由

レシピサイトによって「4人分」「2〜3人分」「1人分」と基準がばらばらです。全レシピを統一的に「二人分」で出力させることで、材料データの比較や集計が可能になります。

エラーハンドリング

ChatGPTは指示に従わず、JSON以外の形式で回答することがあります。特にレシピが長い場合や複雑な場合に、説明文を付け加えてしまうことがありました。

except json.JSONDecodeError:
    print(f"JSON format error at recipe_id {row['recipe_id']}")

json.JSONDecodeError をキャッチして、パースに失敗したレシピはスキップし、後から個別に再処理する方針にしました。全体の5%程度でパースエラーが発生しましたが、再実行で大半は解消されました。


変換結果の例

実際にChatGPTが生成した時短レシピの例です。

例1:チキンのオーブン焼き → フライパン照り焼き

元のレシピ: 鶏もも肉をハーブとオリーブオイルで30分マリネし、200度のオーブンで40分焼く。付け合わせの野菜も別途ロースト。

時短版(name_new: 「とろ照りチキン」): フライパンで鶏もも肉を皮目から焼き、醤油・みりん・砂糖で照り焼きにする。横で冷凍ブロッコリーをレンジ加熱。合計15分。

例2:手作りシチュー → 時短クリーム煮

元のレシピ: ルーから手作り。バターと小麦粉でホワイトソースを作り、野菜を30分煮込む。

時短版(name_new: 「ほっこりクリーム煮」): 市販のシチューのルーを使い、野菜は薄切りにして煮込み時間を10分に短縮。牛乳を多めに入れてクリーミーに仕上げる。

ChatGPTは単に手順を省略するだけでなく、「オーブン → フライパン」「手作りルー → 市販ルー」のように調理手法自体を時短に適したものに置き換えています。これは、レシピの「意味」を理解しているからこそ可能な変換です。


バッチ処理の戦略

2万件のレシピを一度に処理するのは現実的ではありません。API呼び出しにはレートリミットがあり、途中でエラーが発生した場合に全データをやり直すのは非効率です。

そこで、レシピIDの範囲を指定して分割処理する方式を採りました。

# 例: recipe_id 2701〜3000 を処理
df = df[(df['recipe_id'] >= 2701) & (df['recipe_id'] <= 3000)]

300件ずつのバッチで実行し、各バッチの結果をCSVに保存。問題が発生したバッチだけを再実行できるようにしました。

処理速度はgpt-3.5-turboで1件あたり約2〜3秒。300件のバッチで約10〜15分、2万件全体で約11〜17時間の計算になります。一晩回しておけば翌朝には完了しているという運用です。

コスト面では、gpt-3.5-turboの入出力トークンあたりの料金で計算すると、2万件の変換全体で数ドル程度でした。人間が同じ作業をする場合のコストを考えれば、圧倒的に効率的です。


10年のプロジェクトを振り返る

ここで一歩引いて、このレシピAIプロジェクト全体を振り返ってみます。

時期手法アプローチ解決した問題
初期コサイン類似度栄養素ベクトルの類似度計算「似た栄養バランスのレシピ」を発見
中期LSTM時系列パターンの学習「飽きの来ない献立」の予測
現在ChatGPT API自然言語理解による変換「忙しい日でも作れるレシピ」への変換

技術は大きく変わりました。コサイン類似度の計算はNumPyで数行、LSTMはKerasで数十行、ChatGPT APIも数十行。コード量だけ見れば大差ありません。

しかし、できることの質が根本的に変わっています

コサイン類似度やLSTMでは、数値データのパターンを扱っていました。栄養素の数値、メニューIDの並び順。人間が理解できる「意味」は扱えませんでした。

ChatGPTは、レシピの「意味」を理解します。「オーブンで40分焼く」という手順が時間がかかることを理解し、「フライパンで焼く」という代替手法を提案できます。「手作りルー」が手間だと判断し、「市販ルー」を提案できます。

自然言語の「意味」を計算できるようになったこと。 これがLLMの本質的な変化であり、同じ「献立の問題」に対するアプローチが根本から変わった理由です。


良いAIプロジェクトは技術からではなく問題から始まる

この連載を通じて最もお伝えしたかったことは、技術の詳細ではなく、一つの姿勢です。

このプロジェクトは、「機械学習を使ってみたい」から始まったのではありません。**「毎日の献立を考えるのが大変」**という、家庭で料理を担当する人なら誰もが感じる、具体的で切実な問題から始まりました。

その問題が変わらないまま、使える技術が進化していきました。コサイン類似度で解決できた部分がある。LSTMで解決できた部分がある。そしてChatGPTで解決できた部分がある。

良いAIプロジェクトは、技術の流行を追いかけるのではなく、解決すべき問題が先にあるのだと思います。問題がリアルであれば、技術が変わっても同じ問題に何度でも立ち返ることができます。そして、新しい技術が登場するたびに、解決の精度や範囲が広がっていきます。

毎日の「何を作ろう」という悩みは、AIが進化しても変わりません。でも、その悩みに対してAIが提供できるサポートは、この10年で確実に進化しました。


第1回:2万件のレシピと主婦の「今日何作ろう」問題 第2回:栄養バランスをベクトルで解く 第3回:LSTMで「飽きの来ない献立」を時系列予測する

この記事をシェア

関連記事