Amazon Web Services ブログ

Amazon Forecastを組み合わせたSAPでの販売予測

はじめに

販売予測とは、誰か (企業、営業担当者、小売業者など)が何か (製品やサービス)をどれだけ販売するかを予測するプロセスです。正確な予測の重要性を説明するために、オンラインで商品を販売するファッション小売業の場合を考えてみましょう。需要を正しく予測することで、適切なフルフィルメントの配置 (配送費用の削減)による適切なレベルの在庫管理 (運用コストの削減)ができ、お客様への迅速な配送 (顧客満足度の向上)ができます。信頼できる販売予測は、次のような重要な下流部門の決定を可能にし、組織の成功に大きな役割を果たすことができます。

  • 戦略的予測: 総売上高/収益に関して予測される成長率はどれくらいですか?ビジネスは、(もっと)地理的にどこで活動すべきですか?
  • 運用予測: ベンダーから購入する各製品のユニット数とリードタイムはどれくらいで、またどの地域に配置しますか?需要を満たすために必要な労働力はどれくらいですか?
  • 戦術的予測: プロモーションはどのように行う必要がありますか?製品は整理されるべきですか?どの地域で実行すべきですか?

長年に渡って、Amazon.comは、販売予測のリーダーであり続けています。AWSは、Amazon.comで使用しているのと同じテクノロジーを用いたAmazon Forecastを実装し、機械学習の経験がないお客様でも正確な予測ができるようにしています。Amazon Forecastは、時系列データとメタデータ (製品の特徴、休日カレンダーなど)に機械学習を利用します。 このブログ記事では、SAP S/4HANA (または以前のリリースのSAP ERP)とAmazon Forecastを統合し、将来の売り上げを予測する方法を紹介します。このブログをシンプルにするために、要素としては将来の販売日のみを考慮しています。しかしながら、信頼できる予測を行うためには、お客様のユースケースに関連する製品属性 (色、形、大きさなど)、時期、季節などのすべての要素を考慮する必要があります。

ソリューション概要

おおまかには、このソリューションは3つの手順で構成します。

  1. SAP S/4HANAからデータを抽出: SAPの販売伝票データはテーブルVBAPに存在し、ヘッダー情報はテーブルVBAKに存在します。Amazon Forecastが要求するデータのスキーマフォーマットを理解したら、次のことを行う必要があります。
    • ABAPレポートを作成してデータを抽出してください。以下は、私が使用したコードスニペットです
SELECT P~MATNR P~ERDAT SUM( P~KWMENG )
       FROM VBAK AS K INNER JOIN VBAP AS P ON K~VBELN = P~VBELN INTO TABLE IT_SALES_DATA WHERE P~MATNR IN ('MZ-FG-C950','MZ-FG-C900') GROUP BY P~MATNR P~ERDAT.

LOOP AT IT_SALES_DATA.
  DATA S_DEMAND TYPE STRING.
  S_DEMAND = IT_SALES_DATA-KWMENG.
  DATA S_DATE TYPE STRING.
  CONCATENATE IT_SALES_DATA-ERDAT+0(4) IT_SALES_DATA-ERDAT+4(2) IT_SALES_DATA-ERDAT+6(2) INTO S_DATE SEPARATED BY '-'.
  CONCATENATE IT_SALES_DATA-MATNR ',' S_DEMAND ',' S_DATE  INTO LS_FILE_TABLE.
  APPEND LS_FILE_TABLE TO LT_FILE_TABLE.
ENDLOOP.
    • Amazon Forecastを使用するリージョンのS3バケットに、抽出したデータをアップロードしてください。このプロセスを自動化するための選択肢がいくつかあります。例えば、S3 REST APIを使用するか、データ抽出のABAPレポートの中でSAPファンクションモジュールSXPG_COMMAND_EXECUTEを介してSM49で構成した外部OSコマンド (aws s3 cp)を呼び出します。
  1. Amazon Forecastを構成: データがS3バケットにあれば、Amazon Forecastコンソールから数クリックするだけで予測を生成できます。データサイエンティストや機械学習の専門家である必要はありません。Amazon Forecastのブログ記事の詳細な設定手順に従ってください。
  2. レポート用にSAP S/4HANA、またはSAP Fioriで予測データを利用: 予測の準備ができたら、SAPシステムからアクセスできるようにする必要があります。次の追加のAWSサービスを使用します。

