<jupyter_start><jupyter_text>Amazon Personalizeのイベント追加前後のレコメンド内容検証<jupyter_code>from personalize_util import DatasetGroup, DatasetObject, Solution, Campaign, EventTracker, EventObject
from os import path
import pandas as pd
s3_prefix = 's3://{bucket_name}/personalize/events-demo/'
role_arn = 'arn:aws:iam::{account_id}:role/service-role/AmazonPersonalize-ExecutionRole'
event_type = 'CLICK'<jupyter_output><empty_output><jupyter_text>データ準備<jupyter_code># https://grouplens.org/datasets/movielens/100k/
!wget -N http://files.grouplens.org/datasets/movielens/ml-100k.zip
!unzip -o ml-100k.zip
df = pd.read_csv('./ml-100k/u.data', sep='\t', names=['USER_ID', 'ITEM_ID', 'RATING', 'TIMESTAMP'])
df.drop('RATING', axis=1, inplace=True)
df['EVENT_TYPE'] = event_type
df
items = pd.read_csv('./ml-100k/u.item', sep='|', names=[
    'ITEM_ID', 'TITLE', 'RELEASE_DATE', 'VIDEO_RELEASE_DATE', 'IMDB_URL', 'UNKNOWN', 'ACTION', 'ADVENTURE', 'ANIMATION', "CHILDREN'S", 'COMEDY', 'CRIME', 'DOCUMENTARY', 'DRAMA', 'FANTASY', 'FILM-NOIR', 'HORROR', 'MUSICAL', 'MYSTERY', 'ROMANCE', 'SCI-FI', 'THRILLER', 'WAR', 'WESTERN'
], encoding='latin-1')
items.set_index('ITEM_ID', inplace=True)
def extract_genre(row):
    return '|'.join([i for i, v in row[5:].items() if v == 1 ])
items['GENRE'] = items.apply(extract_genre, axis=1)
items = items[['TITLE', 'GENRE']]
items
interactions_data_path = path.join(s3_prefix, 'datasets', 'interactions.csv')
df.to_csv(interactions_data_path)<jupyter_output><empty_output><jupyter_text>Amazon Personalizeの各種リソース作成<jupyter_code>dataset_group = DatasetGroup.create_dataset_group('events_demo')
# スキーマのフィールド定義
fields = [
    {
        "name": "USER_ID",
        "type": "string"
    },
    {
        "name": "ITEM_ID",
        "type": "string"
    },
    {
        "name": "TIMESTAMP",
        "type": "long"
    },
    {
        "name":"EVENT_TYPE",
        "type":"string"
    }
]

# スキーマとデータセットを作成し、データをインポート
dataset_group.create_datasets_and_import_data(
    datasets=[DatasetObject('Interactions', fields, interactions_data_path)],
    role_arn=role_arn,
    use_it_if_existed=True # 同種のリソースが作成ずみなら、そのままそのリソースを使う
)
# レシピを確認
dataset_group.personalize_client.list_recipes()
# HRNNレシピでソリューションを作成
solution = dataset_group.create_solution(
    recipe_arn='arn:aws:personalize:::recipe/aws-hrnn'
)

# ソリューションバージョンを作成(モデルを学習させる)
solution.create_solution_version()<jupyter_output><empty_output><jupyter_text>イベント追加前後でのレコメンド内容の比較<jupyter_code># アイテムの傾向が見やすそうな、データ数が少ないユーザを見つける
df.groupby('USER_ID').size().sort_values()
def view_past_items(df, user_ids):
    dfs = [items.iloc[df.loc[df.USER_ID==user_id, 'ITEM_ID'].values].reset_index(drop=True) for user_id in user_ids]
    return pd.concat(dfs, keys=user_ids, levels=[user_ids], axis=1)
target_user_ids = [202, 441]
view_past_items(df, target_user_ids)
# ユーザ202があまり見ていない、SCI-FI系の映画をイベントとして追加し、レコメンド内容が変化するか確認する
items[items.GENRE.str.contains('SCI-FI')]
def view_recommendation(recommendation, user_ids=None):
    user_ids = user_ids or recommendation.keys()
    recoms = {}
    for user_id in user_ids:
        recoms[user_id] = items.iloc[recommendation[user_id]].reset_index()
    return pd.concat(recoms, axis=1)<jupyter_output><empty_output><jupyter_text>イベント追加前のレコメンド内容の確認<jupyter_code># バッチでのレコメンドを行う
