AWS
機械学習
SageMaker

sagemakerで簡易電力予測ページを作ってみた

前回エンドポイント作るまではできたけどそこからどうやってそのエンドポイントを使ったらいいかわからず1日うーーーんってやってたらできたので記事にしてみました。

できたもの

FireShot Capture 36 - 消費電力予測 - http___localhost_8000_.png
作ったものはすごくシンプルでこのフォームに好きな数字を入れて送信ボタンを押下したら

電力予測結果.png
こんな感じで予測消費電力が返って来るっていうもの

ハマったとこ

このページ自体はflaskを使っていて
フォーム作ったりフォームの内容をPOSTして〜ってところは
色々調べていたらできたのですがハマったのは
どうやったら予測消費電力が返って来るか、というところ
読解力が乏しいので色んな記事やら読んでも理解できず......
AWSのブログにlambdaにこうやったら使えるよっていうのが
書いてあったのでそれを使ってみることに...

import boto3

sagemaker = boto3.client('sagemaker-runtime')
result = sagemaker.invoke_endpoint(
      EndpointName='test',
      Body=pre_data
    )

でもデータの形をどうすればいいかわからず(モデル作成はcsvファイルでやってました)
配列を渡したり適当にオブジェクトにしたりcsvに変換してs3に落としてみたり....色々やっていたのですがずっとエラーが出っ放しでした。
特に出てたエラーが

Received client error (400) from model with message "unable to evaluate payload provided". See https://us-east-1.console.aws.amazon.com/cloudwatch/~~~ (Service: AmazonSageMakerRuntime; Status Code: 424; Error Code: ModelError; Request ID: null)

全然意味がわかんないしCloudWatch見ても訳がわからん状態でした。
色々調べてたらsagemaker.invoke_endpointの引数にContentType='text/csv'って入れたらいいよってここに書いてあったので入れたけど結局データの形をどうしたらいいかわからずまたエラー

Invalid type for parameter Body, value: [12, 0, 12, 12.0, 0], type: <class 'list'>, valid types: <class 'bytes'>, <class 'bytearray'>, file-like object

エラーを読んでみるとfile-like objectにしろって書いてある...
でもfile-like objectが何かわからないで調べていたら

ファイルオブジェクトには実際には 3 種類あります: 生の バイナリーファイル、バッファされた バイナリーファイル、そして テキストファイル です。

ってここにありました。
テキストファイルってことは、モデル作るときcsvだったしカンマ区切りか!って思ってカンマ区切りでデータを送ったら

from flask import Flask, render_template, request, redirect
import json
import boto3

app = Flask(__name__)
@app.route('/')
def index():
  return render_template('/sagemaker.html')

@app.route('/predict', methods=['POST'])
def predict():
  if request.method == 'POST':
    month = request.form['month']
    week = request.form['week']
    time = request.form['time']
    temp = request.form['temp']
    weather = request.form['weather']
    pre_data_str = month + "," + week + "," + time + "," + temp + "," + weather
    sagemaker = boto3.client('sagemaker-runtime', region_name='us-east-1')
    result = sagemaker.invoke_endpoint(
      ContentType='text/csv',
      EndpointName='test',
      Body=pre_data_str
    )
    result_body = json.load(result['Body'])
    pre = int(float(result_body['predictions'][0]['score']))
    return render_template('/predict.html', predict=pre)
  else:
    return redirect('/')

if __name__ == '__main__':
  app.run(port=8000, debug=True)

いけました!
わからんすぎて本当に投げそうになったけど最後まで頑張ってよかったです。

これを次はlambdaで使ってみて、その後カスタムでやってみようと思います。