まずは流れを理解しましょう。

図1. データフロー

図1. データフロー

Amazon Forecastは、指定した基準でフィルターされた単一品目の予測を取得するQueryForecast APIを提供しています。AWS Lambda関数を介してAmazon Forecastで生成された予測をこのAPIで呼び出し、その結果をAmazon API Gatewayを介してSAPアプリケーションに渡します。API Gatewayは、QueryForecastへの呼び出しを許可する前に、AWS Secrets Managerに格納された認証情報と照らし合わせて検証も行います。簡単に思いますか?設定手順を詳しく見ていきましょう。

API Gatewayの構成

Lambdaプロキシ統合を使用したREST APIの作成で詳しく説明されている手順に従ってください。以下は、API GatewayのGETオペレーションのスナップショットです。

図2. API Gateway GETメソッドの構成

図2. API Gateway GETメソッドの構成

特定の期間の特定の品目の予測を取得するために、いつからいつまでの日付、品目IDをパラメーターとして、SAPシステムがAPI GatewayのGETオペレーションを呼び出します。API Gatewayは、プロキシ統合によりLambda関数をそのまま呼び出すように構成しているため、これらのパラメーターはそのままLambda関数に渡されます。

次のPythonのコードスニペットは、API GatewayにアタッチされたLambda関数の中でリクエストとレスポンスが処理される方法の詳細を提供しています。まず、SAPシステムから渡された認証情報をAWS Secrets Managerに格納されている認証情報と照合します。

if ('Authorization' not in event['headers']):
        # User ID Password not provided
        return { "statusCode": 401,"isBase64Encoded" : False }
else:
    secret_name = "<secret name>"
    region_name = "<region id>"
    # Create a Secrets Manager client
    session = boto3.session.Session()
    client=session.client(service_name='secretsmanager',region_name=region_name)

    # Get secret from secrets manager
    get_secret_value_response = client.get_secret_value(SecretId=secret_name)  
    secret = json.loads(get_secret_value_response['SecretString'])

    # Get credentials from authorization header and decode to string
    userdet= event['headers']['Authorization'].split()[1]
    userdet = base64.b64decode(userdet).decode("utf-8")

    # Compare credentials
    if (secret ["<UserName>"] != userdet.split(":")[1]):
       # Password Not Matched
       return {"statusCode": 401", isBase64Encoded" : False}
    else:
	     # Password Matched, Authorization successful

認証情報の検証に成功したら、SAPシステムから渡された品目IDと日付のパラメーターを取り出し、これらのパラメーターでQueryForecast APIを呼び出します。

itemid = event['queryStringParameters']['item_id']
fromdate = event['queryStringParameters']['from_date']
todate = event['queryStringParameters']['to_date']

response = forecastclient.query_forecast(
                  ForecastArn='<Replace with your forcastARN>',
                  StartDate = from_date+'T00:00:00',
                  EndDate = to_date+'T00:00:00',
                  Filters = {
                      'item_id' : item_id
                  }
)

実行が成功すると、Amazon ForecastはデータをJSON形式で返します。SAPに戻す前に、Lambdaで結果を整形できます。例えば、SAPシステムのABAP内部テーブル形式への変換の複雑さを軽減するために、次のようなスキーマに変換してみました。必要に応じてカスタマイズしたり、SAPシステムで処理したりできます。

[
  {
    "I": "string",
    "D": "string",
    "ME": "string",
    "P10": "string",
    "P50": "string",
    "P90": "string"
  }
]

以下は、この変換のためのLambdaのコードスニペットです。レスポンス変数には、Amazon Forecastから返された生のJSONが含まれています。

