Amazon Personalize Web-APIで情報推薦サービスを実現する(1)

Amazon Personalize Web-APIで情報推薦サービスを実現する
▶︎(1)シグネチャバージョン4を使うメソッドの定義と必要なデータ
 (2)データセットグループとスキーマ
 (3)データセットとイベントトラッカー

はじめに

今でこそ無数のECサイトがありますが、2000年代初頭、ECサイトといえばAmazon、Amazonといえば「商品のお薦め(推薦システム)」の代名詞だった頃がありました。

One of the most famous examples of collaborative filtering is item-to-item collaborative filtering (people who buy x also buy y), an algorithm popularized by Amazon.com’s recommender system.
引用元:Recommender system(wikipedia/太字は引用者による)

協調フィルタの最も有名なものは、Amazon.comの推薦システムで有名になったアルゴリズムである、アイテム-アイテム協調フィルタ(xを買う人はyも買っています的な)である
引用元:Amazon.com(日本語版wikipedia)

Amazonの特徴
レコメンデーション機能
Amazonの最大の特徴は強力なレコメンデーション機能にある。現在のところAmazonはレコメンデーションの実用レベルの最先端を走っているという見方が支配的であり、技術の向上にも余念が無い。
引用元:Amazon.com(日本語版wikipedia)

実際、2001年頃のamazon.co.jpで「UNIXの1/4世紀」という和訳本のページを見てみると、「この本を買った人はこんな本も買っています」ということでブライアン・カーニハンの「プログラミング作法」がお薦めされていた事が確認でき、それらしい商品の推薦がされていたようです。
amazon.co.jp-UNIXの1/4世紀(Wayback Machineによる当時のキャプチャ)
(なお、試しに今のamazon.co.jpで「UNIXの1/4世紀」を検索してみたら、「この商品を買った人はこんな商品も買っています」ということでやはり推薦商品の中にカーニハンが入っていました)

そんな有名なAmazonの推薦システムがWeb-APIサービスとして公開されることが2018年末に発表になり、業界では話題になりました。

先週(2018年11月26~30日)、Amazon AWSの大規模な年次開発者イベントAWS re:Invent 2018が開催されたが、その中で驚くべきことが発表された。なんと同社の中核技術のレコメンドAI(Amazon Personalize)を外販すると発表したのだ。今までAmazonが築いてきた強みが、個々の消費者にあわせて商品をレコメンドできるシステムだ。創業以来20年以上かけて、培ってきた虎の子であるその技術を開放することにしたのだ。
引用元:Amazon Personalizeの外販がAmazonにもたらすもの。

そして公開予告から約半年、ついに一般のユーザが利用できるようになりました。

AWS Announces General Availability of Amazon Personalize

その詳細な内容は、既にAmazonのサイトにて、概要及び開発者ドキュメントが整備されています。概要はこちらを御覧下さい。
Amazon Personalize

そのサービスを、実際に使って紹介しようと思います。

Amazon Personalize Web-APIを使うために

AWS(Amazon Web Services)では、WebコンソールだけではなくCLI(コマンドラインインターフェース)や、各種プログラミング言語からアクセスできるAPIを提供しています。特に、主要な言語、Java/JavaScript/.net/PHP/Python/Ruby/Go/C++についてはSDKが提供されており、プログラミング技術があればサービスを利用できるようになっています。いずれにせよAWSのアカウントが必要なので、ここではアカウントを取得していることは前提とします。アカウントについてはこちらを御覧下さい。

AWS ドキュメント » Amazon Personalize » 開発者ガイド » セットアップ

AWSのアカウントが取得済みであれば、AWS CLIの他、主要なプログラミング言語のSDKが公開されており、すぐにWebアプリケーションなどにAmazon Personalizeの機能を組み込むことができます。しかし、この記事ではSDKを使わずにWeb-APIを利用して
Amazon Personalizeの機能を使ってみます。Web-APIを使えば,ネットワークの知識があればより最適なアクセスやプログラミング、効果的なデバッグができますし、SDKが無いプログラミング言語でも、SDKをインストールしていない環境でもアクセスできます。そのため、この記事ではHTTP/HTTPSでのネットワークプログラミングの知識があることも前提とします。

なお、この記事ではプログラミング言語はRubyを使います。

