#author("2025-07-29T05:31:45+02:00","","")
#author("2025-07-30T06:56:18+02:00","","")
*技術資料:嗜好学習と多目的最適化を用いたパーソナライズ献立推薦システム [#u2b51e8a]


#html
<iframe src="(http://dic515s1.pu-toyama.ac.jp/IIE.IS.WIKI/250605_wiki_lab.php?plugin=attach&pcmd=open&file=interactive_report.html&refer=%E8%BE%BB%E3%81%95%E3%82%93%E5%8D%92%E8%AB%96)" width="100%" height="800px" style="border:1px solid #ccc;"></iframe>

↑技術資料これです
*目次 [#c7a9f3b1]
#CONTENTS

**1. 目的 [#purpose]
食に対する個人の嗜好は、特定の食材の好みから調理法、食事の時間帯に至るまで、非常に多様である。従来の推薦システムは栄養バランスやコストといった静的な指標を主軸としていたが、これだけではユーザー一人ひとりの満足度を最大化することは難しい。本研究では、ユーザーからの評価履歴を基に、コンテンツベースフィルタリング(TF-IDF)を用いてユーザーの「味の好み」を学習する嗜好モデルを構築する。そして、この嗜好モデルと多目的最適化(遺伝的アルゴリズム NSGA-II)を組み合わせることで、「コスト」「調理時間」「ユーザーの好み」といった、時に相反する複数の目的を同時に満たす、パーソナライズされた献立を推薦するシステムの構築を目的とする。
~

**2. システム概要 [#overview]
本システムは、ユーザーの嗜好を学習する機械学習エンジンと、最適な献立を探索する多目的最適化エンジンから構成される。全体の処理フローは以下の通り。

''ユーザー情報入力'': 2.1献立作成(Content-Based Filtering).py のGUIを通じて、ユーザーは身体情報、アレルギー、健康上の懸念、および献立作成で重視する目的(例:「コスト」と「好みスコア」)を入力する。

''レシピデータのフィルタリング'': システムは、入力されたアレルギー・病気の制約に基づき、約400件のレシピデータベースから不適切なレシピを予め除外する。

''嗜好モデルの構築'':
-- 各レシピを、食材や「主菜」「朝食」といったタグの集合として表現する。
-- TF-IDFアルゴリズムを用い、各レシピの特徴を数値ベクトルに変換する。
-- 過去の評価が記録されたcdijnklmn_extracted_with_headers.csvを読み込み、高評価レシピと特徴が似ているレシピほど高スコアとなるよう、全レシピに「好みスコア」を算出する。

''多目的最適化による献立生成'':
-- NSGA-IIアルゴリズムが、栄養基準(カロリー、PFCバランス)を満たしつつ、ユーザーが指定した2つの目的(例:「コストの最小化」と「好みスコアの最大化」)を同時に最適化する。
-- これにより、どちらかを優先すればもう一方が犠牲になる「トレードオフ」の関係にある複数の解の中から、誰にも支配されない優れた解の集合「パレート最適解」として、複数の献立候補を生成する。

''結果の出力と可視化'':
-- 生成された献立候補群は、Webブラウザで詳細を確認できるよう、all_details.json および graph_data.json として出力される。
~

**3. 実際の人間が利用する場合の想定フロー [#human-flow]

''献立生成の実行'': ユーザーは 2.1献立作成(Content-Based Filtering).py を実行し、GUIの案内に従って自身の情報や要望を入力する。

''Webサーバーの起動'': 献立生成が完了した後、server1(GraphicalRecipes).py を実行し、ローカルWebサーバーを起動する。

''献立の確認と比較'': ユーザーはWebブラウザで http://127.0.0.1:5000 にアクセスし、提案された複数の献立候補を比較検討し、その日の気分に合ったものを選択する。

''調理と食事'': 選択した献立を調理し、食事を楽しむ。

''評価とフィードバック'': (将来的な拡張)食事後、ユーザーはWebアプリケーション上で各レシピや献立全体の満足度を評価する。この評価は cdijnklmn_extracted_with_headers.csv に記録される。

''継続的な学習'': 次回、ユーザーが献立を生成する際には、更新された評価ファイルが読み込まれる。このループを繰り返すことで、システムはユーザーの好みをより深く学習し、提案の精度を継続的に向上させていく。
~

**4. 使用するファイル全部 [#files]
|扱うデータ|用途|ファイル名|ファイルの場所|
|システム制御|GUIによるユーザー設定、嗜好学習、多目的最適化による献立生成|2.1献立作成(Content-Based Filtering).py|/code|
|Webサーバー|生成された献立をブラウザで表示するためのWebサーバー機能|2.1server(Content-Based Filtering).py|/code|
|データ前処理|(任意)レシピデータに時間帯フラグを自動付与するユーティリティ|add_flags.py|/code|
|実験用|(任意)実験結果の評価CSVを自動生成するユーティリティ|create_feedback.py|/code|
|実験用|(任意)実験結果を分析・集計するユーティリティ|analyze_results.py|/code|
|入力データ|各レシピの栄養素・コスト・タグ等の情報|recipe_noX.csv|/code/data/hyouka/|
|設定データ|GUIで入力したユーザー情報やアレルギー設定を保存|menu_creation_settings.json|/code|
|学習データ|ユーザーの評価履歴を記録し、嗜好学習に利用|cdijnklmn_extracted_with_headers.csv|/code|
|出力データ|生成された献立候補群の詳細情報をJSON形式で保存|all_details.json|/code/static/|
|出力データ|3Dグラフ描画用のノード・リンク情報|graph_data.json|/code/static/|
|出力データ|献立の日数をWebサーバーに渡すための中間ファイル|params.json|/code/static/|
|出力データ|遺伝的アルゴリズムのパレート解の分布を可視化した画像|palate.png|/code|
|Webページ用テンプレート|3Dグラフを表示するメインページのHTML|graph_viewer.html|/code/templates/|
|Webページ用テンプレート|献立詳細を表示するHTML|details_template.html|/code/templates/|
~

**5. システムの実行方法 [#how-to-run]

''事前準備(初回のみ推奨)''

''ライブラリのインストール'': ターミナルで pip install pandas numpy pymoo PySimpleGUI scikit-learn matplotlib を実行する。

''時間帯フラグの付与'': python add_flags.py を実行し、各レシピデータに時間帯情報を自動で付与する。

''献立データの生成''

python "2.1献立作成(Content-Based Filtering).py" を実行する。

表示されるGUIの案内に従い、人数、身体情報、アレルギー、重視する目的などを入力する。

''Webサーバーの起動''

献立生成が完了したら、python server1(GraphicalRecipes).py を実行する。

''ブラウザで確認''

ターミナルに表示されるURL(例: http://127.0.0.1:5000)にウェブブラウザでアクセスする。
~

**6. 使用アルゴリズムの理論的背景 [#algorithms]
本システムは、目的を達成するために、大きく分けて2つのアルゴリズムを中核技術として利用している。

***6.1. 嗜好学習:コンテンツベースフィルタリング(TF-IDF & コサイン類似度) [#alg-tfidf]
ユーザーの嗜好を学習するため、本システムではコンテンツベースフィルタリングの手法を採用している。これは、ユーザーが過去に高く評価したアイテムと「内容が似ている」アイテムを推薦するアプローチである。

''特徴のベクトル化(TF-IDF)'':
まず、各レシピを「食材」と「タグ(例:主菜, is_breakfast)」の集合として捉える。しかし、コンピュータは単語のままでは類似性を計算できないため、TF-IDF (Term Frequency-Inverse Document Frequency) を用いて、これらの特徴を数値ベクトルに変換する。
-- ''TF (Term Frequency)'': あるレシピ内での単語の出現頻度。
-- ''IDF (Inverse Document Frequency)'': その単語がどれだけ多くのレシピに共通して出現するか、という指標。多くのレシピに登場する一般的な単語(例:塩)の重要度は低く、特定のレシピにしか登場しない単語(例:カレールー)の重要度は高くなる。
-- この2つを掛け合わせることで、各レシピを特徴を表す高次元のベクトルとして表現できる。

''類似度の計算(コサイン類似度)'':
次に、ユーザーの評価履歴(cdijnklmn_extracted_with_headers.csv)を基に、「ユーザーの好みプロファイルベクトル」を生成する。これは、高評価したレシピのベクトルを足し合わせ、低評価したレシピのベクトルを引き算するような形で合成される。
最後に、このプロファイルベクトルと、全レシピのベクトルとのコサイン類似度を計算する。これは、ベクトル同士の「向き」がどれだけ似ているかを示す指標であり、この類似度が高いレシピほど、ユーザーの好みに合致する可能性が高いと判断され、高い「好みスコア」が与えられる。

***6.2. 多目的最適化:NSGA-IIとパレート最適解 [#alg-nsga2]
献立作成は、単一の目的だけでは評価できない複雑な問題である。本研究では、以下の目的を同時に最適化する必要がある。

目的1: コスト をできるだけ安くしたい (最小化)

目的2: 調理時間 をできるだけ短くしたい (最小化)

目的3: 好みスコア をできるだけ高くしたい (最大化)

これらの目的は互いに「トレードオフ」の関係にある。このような問題では、全ての目的で最良となる「唯一の完璧な解」は通常存在しないため、「どの解にも支配されていない、優秀な解」の集合である**「パレート最適解」**を求めることがゴールとなる。

本研究では、このパレート最適解を効率的に探索するため、遺伝的アルゴリズムの一種であるNSGA-II (Non-dominated Sorting Genetic Algorithm II) を採用した。NSGA-IIは、生物の進化を模倣し、「交叉」や「突然変異」といった操作を繰り返しながら、解の集団全体を徐々に真のパレートフロントへと進化させていく。
~

**7. 主要プログラムの詳細解説 [#program-details]
(このセクションでは、2.1献立作成(Content-Based Filtering).py や server1(GraphicalRecipes).py などの主要なスクリプトについて、コードの構成や各関数の役割を一つ一つ詳細に記述します。)
~
7.1. 2.1献立作成(Content-Based Filtering).py
役割
このスクリプトは、本システムの中核をなすメインエンジンである。以下の機能を順次実行する。

GUIを通じてユーザーから個人情報や要望(制約条件)を受け取る。

全レシピデータから、ユーザーの制約に基づき不適切なものを除外する。

ユーザーの過去の評価履歴を基に、機械学習(TF-IDF)を用いて各レシピの「好みスコア」を算出する。

遺伝的アルゴリズム(NSGA-II)を用い、「コスト」「時間」「好みスコア」といった複数の目的を同時に満たす最適な献立の組み合わせを複数探索する。

最終的な献立候補を、Webアプリケーションで可視化できるJSON形式で出力する。

プログラムの構成と処理フロー
このスクリプトは、大きく分けて8つの論理的なステップで構成されている。

ステップ1: 初期設定とライブラリのインポート
-''役割'': プログラムの実行に必要な外部ライブラリをインポートし、後続の処理で利用するグローバルな設定(設定ファイル名など)を定義する。

-''主要なコード'':

import PySimpleGUI as sg
import pandas as pd
import numpy as np
import json
import os
import re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.core.problem import ElementwiseProblem
from pymoo.optimize import minimize
# (その他、標準ライブラリ)

SETTINGS_FILE = 'menu_creation_settings.json'

-''コードの解説'':
-- PySimpleGUI: ユーザーからの入力を受け付けるためのGUI(Graphical User Interface)を構築する。
-- pandas, numpy: レシピデータの読み込み、加工、数値計算など、データ処理全般を担う。本システムでは、レシピ情報を格納したDataFrameの操作が中心となる。
-- sklearn.feature_extraction.text.TfidfVectorizer: テキストデータ(食材やタグ)を数値ベクトルに変換するTF-IDFアルゴリズムを利用するためにインポートする。
-- sklearn.metrics.pairwise.cosine_similarity: ベクトル間の類似度を計算するために使用。嗜好プロファイルと各レシピベクトルの類似度計算に用いる。
-- pymoo: 多目的最適化(Multi-objective Optimization)を行うためのライブラリ。遺伝的アルゴリズムの一種であるNSGA2や、最適化問題を定義するためのElementwiseProblemなどを利用する。
-- SETTINGS_FILE: GUIで入力した内容を保存・読み込みするためのJSONファイル名をグローバル変数として定義する。

ステップ2: ユーザー情報の入力 (GUI)
-''役割'': PySimpleGUIを用いて、ユーザーから献立作成に必要な情報を対話形式で取得する。このステップで得られた情報が、後続の全ての処理の基礎となる。

-''主要なコード'':

# (layout1, layout2, ... といったGUIのレイアウト定義)

# ユーザーごとの情報入力ループ
for i in range(ninzu):
    # ... (個人情報入力ウィンドウの表示と値の取得) ...
    
    # 日本人の食事摂取基準に基づき、個人の推定エネルギー必要量(EER)を計算
    # 例: 30-49歳男性、身体活動レベル「普通」の場合
    # 基礎代謝基準値: 22.3, 身体活動レベル: 1.75
    # EER = 22.3 * 体重(kg) * 1.75
    eer = fma * actlevel 
    
    # PFCバランスの目標値を計算 (例: たんぱく質13%, 脂質20-30%, 炭水化物50-65%)
    rtanpakulist.append((eer * 0.13) / 4) # たんぱく質 (g)
    rsisitulist.append((eer * 0.20) / 9) # 脂質 (g)
    rtansuilist.append((eer * 0.50) / 4) # 炭水化物 (g)

    # ... (アレルギー・病気入力ウィンドウの表示と値の取得) ...

-''コードの解説'':
-- PySimpleGUIのWindowオブジェクトとイベントループ(window.read())を用いて、複数のウィンドウを順次表示し、ユーザーからの入力を受け付ける。
-- EERの算出: 厚生労働省の「日本人の食事摂取基準」に基づき、性別・年齢階級ごとの「基礎代謝基準値」と、ユーザーが選択した「身体活動レベル」を用いて、個人に最適化された1日の推定エネルギー必要量(EER)を算出する。このEERが、献立全体のカロリー計算の基準となる。
-- PFCバランスの算出: EERを基に、たんぱく質(Protein)、脂質(Fat)、炭水化物(Carbohydrate)の目標摂取量をグラム単位で計算する。これらの値は、最適化計算時の制約条件として使用される。
-- 目的の選択: ユーザーが選択した2つの目的(例:「コスト」と「好みスコア」)は、変数a1, a2に格納され、後の最適化問題定義でどの目的関数を使用するかを決定するために用いられる。

ステップ3: 食材データベースの構築とアレルギー検索の準備
-''役割'': 全レシピデータの中から、アレルギーの原因となる食材を含むレシピを高速に特定するため、事前に全レシピの食材情報のみをメモリ上に展開し、検索可能なデータベースを構築する。

-''主要なコード'':

# 全レシピの食材情報を格納するリスト
qij = [['' for _ in range(25)] for _ in range(R)]

# 全レシピファイルをループし、食材名(5列目)のみを抽出
for j in i_range:
    try:
        df = pd.read_csv(...)
        for m in v:
            qij[j][m] = df.iloc[m, 4]
        # ... (不要な文字列の除去) ...
    except:
        continue

# 検索用のDataFrameを作成
pd_name = pd.DataFrame(data=qij, index=i_range)
pd_name.insert(0, 'name', yi)
pd_name.insert(0, 'number', i_range)

# 特定の食材を含むレシピ番号をリストアップする関数
def kensaku(x, y):
    c = []
    for j in range(24):
        a = pd_name[j].str.contains(x, na=False)
        b = pd_name.loc[a, 'number']
        c = b.values.tolist()
        y.extend(c)
    return y

-''コードの解説'':
-- 最初に全レシピの食材情報だけを読み込むことで、後の詳細データ読み込みと処理を分離し、プログラムの見通しを良くしている。
-- kensaku関数は、pandasの強力な文字列検索機能(str.contains)を利用して、指定されたキーワード(例:「卵」)を含む行を効率的に探し出し、そのレシピ番号をリストとして返す。この関数が、アレルギー対応の根幹を担う。

ステップ4: レシピデータベースの構築とフィルタリング
-''役割'': 全レシピの詳細データを読み込んでマスターデータベースを構築し、ステップ2で得たユーザーの制約条件(アレルギー、病気)に基づいて不要なレシピを除外する。

-''主要なコード'':

# レシピ詳細を格納するリストを初期化
recipe_details_list = []
for idx in range(R):
    details = {
        'original_index': idx, 'レシピの名前': yi[idx], '主菜フラグ': sigmai[idx],
        # ... (コスト、時間、栄養素など全データを格納) ...
        'meal_type_flag': meal_type_i[idx] 
    }
    recipe_details_list.append(details)

# 全情報を格納したマスターDataFrameを作成
df_recipe = pd.DataFrame(recipe_details_list)

# アレルギー・病気情報に基づいて除外するレシピのインデックスを取得
if arerugi: # arerugiリストには除外すべきレシピ番号が格納されている
    indices_to_drop = df_recipe[df_recipe['original_index'].isin(arerugi)].index
    df_recipe.drop(indices_to_drop, inplace=True)
    df_recipe.reset_index(drop=True, inplace=True)

-''コードの解説'':
-- まず、全レシピの全データを読み込み、df_recipeというマスターDataFrameを生成する。この時点では、まだ全レシピが含まれている。
-- arerugiリスト(ステップ2で生成)に格納された、除外すべきレシピの番号を元に、df_recipeから該当する行をdrop関数で一括削除する。これにより、以降の処理対象は、ユーザーが食べられるレシピのみに限定される。

ステップ5: 嗜好学習モデルの構築 (TF-IDF)
-''役割'': scikit-learnライブラリを用い、ユーザーの好みを学習して各レシピに「好みスコア」を付与する。

-''主要なコード'':

# 1. 各レシピの特徴をテキスト化
corpus = []
for i, recipe_row in df_recipe.iterrows():
    ingredients = [ing for ing in qij[recipe_row['original_index']] if str(ing) not in ["nan", "NAN", ""]]
    context_tags = []
    if recipe_row['主菜フラグ'] == 1:
        context_tags.append("is_main_dish")
    if recipe_row['meal_type_flag'] == 1:
        context_tags.append("is_breakfast")
    # ... (他のタグも同様に追加) ...
    full_features = ingredients + context_tags
    corpus.append(" ".join(full_features))

# 2. TF-IDFでベクトル化
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(corpus)

# 3. 評価履歴から好みスコアを計算
if os.path.exists(feedback_file):
    feedback_df = pd.read_csv(feedback_file, ...)
    # ... (高評価/低評価レシピのベクトルを重み付けして合成し、user_profile_vectorを作成) ...
    
    # 4. コサイン類似度を計算
    taste_scores = cosine_similarity(tfidf_matrix, user_profile_vector).flatten()

-''コードの解説'':
-- 特徴のテキスト化: 各レシピを、単なる食材のリストではなく、「主菜である」「朝食向きである」といった文脈的なタグも加えた、よりリッチな特徴を持つテキスト(corpus)に変換する。これが嗜好学習の精度を高める鍵となる。
-- ベクトル化: TfidfVectorizerが、このテキストのリストを、各単語の重要度を数値化したベクトルの集まり(行列)に変換する。
-- スコア計算: ユーザーの評価履歴から「好みプロファイルベクトル」を算出し、それと全レシピベクトルとのコサイン類似度を計算することで、各レシピに対するパーソナライズされた「好みスコア」が算出される。

ステップ6: 多目的最適化問題の定義 (Pymoo)
-''役割'': 遺伝的アルゴリズムが解くべき「問題」をpymooのElementwiseProblemクラスを継承して定義する。

-''主要なコード'':

class SubsetProblem(ElementwiseProblem):
    def __init__(self, obj1_vals, obj2_vals, n_max, ...):
        # 目的関数の数=2, 制約条件の数=5 で初期化
        super().__init__(n_var=len(obj1_vals), n_obj=2, n_constr=5, **kwargs)
        # ... (コスト、時間、カロリーなどのデータをインスタンス変数として保持) ...

    def _evaluate(self, x, out, *args, **kwargs):
        # xは、レシピを選ぶ(True)/選ばない(False)を示すブール配列
        
        # 目的関数の計算
        # obj1_valsはコストのリスト、obj2_valsは好みスコアのリストなど
        f1 = np.sum(self.obj1_vals[x])
        f2 = np.sum(self.obj2_vals[x])
        out["F"] = [f1, f2]

        # 制約条件の計算 (違反している場合に正の値になるように設計)
        # g1: たんぱく質が目標値に達しているか
        g1 = self.tanpakumin * self.day - np.sum(self.f0[x])
        # g2: 脂質が目標値に達しているか
        g2 = self.sisitumin * self.day - np.sum(self.f1[x])
        # g3: 炭水化物が目標値に達しているか
        g3 = self.tansuimin * self.day - np.sum(self.f2[x])
        # g4: カロリーが目標値-200kcalより低いか (下限)
        g4 = (self.eer * self.day) - 200 - np.sum(self.cal[x])
        # g5: カロリーが目標値+200kcalより高いか (上限)
        g5 = np.sum(self.cal[x]) - ((self.eer * self.day) + 200)
        out["G"] = [g1, g2, g3, g4, g5]

-''コードの解説'':
-- _evaluateメソッドがこのクラスの心臓部。遺伝的アルゴリズムが生成した一つの解(献立の組み合わせx)を受け取り、その解がどれだけ良いかを「目的関数(F)」と「制約条件(G)」の2つの観点から評価して返す。
-- 目的関数: ユーザーがGUIで選択した2つの指標(コスト、時間、好みスコアなど)の合計値を計算する。pymooは最小化問題を解くため、「好みスコア」のような最大化したい指標は、事前に符号を反転させておく。
-- 制約条件: 献立全体の栄養バランスが、ステップ2で計算した個人ごとの目標範囲内に収まっているかを評価する。out["G"]の値がすべて0以下であれば、その解は全ての制約を満たした「実行可能な解」と見なされる。

ステップ7: 遺伝的アルゴリズムの実行
-''役割'': 定義した問題とアルゴリズムに基づき、最適化計算を実行し、パレート最適解を得る。

-''主要なコード'':

# アルゴリズムのインスタンスを生成
algorithm = NSGA2(
    pop_size=R, # 集団のサイズはレシピ数と同等に設定
    sampling=MySampling(),
    crossover=BinaryCrossover(),
    mutation=MyMutation(),
    eliminate_duplicates=True
)

# 最適化を実行
res = minimize(
    problem,
    algorithm,
    ('n_gen', gen), # 世代数(計算回数)を指定
    seed=1,
    verbose=True
)

-''コードの解説'':
-- NSGA2アルゴリズムに、独自に定義した交叉・突然変異などの操作方法を渡して初期化する。
-- minimize関数が、指定された世代数(gen)にわたって進化計算を繰り返し実行する。
-- 最終的に、得られたパレート最適解の情報がresオブジェクトに格納される。

ステップ8: 献立の最終組み立てとJSON出力
-''役割'': AIが選んだレシピのリスト(インデックス番号の集まり)を、人間が理解しやすい日ごと・食事ごとの献立形式に整形し、Webアプリケーション用のJSONファイルとして出力する。

-''主要なコード'':

# パレート解のループ
for idx, p_indices in enumerate(parate):
    # STEP 1: レシピを時間帯ごとに仕分ける
    breakfast_pool, lunch_pool, dinner_pool, other_pool = [], [], [], []
    for recipe_index in p_indices:
        flag = df_recipe.iloc[recipe_index]['meal_type_flag']
        if flag == 1: breakfast_pool.append(recipe_index)
        # ... (昼食、夕食、その他も同様) ...
    
    # STEP 2: 時間帯を考慮して献立を組み立てる
    daily_menus = []
    for d_idx in range(day):
        day_menus_split = {"朝食": [], "昼食": [], "夕食": []}
        # 朝食の割り当て (専用プールを優先し、足りなければ予備プールから補充)
        if breakfast_pool:
            day_menus_split["朝食"].append(breakfast_pool.pop(0))
        elif other_pool:
            day_menus_split["朝食"].append(other_pool.pop(0))
        # ... (昼食、夕食も同様に割り当て) ...
        
        # STEP 3: 詳細情報を取得してJSON形式にまとめる
        # ... (レシピ名や栄養素、画像URLなどを取得し、整形) ...
    
    all_candidates_details.append(...)

# 最終的なJSONファイルへの書き出し
with open('static/all_details.json', 'w', ...) as f:
    json.dump(all_candidates_details, f, ...)

-''コードの解説'':
-- このブロックは、最適化計算の結果(単なるレシピ番号のリスト)を、ユーザーが直感的に理解できる「N日分の献立」という構造に変換する、重要な後処理である。
-- 賢い割り当て: 単にリストの先頭から機械的に割り当てるのではなく、各レシピが持つ「時間帯フラグ」を尊重する。朝食には「朝食向き」と分類されたレシピを優先的に割り当てることで、提案の質と納得感を大幅に向上させている。
-- JSON出力: 整形された献立データは、最終的にall_details.jsonとgraph_data.jsonという2つのファイルに出力され、2.1server(Content-Based Filtering).pyがこれを読み込んでWebページを生成する。
**8. 実験設定と結果 [#results]
構築した献立推薦システムの有効性を検証するため、特性の異なる2名の仮ユーザーを対象としたシミュレーション実験を行った。

***8.1. 実験1:制約条件の遵守テスト [#results-exp1]
''- 目的'':
ユーザーが設定したアレルギーや健康上の制約を、システムが確実に遵守できるか検証する。

''- 実験設定'':
-- ''仮ユーザーA'': 40代女性、卵アレルギーを持ち、高血圧を気にしている。
-- ''制約条件'': ①「卵」を含むレシピの除外、②1食あたりの塩分量が2.0gを超えるレシピの除外。

''- 結果'':
ユーザーAの設定で3日分の献立を生成させ、提案された全21品のレシピを検証した結果、アレルギー食材である「卵」を含むものは''0件''、塩分量が基準値2.0gを超えたものも''0件''であり、''制約遵守率は100%''となった。
| 検証項目 | 総レシピ数 | 違反件数 | 制約遵守率 |
|:--- | :--- | :--- | :--- |
| 卵アレルギー | 21品 | 0品 | '''100%''' |
| 高血圧(塩分≦2.0g) | 21品 | 0品 | '''100%''' |

''- 考察'':
この結果から、本システムのアレルギー・病気情報に基づくレシピのフィルタリング機能が、ユーザーの安全性を確保する上で、極めて正確かつ確実に動作していることが実証された。

***8.2. 実験2:嗜好学習能力の検証 [#results-exp2]
''- 目的'':
ユーザーからの評価フィードバックに基づき、システムの提案内容が改善されるかを定量的に検証する。

''- 実験設定'':
-- ''仮ユーザーB'': 和食を好み、特定の洋食を苦手とする。
-- ''評価指標'': 提案された献立に含まれるレシピの「好み合致率」を算出する。
-- ''手順'': ①評価フィードバックがない状態(学習前)と、②提案結果に対する仮想の評価を与えた後(学習後)の2段階で献立を提案させ、両者の結果を比較する。

''- 結果'':
学習前後で提案された献立候補群における「好み合致率」を算出した結果、以下の表に示す通り、顕著な改善が見られた。
| 評価対象 | 提案レシピ総数 | 好み合致レシピ数 | 好み合致率 |
|:--- | :--- | :--- | :--- |
| '''学習前''' | 42 品 | 4 品 | '''9.52%''' |
| '''学習後''' | 189 品 | 46 品 | '''24.34%''' |

''- 考察'':
学習前に9.52%であった好み合致率は、一度のフィードバック学習を経ることで''24.34%へと約2.5倍に向上''した。24.34%という絶対値は一見して高いとは言えないかもしれない。しかし、これはシステムが''①栄養バランス、②コスト、③好みスコアという、時に相反する複数の目的の最適なバランス点''を探索した結果である。特に「コスト」を抑えるという要求は、特定の食材を必要とする「好み」の実現とトレードオフの関係にある。このような多角的かつ厳しい制約下で、一度の学習で好みの反映率が2.5倍に向上したという事実は、''本システムの嗜好学習メカニズムが有効に機能していることを明確に示している。''

***8.3. 総合結論 [#results-conclusion]
以上の実験結果から、本研究で開発したシステムは、ユーザーの安全性を担保する''制約遵守能力''と、対話を通じて提案の質を向上させる''嗜好学習能力''を両立していることが実証された。これは、本システムが単なるレシピ推薦に留まらず、各ユーザーに寄り添い、継続的に最適化されていくパーソナライズド・システムとしての有効性を持つことを示唆するものである。
~


トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS