食に対する個人の嗜好は、「珍しいものが食べたい」「調理が簡単なものが良い」など、非常に多様かつ動的である。従来の推薦システムは、栄養バランスやコストといった静的な指標に基づいて献立を提案するものが主であった。本研究では、ユーザーからのフィードバック(満足度評価)を元に、強化学習(多腕バンディットアルゴリズム)を用いて「ユーザーがどのようなタイプの献立を好むか」という提案戦略を学習し、その戦略に基づいて多目的最適化(遺伝的アルゴリズム)が献立を生成する、パーソナライズされた動的な献立推薦システムのコアエンジンを構築・検証することを目的とする。
本システムは、強化学習エージェント(バンディット)と多目的最適化エンジン(遺伝的アルゴリズム)を連携させた、クローズドループのシミュレーション環境である。全体の処理フローは以下の通り。
1. 戦略決定: `bandit_logic.py` が過去の学習記録 `mab_feedback.csv` を参照し、今回最適化すべきUX戦略(腕)を決定する。
2. 献立生成: `2献立作成(GraphicalRecipes).py` が、決定された戦略に基づき多目的最適化を実行し、献立候補群(パレート解)を生成する。
3. 評価: `run_experiment.py` が生成された献立の一つをランダムに選択し、`virtual_user.py` に渡して評価させる。
4. フィードバック: `virtual_user.py` は自身の隠れた好みに基づいて満足度(報酬)を計算し、返す。
5. 学習記録: `run_experiment.py` は、(1)で選択された戦略と、(4)で得られた報酬のペアを `mab_feedback.csv` に追記する。
6. 上記1~5を規定回数繰り返し、学習を行う。
現在のシステムは、`virtual_user.py` を用いた自動シミュレーション環境だが、これを実際のサービスとして人間が利用する場合、以下のようなフローが想定される。 1. ユーザーのログイン: ユーザーがシステムにログインする。ユーザーごとに過去の評価履歴が管理される。 2. 戦略決定: `bandit_logic.py` が、そのユーザーの過去の評価履歴(`mab_feedback.csv`に相当)を読み込み、「今日のあなたへのおすすめ方針」として最適な腕(例:腕3「調理しやすさ重視」)を選択する。 3. 献立生成: `2献立作成(GraphicalRecipes).py` が、選択された戦略に基づいて、複数の優れた献立候補(パレート解)を生成する。 4. 献立の提示: `server1(GraphicalRecipes).py`が起動したWebアプリケーションが、生成された複数の献立候補をユーザーに提示する。ユーザーは気分や状況に合わせて、その中から一つを選ぶ。 5. 調理と食事: ユーザーは選んだ献立を実際に調理し、食事をする。 6. 満足度の評価: 後日、ユーザーはWebアプリケーション上で、前回の献立に対する総合的な満足度を1~5の星などで評価する。 7. フィードバックの記録: `server1(GraphicalRecipes).py`は、そのユーザーの評価(報酬)を、「どの戦略で提案したか」という情報と紐づけて、そのユーザーの学習データとして記録する。
| 扱うデータ | 用途 | ファイル名 | ファイルの場所 |
| システム制御 | シミュレーション全体の制御、各モジュールの呼び出し、結果の記録 | run_experiment.py | (ルート) |
| システム制御 | 多目的最適化による献立生成、GUIによる手動設定 | 2献立作成(GraphicalRecipes).py | (ルート) |
| Webサーバー | 生成された献立をブラウザで表示するためのWebサーバー機能 | server1(GraphicalRecipes).py | (ルート) |
| システム制御 | UCB1バンディットアルゴリズムによる戦略決定 | bandit_logic.py | (ルート) |
| システム制御 | 仮想ユーザーによる献立評価と報酬計算 | virtual_user.py | (ルート) |
| 入力データ | 各レシピの栄養素・コスト・UXスコアの格納 | recipe_noX.csv | (./data/hyouka/) |
| 設定データ | 手動実行時のユーザー情報やアレルギー設定を保存 | menu_creation_settings.json | (ルート) |
| 出力データ(学習ログ) | 強化学習の試行ごとの結果(腕、報酬)を記録 | mab_feedback.csv | (ルート) |
| 出力データ(献立) | 生成された献立候補群の詳細情報をJSON形式で保存 | all_details.json | (./static/) |
| 出力データ(グラフ) | 遺伝的アルゴリズムのパレート解の分布を可視化 | palate.png | (ルート) |
1.必要なライブラリをインストールする.
pip install pandas numpy pymoo PySimpleGUI japanize-matplotlib flask
2.(任意・初回のみ)ユーザー設定ファイルを作成する.
python 2献立作成(GraphicalRecipes).py
本システムには、大きく分けて2つの実行モードがある。
【A】シミュレーション実験を実行する場合(開発者・研究者向け)
python run_experiment.py
【B】献立推薦システムを実際に利用する場合(一般ユーザー向け)
python 2献立作成(GraphicalRecipes).py --auto2. Webサーバーの起動: 次に、以下のコマンドを実行してWebサーバーを起動する。
python server1(GraphicalRecipes).py3. ブラウザで確認: ターミナルに表示されるURL(例: `http://127.0.0.1:5000`)にウェブブラウザでアクセスし、献立の確認や評価を行う。
import subprocess
import os
import json
import random
import csv
from datetime import datetime
from virtual_user import get_satisfaction
import pandas as pd
# --- 実験設定 ---
NUM_TRIALS = 200 # 実験の繰り返し回数
MAB_FEEDBACK_FILE = 'mab_feedback.csv'
def run_single_trial():
# ... (関数の実装は省略) ...
if __name__ == '__main__':
if os.path.exists(MAB_FEEDBACK_FILE):
os.remove(MAB_FEEDBACK_FILE)
print(f"古い {MAB_FEEDBACK_FILE} を削除し、実験を初期化しました。")
print(f"\n===== {NUM_TRIALS}回のシミュレーション実験を開始します =====")
for i in range(NUM_TRIALS):
print(f"\n---【 試行 {i + 1}/{NUM_TRIALS} 】---")
success = run_single_trial()
if not success:
break
print(f"\n===== 実験終了 =====")
# ... (最終結果の集計処理) ...from flask import Flask, render_template, request, json, redirect, url_for, session import pandas as pd import os import csv from datetime import datetime app = Flask(__name__) app.secret_key = 'your_secret_key' # セッション管理のための秘密鍵
@app.route('/')
def index():
# ... (省略) ...
@app.route('/details')
def details():
# ... (省略) ...
@app.route('/save_survey', methods=['POST'])
def save_survey():
try:
form_data = request.form
# 1. フォームから総合満足度(報酬)を取得
reward = form_data.get('overall_satisfaction')
# 2. どの戦略(腕)が使われたかをファイルから取得
try:
with open('last_chosen_arm.txt', 'r', encoding='utf-8') as f:
chosen_arm = f.read().strip()
except FileNotFoundError:
chosen_arm = -1
# 3. 報酬と腕の情報を mab_feedback.csv に追記
if reward and chosen_arm != -1:
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# ... (mab_feedback.csvへの追記処理) ...
print(f"MABフィードバックを保存しました: arm={chosen_arm}, reward={reward}")
# 4. レシピごとの詳細なアンケート結果を別途保存
# ... (フォームからq1, q2..の回答を解析し、整形して別ファイルに保存する処理) ...
return "<h3>アンケートへのご協力、ありがとうございました!</h3>"
except Exception as e:
return f"サーバー内部でエラーが発生しました: {e}", 500
if __name__ == '__main__':
app.run(debug=True, port=5000)
# (前述の通り)
# (前述の通り)
200回のシミュレーションを実行した結果、各腕の選択回数は以下のようになった。
#ref(): File not found: "final_arm_selection_chart.png" at page "辻さん卒論"
(ここに、最終的な腕の選択回数の集計結果のグラフ画像を挿入)
#ref(): File not found: "final_arm_selection_table.png" at page "辻さん卒論"
(ここに、最終的な腕の選択回数の集計結果の表画像を挿入)
この結果から、本システムは仮想ユーザーの最も重要な好みである「調理しやすさ(腕3)」を最適戦略として正しく学習し、最も多く選択(42.0%)したことが確認できる。同時に、他の戦略も継続的に探索しており、活用と探索のバランスが機能していることが示された。