更新日:2015/12/11

はじめに

近年,Webレシピの世界的な急増を受け,レシピを対象とした研究が盛んにおこなわれるようになってきました.特に,Web上で急増しているレシピの多くは一般のユーザが自ら製作して投稿したユーザ投稿型レシピです.しかし,料理の専門家でないユーザが投稿しているため,表現が多様であり誤記も多く,手動で構築したルールでの解析は難しいと考えられます.そこで私たちは,ユーザ投稿型レシピによりレシピフローグラフコーパスを構築し,機械学習をベースとした解析手法を提案しました.
本マニュアルは,私たちが提案した手法を用いて,調理手順文書を,その手順の流れを表わすレシピツリーに変換する手順について述べたものです.私たちは,レシピツリーを用いることで,レシピの検索や要約.調理行動認識,調理手順のスケジューリングなど,料理にまつわる様々な課題に応用できると考えています.
本マニュアルに従って処理を行うと,例えば,以下のような結果が得られます.
  • 入力
    糸蒟蒻を下茹でします。
    ...
  • 出力されるレシピツリーデータ
    1,2/糸,蒟蒻\t5/茹で\t2
    ...

    上記出力の書式と意味を以下に示します.
    【書式】
    [ID1]/[レシピ用語の表記1]\t[ID2]/[レシピ用語の表記2]\t[EdgeType]
    ...
    ※ここで,"\t"はタブ文字を表します.
    
    【意味】
    各行が1つのエッジを表しており,[ID1]のレシピ用語から[ID2]のレシピ用語に向かって[labelID]のエッジがあることを表わします.
    [ID1], [ID2]はそれぞれ,各形態素に割り振られた通し番号で,そのファイル内でユニークです.レシピツリーデータファイルには,レシピ用語であると認識された形態素のみ記載されるため,IDは飛び飛びになることが普通です.
    
    "糸蒟蒻"は,形態素解析すると,"糸"と"蒟蒻"に分かれますが,レシピ用語認識器で認識させると,"糸/F-B 蒟蒻/F-I"といったように,1つの食材として認識されます.
    このように,形態素として見ると複数に分かれているが,レシピ用語として見ると1つであるとき,[ID1(2)]や[レシピ用語の表記1(2)]の部分には,カンマ区切りで複数の値が入ります.
    以下の例を合わせてご覧ください.
    
    1,2/糸,蒟蒻\t5/茹で\t2
    
    この場合,「糸=蒟蒻(ID:1,2)」から「茹で(ID:5)」に向かってEdgeType「2」のエッジがあることを表します.

    各EdgeTypeに対応するラベルの一覧を以下に示します.ラベルの意味や概説については,フローグラフコーパスの紹介ページを参照してください.
    【EdgeTypeの一覧とその意味】
    [EdgeType] --- [ラベル]
            1  ---  Agent
            2  ---  Targ
            3  ---  Dest
            4  ---  F-comp
            5  ---  T-comp
            6  ---  F-eq
            7  ---  F-part-of
            8  ---  F-set
            9  ---  T-eq
           10  ---  T-part-of
           11  ---  V-eq
           12  ---  V-tm
           13  ---  other-mod

注意事項

  • 以下の処理は,すべて文字コードUTF-8で行います.
  • 後述のプログラムは,入力されるファイルの改行コードがLF("\n")であることを前提として作られています.
    (必要に応じてプログラムを変更してください.)
  • Unix環境で実行されることを前提として記述しています.なお,動作確認は,Mac OS X 10.11環境で行っています.

前準備