batch_inferenced_recommendations = solution.batch_inference_for_users(
    s3_prefix=s3_prefix,
    user_ids=df.USER_ID.unique(),
    num_results=20,
    role_arn=role_arn
)
view_recommendation(batch_inferenced_recommendations, target_user_ids)
# リアルタイムレコメンド用のキャンペーンを作成する
campaign = solution.create_campaign(min_provisioned_tps=1)
realtime_recommendations = campaign.get_recommendations_for_users(
    user_ids=target_user_ids,
    num_results=20
)
view_recommendation(realtime_recommendations)<jupyter_output><empty_output><jupyter_text>イベント追加後のレコメンド内容の確認<jupyter_code># イベントトラッカーを作成する(同時にイベント用のデータセットも作成される)
event_tracker = dataset_group.create_event_tracker()
# ユーザ202にSCI-FI系のアイテムのクリックイベントを追加する
event_tracker.put_events(
    user_id=202,
    session_id=1,
    event_list=[EventObject(event_type, {'itemId': '7'})]#, EventObject(event_type, {'itemId': '50'})]
)
realtime_recommendations_after_events = campaign.get_recommendations_for_users(
    user_ids=target_user_ids,
    num_results=20
)
view_recommendation(realtime_recommendations_after_events)
# イベント追加前後でのレコメンド内容が変化したかどうか(イベント追加ユーザ)
realtime_recommendations[202] == realtime_recommendations_after_events[202]
# イベント追加前後でのレコメンド内容が変化したかどうか(イベント追加していないユーザ)
realtime_recommendations[441] == realtime_recommendations_after_events[441]
# バッチレコメンドでも同様deleteる
batch_inferenced_recommendations_after_events = solution.batch_inference_for_users(
    s3_prefix=path.join(s3_prefix, 'after_events'),
    user_ids=df.USER_ID.unique(),
    num_results=20,
    role_arn=role_arn
)

view_recommendation(batch_inferenced_recommendations_after_events, target_user_ids)
batch_inferenced_recommendations[202] == batch_inferenced_recommendations_after_events[202]
batch_inferenced_recommendations[441] == batch_inferenced_recommendations_after_events[441]<jupyter_output><empty_output><jupyter_text>イベントデータセット削除後のリアルタイムレコメンド内容の確認<jupyter_code>dataset_group.delete_dataset_by_type('EVENT_INTERACTIONS')
realtime_recommendations_after_events_deletion = campaign.get_recommendations_for_users(
    user_ids=target_user_ids,
    num_results=20
)
view_recommendation(realtime_recommendations_after_events_deletion)
# イベントデータセット削除後とイベント追加後
realtime_recommendations_after_events_deletion[202] == realtime_recommendations_after_events[202]
event_tracker.put_events(
    user_id=202,
    session_id=1,
    event_list=[EventObject(event_type, {'itemId': '50'})]
)
realtime_recommendations_after_events2 = campaign.get_recommendations_for_users(
    user_ids=target_user_ids,
    num_results=20
)
# view_recommendation(realtime_recommendations_after_events2)
realtime_recommendations_after_events2[202] == realtime_recommendations_after_events[202]<jupyter_output><empty_output><jupyter_text>未学習ユーザに対するイベント追加前後のリアルタイムレコメンド内容の確認<jupyter_code># イベント用データセットを削除したので、イベントトラッカーを作り直す
event_tracker.delete()
event_tracker = dataset_group.create_event_tracker()
untrained_user_ids = [df.USER_ID.max() + 1, df.USER_ID.max() + 2] 
untrained_user_recommendations = campaign.get_recommendations_for_users(
    user_ids=untrained_user_ids,
    num_results=20
)
view_recommendation(untrained_user_recommendations)

# 未学習ユーザにSCI-FI系のアイテムのクリックイベントを追加する
event_tracker.put_events(
    user_id=df.USER_ID.max() + 1,
    session_id=1,
    event_list=[EventObject(event_type, {'itemId': '7'}), EventObject(event_type, {'itemId': '50'})]
)

untrained_user_recommendations_after_events = campaign.get_recommendations_for_users(
    user_ids=untrained_user_ids,
    num_results=20
)
view_recommendation(untrained_user_recommendations_after_events)
untrained_user_recommendations[df.USER_ID.max() + 1] == untrained_user_recommendations_after_events[df.USER_ID.max() + 1]<jupyter_output><empty_output><jupyter_text>データセットグループとその関連リソースの削除<jupyter_code>dataset_group.delete_dataset_group(force=True)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11869895803998874, 'fraction_numerical': 0.016755843424387497, 'mean_word_length': 8.40794701986755, 'pattern_counts': {'":': 8, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '319482', 'license': 'MIT', 'n_tokens_mistral': 3211, 'n_tokens_neox': 3094, 'n_words': 284, 'path': 'notebooks/personalize_events_demo/personalize_events_demo.ipynb', 'repo_name': 'tandfy/devio_samples', 'size': '7102'}