準備:シグネチャバージョン 4を作るためのルーチン

SDKではなくWeb-APIを利用してAmazonのAPIにアクセスする場合、シグネチャバージョン 4というアルゴリズムを使って、HTTPリクエストにシグネチャ(署名)を付与しないとなりません。
シグネチャバージョン 4のシグネチャを作るためには、シグネチャキーを計算します。シグネチャキーを計算するメソッドは公開されており(署名バージョン 4 の署名キーを取得する方法の例)、Rubyのコードは次のようになります。

def getSignatureKey(key, dateStamp, regionName, serviceName)
  kDate = OpenSSL::HMAC.digest('sha256', "AWS4#{key}", dateStamp)
  kRegion = OpenSSL::HMAC.digest('sha256', kDate, regionName)
  kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
  kSigning = OpenSSL::HMAC.digest('sha256', kService, 'aws4_request')
  kSigning
end

この署名キーを使って署名したHTTPリクエストを作成するコードは次のようになります。

# @params request_url URL of the api endpoint
# @params target API name
# @params post_data data depends on each API
def call(request_url, target, post_data)
  t = Time.now().getutc()
  amzdate = t.strftime('%Y%m%dT%H%M%SZ')
  datetimestamp = t.strftime('%Y%m%d')
  credential_scope = "#{datetimestamp}/#{REGION}/#{SERVICE}/aws4_request"
  signing_key = getSignatureKey(@secret_key, datetimestamp, REGION, SERVICE)
  uri = URI.parse(request_url)
  host = uri.hostname
  canonical_uri = uri.path
  canonical_headers = "content-type:#{CONTENT_TYPE}\nhost:#{host}\nx-amz-date:#{amzdate}\nx-amz-target:#{target}\n"
  payload_hash = Digest::SHA256.hexdigest(post_data.encode('utf-8'))
  canonical_request = "#{METHOD}\n#{canonical_uri}\n#{CANONICAL_QUERYSTRING}\n#{canonical_headers}\n#{SIGNED_HEADERS}\n#{payload_hash}"
  string_to_sign = "#{ALGORITHM}\n#{amzdate}\n#{credential_scope}\n#{Digest::SHA256.hexdigest(canonical_request.encode('utf-8'))}"
  signature = OpenSSL::HMAC.hexdigest('sha256', signing_key, string_to_sign.encode('utf-8'))
  authorization_header = "#{ALGORITHM} Credential=#{@access_key}/#{credential_scope}, SignedHeaders=#{SIGNED_HEADERS}, Signature=#{signature}"
  req = Net::HTTP::Post.new(uri)
  req["Content-Type"] = CONTENT_TYPE
  req["x-amz-date"] = amzdate
  req["x-amz-target"] = target
  req["Authorization"] = authorization_header
  req["host"] = host
  req.body = post_data
  req_options = {
    use_ssl: uri.scheme = "https"
  }
  r = Net::HTTP.start(host, uri.port, req_options) {|http|
    http.request(req)
  }
  if r.code != "200" then
    puts "error:#{r.code},#{r.message},#{r.body}"
  end
  r
end

このコードは完全なバージョン 4 署名プロセスの例 (Python)を参考に作成したものです。
9行目でシグネチャキーを計算し、17行目でシグネチャを作成し、18行目でシグネチャを含んだHTTPヘッダ文字列を作成、23行目でHTTPリクエストヘッダに含めています。
このメソッドは、次のように使います。

call(APIのエンドポイント, 呼び出したいAPIの名前, APIに渡したいパラメータやデータ)

プログラム文字列中のコロンの後にスペースを入れたり、逆にスペースを取り除いたりすると、APIにアクセスした時にエラーになるので気をつけて下さい
プログラム中の定数は、次のように設定しておきます。

METHOD = 'POST'
SERVICE = 'personalize'
REGION = 'ap-northeast-1'
CONTENT_TYPE = 'application/x-amz-json-1.1'
CANONICAL_QUERYSTRING = "" # request parameters are not used for the post method
SIGNED_HEADERS = 'content-type;host;x-amz-date;x-amz-target'
ALGORITHM = 'AWS4-HMAC-SHA256'

ドキュメント完全なバージョン 4 署名プロセスの例 (Python)では

content_type = 'application/x-amz-json-1.0'

