Amazon Personalizeでユーザの商品購入動向をもとにレコメンドしよう　〜フルマネージド機械学習サービスの素晴らしさとは〜

本日ついに待望のAmazonPersonalizeがGAされました！
機械学習の知識不要で、アプリケーションを使用しているユーザに対して開発者が個別のレコメンデーションを簡単に作成できるという優れものらしく、これを使えばSageMakerなどの機会学習基盤を自前で構築する必要がなくなりそうです。参照：[公式ブログ](https://aws.amazon.com/jp/blogs/news/amazon-personalize-ga-tokyo-launch/)
東京リージョンでも使用可能ですのでさっそく検証してみようかと思います:muscle:

# Amazon Personalizeコンソール

[コンソール画面](https://ap-northeast-1.console.aws.amazon.com/personalize/home?region=ap-northeast-1#start)を開いてみましょう。まだ日本語対応はできていないようですね。
右も左もわからない状態ですがとりあえず`Get started`から開始しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/cd98efc2-1543-3a74-fa35-c9192fe5cfd8.png)

## DataSetグループの作成

解析する対象のデータセットを入力します。まずは `user-item interaction data`の作成から始めます。
ユーザの購入動向がわかればいいので、ユーザID, 購入アイテムID, 購入時刻といった情報が必要でしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/a5959e71-a69e-10ec-5900-203ff2689806.png)

新しくスキーマを作成していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/262c3ebd-0b7f-b569-501f-dc0508b65f59.png)

スキーマの定義は以下の通りです。
簡単のために、USER_ID, ITEM_ID, TIMESTAMPとしてみました。

```json:SchemaDefinition
{
    "type": "record",
    "name": "Interactions",
    "namespace": "com.amazonaws.personalize.schema",
    "fields": [
        {
            "name": "USER_ID",
            "type": "string"
        },
        {
            "name": "ITEM_ID",
            "type": "string"
        },
        {
            "name": "TIMESTAMP",
            "type": "long"
        }
    ],
    "version": "1.0"
}
```

## データセット（CSVファイル）を用意する

データフォーマットの詳細に関しては[こちら](https://docs.aws.amazon.com/personalize/latest/dg/data-prep-formatting.html)の公式ドキュメントを参照してください。
実際に稼働しているサービスからデータを引っこ抜いてくるのがいいんでしょうが、今回はサンプルとして乱数を使ってテストデータを生成してみます。

```python:generate_data.py
import random
for i in range(10000):
    user_id = random.randrange(1, 300)
    item_id = random.randrange(1, 500)
    timestamp = random.randrange(886324817, 1086324817)
    print(str(user_id) + ',' + str(item_id) + ',' + str(timestamp))
```

```bash:bash
$ python generate_data.py >> data.csv
```

以下のようなデータが出来上がります。正直1000レコード程度で何がわかるんだという気もしますがユーザ数を300としているのでデータの分散度合いはまぁまぁいいんじゃないでしょうか。

```csv:data.csv
USER_ID,ITEM_ID,TIMESTAMP
181,333,1026614155
187,235,1025344422
191,219,1085141557
290,154,1048393159
55,66,1040676271
135,85,914594952
113,100,1006765257
240,286,931923683
187,225,939639985
203,318,1062057637
101,377,934623475
```

作成したCSVファイルはS3にアップロードしておきます。
適当なバケット（今回はamazon-personalize-sample-bucket）を作成してアップロードしましょう。

```
# バケットの作成
$ aws s3 mb s3://amazon-personalize-sample-bucket
make_bucket: amazon-personalize-sample-bucket
# ファイルのアップロード
$ aws s3 cp data.csv s3://amazon-personalize-sample-bucket
```

## CSVファイルを取り込む

`Create user-item interaction data`の項目に入力を完了し、次の画面に進むと解析対象のCSVファイルを選択をすることになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/36d7fa59-2121-4215-913d-5062a567bb76.png)

AmazonPersonalizeが使用するIAMロールを新規に作りましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/3781ac4f-ebb7-e078-ff5d-d3770193e3eb.png)

先ほど用意したバケット名を入力してIAMロールを作成してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/6487383a-83fd-c13e-f46f-2a650ee403ef.png)

以上で完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/1d93e555-09eb-c06c-bd88-afeff9953108.png)

入力が完了すると`Upload datasets`の作成が開始されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/ca74537c-b5a8-7f6e-96a0-bf67ec8c729c.png)

失敗しました。
:sweat: :thought_balloon: `Get started`にしたがって進めていたのにには失敗するなんて不親切だなぁ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/f56c0adf-aa15-7c2a-f713-039217ecaadb.png)

## S3にアクセス権限を付与する

どうやらS3に対する権限が無いようです。ドキュメントにしたがってS3にバケットポリシーをつけます。

> Insufficient privileges for accessing data in S3. Please look at https://docs.aws.amazon.com/personalize/latest/dg/getting-started.html#gs-upload-to-bucket and fix bucket policy on amazon-personalize-sample-bucket.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/8d654fb1-7345-b4a9-0a59-237001be9599.png)



