Amazon Personalizeを導入して得られた知見

# Amazon Personalizeを導入するにあたって
ユーザへのレコメンド機能を実装するにあたってAmazon Personalizeを導入することとなり、様々調べながら進めていって得られた知見をまとめていきたいと思います。
[ドキュメント](https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/what-is-personalize.html)はもちろん存在していて、こちらを読み進めながらやっていけばいい話なのですが、2019年1月リリースのサービスなのでまだまだドキュメントの充実が足りていない部分があり、記載方法にも不親切な点もあったため自分なりにまとめました。

AWSサポートに問い合わせするも、回答に数日〜1週間掛かることがざらにあり、待ち時間を含めると開発は思ったより時間がかかりました。
実装するにあたって機械学習の知識は基本的にいりませんが、パラメタ調整やそもそものデータ作成に関してはやはり機械学習の知識があったほうが良いのは間違いないでしょう。

## Amazon Personalizeの処理の流れ
1. データセットの作成、インポート
2. ソリューションの作成
3. キャンペーンの作成

という大まかな流れでレコメンドエンジンを作成します。以下がその詳細です。

## 1. データセットの作成
まずはデータセットを作成します。
データセットには基本的には以下の3つがあります。これはどのユーザーも共通です。

- Item
- User 
- Interaction

AWSのハンズオンに参加した際の例では以下のようになっていました。

- Item...映画データ。映画のカテゴリなどの属性
- User...ユーザーデータ。ユーザーの年齢性別などの属性
- Interaction...評価履歴。どのユーザーがどの映画をどのように評価したのか

Personalizeを使うときはこの3つのデータを作成します。各々のサービスで、例えばユーザーの属性は何で決まるか（年齢のか性別なのか住所なのか、またその全てなのか）は別だと思うのでそれぞれの詳細は自分たちで決めていく形になります。

このデータをCSVファイルとして作成してS3に置きます。それをインポートジョブを作成してインポートします。
このインポート処理はだいたい10~15分程度かかります。


## 2. ソリューションの作成
ソリューションの作成時に困るのはRecipeの選択です。
基本的にMLの知識がなければAutomatic (AutoML)を選択するのが良いみたいです。それでうまくレコメンドされれば特には困ることはないでしょう。

ただ、今回の実装ではどんどん新しいデータが入ってくるためそれらを優先してレコメンドしてほしいという背景からManualレシピの[HRNN-Coldstart レシピ](https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/native-recipe-hrnn-coldstart.html)をAWS側から勧められ、こちらで作成することとしました。
Manualになった場合はパラメタを自ら決める必要があり`Feature transformation parameters`を自分で決めなければいけません。
coldstartの場合以下の3つのパラメータが重要でした。今回は閲覧履歴をもとに考えてみます。

- ***cold_start_max_interactions***
    - 閲覧履歴の総数の限度。例えば100と設定すると100以上閲覧されたものはレコメンドされない

- ***cold_start_max_duration***
    - 次で決める`cold_start_relative_from`から何日遡った日数のデータをレコメンドするか。例えば`cold_start_relative_from`が現在時刻であった場合、この数字を5とすると、５日以内のデータがレコメンドされます

- ***cold_start_relative_from***
    - いつからのデータをレコメンドするかの開始点を決定する。現在時刻の`currentTime`と最新データの時刻を参照する`latestItem`の2つが設定可能。Itemの最新時刻とはいえタイムスタンプが設定可能なのはInteractionのみなので5日以内という設定とすると、5日以内に閲覧履歴の存在するデータのみがレコメンド対象となる。しっかり理解できていないが、インタラクションデータセットに存在しないItemはレコメンド対象にならない？

またcoldstartの場合、Item数のMaxは80,000件となる。これは現在のドキュメントには記載されているが、私が触っていたときはドキュメントに記載がなく、エラーが発生してしまった…

ソリューションの作成は従量課金となります。
0.24 USD/トレーニング時間が発生するのでやりすぎには注意しましょう。私がやった限り、都度１時間程度かかりました。もちろんデータ量にはよるとは思いますが、AWS側の説明としてはデータ量が少なくともこれくらいは掛かるとのことでした。

## 3. キャンペーンの作成
キャンペーンというのが実際にレコメンド機能となる。キャンペーンを起動すると、エンドポイントにユーザID付きで投げるとレコメンドのItem_IDが帰ってくる。これはただ作成ボタンを押して、先程作成したソリューションを選択するだけなので簡単に終わる。
注意点としてはキャンペーンは作成しただけで従量課金が発生します。
最低でも0.20USD/時間かかります。これは一ヶ月放置しておくと140ドルくらいかかってしまうので要注意です。データセットやソリューションは作成しておいても料金はかかりません。
キャンペーンのみ、テストする際は必要なときだけ起動するようにしましょう。