result = [];
    for num, res in enumerate(response['Forecast']['Predictions']['mean']):
           data = {}
           data['I'] = itemid
           data['D'] = res['Timestamp'][0:10]
           data["ME"] =  str(round(response['Forecast']['Predictions']['mean'][num]['Value'],2))
           data["P10"] = str(round(response['Forecast']['Predictions']['p10'][num]['Value'],2))
           data["P50"] = str(round(response['Forecast']['Predictions']['p50'][num]['Value'],2))
           data["P90"] = str(round(response['Forecast']['Predictions']['p90'][num]['Value'],2))
           data["P99"] = str(round(response['Forecast']['Predictions']['p99'][num]['Value'],2))
           result.append(data)

最後に、Lambda関数から結果を返します。

return {
            "statusCode": 200,
            "isBase64Encoded" : False,
            "body": json.dumps(result),
            "headers" : { 'Content-Type' : 'application/json' },
            "multiValueHeaders" : {}
}

API Gatewayについては以上です。SAPシステムはどうでしょうか?SAPはAPI GatewayのGETメソッドをどのように呼び出すのでしょう?次は、それを見ていきましょう。

SAPシステムの構成

  • RFC宛先を作成: SAPシステムで、API GatewayのURLを呼び出すターゲットホストに、サービス番号を443 (HTTPSの標準ポート)に設定した、タイプ”G”のRFC宛先を用意します。
図3. REST APIを呼び出すためのRFC宛先

図3. REST APIを呼び出すためのRFC宛先

QueryForecast APIを保護するために、私は基本認証を使用し、RFC宛先でユーザーとパスワードを保持しています。このソリューションでは、Lambda関数により、SAPシステムによって渡された認証情報 (RFC宛先で保存されている内容)とAWS Secrets Managerに保存されている認証情報と照らし合わせて、QueryForecast APIへのアクセスを許可します。また、Amazon Cognitoや外部認証プロバイダーなどを使用したカスタム認証のために、API Gateway Lambda オーソライザーを使用することもできます。

図4. 基本認証を使用したSSL対応のRFC宛先

図4. 基本認証を使用したSSL対応のRFC宛先

API GatewayのエンドポイントはHTTPS対応しているので、トランザクションSTRUST、またはSTRUSTSSO2を使用して、Amazon API GatewayのSSLクライアント証明書をSAPシステムにインポートします。設定手順は、SAP KB 2521098を参照してください。また、SAPシステムがTLS 1.2用に構成されているか確認してください (SAPノート 510007を参照)。

  • GET APIを呼び出すためのABAPプログラム: SAPは、HTTP/Sのエンドポイントを呼び出すための複数のユーティリティクラスを提供しています。ここでは、外部RFC宛先用のユーティリティクラスCL_REST_HTTP_CLIENTを使用してREST APIを呼び出す一つの例を紹介します。以下は、ABAPプログラムからこの関数を呼び出すためのコードスニペットです。
DATA HTTP_CLIENT TYPE REF TO IF_HTTP_CLIENT.

* HTTP Client
CL_HTTP_CLIENT=>CREATE_BY_DESTINATION(
      EXPORTING
        DESTINATION              = '<RFC Destination Created>'
      IMPORTING
        CLIENT                   = HTTP_CLIENT
      EXCEPTIONS
        ARGUMENT_NOT_FOUND       = 1
        DESTINATION_NOT_FOUND    = 2
        DESTINATION_NO_AUTHORITY = 3
        PLUGIN_NOT_ACTIVE        = 4
        INTERNAL_ERROR           = 5
        OTHERS                   = 6
        ).

DATA URI TYPE STRING.

* FROMDATE and TODATE in YYYY-MM-DD format
CONCATENATE '/<API Gateway stage>?item_id=' SELECTEDITEM 'andfrom_date=' FROMDATE 'andto_date=' TODATE INTO URI.
CONDENSE URI.

CL_HTTP_UTILITY=>SET_REQUEST_URI( EXPORTING REQUEST = HTTP_CLIENT->REQUEST URI = URI ).

