Tradeoff AnalyticsでAMIMOTOマネージドの最適プランを調べてみる

Tradeoff Analyticsについて Tradeoff Analyticsについては下の記事で紹介書いてるので、参考にしてください。 https://wp-kyoto.cdn.rabify.me/try-to-r […]

広告ここから
広告ここまで

目次

    Tradeoff Analyticsについて

    Tradeoff Analyticsについては下の記事で紹介書いてるので、参考にしてください。
    https://wp-kyoto.cdn.rabify.me/try-to-run-tradeoff-analytics/

    どうせなら身近なものでテストしてみたいなと思ったので、ウチのサービスを題材にしてみました。

    やってみたいこと

    「価格・PV・WordPressインストール数とDBタイプの4種類から、希望条件に近いプランのリスト」を出すということを試してみます。

    リクエストJSONを作る

    最終的には1つのJSONにまとめますが、とりあえずパーツごとに作ります。

    プロダクトリストを作る

    まずは候補リストから。
    候補はoptionsの中にリストとしてまとめていきます。

    データソース

    データはプラン一覧から拾ってきます。
    https://ja.amimoto-ami.com/plans/hosting/single-instance-plan/
    https://ja.amimoto-ami.com/plans/hosting/multi-instances-plan/

    JSON

    valuesの中に、価格・PV・DBタイプ・WordPressインストール数を入れてます。

    "options": [
        {
            "key": "1",
            "name": "t2.micro",
            "values": {
                "price": 3000,
                "pv": 100000,
                "db": "EC2",
                "wp": 3
            }
        },{
            "key": "2",
            "name": "t2.small",
            "values": {
                "price": 6000,
                "pv": 300000,
                "db": "EC2",
                "wp": 3
            }
        },{
            "key": "3",
            "name": "t2.medium",
            "values": {
                "price": 15000,
                "pv": 500000,
                "db": "EC2",
                "wp": 3
            }
        },{
            "key": "4",
            "name": "c4.large",
            "values": {
                "price": 20000,
                "pv": 1000000,
                "db": "EC2",
                "wp": 5
            }
        },{
            "key": "5",
            "name": "c4.8large",
            "values": {
                "price": 350000,
                "pv": 20000000,
                "db": "EC2",
                "wp": 5
            }
        },{
            "key": "6",
            "name": "w-Small",
            "values": {
                "price": 80000,
                "pv": 3000000,
                "db": "RDS-Single",
                "wp": 5
            }
        },{
            "key": "7",
            "name": "w-Large",
            "values": {
                "price": 120000,
                "pv": 6000000,
                "db": "RDS-Single",
                "wp": 5
            }
        },{
            "key": "8",
            "name": "w-XLarge",
            "values": {
                "price": 160000,
                "pv": 10000000,
                "db": "RDS-Single",
                "wp": 10
            }
        },{
            "key": "9",
            "name": "w-2XLarge",
            "values": {
                "price": 320000,
                "pv": 20000000,
                "db": "RDS-Single",
                "wp": 10
            }
        },{
            "key": "10",
            "name": "HA-Small",
            "values": {
                "price": 120000,
                "pv": 3000000,
                "db": "RDS-Multi",
                "wp": 5
            }
        },{
            "key": "11",
            "name": "HA-Large",
            "values": {
                "price": 180000,
                "pv": 6000000,
                "db": "RDS-Multi",
                "wp": 5
            }
        },{
            "key": "12",
            "name": "HA-XLarge",
            "values": {
                "price": 200000,
                "pv": 10000000,
                "db": "RDS-Multi",
                "wp": 10
            }
        },{
            "key": "13",
            "name": "HA-2XLarge",
            "values": {
                "price": 460000,
                "pv": 20000000,
                "db": "RDS-Multi",
                "wp": 10
            }
        }
    ]
    

    検索条件を入れる

    columnsキーの中に検索候補を入れていきます。

    "columns": [
            {
                "key": "price",
                "type": "numeric",
                "goal": "min",
                "is_objective": true,
                "full_name": "Price",
                "range": {
                    "low": 0,
                    "high": 300000
                },
                "format": "currency: 'JPY¥' : 2"
            },{
                "key": "pv",
                "type": "numeric",
                "goal": "max",
                "is_objective": true,
                "full_name": "Monthly Pageview",
                "range": {
                    "low": 10000000,
                    "high": 20000000
                },
                "format": "number:2"
            },{
              "key": "db",
              "type": "categorical",
              "goal": "min",
              "is_objective": true,
              "full_name": "Database Type",
              "range": [
                "EC2",
                "RDS-Single",
                "RDS-Multi"
              ],
              "preference": [
                "EC2",
                "RDS-Single",
                "RDS-Multi"
              ]
            },{
                "key": "wp",
                "type": "numeric",
                "goal": "max",
                "is_objective": true,
                "full_name": "WordPress installation ",
                "range": {
                    "low": 1,
                    "high": 10
                }
            }
        ]
    

    検索条件を表にまとめるとこうなります。

    項目名 範囲
    価格 0~300,000円
    PV 10,000,000~20,000,000 PV
    DBタイプ EC2内DB > RDS1台 > RDSマルチAZ
    WPインストール数 1 ~ 10
    categoricalの分類方法

    type:categoricalは、preferenceの配列順序でレコメンド度合いを取ります。
    goalをmaxにした場合は配列の後ろの方にあるものを優先、minにした場合は前にあるものを優先します。

    JSONにまとめる

    最後にsubjectにタイトルを付けて、JSONにまとめます。
    まとめたものが以下の通り。

    {
        "subject": "amimoto",
        "columns": [
            {
                "key": "price",
                "type": "numeric",
                "goal": "min",
                "is_objective": true,
                "full_name": "Price",
                "range": {
                    "low": 0,
                    "high": 300000
                },
                "format": "currency: 'JPY¥' : 2"
            },{
                "key": "pv",
                "type": "numeric",
                "goal": "max",
                "is_objective": true,
                "full_name": "Monthly Pageview",
                "range": {
                    "low": 10000000,
                    "high": 20000000
                },
                "format": "number:2"
            },{
              "key": "db",
              "type": "categorical",
              "goal": "min",
              "is_objective": true,
              "full_name": "Database Type",
              "range": [
                "EC2",
                "RDS-Single",
                "RDS-Multi"
              ],
              "preference": [
                "EC2",
                "RDS-Single",
                "RDS-Multi"
              ]
            },{
                "key": "wp",
                "type": "numeric",
                "goal": "max",
                "is_objective": true,
                "full_name": "WordPress installation ",
                "range": {
                    "low": 1,
                    "high": 10
                }
            }
        ],
        "options": [
            {
                "key": "1",
                "name": "t2.micro",
                "values": {
                    "price": 3000,
                    "pv": 100000,
                    "db": "EC2",
                    "wp": 3
                }
            },{
                "key": "2",
                "name": "t2.small",
                "values": {
                    "price": 6000,
                    "pv": 300000,
                    "db": "EC2",
                    "wp": 3
                }
            },{
                "key": "3",
                "name": "t2.medium",
                "values": {
                    "price": 15000,
                    "pv": 500000,
                    "db": "EC2",
                    "wp": 3
                }
            },{
                "key": "4",
                "name": "c4.large",
                "values": {
                    "price": 20000,
                    "pv": 1000000,
                    "db": "EC2",
                    "wp": 5
                }
            },{
                "key": "5",
                "name": "c4.8large",
                "values": {
                    "price": 350000,
                    "pv": 20000000,
                    "db": "EC2",
                    "wp": 5
                }
            },{
                "key": "6",
                "name": "w-Small",
                "values": {
                    "price": 80000,
                    "pv": 3000000,
                    "db": "RDS-Single",
                    "wp": 5
                }
            },{
                "key": "7",
                "name": "w-Large",
                "values": {
                    "price": 120000,
                    "pv": 6000000,
                    "db": "RDS-Single",
                    "wp": 5
                }
            },{
                "key": "8",
                "name": "w-XLarge",
                "values": {
                    "price": 160000,
                    "pv": 10000000,
                    "db": "RDS-Single",
                    "wp": 10
                }
            },{
                "key": "9",
                "name": "w-2XLarge",
                "values": {
                    "price": 320000,
                    "pv": 20000000,
                    "db": "RDS-Single",
                    "wp": 10
                }
            },{
                "key": "10",
                "name": "HA-Small",
                "values": {
                    "price": 120000,
                    "pv": 3000000,
                    "db": "RDS-Multi",
                    "wp": 5
                }
            },{
                "key": "11",
                "name": "HA-Large",
                "values": {
                    "price": 180000,
                    "pv": 6000000,
                    "db": "RDS-Multi",
                    "wp": 5
                }
            },{
                "key": "12",
                "name": "HA-XLarge",
                "values": {
                    "price": 200000,
                    "pv": 10000000,
                    "db": "RDS-Multi",
                    "wp": 10
                }
            },{
                "key": "13",
                "name": "HA-2XLarge",
                "values": {
                    "price": 460000,
                    "pv": 20000000,
                    "db": "RDS-Multi",
                    "wp": 10
                }
            }
        ]
    }
    

    あとはこれをPOSTでwatson APIにぶん投げます。

    Tradeoff Analytics APIで分析する

    検索パラメータができたので、あとはAPIを使って解析します。

    $ curl -X POST --user YOUR_USERNAME:YOUR_PASSWORD \
     --header "Content-Type: application/json" \
     --data @problem.json \
     "https://gateway.watsonplatform.net/tradeoff-analytics/api/v1/dilemmas?generate_visualization=false" 
    

    戻り値を確認する

    リクエストパラメータなども含まれた状態で返ってくるので、jqで見たい部分だけ抜き出します。

    $ curl -X POST --user YOUR_USERNAME:YOUR_PASSWORD \
     --header "Content-Type: application/json" \
     --data @problem.json \
     "https://gateway.watsonplatform.net/tradeoff-analytics/api/v1/dilemmas?generate_visualization=false" \
     | jq ".resolution"
    {
      "solutions": [
        {
          "solution_ref": "1",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"1\" has a value in column \"pv\" which is:\"100000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "100000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "2",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"2\" has a value in column \"pv\" which is:\"300000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "300000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "3",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"3\" has a value in column \"pv\" which is:\"500000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "500000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "4",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"4\" has a value in column \"pv\" which is:\"1000000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "1000000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "5",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"5\" has a value in column \"price\" which is:\"350000\" while the column range\" is: [0.0,300000.0]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "price",
              "350000",
              "[0.0,300000.0]"
            ]
          }
        },
        {
          "solution_ref": "6",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"6\" has a value in column \"pv\" which is:\"3000000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "3000000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "7",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"7\" has a value in column \"pv\" which is:\"6000000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "6000000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "8",
          "status": "FRONT"
        },
        {
          "solution_ref": "9",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"9\" has a value in column \"price\" which is:\"320000\" while the column range\" is: [0.0,300000.0]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "price",
              "320000",
              "[0.0,300000.0]"
            ]
          }
        },
        {
          "solution_ref": "10",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"10\" has a value in column \"pv\" which is:\"3000000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "3000000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "11",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"11\" has a value in column \"pv\" which is:\"6000000\" while the column range\" is: [1.0E7,2.0E7]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "pv",
              "6000000",
              "[1.0E7,2.0E7]"
            ]
          }
        },
        {
          "solution_ref": "12",
          "status": "EXCLUDED"
        },
        {
          "solution_ref": "13",
          "status": "INCOMPLETE",
          "status_cause": {
            "message": "A column of a option is out of range. Option \"13\" has a value in column \"price\" which is:\"460000\" while the column range\" is: [0.0,300000.0]",
            "error_code": "RANGE_MISMATCH",
            "tokens": [
              "price",
              "460000",
              "[0.0,300000.0]"
            ]
          }
        }
      ]
    }
    

    solutions.statusを確認していくと、solutions.solution_refが8のオブジェクトのみFRONTで、それ以外はINCOMPLETEEXCLUDEDになっています。

    INCOMPLETEはrangeの範囲外の値が設定されているもの、EXCLUDEDはTradeoff Analyticsが最適解で無いと判断したもので、FRONTとなっているものがTradeoff Analyticsからレコメンドされるアイテムです。

    ということで今回はsolutions.solution_refが8のプランが希望する条件に一致する様子です。
    solutions.solution_refoptions.keyの値と同じなので、options.keyが8のアイテムを見てみましょう。

    {
            "key": "8",
            "name": "w-XLarge",
            "values": {
              "price": 160000,
              "pv": 10000000,
              "db": "RDS-Single",
              "wp": 10
            }
          }
    

    と、いうことで

    項目名 範囲
    価格 0~300,000円
    PV 10,000,000~20,000,000 PV
    DBタイプ EC2内DB > RDS1台 > RDSマルチAZ
    WPインストール数 1 ~ 10

    の条件に一致するプランは「w-XLarge」だということになりました。

    "solution_ref": "12"EXCLUDEDになっていますが、これはDBタイプが「RDS-Multi」と一番低い優先順位になっているためだと思われます。

    {
        "key": "12",
        "name": "HA-XLarge",
        "values": {
            "price": 200000,
            "pv": 10000000,
            "db": "RDS-Multi",
            "wp": 10
        }
    }
    

    optionskey: dbgoalをminではなくmaxに変更すると、このHA-XLargeプランもFRONTとなります。

    まとめ

    「複数の条件から最適なプランを探す」というのはシステム化するのが手間な印象がありましたが、Tradeoff Analyticsを使うことでJSONさえ作ればかなり簡単に作れることがわかりました。
    あとはJSON生成の自動化やプランレコメンド調査用のUIを作るところのノウハウなどが集まるようになれば、もっと便利になるかなと思います。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark