JINSテックブログ
🥷

Snowflake Cortex AIで時系列予測モデルを作ってみた

2024/12/25に公開

この投稿は、2024年JINSのアドベントカレンダー25日目の記事です。
JINSで、アーキテクト/テックリードをしている佐藤(@Takuma3ato)です。
今回は、Snowflakeを使った時系列予測モデル構築の話をします。
先に結論から入りたいと思います。

3行まとめ

・必要な事業データを格納しているDWH上で、予測モデルを構築することは開発効率が良い
・Snowflake Cortex AIは、予測モデルを構築しやすいマネージドな機能があって使い易い
・予測モデルの構築に必要なローデータの準備は、(当たり前だが)頑張ってやるしかない

時系列予測モデルを作ってみた

1.この話の背景

JINSの店舗には、日々たくさんの方にご来店いただいています。一方で店舗スタッフは、接客や度数の測定、レンズ加工など様々な店舗業務をこなしています。もし、来店者数が予測できたらそれに合わせて店舗スタッフを配置することでこれらの業務がし易くなります。

この課題を来店者に実施いただいている「店舗受付のデータ」を用いて来店者数を予測することで解決できるのではないかと考えました。予測の方法としては、時系列予測モデルが良さそうと思いまして、まずは実験的に作ってみました。

2.モデルの構築

(1)構築に使う機能

JINSではSnowflakeをDWHとして利用しておりまして様々な事業データをここに集めています。
https://zenn.dev/jins/articles/eb3fa643dc4d43

Snowflakeのサービスの一つにSnowflake Cortex AIというものがあり、GAリリースしているものやプレビューなど様々な機能があり、Snowflakeとしても力を入れているようです。
https://docs.snowflake.com/ja/user-guide/snowflake-cortex/overview

Snowflake Cortex AIのサービスの中にML-駆動型関数というものがあり、時系列予測モデルを構築することができますので、今回はこれを利用します。このML-駆動型関数の時系列予測モデルのコアとなる機能は、SNOWFLAKE.ML.FORECASTというクラスです。

(2)構築手順

実験的な活動で簡易手順ではありますが、一応、MLOpsのフローに基づいて進めます。

①データ定義、評価定義

まずは、モデル構築に用いるローデータを準備します。やりたいことは、「店舗」に来店される方の「受付数」を「時間帯別」で予測する事としました。店舗については、店舗コードがあり、受付数や時間帯別は、受付時間受付理由のデータがありますので、これらを利用します。

その他、予測に相関しそうな変数としては、店舗売上店舗種別(インショップ・ロードサイド等)などの店舗属性に関するものや、天候平日・週末・祝祭日季節など外部環境に関するものなどありますが、今回の目的はSnowflakeのML-駆動型関数の実験的な利用なのでその辺りの変数は使わないこととしました。

②モデル構築に使うデータの準備

次にデータを準備します。
店舗毎の受付データを記録しているテーブル(以降、受付テーブル)があるので、これをSANDBOX環境にVIEWとして(以降、受付ビュー)コピーします。(JINSにおいては、Snowflake上の実験は該当テーブルをVIEWとしてコピーし、それを利用することが慣例になってます)

create or replace view <受付ビュー>(
	DATE_PART_V1,
	VISITED_AT,
	SHOP_CODE,
	VISITED_AT2
) as SELECT
    to_timestamp_ntz(DATE_PART) as DATE_PART_v1,  -- とりあえず、Data型からTimestamp型にしておく
    VISITED_AT,
    SHOP_CODE,
    CAST(EXTRACT(EPOCH_SECOND FROM VISITED_AT) AS NUMBER(38,0)) AS VISITED_AT2  -- エポック秒を持ちたかったので追加したが、結局使わなかった
FROM <受付テーブル>;

受付ビューテーブルには受付データがありますが、「時間帯別」の集計情報が無いのと、後で使うかもしれないので、一応「店舗売上」のデータも追加したビューを作ります。(以降、集計ビュー)