DATA: REST_CLIENT TYPE REF TO CL_REST_HTTP_CLIENT.

CREATE OBJECT REST_CLIENT EXPORTING IO_HTTP_CLIENT = HTTP_CLIENT.

* Make GET call
  TRY.
    REST_CLIENT->IF_REST_CLIENT~GET( ).
  CATCH CX_REST_CLIENT_EXCEPTION INTO DATA(LO_EXCEPTION).
      DATA(LV_MSG) = `HTTP GET failed: ` Andand LO_EXCEPTION->GET_TEXT( ).
  ENDTRY.

* Get Response
  DATA(LO_RESPONSE) = REST_CLIENT->IF_REST_CLIENT~GET_RESPONSE_ENTITY( ).
  DATA(LV_HTTP_STATUS) = LO_RESPONSE->GET_HEADER_FIELD( '~status_code' ).

* Check status Code
  IF LV_HTTP_STATUS NE 200.
    DATA(LV_REASON) = LO_RESPONSE->GET_HEADER_FIELD( '~status_reason' ).
    WRITE: LV_HTTP_STATUS.
    EXIT.
  ENDIF.

* Get JSON Data from response
  DATA(LV_JSON_DATA) = LO_RESPONSE->GET_STRING_DATA( ).

GETメソッドの呼び出しが成功したら、SAPはJSONレスポンスを受け取ります。これは、別のユーティリティクラス/UI2/CL_JSONによってABAP内部テーブルを変換することができるものです。SDNの記事 – One more ABAP to JSON Serializer and Deserializerを参照してください。

Lambda関数によって返される上述のJSON形式の場合、JSONからABAP内部テーブル形式に変換するためのABAPのコードスニペットは以下になります。

TYPES : BEGIN OF INNERMOST,
            I   TYPE STRING,
            D   TYPE STRING,
            ME  TYPE STRING,
            P10 TYPE STRING,
            P50 TYPE STRING,
            P90 TYPE STRING,
            P99 TYPE STRING,
          END OF INNERMOST.

  TYPES : PREDICTIONS TYPE STANDARD TABLE OF INNERMOST WITH DEFAULT KEY.
  DATA PREDICT TYPE PREDICTIONS.
  DATA WA TYPE INNERMOST.

  /UI2/CL_JSON=>DESERIALIZE( EXPORTING JSON = LV_JSON_DATA PRETTY_NAME = /UI2/CL_JSON=>PRETTY_MODE-CAMEL_CASE CHANGING DATA = PREDICT ).

  LOOP AT PREDICT INTO WA .
    WRITE: / WA-I UNDER 'Material' , WA-D UNDER 'Date', WA-ME UNDER 'Mean', WA-P50 UNDER 'P50', WA-P90 UNDER 'P90', WA-P99 UNDER 'P99'.
  ENDLOOP.

これだけです。とても簡単ですよね?以下が、SAPシステムで実行した結果です。

図5. SAPレポートの結果

図5. SAPレポートの結果

SAPアプリケーションからSAP販売データを抽出し、Amazon Forecastで販売予測を生成して、Amazon API GatewayとAWS Lambda関数によってREST API経由でSAPに予測を戻しました。

片付け

これで、SAPとAmazon Forecastを正常に統合できました。AWSアカウントを綺麗に掃除し、このブログで作成したリソースに関する継続的な請求を回避するためには、次の削除が必要です。

次のステップ

SAPを統合し、Amazon Forecastで販売予測を実行する方法の簡単な例を紹介しました。次のステップとしては、

  • このソリューションを構成して、前述のような手順で定期的に販売予測を更新します。
  • このデータを使用し、SAP Fioriでダッシュボードを構築して、簡単に可視化します。
  • Amazon Forecastは、あらゆる時系列データを使用できるため、経費予測、収益予測など、他のシナリオの使用法も検討できます。

今すぐ構築してみましょう。

翻訳はPartner SA 河原が担当しました。原文はこちらです。