```
{
    "Version": "2012-10-17",
    "Id": "PersonalizeS3BucketAccessPolicy",
    "Statement": [
        {
            "Sid": "PersonalizeS3BucketAccessPolicy",
            "Effect": "Allow",
            "Principal": {
                "Service": "personalize.amazonaws.com"
            },
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::amazon-personalize-sample-bucket",
                "arn:aws:s3:::amazon-personalize-sample-bucket/*"
            ]
        }
    ]
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/5b46c988-2515-bfe2-a52d-baf0494047d3.png)

S3に対するアクセス権限が設定できたので、再度データセットの作成を行います。

## ソリューションの作成

問題なく10~15分ほどで、データセットの解析が完了しました。
次にソリューションを作っていきます。

ソリューションを使用すると、ユーザに対する推奨項目（レコメンド商品）を生成できます。
推奨項目を生成するために機械学習をしますが、そのために必要な基盤となるインフラはAWSがマネージドで用意してくれるのでとても楽チンです。
このソリューションはデータセットについてトレーニングされたカスタムモデルで構成されています。
では進めていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/beadb3b7-ffbe-2ca2-3c29-6484e672d2d8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/21603bf7-b4d4-f546-7fea-d8daebe2cbcb.png)

レコメンドアルゴリズム（レシピ）の選択をします。
いくつか用意されていますね。今回は`aws-hrnn`にしてみましょう。
各モデルの概要は以下のような感じです。[（公式ドキュメント参照）](https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/training-deploying-solutions.html)

| レシピ                 | 説明              |
|:----------------------|:------------------|
| PersonalizedReranking | 検索結果または選別リストのパーソナライズされた<br>再ランク付けに使用 |
| SIMS                  | アイテムの検出可能性を高めるために<br>詳細ページで使用高速パフォーマンス。|
| DeepFM                |高速なトレーニングと推論に推奨。<br>全般的に良好なパフォーマンス。|
| FFNN                  |高速なトレーニングと推論に推奨。<br>大規模なデータセットに応じたスケーラビリティ。|
| HRNN                  |ユーザーの行動が時間の経過に伴って<br>変化する場合に推奨 (進化していくインテントの問題)。|
| HRNN-coldstart        |カタログに新しいアイテムを追加する頻度が高く、<br>アイテムを即座にレコメンデーションに表示する場合に推奨。|
| HRNN-metadata         |高品質のメタデータが使用できる場合は、<br>メタデータ以外のモデルよりパフォーマンスが向上。<br>トレーニング時間が長びく可能性がある。|
| Popularity-baseline   | 他のパーソナライゼーションレシピを<br>比較するためのベースラインとして使用。|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/db60a139-6d51-8aef-fd61-583343a5b9e0.png)

ハイパーパラメータのチューニング項目が出てきますが、デフォルト値のままでOKです。
先に進みましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/6f52f597-c887-fd98-12c7-be6304ea66a6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/9ec76e02-9279-bf30-6a9d-d3093b28d487.png)

データ量にも依存しますが、作成まで30分ほどかかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/530f0810-5b1d-432a-2bc2-acab4c487d8e.png)


## キャンペーンを作成する

ソリューションの作成が完了しました。最後にAPIとしてこのソリューションをしようするためのエンドポイントをデプロイしましょう。
このAPIエンドポイントのことをキャンペーンと呼ぶようです。SageMakerと同じような感覚でAPIエンドポイントをデプロイできますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/373c0705-74c1-6945-c1a5-b23421b5a152.png)

必要な項目を入力して作成すればOKです。簡単ですね。
`Mininum provisioned transactions per-second`は１にしておきましょう。利用ケースに応じて調整すれば良いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/2270b2b9-336f-c58d-e736-df27d840d229.png)

Detailタブを確認するとステータスを見ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/bde63247-46a2-d7f7-4b52-14643510bc01.png)

10分ほどするとActiveになります。しばらく待ちましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/74261e60-1b6b-40aa-de9e-f3887926947f.png)


## レコメンデーションを取得する

キャンペーンを作成すると、アプリに組み込むことで、レコメンデーションを取得できます。
レコメンデーションを取得するには、GetRecommendations API を呼び出します。
必要なキャンペーンの Amazon リソースネーム (ARN)、ユーザー ID、アイテム ID (省略可能) を渡すと、ユーザーへのレコメンドアイテムのリストが GetRecommendations から返却されます。

公式に従い、次のコードを使用してレコメンデーションを取得します。
アイテムIDは省略し、ユーザIDを指定することでレコメンド商品のリストを返却させてみます。

```python:get_recommendation.py
import boto3

personalizert = boto3.client('personalize-runtime', region_name='us-west-2')

response=personalizert.get_recommendations(
    campaignArn="arn:aws:personalize:ap-northeast-1:<AWS_ACCOUNT_ID>:campaign/my-campaign",
    userId='100')

print("Recommended items")

for item in response['itemList']:
        print (item['itemId'])
```

以下が実行結果です。
いい感じですね。ユーザIDが100のユーザさんに対するレコメンド商品の一覧が取得できました。

```bash:bash
$ python get_recommendations.py
Recommended items
281
292
159
31
18
260
230
484
276
160
201
180
364
275
212
241
98
45
147
498
402
133
464
453
56
```

ざっとハンズオン的に解説をしてみましたが
記事を書く作業を含め2時間程度で学習と結果取得まで簡単にできてしまいました。

AmazonPersonalizeを使うことで、SageMakerや自前の機械学習基盤を使って苦労していたのが一気に無くなりそうですね！
[AWS公式のハンズオン](https://aws-seminar.smktg.jp/public/seminar/view/105)もあるようですのでキャッチアップしていき、ぜひ有効活用していきたいところです。