AWS PersonalizeをGoで書いてSAMで動かす

## はじめに
Media Do Advent Calendar 2019 23日目担当の@mimo0905です！

ここではAWS SDK（Go言語）を使用した、Personalizeの構築方法と、SAMでのデプロイ方法を紹介いたします！
PersonalizeはCloudFormationやCDKでサポートされていないため、SDKかAWSコンソールからの構築が一般的です。

AWSコンソールでの作成方法はすでにQiitaに投稿がたくさんあるので今回はSDKにしました！

## Personalizeとは
PersonalizeはAWSが提供するレコメンドエンジンであり、機械学習の知識がないエンジニアでも簡単にレコメンド機能を提供できるサービスです。
ユーザーの地理的情報や年齢、アイテムのジャンルなどの統計情報も追加でき、それを踏まえた推奨結果を出力させることも可能です。

PersonalizeをSDKで作成する際に、最低限作成が必要な項目はこちらです。

 * Dataset Group
 * Schema
 * Dataset
 * Dataset Import Job
 * Solution
 * Solution Version
 * Campaign

※各項目で何をしているかは[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/how-it-works.html)で確認しましょう！


## AWS SAMとは
AWSが提供するサーバーレスアプリケーションのワークフレームで、テンプレートのフォーマットを作成し、
CloudFormationに渡すことでAWS上にスタックが作成されます。
本章ではLambdaのデプロイのためだけに使用しますが、DynamoDBやAPI GatewayをトリガーとしたLambdaの作成や
Lambdaをローカルでテスト実行することもできる便利なサービスです。


## 事前準備

 * S3 BucketにPersonalizeで使用するcsv形式のデータを配置
 * Go SDKのインストール
 * AWS CLIのインストール
 * Pseronalize、S3の実行で必要なIAM Roleの作成

それでは早速Personalize作っていきましょう♪

## Dataset Groupの作成

Dataset Groupの作成には下記の設定項目が必要です。

 * Name：Dataset Groupの名前 
 * KmsKeyArn：データセットの暗号化に使用されるKMSキーのARN　※オプション
 * RoleArn：KMSキーにアクセスする権限を持つIAMロールのARN　※オプション

実際のコードがこちらになります。次の項目からはPersonalizeのAPI使用時に必要な内容だけが変わるためコードは割愛します。

※オプション のところは記載しなくても動くのでカスタマイズしたい場合に使いましょう！

``` go

func CreateDatasetGroup() (*personalize.CreateDatasetGroupOutput, error) {
	
    // AWSセッション管理　
    sess, _ := session.NewSession(&aws.Config{
		Region: aws.String("リージョン名")},
	)
	svc := personalize.New(sess)

    // Dataset Groupの作成に必要な項目を定義 ※他の設定項目ではここの記述内容だけが変わります。
	datasetgroup, _ := svc.CreateDatasetGroup(&personalize.CreateDatasetGroupInput{
		Name: aws.String("Dataset Group名"),
		// KmsKeyArn: aws.String("KMSのARN"),
		// RoleArn:   aws.String("KMS利用時のRoleARN"),
	})
	return datasetgroup, nil
}

// SAMでデプロイするためLambdaを定義 
func main() {
	lambda.Start(CreateDatasetGroup)
}
```

## Schemaの作成

Schemaの作成には下記の設定項目が必要です。

 * Name：Schemaの名前
 * Schema：参照するSchema

最低限必要なのがInteractionsのSchemaになります。
下記のように定義したInteractionsを参照させると、PersonalizeはUSER_IDとITEM_IDとTIMESTAMPから推論を行います。

```json
{
	"type": "record",
	"name": "Interactions",
	"namespace": "com.amazonaws.personalize.schema",
	"fields": [
		{
			"name": "USER_ID",
			"type": "string"
		},
		{
			"name": "ITEM_ID",
			"type": "string"
		},
		{
			"name": "TIMESTAMP",
			"type": "long"
		}
	],
	"version": "1.0"
}
```

## Datasetの作成
Datasetの作成には下記の設定項目が必要です。

 * DatasetGroupArn：Dataset Group作成時に発行されるDataset GroupのARN
 * DatasetType：Datasetのタイプ。Interactions / Items / Usersから選択
 * Name：Datasetの名前
 * SchemaArn：Schema作成時に発行されるSchemaのARN

## Dataset Import Jobの作成
Dataset Import Jobの作成には下記の設定項目が必要です。

 * DataSource：作成したcsvファイルのファイルパス
 * DatasetArn：Datasetの作成時に発行されるDatasetのARN
 * JobName：Dataset Import Jobの名前
 * RoleArn ：S3参照に必要なIAM Role


## Solutionの作成

Solutionの作成には下記の設定項目が必要です。

 * DatasetGroupArn：Dataset Group作成時に発行されるDataset GroupのARN
 * EventType：複数のイベントタイプがある場合指定する　※オプション
 * Name：Solutionの名前
 * PerformAutoML：使用する機械学習のモデルを自動で選択するかの有無　※オプション
 * PerformHPO：ハイパーパラメーター最適化（HPO）を実行するか　※オプション
 * RecipeArn：モデルトレーニングに使用するRecipeのARN。PerformAutoMLが無効の際に指定可能　※オプション
 * SolutionConfig：レシピのデフォルトパラメーターを上書きする際に使用　※オプション


## Solution Versionの作成

Solution Versionの作成には下記の設定項目が必要です。

 * SolutionArn：Solution作成時に発行されるSolutionのARN
 * TrainingMode：FULLを選択するとすべてのソリューションバージョンを対象、UPDATEを選択すると入力ソリューションと比較して変更されたデータのみを処理する　※オプション

## Campaignの作成

Campaignの作成には下記の設定項目が必要です。

 * MinProvisionedTPS：1​​秒あたりの要求された最小プロビジョニングトランザクション
 * Name：Campaignの名前
 * SolutionVersionArn：Solution Version作成時に発行されるSolution VersionのARN


## SAMでデプロイ
Lambdaの管理ツールとしてServerless Framework、Apex、SAMなどがありますが、
その中でもっともAWSとの親和性が高いSAMを使用し、作成したPersonalizeのコードをLambdaで実行します。

まずは、SAMで使用するテンプレートをYAMLで定義します。

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: MakePersonalizeLambda
Resources:
  SAMPLE:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./main.zip
      Handler: datasetgroup
      Runtime: go1.x
      Role: arn:aws:iam::xxxx:role/service-role/sample-lambda
      Timeout: 60 
```

先ほど作成したPersonalizeのコードをビルドします。

```
$ GOOS=linux GOARCH=amd64 go build main.go
```

ビルドしたファイルを圧縮します。

```
$ zip -r main.zip main
```

圧縮したファイルをS3に配置します。

```
$ aws cloudformation package --template-file template.yaml --output-template-file template-output.yaml --s3-bucket sample-bucket
```

作成されたoutput-template-fileを使用してデプロイを行います。

```
aws cloudformation deploy --template-file template-output.yaml --stack-name SamSample --capabilities CAPABILITY_IAM
```

Successfullyと表示されれば成功です！

あとはLambdaで試すなり、Step Functionで実行するなりご自由に！

## おわりに
各項目で設定できる内容を調査するのに時間がかかりましたが、推論に使うレシピやHPOなど機械学習に必要なチューニングの部分をAWSが適切なものを選んでくれるので、機械学習初心者の私でも簡単に動かすことができました！

今度もAWSに新しいサービスが追加されたら試していこうと思います！