create or replace view <集計ビュー>(
	VISITED_DAY,
	DATE_PART_V1,
	SHOP_CODE,
	VISITED_COUNT,
	SALES_AMOUNT
) as
SELECT * FROM (
  SELECT
      DATE_TRUNC('day', v.VISITED_AT) AS visited_day, -- hourでは無い理由は後で話します 
      v.DATE_PART_V1,
      v.SHOP_CODE,
      COUNT(*) AS visited_count,
      s.SALES_AMOUNT -- 日別店舗売上 を追加
  FROM
      <受付ビュー> AS v -- エイリアス v を付与
  LEFT JOIN
      <日次店舗売上テーブル> AS s -- エイリアス s を付与
      ON v.SHOP_CODE = s.SHOP_CODE AND v.DATE_PART_V1 = s.SALE_DATE
  WHERE
      v.VISITED_AT >= '2023-01-01' AND v.VISITED_AT <= '2024-09-30' -- 期間は適当
      AND v.SHOP_CODE = <店舗コード> -- Snowflakeのコストの関係で対象とする店舗を絞り込む
  GROUP BY
      visited_day, v.DATE_PART_V1, v.SHOP_CODE, s.SALES_AMOUNT
  ORDER BY visited_day ASC
);

Snowflakeは、DATE_TRUNC()が使えまして、サポートしている日付と時刻は、以下の通りです。
https://docs.snowflake.com/ja/sql-reference/functions-date-time#supported-date-and-time-parts

当初は、「時間帯別」の時系列予測モデルを作るつもりで、DATE_TRUNC()は、hourで処理していたのですが、構築したモデルが出力する時間帯別が不安定になってしまいました。

原因を調べたところ、Snowflake の時系列予測アルゴリズムは、勾配ブースティングマシンを使用しており、ローデータのタイムスタンプは、一定の時間間隔を表す必要があります。
Snowflakeの処理として、この辺りを自動で判定するような動きになっているようで、ローデータの時間間隔が不規則なままだと行けないようでした。

店舗の営業時間は24時間では無いので、ロストしている時間帯が存在しておりこれが原因でした。データ補正するのも良いのですがその作業がちょっと面倒だったので、今回は予測を「日別」に変更しました。
https://docs.snowflake.com/ja/user-guide/ml-powered-forecasting#about-the-forecasting-algorithm

ちなみに、Snowflakeは処理で使用するコンピューティング能力と時間に応じて課金が発生します。今回はなるべくコストを節約したいので、データは1店舗分のみにしています。
https://zenn.dev/jins/articles/c80d57f91f2858

③トレーニング

さて、データの準備はできました。いよいよ、時系列予測モデルの構築です!

設定0

左メニューのAIとMLのStudioから、「予測」を選択します

設定1

今回構築するモデル名と、構築に用いるコンピューティングリソースを選択します。ウェアハウスのサイズは、こちらの種類から適するものを選んでください。

設定2

予め準備したトレーニングデータを指定します。今回は、<集計ビュー>です。

設定3

ターゲット列(予測したい対象)を指定します。

設定4

予測する時間単位に関わる列を指定します。

設定5

今回は、スキップします。

設定6

今回は、スキップします。

設定7

予測する期間と範囲(全体の何%を含めるか)を指定します。

設定が完了すると、Snowflakeでそれを処理するSQLが生成されますので、その一部を記載します。

-- This is your Cortex Project.
-----------------------------------------------------------
-- SETUP
-----------------------------------------------------------
use role USER_DEVELOPER;
use warehouse SURVEY_WH;
use database <モデル構築作業に属するデータベース>;
use schema SANDBOX;

-- Inspect the first 10 rows of your training data. This is the data we'll use to create your model.
select * from <集計ビュー> limit 10;

-- Prepare your training data. Timestamp_ntz is a required format. Also, only include select columns.
CREATE OR REPLACE VIEW <予測モデル構築の為に作成するビュー> AS SELECT
    to_timestamp_ntz(VISITED_DAY) as VISITED_DAY_v1,
    VISITED_COUNT
