AWS日記14 (Forecast)

# はじめに
今回は Amazon Forecastを試します。
Webページ上でデータを入力し、時系列予測結果をページ上に表示します。
[作成したページ]
(https://labo.systems/forecast)


# 準備
[Lambda , API Gatewayの準備をします。]
(https://qiita.com/tanaka_takurou/items/3f93295de6cff060ec09)
[S3の準備をします。]
(https://qiita.com/tanaka_takurou/items/de897b0906087cec1a86)


[Amazon Forecastの資料]
[Amazon Forecast](https://aws.amazon.com/jp/forecast/)
[Amazon Forecast を使ってお手軽に時系列予測する（GUI編）](https://qiita.com/osamasao/items/7eef286ea9a0f73e88b0)
[【初心者】Amazon Forecast を使ってみる](https://qiita.com/mksamba/items/576d1de7678131f0d4d8)

# WEBページ・API作成
GO言語のAWS Lambda関数ハンドラー aws-lambda-go を使用してHTMLやJSONを返す処理を作成します。
また、Forecast を使用するため aws-sdk-go を利用します。

[参考資料]
[AWS SDK for Go API Reference](https://docs.aws.amazon.com/sdk-for-go/api/service/forecastservice/)


##### 全体の処理の流れ

1.  データファイルをS3にアップロード
2.  データをインポート
3.  学習モデルの作成
4.  予測の作成
5.  予測結果をエクスポート
6.  結果ファイルをS3からダウンロード


##### DatasetGroupを作成するには CreateDatasetGroup を使う。

```golang:main.go
func createDatasetGroup(name string) error {
        svc := getForecastservice()

        input := &forecastservice.CreateDatasetGroupInput{
                DatasetGroupName: aws.String(name),
                Domain: aws.String("CUSTOM"),
        }
        res, err := svc.CreateDatasetGroup(input)
        if err != nil {
                return err
        }
        log.Println(aws.StringValue(res.DatasetGroupArn))
        return nil
}
```


##### Datasetを作成するには CreateDataset を使う。

```golang:main.go
func createDataset(name string)(string, error) {
        svc := getForecastservice()

        input := &forecastservice.CreateDatasetInput{
                DatasetName: aws.String(name),
                DataFrequency: aws.String("D"),
                DatasetType: aws.String("TARGET_TIME_SERIES"),
                Domain: aws.String("CUSTOM"),
                Schema: &forecastservice.Schema{
                        Attributes: []*forecastservice.SchemaAttribute{
                                {
                                        AttributeName: aws.String("item_id"),
                                        AttributeType: aws.String("string"),
                                },
                                {
                                        AttributeName: aws.String("timestamp"),
                                        AttributeType: aws.String("timestamp"),
                                },
                                {
                                        AttributeName: aws.String("target_value"),
                                        AttributeType: aws.String("float"),
                                },
                        },
                },
        }
        res, err := svc.CreateDataset(input)
        if err != nil {
                return "", err
        }
        return aws.StringValue(res.DatasetArn), nil
}
```


##### DatasetGroup に Datasetを追加するには UpdateDatasetGroup を使う。

```golang:main.go
func updateDatasetGroup(datasetArn string, datasetGroupArn string) error {
        svc := getForecastservice()

        input := &forecastservice.UpdateDatasetGroupInput{
                DatasetArns: []*string{aws.String(datasetArn)},
                DatasetGroupArn: aws.String(datasetGroupArn),
        }
        _, err := svc.UpdateDatasetGroup(input)
        if err != nil {
                return err
        }
        return nil
}
```


##### データをインポートするには CreateDatasetImportJob を使う。

```golang:main.go
func createDatasetImportJob(name string, datasetArn string, path string, roleArn string)(string, error) {
        svc := getForecastservice()

        input := &forecastservice.CreateDatasetImportJobInput{
                DatasetImportJobName: aws.String(name),
                DatasetArn: aws.String(datasetArn),
                DataSource: &forecastservice.DataSource{
                        S3Config: &forecastservice.S3Config{
                                Path: aws.String(path),
                                RoleArn: aws.String(roleArn),
                        },
                },
        }
        res, err := svc.CreateDatasetImportJob(input)
        if err != nil {
                return "", err
        }
        return aws.StringValue(res.DatasetImportJobArn), nil
}
```


##### 学習モデルを作成するには CreatePredictor を使う。

```golang:main.go
func createPredictor(name string, datasetGroupArn string)(string, error) {
        svc := getForecastservice()

        input := &forecastservice.CreatePredictorInput{
                PredictorName: aws.String(name),
                PerformAutoML: aws.Bool(true),
                ForecastHorizon: aws.Int64(10),
                InputDataConfig: &forecastservice.InputDataConfig{
                        DatasetGroupArn: aws.String(datasetGroupArn),
                },
                FeaturizationConfig: &forecastservice.FeaturizationConfig{
                        ForecastFrequency: aws.String("D"),
                },
        }
        res, err := svc.CreatePredictor(input)
        if err != nil {
                return "", err
        }
        return aws.StringValue(res.PredictorArn), nil
}
```


##### 予測を作成するには CreateForecast を使う。

```golang:main.go
func createForecast(name string, predictorArn string)(string, error) {
        svc := getForecastservice()

        input := &forecastservice.CreateForecastInput{
                ForecastName: aws.String(name),
                PredictorArn: aws.String(predictorArn),
        }
        res, err := svc.CreateForecast(input)
        if err != nil {
                return "", err
        }
        return aws.StringValue(res.ForecastArn), nil
}
```


##### 予測結果をエクスポートするには CreateForecastExportJob を使う。

```golang:main.go
func createForecastExportJob(name string, forecastArn string, path string, roleArn string)(string, error) {
        svc := getForecastservice()

        input := &forecastservice.CreateForecastExportJobInput{
                ForecastExportJobName: aws.String(name),
                ForecastArn: aws.String(forecastArn),
                Destination: &forecastservice.DataDestination{
                        S3Config: &forecastservice.S3Config{
                                Path: aws.String(path),
                                RoleArn: aws.String(roleArn),
                        },
                },
        }
        res, err := svc.CreateForecastExportJob(input)
        if err != nil {
                return "", err
        }
        return aws.StringValue(res.ForecastExportJobArn), nil
}
```
※ 各処理の進捗状態を確認するには DescribeDataset などを使い、Status を見て判定します。


# 終わりに
今回はAmazon Forecastを試しました。
今回試したデータ量は100行でしたが、全工程で１〜２時間程度の処理時間がかかりました。