以下に示す番号順に行ってください.
  • Python3が入っていない場合はインストールしてください(プログラムを利用する際に必要です).
    また,gcc, g++が入っていない場合はインストールしてください(機械学習ライブラリliblinearをコンパイルする際に必要です).
  • zip圧縮されたプログラムをこちら【現在未公開】からダウンロードし,適当な場所に解凍してください.
    ファイル構成は次のようになっています.
    【ファイル構成】
    flow_graph/
      ├00README --------------- はじめにお読みいただくファイルです
      |
      ├data/ ------------------ データが入ります
      |  ├models/ ------------- LRモデル本体が入ります
      |  |
      |  ├test_data/ ---------- レシピツリー導出用データが入ります
      |  |  ├likelihood_data/ - LRモデルによって出力される尤度データが入ります
      |  |  ├mst_data/ -------- 最小全域有向木を求めた結果が入ります
      |  |  ├sa_files/ -------- レシピ言語処理マニュアルの最終的な出力結果が入ります
      |  |  └tree_data/ ------- treeデータが入ります
      |  |
      |  ├train_data/ --------- モデル学習用データが入ります
      |  |  ├combined/ -------- モデル学習用データを作成するためのデータが入ります
      |  |  ├estimated/ ------- 現時点では使用しませんが,今後使うかもしれないフォルダです
      |  |  ├features/ -------- liblinearへの入力となるモデル学習用データが入ります
      |  |  ├flow/ ------------ モデル学習用データを作成するためのデータが入ります
      |  |  └pnat/ ------------ 現時点では使用しませんが,今後使うかもしれないフォルダです
      |  |
      |  └word_list/ ---------- liblinearへ入力するデータを作る際に使用するファイルが入ります
      |
      ├lib/ ------------------- Pythonでliblinearを使うために必要なファイルが入ります
      |
      ├liblinear-1.96/ -------- liblinear本体が入っています
      |
      ├src/ ------------------- プログラム本体が入っています
      |  ├__init__.py --------- Pythonスクリプトが入っているフォルダであることを表わす空のファイルです
      |  ├common.py ----------- ツリー生成プログラム共通のプログラム
      |  ├liblinear.py -------- liblinearをPythonで使うためのプログラム
      |  ├liblinearutil.py ---- liblinearをPythonで使うためのプログラム
      |  ├lr_test.py ---------- LRモデルを用いたテストを行うプログラム
      |  ├lr_train_prep.py ---- 
      |  ├lr_train.py --------- LRモデルを学習するプログラム
      |  └mst.py -------------- 最小全域有向木を求めるプログラム
      |
      └temp/ ------------------ プログラムが一時的な作業をするときに使用します
  • Pythonでliblinearを使うために必要なファイルを作成します.以下のコードを実行してください.
    # flow_graph/liblinear-1.96/python へ移動します
    cd flow_graph/liblinear-1.96/python
    
    # make コマンドを実行し,必要なファイルを生成します.
    make
    
    # flow_graph/liblinear-1.96/ に生成された liblinear.so.2 を flow_graph/lib/ に移動させます
    mv flow_graph/liblinear-1.96/liblinear.so.2 flow_graph/lib/
    "liblinear.so.2" が出力されない場合は,liblinearのサイトを参考に,解決してください.
  • 208のレシピに対して手動アノテートしたファイルから学習したLRモデルを配布しています.必要に応じてこちら【バグを発見したため,一時的に配布停止】からダウンロードしてください.解凍後のファイル配置方法については,同梱されているREADMEファイルをご覧ください.なお,現時点では,6種類の特徴量すべてを用いて学習したLRモデルのみの配布となります.

レシピツリー導出の手順