FROM <集計ビュー>;

-----------------------------------------------------------
-- CREATE PREDICTIONS
-----------------------------------------------------------
-- Create your model.
CREATE OR REPLACE SNOWFLAKE.ML.FORECAST poc_reception_forecast(
    INPUT_DATA => SYSTEM$REFERENCE('VIEW', '<予測モデル構築の為に作成するビュー>'),
    TIMESTAMP_COLNAME => 'VISITED_DAY_v1',
    TARGET_COLNAME => 'VISITED_COUNT'
);

-- 以降、Snowflakeの予測モデル構築の処理が入ります。

Snowflakeの画面の「プロジェクト」でこのSQLを実行します。指定したコンピューティングリソースによりモデル構築の時間は変わりますので(数分以上)、その間に一服します。(私は、たばこは止めました)

処理開始
右上のボタンから「すべて実行」を押下します

完了
こんなメッセージが出ても残念がらないでください。きちんと所定の場所にテーブルが作成されています。

さて、予測モデルを構築し、そのモデルで予測した結果が格納されたテーブルが無事に出来上がりました。FORECASTが予測値ですが、予測範囲の下限値、上限値も出力されるようです。これは良いですね。

精度はいかほど?

1.実データとの比較

さて、予測テーブルが作成できましたので、実データテーブルの値と比較してみます(わくわく)。
二つのテーブルを結合したビューを作成してみます。

CREATE OR REPLACE VIEW combined_data AS
SELECT
    f.TS,
    f.FORECAST,
    a.VISITED_COUNT,
    f.LOWER_BOUND,
    f.UPPER_BOUND
FROM
    <予測テーブル> f
JOIN
    <実データテーブル> a ON f.TS = a.VISITED_DAY;

このビューに対して、予測値と実データ値の誤差を調べてみます。

SELECT
    AVG(ABS(FORECAST - VISITED_COUNT)) AS MAE, -- 平均絶対誤差 (MAE): 予測値と実績値の平均的な誤差の大きさを示す。
    AVG(POWER(FORECAST - VISITED_COUNT, 2)) AS MSE,  -- 平均二乗誤差 (MSE): 予測値と実測値の差の二乗の平均。外れ値の影響を受けやすいが、誤差を際立たせる。
FROM combined_data;

結果は、以下の通りとなりました。

2.気づき事項

扱う値(日次の「店舗」に来店する「受付数」)のスケールはあまり大きくは無いので、それを踏まえるとこのMAE、MSEの値はGoodとは言えなさそうです。

とは言え、私の感覚ではありますが、平日の「受付数」は精度が良かったです。一方で、週末の「受付数」に誤差が結構ありました。この事から、週末の受付数に影響を及ぼす変数があるはずなので、それをうまく捉えると精度が上がるかもしれません。

このままでは本番業務として利用できる精度ではありませんでしたが、今回の実験としては相関する変数をもっと活用する事で有用なレベルに達しそうな感触を持ちました。
また、あらかじめ準備されているML-駆動型関数を用いることで、このような時系列予測モデルの構築がやりやすかったこともポジティブな印象でした。

終わりに

Snowflakeで用意されているサービスを使うことで、予測モデルを簡単に構築することができました。普段のAIモデルの構築にはAmazon BedrockやAmazon SageMakerを使うのですが、Pythonなどのアプリケーション開発言語を使わず、SQLでAIモデルを作れるのは新鮮でした。
今後は、他の店舗属性情報(例:店舗売上や店舗種別)や、外部情報(例:天候、季節性)も組み合わせながらモデルの精度を上げてみたいと思います。また、Snowflake Cortex AIの他の良さそうな機能もあるので、こういうのも使ってみたいと思います。
https://docs.snowflake.com/ja/sql-reference/functions/top_insights

JINSテックブログ
JINSテックブログ

Discussion