AWSの機械学習レコメンデーションサービスである Amazon Personalize を案件で利用したのでSDKの使い方メモです。
aws.amazon.com
Amazon Personalize は、機械学習の知識がなくても、簡単にレコメンデーションをアプリに組み込むことができるサービスです。
これまで、AWSで機械学習レコメンデーションを行うには、ElasticMapReduce + Mahoutで自前で計算するなど、インフラ構築と保守の手間があったり、モデル設計や評価についての知識が必要だったりとなかなかに大変だったのですが、一般的なレコメンデーションに限っては Amazon Personalize を使うことで、サーバーレスで簡単に実現できるようになりました。
この記事では Amazon Personalize の使い方については解説しません。公式ドキュメントその他を参照してください。
docs.aws.amazon.com
github.com
リソースの作成
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
ユーザー、アイテム、インタラクションのどのデータセットタイプなのかににより、適切な形式のスキーマを作成する。
docs.aws.amazon.com
SCHEMA_NAME = "Users"
DATASET_TYPE = "users"
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"
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,
)
イベントトラッカー
イベントトラッキングを行うには、イベントトラッカーを作る必要があります。
イベントトラッカーを作ると、イベントトラッカーが受け取ったデータを投入するデータセットなども一緒に作られます。
docs.aws.amazon.com
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ロールを指定します。
BUCKET_NAME = "myapp-personalize"
OBJECT_KEY = "/users.csv"
FILE_PATH = "/tmp/users.csv"
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"
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 = 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 = personalize_runtime.get_recommendations(
campaign_arn: CAMPAIGN_ARN ,
user_id: USER_ID.to_s,
num_results: NUM_RESULTS,
).item_list