Snowflake Cortex ML-Based Functionsを使った複数系列の時系列予測を試してみる

複数時系列に対しても1つのモデルとして学習・推論を行うことができ、非常に扱いやすいことが分かります。
2023.12.16

データアナリティクス事業本部 機械学習チームの鈴木です。

この記事はSnowflake Advent Calendar 2023シリーズ2の15日目です。

先日、プレビュー提供中のSnowflake Cortex ML-Based Functionsを使った時系列予測機能について、『Snowflake Cortex ML-Based Functionsを使った時系列予測について確認した』にてご紹介しました。

この記事では単一系列の時系列データを対象にしましたが、複数系列の時系列データに対してもまとめてモデルの訓練と推定が行えるため、この記事では簡単にですが試してみたいと思います。

はじめに

Snowflake Cortex ML-Based Functionsの時系列予測について

Snowflake組み込みクラスであるFORECASTを使用し、勾配ブースティングマシンを使用した予測アルゴリズムを一つのSQLだけで学習可能で、推論も同じく一つのSQLだけで実行できます。

詳しくは冒頭でも言及した以下の記事にてご紹介しましたので、ご確認ください。

この機能は記事執筆時点ではプレビュー提供の機能になります。

複数時系列への適用について

複数時系列を入出力として、まとめて時系列予測モデルを学習・推論実行することが出来ます。この場合は、『Forecast on Multiple Series』ガイドに記載のように、系列の列として数値とテキストを含むVARIANTを指定する必要があります(FORECASTクラスのコンストラクター引数の項目を参照)。

ガイドの例では[ 1, "jacket" ]のような値を持つSTORE_ITEM列を作成し、CREATE SNOWFLAKE.ML.FORECASTのうちSERIES_COLNAMEで指定していました。

この記事では、実際にふたつのケースを例に、複数系列の時系列データに対してのまとめてのモデルの訓練と推定の方法について確認します。

前準備

ウェアハウス・データベースおよびロールの作成

クイックスタートにならってSnowflake Cortex ML-Based Functionsを実行する用のウェアハウスおよびロールを作成しました。

まず以下のようにウェアハウス・データベースを作成しました。

-- 2023/12/16にクイックスタートより引用した。
-- https://quickstarts.snowflake.com/guide/predict_ad_impressions_with_ml_powered_analysis/index.html

-- setup warehouse, database and schema
USE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE AD_FORECAST_DEMO_WH WITH WAREHOUSE_SIZE='XSmall'  STATEMENT_TIMEOUT_IN_SECONDS=600 STATEMENT_QUEUED_TIMEOUT_IN_SECONDS=15;
USE WAREHOUSE AD_FORECAST_DEMO_WH;
CREATE DATABASE AD_FORECAST_DEMO;
CREATE SCHEMA AD_FORECAST_DEMO.DEMO;

次に、以下のように実行用のロールを作成し、自分が利用するユーザーに対して使用する権限を付与しました。

-- 2023/12/16にクイックスタートより引用した。
-- https://quickstarts.snowflake.com/guide/predict_ad_impressions_with_ml_powered_analysis/index.html

-- set up analyst role
CREATE ROLE analyst;
GRANT USAGE ON DATABASE AD_FORECAST_DEMO TO ROLE analyst;
GRANT USAGE ON SCHEMA AD_FORECAST_DEMO.DEMO TO ROLE analyst;
GRANT USAGE ON WAREHOUSE AD_FORECAST_DEMO_WH TO ROLE analyst;
GRANT CREATE TABLE ON SCHEMA AD_FORECAST_DEMO.DEMO TO ROLE analyst;
GRANT CREATE VIEW ON SCHEMA AD_FORECAST_DEMO.DEMO TO ROLE analyst;
GRANT CREATE SNOWFLAKE.ML.FORECAST ON SCHEMA AD_FORECAST_DEMO.DEMO TO ROLE analyst;
GRANT CREATE SNOWFLAKE.ML.ANOMALY_DETECTION ON SCHEMA AD_FORECAST_DEMO.DEMO TO ROLE analyst;
GRANT ROLE analyst TO USER <your_user_here>;