## 運用にあたって
上記手順はほぼドキュメントを見ればわかることかと思います。実運用するにあたって困ったこととその対処法を記載していきます。

## 困ったこと①リアルタイムデータの反映
運用サービスではリアルタイムでItem、User、Interactionが増えていくのでリアルタイムにデータを追加していきたいと考えました。
その際には[イベントの記録](https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/recording-events.html)という項目があり、こちらをつかうと行けそうにみえます。ただ以下の問題点がわかりました

- 追加できるのはInteractionデータのみ
- Interactionに新しいItemやUserが存在してもレコメンドに反映できない

という事がわかり、イベントの記録をやったとしても、大して精度が上がらなさそうで、イベントの記録はあきらめることにしました。
ItemやUserがある程度固定されている場合はイベントの記録で対応できそうです。

## 困ったこと②InteractionデータのEventがわからない
今回の自分の実装にはInteractionのEventTypeとEventValueは必要ないカラムだったので使わなかったのでなかなか理解できませんでした。AWSのドキュメントを参考にすると…

|USER_ID| ITEM_ID| TIMESTAMP| EVENT_TYPE| EVENT_VALUE| NUM_RATINGS|
|:-----------------|:------------------|:------------------|:------------------|:------------------|:------------------|
|user123| movie_xyz| 1543531139| rating| 5| 12|
|user321| choc-ghana| 1543531760| like| true|
|user111| choc-fake| 1543557118| like| false|

このようなInteractionデータセットを作成することができて、映画の評価をratingしているものだけで学習することもlikeで評価しているものだけで学習することもできるようになるようです。

## 困ったこと③ トレーニング費用が想定よりかかった
上述の通り、トレーニング費用は0.24 USD/トレーニング時間ということで、都度のトレーニングは1~2時間程度で終わっていた。つまり0.5ドル程度しかかかっていないという予想を立てていたのだが、実際の請求をみると都度2~30時間ドルの費用がかかっているようだった。
これをAWSサポートに問い合わせたところ、料金ページを引用して説明されました。
>トレーニング
データを使用してカスタムモデルをトレーニングするのに費やされるトレーニング時間に課金されます。
注意: トレーニング時間とは、4v CPU と 8 GiB メモリを使用する 1 時間のコンピューティング能力です。
Amazon Personalize は、データをトレーニングするための最も効率的なインスタンスタイプを自動的に選択します。
これは、ジョブをより迅速に完了するためにベースラインの仕様を超えるインスタンスである可能性があります。
したがって、請求されるトレーニング時間数が、経過時間数よりも大きくなる可能性があります。

大したデータ量ではなかったので再度問い合わせてみると…
>ご連絡いただきましたソリューション ****** を確認いたしましたところ、HPO と AutoML を有効化されているように見受けられました。
HPO が有効化された場合多くのトレーニングジョブが実行され、トレーニング時間の増加が発生する可能性がございます。

つまりAutoMlとHPOを有効化すると、1~2時間程度でトレーニングが終わっても請求はそれ以上来てしまうようです。渡しの場合、100~200時間程度はトレーニングに時間がかかっていることになっていました。
このあたりはAutoMlとHPOを無効化することで費用は回避できますが、レコメンドの精度は下がることになると思われます。そのあたりのバランスは都度見ていくしかないと思われます。


## 実運用
リアルタイムイベントの記録を断念したので、毎日データセットをインポートし、ソリューション作成、キャンペーンの作成を行いました。
バージョンという考えがPersonalizeにあるのでそれを使うと比較的簡単です。以下の手順になります。上記手順でキャンペーンまで作成しているものとします。
1. データの作成、S3アップロード
2. インポートジョブの作成
3. ソリューションバージョンアップ
4. キャンペーンバージョンアップ

データの作成・S3アップロードをしたあとはアップロードしたS3ファイルを指定してインポートジョブを作成します。インポートが終わったらあとはバージョンアップさせるだけで終了です。
ひとつのデータセットグループには一つずつのUser・Item・Interactionしか持てないため、ソリューション・キャンペーンは同一arnが利用可能です。
キャンペーンarnが変わらないので、アプリケーション側は固定のエンドポイントを使用することが可能なので実装をしっかり切り分けることが可能となっています。

最初のキャンペーン作成まではAWSコンソールから行って、バージョンアップはAWS SDKを介して行うのが一番ラクかと思われます。

このサイクルを回してけばそれなりに最新データをレコメンドさせることが可能かと思います。ただトレーニング時間あたりで課金されますし、データのインポートにも課金されるのでその分費用が当然かかります。

## これから
ここまででレコメンド機能は作成できました。が、これでようやくスタートラインに立てただけです。
ここから実際に運用してみてどれくらいアクセス数が変わるのか、レコメンドの精度はどうなのかなどを検討しパラメータの調整、再トレーニングの繰り返しになるかと思います。またそこでわかったことがあればまとめていきたいと思います。