Ruby による Amazon Personalize の使い方
AWSの機械学習レコメンデーションサービスである Amazon Personalize を案件で利用したのでSDKの使い方メモです。
Amazon Personalize
Amazon Personalize は、機械学習の知識がなくても、簡単にレコメンデーションをアプリに組み込むことができるサービスです。
これまで、AWSで機械学習レコメンデーションを行うには、ElasticMapReduce + Mahoutで自前で計算するなど、インフラ構築と保守の手間があったり、モデル設計や評価についての知識が必要だったりとなかなかに大変だったのですが、一般的なレコメンデーションに限っては Amazon Personalize を使うことで、サーバーレスで簡単に実現できるようになりました。
この記事では Amazon Personalize の使い方については解説しません。公式ドキュメントその他を参照してください。
Ruby SDK
リソースの作成
データセットグループ
DATASET_GROUP_NAME = "my-first-datasetgroup" personalize = Aws::Personalize::Client.new dataset_group_arn = personalize.create_dataset_group(name: DATASET_GROUP_NAME).dataset_group_arn
スキーマ
ユーザー、アイテム、インタラクションのどのデータセットタイプなのかににより、適切な形式のスキーマを作成する。
SCHEMA_NAME = "Users" DATASET_TYPE = "users" # or "items" or "interactions " personalize = Aws::Personalize::Client.new avro_schema = case DATASET_TYPE when "users" { "type": "record", "name": "Users", "namespace": "com.amazonaws.personalize.schema", "fields": [ { "name": "USER_ID", "type": "string" }, { "name": "AGE", "type": "int" }, { "name": "GENDER", "type": "string" } ], "version": "1.0" } when "items" { "type": "record", "name": "Items", "namespace": "com.amazonaws.personalize.schema", "fields": [ { "name": "ITEM_ID", "type": "string" }, { "name": "LABEL", "type": "string", "categorical": true }, { "name": "CAST_ID", "type": "string", "categorical": true }, { "name": "DURATION", "type": "long" }, { "name": "PUBLISH_AT", "type": "long" } ], "version": "1.0" } when "interactions" { "type": "record", "name": "Interactions", "namespace": "com.amazonaws.personalize.schema", "fields": [ { "name": "USER_ID", "type": "string" }, { "name": "ITEM_ID", "type": "string" }, { "name": "EVENT_TYPE", "type": "string" }, { "name": "EVENT_VALUE", "type": "float" }, { "name": "TIMESTAMP", "type": "long" } ], "version": "1.0" } end schema_arn = personalize.create_schema( name: SCHEMA_NAME, schema: avro_schema.to_json ).schema_arn
データセット
DATASET_NAME = "users" DATASET_TYPE = "users" # or "items" or "interactions " SCHEMA_ARN = "arn:aws:personalize:{{REGION}}:{{ACCOUNT_ID}}:schema/{{SCHEMA_NAME}}" DATASET_GROUP_ARN = "arn:aws:personalize:{{REGION}}:{{ACCOUNT_ID}}:dataset/{{DATASET_GROUP_NAME}}" personalize = Aws::Personalize::Client.new personalize.create_dataset( name: DATASET_NAME, schema_arn: SCHEMA_ARN, dataset_group_arn: DATASET_GROUP_ARN , dataset_type: DATASET_TYPE )
学習用データを置くS3バケット
Amazon Personalize からアクセスできるようにバケットポリシーを設定する必要があります。
BUCKET_NAME = "myapp-personalize" s3 = Aws::S3::Client.new s3.create_bucket(bucket: S3_BUCKET_NAME) s3.put_bucket_policy( bucket: S3_BUCKET_NAME, policy: { "Version": "2008-10-17", "Id": "PolicyForPersonalizePrivateContent", "Statement": [ { "Sid": "PersonalizeS3BucketAccessPolicy", "Effect": "Allow", "Principal": { "Service": "personalize.amazonaws.com" }, "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::#{S3_BUCKET_NAME}", "arn:aws:s3:::#{S3_BUCKET_NAME}/*" ] } ] }.to_json )
学習用データのインポートに使う実行ロール
AmazonPersonalizeFullAccess
のサービスロールを使うのに加え、学習用データを置くS3バケットへのアクセスを許可します。
BUCKET_NAME = "myapp-personalize" ROLE_NAME = "personalizeExecutionRole" ROLE_PATH = "/myapp/" iam = Aws::IAM::Client.new role = iam.create_role( assume_role_policy_document: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "personalize.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }.to_json, path: ROLE_PATH, role_name: ROLE_NAME, ).role iam.attach_role_policy( policy_arn: "arn:aws:iam::aws:policy/service-role/AmazonPersonalizeFullAccess", role_name: role.role_name, ) iam.put_role_policy( policy_document: { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListBucket" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::#{S3_BUCKET_NAME}" ] }, { "Action": [ "s3:GetObject", "s3:PutObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::#{S3_BUCKET_NAME}/*" ] } ] }.to_json, policy_name: "AllowAccessToS3Bucket", role_name: role.role_name, )
イベントトラッカー
イベントトラッキングを行うには、イベントトラッカーを作る必要があります。 イベントトラッカーを作ると、イベントトラッカーが受け取ったデータを投入するデータセットなども一緒に作られます。
DATASET_GROUP_ARN = "arn:aws:personalize:{{REGION}}:{{ACCOUNT_ID}}:dataset/{{DATASET_GROUP_NAME}}" personalize = Aws::Personalize::Client.new event_tracker_arn = personalize.create_event_tracker( name: EVENT_TRACKER_NAME, dataset_group_arn: DATASET_GROUP_ARN ).event_tracker_arn
学習用データのインポート
ユーザー、アイテム、インタラクションそれぞれ学習させたいデータセットタイプにあわせてCSVを作成します。 アップロードしたCSVを指定したデータセットインポートジョブを作成します。
データセットインポートジョブのパラメータとして、インポート先のデータセット、データセットタイプに対応するスキーマで作成したCSVのS3オブジェクトキー、実行IAMロールを指定します。
# CSV BUCKET_NAME = "myapp-personalize" OBJECT_KEY = "/users.csv" FILE_PATH = "/tmp/users.csv" # Amazon Personalize リソース DATASET_ARN = "arn:aws:personalize:{{REGION}}:{{ACCOUNT_ID}}:dataset/{{DATASET_GROUP_NAME}}/{{DATASET_NAME}}" # 作成したデータセット ROLE_ARN = "arn:aws:iam::{{ACCOUNT_ID}}:role/myapp/{{ROLE_NAME}}" # 作成した実行ロール s3 = Aws::S3::Client.new File.open(FILE_PATH, 'r') do |file| s3.put_object( bucket: BUCKET_NAME, key: OBJECT_KEY, body: file, content_type: 'text/csv' ) end personalize = Aws::Personalize::Client.new personalize.create_dataset_import_job( job_name: "users-import-job-#{Time.current.to_i}", # 重複はできない dataset_arn: DATASET_ARN, data_source: { data_location: File.join('s3://', BUCKET_NAME , OBJECT_KEY) }, role_arn: ROLE_ARN, )
リアルタイムイベントトラッキング
EVENT_TRACKER_TRACKING_ID = "0000000-0000-0000-0000-000000000000" USER_ID = current_user.id SESSION_ID = session.id EVENT_TYPE = "rating" # 今回は★1-5のレビュー評価をトラッキングする想定。自由に決められる。 ITEM_ID = review.item_id EVENT_VALUE = review.rating SENT_AT = review.created_at personalize_events = Aws::PersonalizeEvents::Client.new payload = { tracking_id: EVENT_TRACKER_TRACKING_ID, user_id: USER_ID.to_s, session_id: SESSION_ID , event_list: [ { event_type: EVENT_TYPE, properties: { itemId: ITEM_ID.to_s, eventValue: EVENT_VALUE, }.to_json, sent_at: SENT_AT, } ] } personalize_events.put_events(payload)
レコメンデーション結果の取得
アイテムベース (SIMS)も、ユーザーベースも、レコメンデーション結果を取得するAPIは同じです。 パラメータが違います。
アイテムベース (SIMSレシピのソリューションを使ったキャンペーン)
item_id
を渡します。
CAMPAIGN_ARN = "arn:aws:personalize:{{REGION}}:{{AWS_ACCOUNT_ID}}:campaign/{{CAMPAIGN_NAME}}" ITEM_ID = 1234 NUM_RESULTS = 20 personalize_runtime = Aws::PersonalizeRuntime::Client.new # item_list[0].item_id #=> String # item_list[0].score #=> 常に 0 item_list = personalize_runtime.get_recommendations( campaign_arn: CAMPAIGN_ARN , item_id: ITEM_ID.to_s, num_results: NUM_RESULTS, ).item_list
ユーザーベース (HRNN レシピのソリューションを使ったキャンペーン)
user_id
を渡します。
結果セットにスコアが含まれるので、おすすめ順に並べるのに使えますね。
CAMPAIGN_ARN = "arn:aws:personalize:{{REGION}}:{{AWS_ACCOUNT_ID}}:campaign/{{CAMPAIGN_NAME}}" USER_ID = 1234 NUM_RESULTS = 20 personalize_runtime = Aws::PersonalizeRuntime::Client.new # item_list[0].item_id #=> String # item_list[0].score #=> Float item_list = personalize_runtime.get_recommendations( campaign_arn: CAMPAIGN_ARN , user_id: USER_ID.to_s, num_results: NUM_RESULTS, ).item_list