今回はかなり小さいデータで検証をするため、ウェアハウスはXSmallのままとしています。ウェアハウスの選定については前回の記事で確認しました。

系列を表す値が一つのとき

まずはある店舗における複数の商品の売れ行きのような、複数時系列の場合でも一番簡単な例をみていきます。

データの作成

以下のようにデータを格納したテーブルと、FORECASTの仕様に合わせた読み出しができるビューを作成しました。

データとしては単純なもので、傾きの同じ単調増加の時系列データを2つ用意し、テーブルに格納しました。ある店舗におけるジャケットと傘の売れ行きになります。

-- 2023/12/16にガイドより引用、一部改変した。
-- https://docs.snowflake.com/ja/user-guide/ml-powered-forecasting

-- データを格納したテーブルの作成
CREATE OR REPLACE TABLE sales_data (item VARCHAR, date TIMESTAMP_NTZ,
  sales FLOAT, temperature NUMBER, humidity FLOAT, holiday VARCHAR);

INSERT INTO sales_data VALUES
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-01'), 2.0, 50, 0.3, 'new year'),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-02'), 3.0, 52, 0.3, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-03'), 4.0, 54, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-04'), 5.0, 54, 0.3, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-05'), 6.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-06'), 7.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-07'), 8.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-08'), 9.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-09'), 10.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-10'), 11.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-11'), 12.0, 55, 0.2, NULL),
  ('jacket', TO_TIMESTAMP_NTZ('2020-01-12'), 13.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-01'), 2.0, 50, 0.3, 'new year'),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-02'), 3.0, 52, 0.3, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-03'), 4.0, 54, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-04'), 5.0, 54, 0.3, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-05'), 6.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-06'), 7.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-07'), 8.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-08'), 9.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-09'), 10.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-10'), 11.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-11'), 12.0, 55, 0.2, NULL),
  ('umbrella', TO_TIMESTAMP_NTZ('2020-01-12'), 13.0, 55, 0.2, NULL);

CREATE OR REPLACE VIEW v_i AS SELECT [item] AS store_item, date, sales FROM sales_data;
SELECT * FROM v_i;

ビューからは以下のように値を読み取れるようにしています。

ビューのクエリ結果

チャートに描くと以下のようになりました。

訓練結果の傾向

モデルの訓練

以下のようにSERIES_COLNAMEで系列を表すカラムを指定して実行しました。

CREATE SNOWFLAKE.ML.FORECAST model_i(INPUT_DATA => SYSTEM$REFERENCE('VIEW', 'v_i'),
                                      SERIES_COLNAME => 'store_item',
                                      TIMESTAMP_COLNAME => 'date',
                                      TARGET_COLNAME => 'sales'
                                     );

推論の実行

まずそのまま推論を実行しました。

CALL model_i!FORECAST(FORECASTING_PERIODS => 5);

以下のように全系列について推論結果が出力されました。

推論結果

Snowsightでチャートが描きづらかったのもあり、一つの系列に絞って推論結果を出すのもやってみました。ついでに訓練データとの比較もしているので少しSQLが複雑になっています。

CALL model_i!FORECAST(SERIES_VALUE => ['jacket'], FORECASTING_PERIODS => 5);

-- select actual data and forecast
SELECT store_item, date AS ts, sales AS actual, NULL AS forecast, NULL AS lower_bound, NULL AS upper_bound
  FROM v_i
  WHERE store_item = ['jacket']
UNION ALL
SELECT series AS store_item, ts, NULL AS actual, forecast, lower_bound, upper_bound
  FROM TABLE(RESULT_SCAN(-1));

以下のように可視化できました。訓練データをもとに傾向が掴めていることが分かりました。