となっていますが、x-amz-jsonのバージョンが1.0だとAmazon Personalize Web-APIは使えないので、こちらも気をつけて下さい
また、Amazon Personalizeのドキュメントには例えば次のような記載がありますが、

Request Syntax

POST /recommendations HTTP/1.1
Content-type: application/json

content-typeは全てx-amz-jsonだけでAmazon Personalize Web-APIは利用できます。

なお、プログラム中の2つのクラス変数、@access_keyと@secret_keyには、AWSアカウントのアクセスキーIDとシークレットアクセスキーの文字列をそれぞれセットします。通常、AWSアカウントではなくIAMユーザーのキーを使うと思います。
IAM ユーザーのアクセスキーの管理

アクセスキーは、IAM ユーザーまたは AWS アカウントのルートユーザー の長期的な認証情報です。アクセスキーを使用して、AWS CLI または AWS API (直接または AWS SDK を使用) にプログラムでリクエストに署名することができます。


ここで準備した、署名入りAPIリクエスト生成メソッドを使ってAmazon Personalize APIにリクエストを送信することで、Amazon Personalizeを利用することができます。

次に、Amazon Personalizeのデータについて説明します。

Amazon Personalizeのデータ構造

Amazon Personalizeでは、依存関係が存在する階層的なデータ構造を持ちます。また、Web-APIを利用するアカウントのみに依存する独立したデータも存在します。それらを簡単に説明します。

  1. DatasetGroup。データの最上位に位置する概念で、まずはデータセットグループを作らないと何も出来ません。
  2. Dataset。データセットはデータセットグループの中に存在し、ユーザーIDや商品IDなど、情報推薦に必要な学習データと、学習データのスキーマを持ちます。データセットグループの中には複数のデータセットを持つことができます。
  3. Solution。ソリューションはデータセットグループの中に存在し、データセットから推薦情報を取り出す方法を持っています。例えば使う機械学習アルゴリズムなどをデータとして持ちます。データセットグループの中には、複数のソリューションを持つことができます。
  4. SolutionVersion。ソリューションバージョンはソリューションの中に存在し、ソリューションを実行する、つまりデータセットに機械学習アルゴリズムを適用して作成された学習済みデータを持ちます。データセットグループの中には、ソリューションに対応した複数のソリューションバージョンを持つことができます。
  5. Campaign。キャンペーンはソリューションバージョンの中に存在し、ソリューションバージョンをデプロイして利用できる状態にしたデータを持ちます。データセットグループの中には、ソリューションバージョンに対応した複数のキャンペーンを持つことができます。
  6. EventTracker。イベントトラッカーはデータセットグループの中に存在し、データセットにデータを追加するためのIDを持ちます。データセットグループの中には複数のイベントトラッカーを持つことができます。
  7. Schema。スキーマはデータセットグループに依存せずに存在し、データセットの種類及びデータセットの中のデータのフィールドを持ちます。データセットの種類はUsers/Items/Interactionsの3種類が現時点でサポートされています。フィールドはデータセットの種類に依存し、例えばUsersならユーザーIDが必須でユーザー属性情報(年齢や性別など)は自由に設定できます。
  8. Recipe。レシピは情報推薦の種類とそれに対応する前処理及び機械学習アルゴリズムで、ドキュメントによると現在6種類のレシピがサポートされているようです。なお、日本語翻訳版のドキュメントには8種類のレシピが記載されていますが、実際にWeb-APIにアクセスしてみるといくつかはエラーになり利用できませんでした
    {
      "__type": "InvalidInputException",
      "message": "This recipe is a deprecated predefined recipe. Please use the new predefined recipes found at https://docs.aws.amazon.com/personalize/latest/dg/working-with-predefined-recipes.html"
    }
    

    公式ブログにも8種類とありますが、こちらも情報が古いです。

    Amazon Personalize – すべてのユーザにリアルタイムパーソナライゼーションとレコメンデーションを

簡単に図示するとこのようになります。

実際にはまだまだあるのですが(ドキュメントのデータ型一覧)、今回の一連の記事ではこれらのデータ構造だけを使います。

次の予定

次の記事では、データを作成していく具体的なプログラムを紹介していきたいと思います。
Amazon Personalize Web-APIで情報推薦サービスを実現する(2)

あなたにおすすめの記事