九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、現在AWS
といえばAmazon
が提供するクラウドサービスとして有名な存在になりました。
私もずっと注目していたのですが、あいにくAWS
をガッツリ使うという開発に携わることがなかったため、正直なところ「出遅れた」感があり、そのまま放置してきました。
しかし、さすがにそれでは今後のためにならないだろうと思い、少しずつ体験することにしました。
そして、今回その一発目として、以前クライアントさんから教えてもらったAWS Personalize
で、
『お客さんコレ好きでしょ?』
機能をつくっていたいと思います。
これは、いわゆる「レコメンデーション(おすすめ)機能」のことで、例えばAmazon
でお買い物をしているときに、「こんな商品もありますけどね…?(チラッ😘)」というようなコンテンツが出ることがあると思います。
まさにこれがレコメンデーション機能ですね。
ということで、今回はLaravel
を使って実装してみたいと思います。
「ジークンドーの動きって
スゴイですね👊✨」
開発環境: Laravel 8.x、Analytics Reporting API v4、AWS Personalize
目次
Amazon Personalize でレコメンデーション機能を実装する流れ
Amazon Personalize
は機械学習を使って「このお客さんは、きっとこの商品が好き!」を見つけ出すことができます。
流れとしておおまかに次のようになります。
- データを用意する
- AWSへアップロード
- 機械学習する
- APIから「おすすめデータ」を取得する
では実際にやってみましょう❗
準備する
今回は学習データとして、今見ていただいてるブログ「Console dot Log」のアクセスログを使います。
そこで、Google Analytics
が提供するAnalytics Reporting API
を使えるようにしていきます。
パッケージをインストールする
以下のコマンドを実行してください。
composer require google/apiclient:^2.0
また、AWS
の認証設定は後でご紹介しますが、同じくcomposer
を使うので先にこちらもインストールしておいてください。
composer require aws/aws-sdk-php
認証用JSONファイルをつくる
そして、Google Cloud Platform へログインし、APIアクセスの際に必要になる認証ファイル(JSON)を用意しておいてください。
📝 参考URL: https://blog.capilano-fw.com/?p=1816#i-2
なお、JSON
ファイルを作成したらサービスアカウントのメールアドレスが取得できます。後で使うので控えておいてください。(JSON
ファイル内にも同じメールアドレスが書かれています)
Analytics Reporting APIの準備をする
では次に、Analytics API
を有効にします。
Google Cloud Platform
画面左にある「APIとサービス」をクリック。
そして、「API とサービスの有効化」リンクをクリック。
検索ボックスが表示されるので、「analytics」と入力。
検索結果の中から「Google Analytics Reporting API」を探してクリック。
最後に「有効にする」ボタンをクリックしたら完了です。
Google Analyticsに権限をセットする
次に、Google Analytics
で先ほど作成したサービスアカウント(JSON
ファイルをつくったときのメールアドレス)を登録してアクセス権限を与えます。
まず、Google Analytics
(⚠ご注意:Google Cloud Platform
ではないです)へログインし、ページ右下にある「管理」リンクをクリック。
アカウントユーザーの管理をクリック。
「+」ボタンから「ユーザーを追加」リンクをクリック。
すると、メールアドレスの入力ボックスが表示されるので、ここにJSON
ファイルを作成したときに取得した「********@iam.gsserviceaccount.com」を入力します。(つまり、Google Cloud
のアカウントがGoogle Analytics
にアクセスできるようにしています)
※ なお、メール通知は不要なのでチェックは外しました。
そして、最後に「追加」ボタンをクリックしたらGoogle Analytics
での作業は完了です👍
学習用データを作る
Analytics Reporting API
の用意ができたので、続いて機械学習に使うデータを用意していきます。
AWS Personalizeで使うデータの概要
AWS Personalize
では、以下3つのデータを使って機械学習をすることができます。
- interactions: どのユーザーはどの商品に好きか?(例:ある商品にアクセスした、いいねを押した等)
- users: 年齢、性別、プレミアム会員かどうか、などのユーザー情報
- items: 価格などの商品情報
※ ただし、必須なのはinteractions
だけなので、今回はusers
とitems
は省略します。
なお、学習データとしてGoogle Analytics
から取得するデータは次のとおりです。
- USER_ID: ユーザーの居住地(Tokyo、Osakaなど)※1
- ITEM_ID: ブログ記事のページ番号
- TIMESTAMP: アクセス日時
※1:Analytics API
ではユーザーを特定するようなIDを発見できませんでしたのでga:region
を使うようにしました。(もしあれば教えてください…plz)
つまり、このデータは以下のようになります。
- 東京に住んでいる人は ページ 111 の記事が好き
- 大阪に住んでいる人は ページ 222 の記事が好き
- 福岡に住んでいる人は ページ 333 の記事が好き
そして、実運用ではIPアドレスなどから住んでいる地方を割り出し「これ好きでしょ❓」コンテンツとして提供することになります。
では、実際にLaravel
で作業をしていきましょう❗
コントローラーをつくる
まずは、コントローラーです。
以下のコマンドを実行してください。
php artisan make:controller GoogleAnalyticsController
すると、ファイルが作成されるので中身を以下のように変更してください。
app/Http/Controllers/GoogleAnalyticsController.php
<?php namespace App\Http\Controllers; use Carbon\Carbon; use Illuminate\Http\Request; class GoogleAnalyticsController extends Controller { private $analytics, $csv_file; public function __construct() { $api_key_path = storage_path('app/json/google_api_key.json'); $client = new \Google_Client(); $client->setAuthConfig($api_key_path); $client->setScopes([\Google_Service_Analytics::ANALYTICS_READONLY]); $this->analytics = new \Google_Service_AnalyticsReporting($client); $csv_path = storage_path('app/csv/aws_personalize.csv'); file_put_contents($csv_path, ''); $this->csv_file = new \SplFileObject($csv_path, 'w'); $this->csv_file->fputcsv(['USER_ID', 'ITEM_ID', 'TIMESTAMP']); } public function make_aws_data() { $dt = today(); for($i = 0 ; $i < 10 ; $i++) { $end_dt = $dt->copy(); // 月の終了日 $dt->subDays(10); // 10日ずつさかのぼる $start_dt = $dt->copy(); // 月の開始日 $dt->subDay(); // 同じ日が重複しないように1日ずらす $reports = $this->getAnalyticsReport($start_dt, $end_dt); // 該当期間のレポートを取得(最大10,000件) foreach ($reports as $report) { $header = $report->getColumnHeader(); $dimensionHeaders = $header->getDimensions(); $rows = $report->getData()->getRows(); foreach ($rows as $row) { $dimensions = $row->getDimensions(); $user_id = ''; // 今回はユーザーの地方(Tokyo, Osakaなど) $item_id = ''; // ブログ記事のページ番号 $timestamp = -1; // UNIXタイムスタンプ foreach ($dimensions as $index => $dimension) { $dimension_header = $dimensionHeaders[$index]; if($dimension_header === 'ga:pagePath') { $pattern = '|p=([0-9]+)|'; if(preg_match($pattern, $dimension, $matches)) { $item_id = $matches[1]; } } else if($dimension_header === 'ga:dateHourMinute') { $timestamp = Carbon::createFromFormat('YmdHi', $dimension)->timestamp; } else if($dimension_header === 'ga:region') { $user_id = $dimension; } } if(!empty($user_id) && !empty($item_id) && $timestamp > 0) { $this->csv_file->fputcsv([ $user_id, $item_id, $timestamp ]); } } } } } private function getAnalyticsReport($start_dt, $end_dt) { $analytics_view_id = env('ANALYTICS_VIEW_ID'); // Metrics $dateRange = new \Google_Service_AnalyticsReporting_DateRange(); $dateRange->setStartDate($start_dt->format('Y-m-d')); $dateRange->setEndDate($end_dt->format('Y-m-d')); $page_views_metric = new \Google_Service_AnalyticsReporting_Metric(); $page_views_metric->setExpression("ga:pageviews"); $page_views_metric->setAlias("pageviews"); $request = new \Google_Service_AnalyticsReporting_ReportRequest(); $request->setViewId($analytics_view_id); $request->setDateRanges($dateRange); $request->setMetrics([$page_views_metric]); // Dimensions $page_path_dimension = new \Google_Service_AnalyticsReporting_Dimension(); $page_path_dimension->setName('ga:pagePath'); $date_dimension = new \Google_Service_AnalyticsReporting_Dimension(); $date_dimension->setName('ga:dateHourMinute'); $region_dimension = new \Google_Service_AnalyticsReporting_Dimension(); $region_dimension->setName('ga:region'); $request->setDimensions([ $page_path_dimension, $date_dimension, $region_dimension ]); $request->setFiltersExpression('ga:pagePath=~p=[0-9]+$'); // p=数字がついてるもので絞り込み $request->setPageSize(10000); $order_by = new \Google_Service_AnalyticsReporting_OrderBy(); $order_by->setFieldName('ga:dateHourMinute'); $order_by->setOrderType('VALUE'); $order_by->setSortOrder('ASCENDING'); $request->setOrderBys($order_by); // データが偏らないよう日付による並べ替えをセット $body = new \Google_Service_AnalyticsReporting_GetReportsRequest(); $body->setReportRequests([$request]); return $this->analytics->reports->batchGet($body); } }
この中では過去100日分(10日ずつ時間をさかのぼる × 10回)でAnalytics Reporting API
からデータを取得し、さらにデータをAWS用に形を変えてCSV
として保存しています。
なお、データ取得する$analytics_view_idはGoogle Analytics
内で、以下のようにして取得できますので、.env
に書き込んでおいてください。
.env
ANALYTICS_VIEW_ID=********
ルートをつくる
では、続いてルートです。
routes/web.php
use App\Http\Controllers\GoogleAnalyticsController; // 省略 Route::prefix('google_analytics')->group(function(){ Route::get('make_aws_data', [GoogleAnalyticsController::class, 'make_aws_data']); });
データをつくる
では、準備が完了しましたので、実際に「http://******/google_analytics/make_aws_data」へアクセスして学習データを作成してみましょう❗
すると・・・・・・
このようにCSV
ファイルが作成されました。
AWSで機械学習をする
では、本題のAWS Personalize
です。
S3へCSVファイルをアップロードする
先ほど作成したCSVファイルはS3
(AWSのストレージサービス)経由で提供することになりますので、まずはアップロードから行いましょう。
なお、バケットの作り方は以下のページを参考にしてみてください。
📝 参考URL: バケット(保存領域)をつくる
※ なお、S3
には権限へ以下のバケットポリシーを追加しておいてください。(本家のページはこちら)
{ "Version": "2012-10-17", "Id": "PersonalizeS3BucketAccessPolicy", "Statement": [ { "Sid": "PersonalizeS3BucketAccessPolicy", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::(あなたのバケット名)", "arn:aws:s3:::(あなたのバケット名)/*" ] } ] }
該当するバケットを選択してページ移動し、「アップロード」ボタンをクリックします。
すると、ファイル選択ダイアログが表示されるので、先ほど作成した「aws_personalize.csv」を選択し、ページ下部にある「アップロード」ボタンをクリックしてください。
アップロードが完了すると、aws_personalize.csv
が一覧表示されるのでファイル名をクリックします。
すると詳細が表示されるので、「S3 URI」に書かれているテキストを控えておきます。(後で、AWS Personalize
の方で指定します)
また、今の状態ではこの後で使うAWS Personalize
からのアクセス権限がついていません。そのため、次の手順でアクセス権限をつけてあげます。
まず「アクセス許可」リンクをクリック。
移動先のページの中にある「バケットポリシー」の「編集する」ボタンをクリック。
ポリシーの中へ以下のJSON
コードを入力し、保存してください。
{ "Version": "2012-10-17", "Id": "PersonalizeS3BucketAccessPolicy", "Statement": [ { "Sid": "PersonalizeS3BucketAccessPolicy", "Effect": "Allow", "Principal": { "Service": "personalize.amazonaws.com" }, "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::(ここをあなたのバケット名に入れかえる)", "arn:aws:s3:::(ここをあなたのバケット名に入れかえる)/*" ] } ] }
入力したところの例はこちら。
なお、この部分については以下のページがわかりやすいです。
AWS Personalizeで機械学習をする
インポートする
では、続いて機械学習の部分です。
先ほどと同じくページ上部から「personalize」と検索して移動します。
そして、ページ移動したら「Get started」ボタンをクリック。
データセットのグループ名を入力して「Next」ボタンをクリック。
詳細を入力するページが表示されるので、データセット名に「region」、そして、Schema
は新しく「basic-schema」という名前のものをつくります。
では、これで「Next」ボタンをクリックします。
そして、次にデータセットのインポート情報の入力です。
まずrole
を作成しますので、「Create role」を選択。
すると、以下のようなポップアップが表示されるので、今回は全てのバケットへの権限を与えることにします。
「Any S3 bucket」を選択して「Create role」をクリックしてください。
これでrole
が作成されたので、後は以下のように「インポート作業名」、そして、先ほどS3
で取得したURLを入力。
そして最後に「Finish」ボタンをクリックします。
すると、以下のように「作業中ですよ👍」という表示が現れますので、しばらくヒカルくんやエガちゃんねるでも見て時間をつぶしてください😂
そして・・・・・・・
時間が経って、Active
という表示になったらインポートは完了です。
学習データ(solution)をつくる
では、インポートしたデータを使って学習済みデータ「Solution」をつくっていきましょう。
同じページにある「Solution」の項目に「Start」ボタンが表示されていると思いますので、これをクリックします。
すると、学習方法を選択するページが表示されるので、適当な名前を入力し、「aws-user-personalization」を選択します。
選択が終わったら、「Next」ボタンをクリック。
確認ページが表示されるので、「Finish」ボタンをクリックしてください。
するとまた、「作業中ですよ👍」状態になるので、朝倉未来さんやけいちょんチャンネルでも見ながら時間を待ってください。(私の場合、30分ぐらいかかりました)
完了したら、次はCampaign
です。
APIが使えるようにする(Campaign)
機械学習データを作ってもそのままでは、APIが用意されていません。
そのため、次はLaravel
からAWS Personalize
にアクセスできるよう専用APIを作っていきましょう。
先ほどのSolution
のすぐ右側にCampaign
の項目があるので、「Create campaign」ボタンをクリックします。
すると、詳細ページが表示されるので、キャンペーン名を入力、さらに先ほど作成したSolution
を選択します。
そして、ページ下部にある「Create campaign」ボタンをクリックします。
すると、やはり「作業中ですよ👍」になりますので、少し待ちます(今回は表示が違うので分かりにくいかもしれませんが、比較的すぐ終わりますのでYouTubeは見ないほうがいいです😂)
そして・・・・・・・
キャンペーンの作成が完了すると、すぐ下にテストできるセクションがでるので、Tokyo
と入力して東京に住んでる人たちの「これ好きでしょ❓」データを取得してみましょう。
すると・・・・・・
はい❗
すぐ下にRecommendation
セクションが表示されました。
どうやらScore
は「よりおすすめ」指数のようですね。
これで機械学習部分は完了です😊
なお、同じページにARN
(Amazon リソースネーム)が書かれています。
次の項目で必要になるので、この文字列を控えておいてください。
LaravelからAWS Personalizeにアクセスしておすすめデータ取得する
認証情報を取得する
Laravel
からAWS
のAPIに接続するには、認証情報が必要になります。
そのため、先にAWS
から「アクセスキーID」と「シークレットアクセスキー」を取得して.env
へ設置しておいてくだい。
📝 参考URL: アクセスキー&秘密キーを取得する
.env
AWS_ACCESS_KEY_ID="(ここにアクセスキーID)" AWS_SECRET_ACCESS_KEY="(ここにシークレットアクセスキー)" AWS_DEFAULT_REGION=ap-northeast-1
※ これらのキーはすでに.env
に存在しています。
また、先ほどCampaign
を作成したときに取得したARN
もここに書き込んでおきましょう。
AWS_RECOMMENDATION_ARN="arn:aws:personalize:ap-northeast-1:***********************:"
コントローラーをつくる
では、コントローラーです。
以下のコマンドを実行してください。
php artisan make:controller AwsPersonalizeController
するとファイルが作成されるので、中身を以下のように変更します。
app/Http/Controllers/GoogleAnalyticsController.php
<?php namespace App\Http\Controllers; use Aws\PersonalizeRuntime\PersonalizeRuntimeClient; use Illuminate\Http\Request; class AwsPersonalizeController extends Controller { public function get_recommendations() { $user_id = 'Tokyo'; // 👈 Tokyoに住んでいる人へのおすすめを取得します $client = new PersonalizeRuntimeClient([ 'region' => 'ap-northeast-1', 'version' => 'latest' ]); $result = $client->getRecommendations([ 'campaignArn' => env('AWS_RECOMMENDATION_ARN'), 'userId' => $user_id, ]); $recommendations = $result->get('itemList'); foreach($recommendations as $recommendation) { $item_id = $recommendation['itemId']; $score = $recommendation['score']; $url = 'https://blog.capilano-fw.com/?p='. $item_id; echo '<a href="'. $url .'" target="_blank">'. $url .'</a><br>Score: '. $score .'<hr>'; } } }
この中では、「Tokyoに住んでいるへのおすすめ」取得しています。
そのため、実際の運用では ipinfo.io などのサービスでアクセスした人がどの地方にいるかを取得し、それを$user_id
としておすすめ情報を取得することになります。
ルートを追加する
続いてルートです。
routes/web.php
use \App\Http\Controllers\AwsPersonalizeController; // 省略 Route::prefix('aws_personalize')->group(function(){ Route::get('get_recommendations', [AwsPersonalizeController::class, 'get_recommendations']); });
これで全てが完了しました❗
テストしてみる
では、実際にブラウザでアクセスしてみましょう❗
ブラウザで「http://*****/aws_personalize/get_recommendations」にアクセスします。
すると・・・・・・
スコア付きでリンクが表示されました❗
では、次にTokyo
をKanagawa
に変えてリロードしてみましょう。
すると・・・・・・
はい❗
内容の違うおすすめページが表示されました。
成功です😊(精度としても「こんな感じだろうな」というラインナップになっていました👍)
企業様へのご提案
今回のAWS Personalize
で何ができるかを考えてみました。
ぜひ気になるものがありましたら、お気軽にご相談ください👍
- 過去の売上データの中から年齢・性別・家族構成などを取り出し「どの商品を提案すれば気にいってもらいやすいか」を割り出す
- 時間帯で区切った売上データを使って「この時間帯は●●が売れやすいか」のデータを取得する
- ユーザーの居住地(都道府県や市区町村)データを使ってよく閲覧されているページを調べる
- 過去の天気や温度データを使って、「晴れの日はこれが売れる」「寒い日は意外とことが人気になる」などを見つけ出す
- マッチングサービスで、「こういう経歴の人はこういう人とマッチングしやすい」など
参考にしたページ
今回の開発をするに当たって以下3つのページを参考にさせていただきました。特にDevelopersIO
さんの方はスクリーンショットまで用意してくれていたのでとても助かりました!
Amazon Personalize – Real-Time Personalization and Recommendation for Everyone(本家のブログ)
AWS Personalize : 開始方法(これも本家)
Amazon Personalizeを使ってみた(DevelopersIO)
先人たちに感謝❗
有益な情報、ありがとうございます。m(_ _)m
ちなみに:今回のかかった料金
ちなみに今回は(1回目間違えたので)合計2回機械学習をしました。
そして、その料金は次のとおりでした。
実現できることと比べるとホント格安ですよね👍
おわりに
ということで、今回はAWS Personalize
を使って「これ好きでしょ❓」機能を作ってみました。
AWS
はEC2
やS3
ばかりで他のものをあまり使ったことがありませんでしたが、今回念願の機械学習を使うことができました。
なお、雑感としてはこういった機械学習・AIは大量のデータが必要になってくるので、やはり規模の大きい企業が有利だろうなという気がしました。
というのも、規模が小さいとデータそのものの量も多くなりにくいですし、たくさんの条件下のデータではないので偏り含まれる可能性が高いからです。
そう考えると、やはり日頃から何かとデータは残しておくべきということになるので、これも先日別のクライアントさんから教えていただいた Obniz のようなキットを使ってデータを「貯金」していくことが今後の戦略としては正しいのかもしれません。
ぜひ皆さんも「これ好きでしょ?」機能を試してみてくださいね。
ではでは〜❗
「予想以上に記事のボリュームが
大きくなってしまいました💦」