推論結果の可視化

系列を表す値が組み合わせた値のとき

次に、店舗ごとの複数商品の売れ行きのような、組み合わせで系列を表す例についても確認しました。

データの作成

以下のようにデータを格納したテーブルと、FORECASTの仕様に合わせた読み出しができるビューを作成しました。

-- 2023/12/16にガイドより引用、一部改変した。
-- https://docs.snowflake.com/ja/user-guide/ml-powered-forecasting

-- データを格納したテーブルの作成
CREATE OR REPLACE TABLE sales_data (store_id NUMBER, item VARCHAR, date TIMESTAMP_NTZ,
  sales FLOAT, temperature NUMBER, humidity FLOAT, holiday VARCHAR);

INSERT INTO sales_data VALUES
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-01'), 2.0, 50, 0.3, 'new year'),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-02'), 3.0, 52, 0.3, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-03'), 4.0, 54, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-04'), 5.0, 54, 0.3, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-05'), 6.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-06'), 7.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-07'), 8.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-08'), 9.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-09'), 10.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-10'), 11.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-11'), 12.0, 55, 0.2, NULL),
  (1, 'jacket', TO_TIMESTAMP_NTZ('2020-01-12'), 13.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-01'), 2.0, 50, 0.3, 'new year'),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-02'), 6.0, 52, 0.3, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-03'), 8.0, 54, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-04'), 10.0, 54, 0.3, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-05'), 12.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-06'), 14.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-07'), 16.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-08'), 18.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-09'), 20.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-10'), 22.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-11'), 24.0, 55, 0.2, NULL),
  (2, 'umbrella', TO_TIMESTAMP_NTZ('2020-01-12'), 26.0, 55, 0.2, NULL);

-- 学習データ読み取り用のビューの作成
CREATE OR REPLACE VIEW v_si AS SELECT [store_id, item] AS store_item, date, sales FROM sales_data;
SELECT * FROM v_si;

この場合は、傾きの異なる単調増加の系列としました。基本的に一つ目の例と同じです。

店舗1のジャケットの売れ行きと、店舗2の傘の売れ行きになります。

訓練結果の傾向

モデルの訓練

以下のようにSERIES_COLNAMEで系列を表すカラムを指定して実行しました。

CREATE SNOWFLAKE.ML.FORECAST model_si(INPUT_DATA => SYSTEM$REFERENCE('VIEW', 'v_si'),
                                      SERIES_COLNAME => 'store_item',
                                      TIMESTAMP_COLNAME => 'date',
                                      TARGET_COLNAME => 'sales'
                                     );

推論の実行

まずそのまま推論を実行しました。

CALL model_si!FORECAST(FORECASTING_PERIODS => 5);

以下のように全系列について推論結果が出力されました。

推論結果1

先と同じように、一つの系列に絞った推論結果の出力・可視化もやってみました。

CALL model_si!FORECAST(SERIES_VALUE => [2,'umbrella'], FORECASTING_PERIODS => 5);

SELECT store_item, date AS ts, sales AS actual, NULL AS forecast, NULL AS lower_bound, NULL AS upper_bound
  FROM v_si
  WHERE store_item = [2,'umbrella']
UNION ALL
SELECT series AS store_item, ts, NULL AS actual, forecast, lower_bound, upper_bound
  FROM TABLE(RESULT_SCAN(-1));

以下のように可視化できました。

推論結果2

最後に

プレビュー提供中のSnowflake Cortex ML-Based Functionsを使った時系列予測機能について、複数の時系列をまとめて学習・推論の対象とする場合の例を試してみました。

系列を表すカラムについては仕様を理解しておく必要がありますが、特に難しいことはなく非常に簡単に複数時系列に対しても学習・推論を行うことが出来ました。推論対象が複数系列であっても、Snowflakeからは単一のモデルとして扱うことができるのでとても便利ですね。

参考になりましたら幸いです。