TypeScriptでAmazon PersonalizeのCampaignを使った推論を試す
Amazon Personalizeをざっと触ってみました。 で、どうも推論する際のレシピが複数存在するみたいなので、違いを知るために3つほど試してみたのでその結果をまとめます。 データセットなど Amazon Pers […]
目次
Amazon Personalizeをざっと触ってみました。
で、どうも推論する際のレシピが複数存在するみたいなので、違いを知るために3つほど試してみたのでその結果をまとめます。
データセットなど
Amazon Personalizeのチュートリアルで用意されている映画のデータを使っています。
事前準備
Solution and recipesでCreate solutionする際に、以下の3種類を用意しました。
- Recipe selection: AutoML
- Recipe selection: Manual / Recipe: aws-sims
- Recipe selection: Manual / Recipe: aws-personalized-ranking
それぞれの違いの詳細については、ドキュメントをみてください。
AutoMLはどのレシピを使うのか
ドキュメントを見る限り、HRNN / HRNN-Metadata / HRNN-Coldstartから調査する様子です。
なのでSIMSやPersonalized Rankingのレシピを使いたい場合は、AutoMLではなく自分で設定する必要がありそうです。
AutoMLで推論
まずはAutoMLで試します。AutoMLの場合、userIdを使って推論を行います。
import {
  PersonalizeRuntime
} from 'aws-sdk'
const client = new PersonalizeRuntime({
    region: 'ap-northeast-1'
})
const params: PersonalizeRuntime.Types.GetRecommendationsRequest = {
      campaignArn: 'arn:aws:personalize:ap-northeast-1:99999:campaign/automl',
      userId: '1'
    }
client.getRecommendations(params).promise()
    .then(data => console.log(data))
    .catch(e => console.log(e))実行結果はこちら
{ itemList:
   [ { itemId: '1304' },
     { itemId: '1127' },
     { itemId: '1394' },
     { itemId: '2916' },
     { itemId: '3273' },
     { itemId: '1029' },
     { itemId: '2599' },
     { itemId: '1220' },
     { itemId: '1079' },
     { itemId: '1307' },
     { itemId: '2640' },
     { itemId: '1183' },
     { itemId: '1644' },
     { itemId: '1293' },
     { itemId: '2421' },
     { itemId: '2985' },
     { itemId: '2987' },
     { itemId: '2947' },
     { itemId: '1625' },
     { itemId: '2641' },
     { itemId: '1909' },
     { itemId: '1230' },
     { itemId: '2424' },
     { itemId: '2797' },
     { itemId: '2174' } ] }AWS SIMSで推論
続いてSIMSを試しましょう。こちらはitemIdが必須となります。
import {
  PersonalizeRuntime
} from 'aws-sdk'
const client = new PersonalizeRuntime({
    region: 'ap-northeast-1'
})
const params: PersonalizeRuntime.Types.GetRecommendationsRequest = {
      campaignArn: 'arn:aws:personalize:ap-northeast-1:99999:campaign/sims',
      itemId: '1'
    }
client.getRecommendations(params).promise()
    .then(data => console.log(data))
    .catch(e => console.log(e))結果のフォーマットはautoMLと同じです。
{ itemList:
   [ { itemId: '3114' },
     { itemId: '648' },
     { itemId: '588' },
     { itemId: '34' },
     { itemId: '780' },
     { itemId: '788' },
     { itemId: '364' },
     { itemId: '1073' },
     { itemId: '1270' },
     { itemId: '595' },
     { itemId: '2054' },
     { itemId: '367' },
     { itemId: '1265' },
     { itemId: '1210' },
     { itemId: '919' },
     { itemId: '736' },
     { itemId: '1580' },
     { itemId: '2918' },
     { itemId: '1917' },
     { itemId: '4886' },
     { itemId: '1517' },
     { itemId: '480' },
     { itemId: '150' },
     { itemId: '596' },
     { itemId: '2355' } ] }autoMLはユーザーIDをキーにマッチするアイテムを推論し、SIMSの場合はアイテムIDと類似性の高いアイテムを推論する挙動の様子です。
AWS Personalized Raningで推論
この推論はメソッドが変わります。getRecomendationsを使うとRecipe type PERSONALIZED_RANKING requires list of item-ids and user-id to be specified in request'というエラーが飛んでくるので要注意です。
import {
  PersonalizeRuntime
} from 'aws-sdk'
const client = new PersonalizeRuntime({
    region: 'ap-northeast-1'
})
const params: PersonalizeRuntime.Types.GetPersonalizedRankingRequest = {
      campaignArn: 'arn:aws:personalize:ap-northeast-1:372284591230:campaign/ranking',
      inputList: [
        '999', '565', '1', '20', '34', '50', '1234', '987654'
      ],
      userId: '1'
    }
client.getPersonalizedRanking(params).promise()
    .then(data => console.log(data))
    .catch(e => console.log(e))実行すると、1位を先頭にした配列が返ってきます。
{ personalizedRanking:
   [ { itemId: '1' },
     { itemId: '1234' },
     { itemId: '20' },
     { itemId: '50' },
     { itemId: '34' },
     { itemId: '999' },
     { itemId: '565' },
     { itemId: '987654' } ] }検索結果などをパーソナライズした順序に並び替える用途の時は、このレシピを使うとよさそうです。
合わせ技:推論 + 並び替え
autoMLやSIMSも上から順に予測してる(はず)ですが、せっかく複数立ててみたのでつなげてみました。
import {
  PersonalizeRuntime
} from 'aws-sdk'
const client = new PersonalizeRuntime({
    region: 'ap-northeast-1'
})
const getRecomendations = async (userId: string) => {
   const recomendations = await client.getRecommendations({
      campaignArn: 'arn:aws:personalize:ap-northeast-1:372284591230:campaign/automl',
      userId
    }).promise()
    if (!recomendations.itemList) return []
    const inputList = recomendations.itemList.map(item => item.itemId || '')
    console.log(inputList)
    if (!inputList) return []
    const params: PersonalizeRuntime.Types.GetPersonalizedRankingRequest = {
      campaignArn: 'arn:aws:personalize:ap-northeast-1:372284591230:campaign/ranking',
      inputList,
      userId
    }
    const { personalizedRanking } = await client.getPersonalizedRanking(params).promise()
    console.log(personalizedRanking)
    return personalizedRanking || []
}
わざわざ2つ使う意味はないと思いますので、実際に使う時はどちらか1つでいいと思います。やってみたくなっただけなんです。
おわりに
チュートリアルのデータセットをそのまま複数のレシピに突っ込んでいいのかもよくわかってません。が、動かしてみないとわからないかなということでいろいろぶん回してみました。
購入・閲覧履歴などのユーザー行動を軸にレコメンドを出したい場合は、AutoMLなどの USER_PERSONALIZATION系レシピを使う。
検索結果や特定のリストを並び替えるパーソナライズをしたい場合は、aws-personalized-rankingなどのPERSONALIZED_RANKINGレシピを使う。
選ばれたアイテムと類似するアイテムをレコメンドしたい場合はaws-simsなどのRELATED_ITEMSレシピを使う。
多分このような使い分けをするとよさそうです。