ここでは,こちらに用意したLRモデルを使って,レシピツリーを自動生成する手順をご説明致します.
なお,後述のモデル学習の手順で作成したLRモデルファイルを使うこともできます.
  • ツリーにしたいレシピ手順文が記述されたファイルをUTF-8で作成してください(サンプル
    その際,必ず1つの手順を1行に記してください(1行が1つの手順を表わすようにしてください).
  • レシピ言語処理マニュアルに従って,用意したレシピ手順文を処理し,最終的な出力結果ファイルに対して,以下の変更を行ってください.
    • ファイル名は,"[recipe_id(8桁0埋めの数値)]-sa_file.txt"とし,以下のパスに保存してください.
      flow_graph/data/test_data/sa_files/[recipe_id(8桁0埋めの数値)]-sa_file.txt
    • recipe_idが"123456"の場合,ファイル名は,"00123456-sa_file.txt"となり,その内容は例えば以下のようなものです.
      【例】(00123456-sa_file.txt)
      糸,名詞,名詞-普通名詞-一般+,し=蒟蒻,名詞,名詞-普通名詞-一般+,こんにゃく/F を,助詞,助詞-格助詞+,を 下茹で,名詞,名詞-普通名詞-サ変可能+,したゆで/Ac し,動詞,動詞-非自立可能+サ行変格,し ま,助動詞,助動詞+助動詞-マス,ま す,語尾,語尾+助動詞-マス,す 。,補助記号,補助記号-句点+,。
  • レシピツリー導出手順2で出力されたファイル(*-sa_file.txt)を用いて,全レシピ用語間に対する,全種類両方向の有効枝の尤度データを作成します.
    以下のコードを実行してください.
    python ["lr_test.py"のパス] [recipe_id(8桁0埋めの数値)] [使用特徴] [modelのファイル名] [word_listのファイル名]
    # [使用特徴]には,後述のモデル学習でLRモデルを作成する際に用いたものと同じものを指定してください.
    
    # 例えば,[使用特徴]として1,2,5番目を用い,レシピID"123456"のファイルを処理するときは,次のようにして実行します.
    python src/lr_test.py 00123456 110010 model_110010_XXX_YYYYYYYY word_list_XXX_YYYYYYYY
    
    # LRモデルを本サイトからダウンロードし,レシピID"123456"のファイルを処理するときは,次のようにして実行します.
    python src/lr_test.py 00123456 111111 model_111111_208_20150730 word_list_208_20150710.txt
    処理結果の尤度データは,以下のパスに出力されます.すでにファイルが存在する場合は,上書きされます.
    flow_graph/data/test_data/likelihood_data/[recipe_id(8桁0埋めの数値)]-likelihood.gz
    
    # 例えば,レシピID"123456"から出力された尤度データファイルは以下のパスに出力されます.
    flow_graph/data/test_data/likelihood_data/00123456-likelihood.gz
  • レシピツリー導出手順3で出力された尤度データを用いて,最小全域有向木を求めます.
    以下のコードを実行してください.
    python ["mst.py"のパス] [recipe_id(8桁0埋めの数値)]
    
    # 例えば,レシピID"123456"に対して処理する場合は,次のようにして実行します.
    python src/mst.py 00123456
    最小全域有向木を求めた結果は,以下のパスに出力されます.すでにファイルが存在する場合は,上書きされます.
    flow_graph/data/test_data/mst_data/[recipe_id(8桁0埋めの数値)]-mst.gz
    
    # 例えば,レシピID"123456"に対して最小全域有向木を求めた結果は以下のパスに出力されます.
    flow_graph/data/test_data/mst_data/00123456-mst.gz
  • レシピツリー導出手順4で出力されたデータを整形し,レシピツリーデータを生成します.
    以下のコードを実行してください.
    python ["finalizer.py"のパス] [recipe_id(8桁0埋めの数値)]
    
    # 例えば,レシピID"123456"に対して処理する場合は,次のようにして実行します.
    python src/finalizer.py 00123456
    レシピツリーデータは,以下のパスに出力されます.すでにファイルが存在する場合は,上書きされます.
    flow_graph/data/test_data/tree_data/[recipe_id(8桁0埋めの数値)]-tree.tsv
    
    # 例えば,レシピID"123456"のツリーデータは以下のパスに出力されます.
    flow_graph/data/test_data/tree_data/00123456-tree.tsv
    レシピツリーデータの書式と意味を以下に示します.
    【書式】([recipe_id(8桁0埋めの数値)]-tree.tsv)
    [ID1]/[レシピ用語の表記1]\t[ID2]/[レシピ用語の表記2]\t[EdgeType]
    ...
    ※ここで,"\t"はタブ文字を表します.
    
    【意味】
    各行が1つのエッジを表しており,[ID1]のレシピ用語から[ID2]のレシピ用語に向かって[labelID]のエッジがあることを表わします.
    [ID1], [ID2]はそれぞれ,各形態素に割り振られた通し番号で,そのファイル内でユニークです.レシピツリーデータファイルには,レシピ用語であると認識された形態素のみ記載されるため,IDは飛び飛びになることが普通です.
    
    "糸蒟蒻"は,形態素解析すると,"糸"と"蒟蒻"に分かれますが,レシピ用語認識器で認識させると,"糸/F-B 蒟蒻/F-I"といったように,1つの食材として認識されます.
    このように,形態素として見ると複数に分かれているが,レシピ用語として見ると1つであるとき,[ID1(2)]や[レシピ用語の表記1(2)]の部分には,カンマ区切りで複数の値が入ります.
    以下の例を合わせてご覧ください.
    
    1,2/糸,蒟蒻\t5/茹で\t2
    
    この場合,「糸=蒟蒻(ID:1,2)」から「茹で(ID:5)」に向かってEdgeType「2」のエッジがあることを表します.
    各EdgeTypeに対応するラベルの一覧を以下に示します.ラベルの意味や概説については,フローグラフコーパスの紹介ページを参照してください.
    【EdgeTypeの一覧とその意味】
    [EdgeType] --- [ラベル]
            1  ---  Agent
            2  ---  Targ
            3  ---  Dest
            4  ---  F-comp
            5  ---  T-comp
            6  ---  F-eq
            7  ---  F-part-of
            8  ---  F-set
            9  ---  T-eq
           10  ---  T-part-of
           11  ---  V-eq
           12  ---  V-tm
           13  ---  other-mod
    以上で,レシピツリー導出の処理はすべて終了です.お疲れ様でした.

モデル学習の手順

ここでは,モデル学習用データを用いて,レシピツリー自動生成のために必要なモデルファイルの作り方をご説明致します.
  • モデル学習用データ作成のために,2種類のファイルを使います.レシピ手順文を形態素解析し,それをもとに,1つ目のファイル手順文解析結果ファイル"[recipe_id(8桁0埋めの数値)]-combined.tsv"を次の書式で作成してください.なお,学習に使いたいレシピすべてに対して行ってください.その際,レシピごとにファイルを分けてください.
    【書式】([recipe_id(8桁0埋めの数値)]-combined.tsv)
    [手順番号(3桁表記)]-[文番号(2桁表記)]-[単語番号(3桁表記)]\t[単語]\t[品詞]\t[読み]\t[レシピ用語タグ]
    ...
    
    ※ここで,"\t"はタブ文字を表します.
    上記書式に従って作成した例を,以下に示します.
    【例】(00115155-combined.tsv)
    001-01-001\tパスタ\t名詞\tぱすた\tF-B
    001-01-002\tを\t助詞\tを\tO
    001-01-003\t茹で\t動詞\tゆで\tAc-B
    ...
    作成した手順文解析結果ファイル"[recipe_id(8桁0埋めの数値)]-combined.tsv"を,以下のパスに保存してください.
    flow_graph/data/train_data/combined/[recipe_id(8桁0埋めの数値)]-combined.tsv
  • モデル学習用データ作成のために,2種類のファイルを使います.レシピ手順文を形態素解析し,それをもとに,2つ目のファイルレシピフローグラフファイル"[recipe_id(8桁0埋めの数値)]-flow.csv"を次の書式で作成してください.なお,学習に使いたいレシピすべてに対して行ってください.その際,レシピごとにファイルを分けてください.
    書式の詳細については,フローグラフコーパスの紹介ページをご覧ください.
    【書式】([recipe_id(8桁0埋めの数値)]-flow.csv)
    [ID],[手順番号(3桁表記)]-[文番号(2桁表記)]-[単語番号(3桁表記)],[レシピ用語タグ],[単語列],[主語(ガ格)],[対象(ヲ格)],[方向(ニ格)],[食材デ],[道具デ],[食材equal],[食材part-of],[食材set],[道具equal],[道具part-of],[動詞equal],[動作のタイミング],[その他の修飾語句],[備考]
    ...
    上記書式に従って作成した例を,以下に示します.
    【例】(00115155-flow.csv)
    1,001-01-001,F,パスタ,,,,,,,,,,,,,,
    2,001-01-005,Ac,茹で,,1,,,,,,,,,,,,
    3,001-01-009,T,フライパン,,,,,,,,,,,,,,
    4,001-01-015,Ac,茹で,,1,,,3,,,,,,2,,,
    ...
    作成したレシピフローグラフファイル"[recipe_id(8桁0埋めの数値)]-flow.csv"を,以下のパスに保存してください.
    flow_graph/data/train_data/flow/[recipe_id(8桁0埋めの数値)]-flow.csv
  • 【注意】以下の処理は,前の手順の処理に大きく依存します.学習に用いたいすべてのレシピそれぞれに対して,必ず事前にモデル学習手順1モデル学習手順2の処理を行っておいてください.もし,手順1または手順2の処理をやり直した場合は,この手順以降の処理もやり直す必要があります.
    ここでは,この後の手順でliblinearを使用する際,必要となるデータを作成します.以下のコードを実行してください.
    python ["lr_train_prep.py"のパス]
    
    # 例えば,次のようにして実行します.
    python src/lr_train_prep.py
    処理が完了すると,以下のパスにword_listファイルが出力されます.後述の処理で,そのファイル名を使いますので,メモしておいてください.
    flow_graph/data/word_list/word_list_[処理に用いたレシピ数]_[処理年月日].txt
    
    # 例えば,手順1と手順2で用意したレシピの数が123個あり,処理年月日が2015/08/31だった場合,以下のようになります.
    flow_graph/data/word_list/word_list_123_20150831.txt
    
    # 上記例の場合,メモすべきファイル名は,以下の部分です.
    word_list_123_20150831.txt
  • モデル学習手順1モデル学習手順2で作成した2種類のファイル(手順文解析結果ファイルレシピフローグラフファイル)およびモデル学習手順3で作成したword_listファイルを用いて,モデル学習用データを作成し,それをもとにLRモデルを学習します.
    学習の際に用いる特徴量として以下の6つを用意しています.
    【特徴量一覧】
    1. uとvの周囲にある3単語
    2. uとvの間の助詞
    3. uとvの表記,読み,重要語のタグ
    4. uとvが同じ文内にあるか
    5. uとvが同じ手順内にあるか
    6. uとvの間にAcタグ, Afタグはあるか
    ※ここで,"u"と"v"は,NEタグが付いた語を表します.
    以下のコードを実行してください.以下を実行すると,"combined"フォルダ内および"flow"フォルダ内のすべてのファイルを用いて,モデル学習用ファイルを作成し,それをもとにLRモデルを学習します.
    python ["lr_train.py"のパス] [使用特徴] [word_listファイル名]
    # [word_listファイル名]には,手順3の処理後に表示されたファイル名を指定してください.
    # 手順3の例では,"word_list_123_20150831.txt"が,[word_listファイル名]に相当します.
    
    # 特徴量として,1,2,5番目を用いる場合,次のように[使用特徴]を指定します.
    python src/lr_train.py 110010 word_list_123_20150831.txt
    処理途中で,モデル学習用データが以下のパスに出力されます.すでにファイルが存在する場合は,上書きされます.
    flow_graph/data/train_data/features/[使用特徴]/features[recipe_id(8桁0埋めの数値)]_[使用特徴].txt
    
    # 例えば,特徴量として1,2,5番目を用いて,レシピID123456のレシピから生成されたモデル学習用データは以下のようになります.
    flow_graph/data/train_data/features/110010/features00123456_110010.txt
    処理が完了すると,学習されたLRモデルが以下のパスに出力されます.すでにファイルが存在する場合は,上書きされます.
    flow_graph/data/models/model_[使用特徴]_[処理に用いたレシピ数]_[処理年月日]
    
    # 例えば,特徴量として1,2,5番目を用い,レシピ123個を使って,2015/08/31に処理した場合,以下のように出力されます.
    flow_graph/data/models/model_110010_123_20150831
    なお,ここで出力されたモデルをレシピツリー導出で使う際には,以下のmodelファイル名が必要となります.必要に応じて,メモしてください.
    model_[使用特徴]_[処理に用いたレシピ数]_[処理年月日]
    
    # 上記例の場合,modelファイル名は,以下の部分です.
    model_110010_123_20150831
    以上で,モデル学習の処理はすべて終了です.お疲れ様でした.

参考文献

連絡先

  • 山肩 洋子 (yamakata [at] dl.kuis.kyoto-u.ac.jp)
  • 門脇 拓也 (kadowaki [at] dl.kuis.kyoto-u.ac.jp)
  • 笹田 鉄郎 (sasada [at] ar.media.kyoto-u.ac.jp)
  • 今堀 慎治 (imahori [at] ise.chuo-u.ac.jp)
  • 森 信介 (forest [at] i.kyoto-u.ac.jp)
※[at]を@に置き換えてください.
※プログラムやリンク切れ,記載内容の不備に関するお問い合わせは,門脇までお願い致します.

更新履歴

  • 2015/12/11(最新の更新)
    • LRモデルにバグを発見したため,一時的に配布停止
更新履歴をすべて展開