見出し画像

Snowflakeで機械学習の実装してみた

分析屋の中田(ナカタ)です。
Snowflakeで機械学習を実装してみました。


今回やること

機械学習ライブラリScikit-learnを用いて、教師あり学習の「分類」を実装します。

サンプルデータとしてScikit-learnから「ワインデータセット」という公開データセットを取得します。
アルコールやリンゴ酸などの数値を説明変数として、ワインがどのクラスに分類されるかを予測します。

細かい作りこみまではせず、とりあえず動く土台を作っています。


環境

Snowflakeのエディション:エンタープライズ版
クラウド:AWS(東京リージョン)


前提

機械学習の基礎用語、Snowflakeの基本操作はある程度理解しているものとします。
細かい解説は書かないものの、Web検索で調べられる範囲の用語で簡潔にまとめます。
イメージがつかめるよう、スクリーンショットを中心にまとめます。


事前準備

Pythonワークシートを新規で作成します。


デフォルトのデータベースとスキーマを指定します。


パッケージでscikit-learnをインストール(バージョン1.3.0)します。


他の設定は以下の通り、デフォルトのままにしています。


コード全体

先に全体像を載せます。
空白行込みで72行、実行時間はウェアハウスXSサイズで1分程度です。
main関数1本に全工程を書き並べています。
探索的データ分析も前処理も無しです。

import snowflake.snowpark as snowpark
from snowflake.snowpark.session import Session
import pandas as pd

from sklearn.datasets import load_wine
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score 

def main(session: snowpark.Session): 
    # ワインデータセットの読み込み
    wine = load_wine()

    # データ加工
    df_base = pd.DataFrame(wine.data, columns=wine.feature_names) # 説明変数
    df_base['class'] = [wine.target_names[_] for _ in wine.target] # 目的変数の列を追加
    df_base.columns = [x.upper() for x in df_base.columns] # 列名を大文字変換
    df_snowpark = session.create_dataframe(df_base)
    
    # 学習、検証、テスト用に分割
    WEIGHTS = [0.7, 0.2, 0.1]
    SEED = 316
    df_train, df_valid, df_test = df_snowpark.random_split(WEIGHTS, seed=SEED)

    # テーブルに格納
    df_train.write.mode('overwrite').saveAsTable('WINE_TRAIN')
    df_valid.write.mode('overwrite').saveAsTable('WINE_VALIDATION')
    df_test.write.mode('overwrite').saveAsTable('WINE_TEST')

    # 学習、検証データ
    train_x = df_train.drop('class').to_pandas() 
    train_y = df_train.select('class').to_pandas()
    valid_x = df_valid.drop('class').to_pandas()
    valid_y = df_valid.select('class').to_pandas()
    
    # モデル構築
    model = RandomForestClassifier(n_estimators=100,
                                   criterion='gini',
                                   max_depth=10,
                                   random_state=SEED)
    # 学習
    model.fit(train_x, train_y)

    # 検証
    valid_pred = model.predict(valid_x)
    accuracy = accuracy_score(valid_y, valid_pred)

    # 検証結果をテーブル格納
    valid_result = session.create_dataframe([str(accuracy)], schema=['accuracy'])
    valid_result.write.mode('overwrite').saveAsTable('WINE_VALID_ACCURACY')
    
    # 使用した検証データと予測結果をテーブルに保存
    df_valid_pred_base = pd.concat([df_valid.to_pandas(),
                        pd.DataFrame(valid_pred , columns=['PREDICTION']) ],
                       axis=1)
    df_valid_pred = session.create_dataframe(df_valid_pred_base)
    df_valid_pred.write.mode('overwrite').saveAsTable('WINE_VALID_PRED')

    # テストデータ
    test_x = df_test.drop('class').to_pandas()
    
    # 予測
    pred = model.predict(test_x)

    # 予測結果
    df_pred_base = pd.concat([df_test.to_pandas(),
                        pd.DataFrame(pred , columns=['PREDICTION']) ],
                       axis=1)

    # 結果をテーブル出力
    df_pred = session.create_dataframe(df_pred_base)
    df_pred.write.mode('overwrite').saveAsTable('WINE_TEST_PRED')
    return df_pred

以降、各コードの解説です。

①ライブラリのインポート

import snowflake.snowpark as snowpark
from snowflake.snowpark.session import Session
import pandas as pd

from sklearn.datasets import load_wine
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score 

pandasは事前準備でインストールすることもできますが、実はしなくても動きます。
load_wineはワインデータセットを取得する関数です。
RandomForestClassifierはランダムフォレストで分類を行うクラスです。
accuracy_scoreは精度の指標を取得する関数です。

以降はmain関数内部です。

②サンプルデータの取得

    # ワインデータセットの読み込み
    wine = load_wine()

    # データ加工
    df_base = pd.DataFrame(wine.data, columns=wine.feature_names) # 説明変数
    df_base['class'] = [wine.target_names[_] for _ in wine.target] # 目的変数の列を追加
    df_base.columns = [x.upper() for x in df_base.columns] # 列名を大文字変換
    df_snowpark = session.create_dataframe(df_base)

df_baseに説明変数のデータフレームを格納しています。

その後、目的変数の列「class」を追加しています。
以下はリスト内包表記で、目的変数列のデータをリストとして取得しています。
[wine.target_names[_] for _ in wine.target]

この部分だけ取り出すと、以下のようになっています。
['class_0', 'class_0', … , 'class_1', … , 'class_2']
class_0、class_1、class_2の3種類の値を持っています。

さらに以下のコードで列名を大文字にしています。
これを忘れると、後続でエラーになります。

df_base.columns = [x.upper() for x in df_base.columns]

最後に以下のコードで、PandasのデータフレームからSnowparkのデータフレームに変換しています。

df_snowpark = session.create_dataframe(df_base)

Snowparkのデータフレームに変換することで、後続で使用するrandom_splitやwriteが使えるようになります。

③データの分割

    # 学習、検証、テスト用に分割
    WEIGHTS = [0.7, 0.2, 0.1]
    SEED = 316
    df_train, df_valid, df_test = df_snowpark.random_split(WEIGHTS, seed=SEED)

元のサンプルデータを、学習用・検証用・テスト用に分割しています。
学習用データでモデルを構築
検証用データで精度を確認
テスト用データの正解を予測します。

取得したサンプルデータにはすべて正解(目的変数のCLASS列)が入っていますが
テスト用データではその正解が分からないということにして予測を行います。

ここでは取得したサンプルデータ100%のうち
学習用70%、検証用20%、テスト用10%に振り分けています。
seedは乱数を生み出すシード値で、適当に私の誕生日3月16日から316を指定しました。

④元データをテーブルに格納

    # テーブルに格納
    df_train.write.mode('overwrite').saveAsTable('WINE_TRAIN')
    df_valid.write.mode('overwrite').saveAsTable('WINE_VALIDATION')
    df_test.write.mode('overwrite').saveAsTable('WINE_TEST')

Snowparkのデータフレームをテーブルに上書きする書き方です。
saveAsTableの中にテーブル名を指定します。
テーブルは事前に作成する必要はありません。便利ですね!

⑤目的変数の処理

    # 学習、検証データ
    train_x = df_train.drop('class').to_pandas() 
    train_y = df_train.select('class').to_pandas()
    valid_x = df_valid.drop('class').to_pandas()
    valid_y = df_valid.select('class').to_pandas()

学習用データをさらに説明変数群と目的変数に分けます。
説明変数群はCLASS列を削除
目的変数はCLASS列のみを残す
という書き方です。
検証用データも同様です。

⑥モデル構築

    # モデル構築
    model = RandomForestClassifier(n_estimators=100,
                                   criterion='gini',
                                   max_depth=10,
                                   random_state=SEED)
    # 学習
    model.fit(train_x, train_y)

ハイパーパラメータの指定と学習をしています。
ここでは以下の4つだけ、パラメータを指定しています。

n_estimators:決定木の数
criterion:分割基準
max_depth:木の深さ
random_state:使用する乱数

⑦検証データで精度の確認

    # 検証
    valid_pred = model.predict(valid_x)
    accuracy = accuracy_score(valid_y, valid_pred)

    # 検証結果をテーブル格納
    valid_result = session.create_dataframe([str(accuracy)], schema=['accuracy'])
    valid_result.write.mode('overwrite').saveAsTable('WINE_VALID_ACCURACY')
    
    # 使用した検証データと予測結果をテーブルに保存
    df_valid_pred_base = pd.concat([df_valid.to_pandas(),
                        pd.DataFrame(valid_pred , columns=['PREDICTION']) ],
                       axis=1)
    df_valid_pred = session.create_dataframe(df_valid_pred_base)
    df_valid_pred.write.mode('overwrite').saveAsTable('WINE_VALID_PRED')

検証データに対して、構築したモデルで予測を行っています。
その結果を実際の正解と比較して、正解率を測定しています。

正解率と検証データをそれぞれテーブルに格納しており、
検証データについては予測結果も列として持たせています。
以下はプレビュー画面です。

ちなみに正解率100%なので、CLASS列(実際の正解)とPREDICTION(予測結果)は完全一致しています。

⑧テストデータの予測

    # テストデータ
    test_x = df_test.drop('class').to_pandas()
    
    # 予測
    pred = model.predict(test_x)

    # 予測結果
    df_pred_base = pd.concat([df_test.to_pandas(),
                        pd.DataFrame(pred , columns=['PREDICTION']) ],
                       axis=1)

    # 結果をテーブル出力
    df_pred = session.create_dataframe(df_pred_base)
    df_pred.write.mode('overwrite').saveAsTable('WINE_TEST_PRED')

最後にテスト用データの予測を行い、結果をテーブルに格納しています。
やっていることは検証用データの予測と同じです。

returnで戻り値に結果データを指定しています。
取得された結果は以下の通りです。
全19行の予測で、誤判定は1行のみでした。


最後に

今回は使用していませんが、SnowflakeのMLモデリングを使用するとシンプルに様々な機械学習モデルを実装できるようなので、今後試していきます。
Snowflake公式 | ML modeling



ここまでお読みいただき、ありがとうございました!
この記事が少しでも参考になりましたら「スキ」を押していただけると幸いです!

これまでの記事はこちら!


株式会社分析屋について

弊社が作成を行いました分析レポートを、鎌倉市観光協会様HPに掲載いただきました。

ホームページはこちら。

noteでの会社紹介記事はこちら。

【データ分析で日本を豊かに】
分析屋はシステム分野・ライフサイエンス分野・マーケティング分野の知見を生かし、多種多様な分野の企業様のデータ分析のご支援をさせていただいております。 「あなたの問題解決をする」をモットーに、お客様の抱える課題にあわせた解析・分析手法を用いて、問題解決へのお手伝いをいたします!
【マーケティング】
マーケティング戦略上の目的に向けて、各種のデータ統合及び加工ならびにPDCAサイクル運用全般を支援や高度なデータ分析技術により複雑な課題解決に向けての分析サービスを提供いたします。
【システム】
アプリケーション開発やデータベース構築、WEBサイト構築、運用保守業務などお客様の問題やご要望に沿ってご支援いたします。
【ライフサイエンス】
機械学習や各種アルゴリズムなどの解析アルゴリズム開発サービスを提供いたします。過去には医療系のバイタルデータを扱った解析が主でしたが、今後はそれらで培った経験・技術を工業など他の分野の企業様の問題解決にも役立てていく方針です。
【SES】
SESサービスも行っております。

この記事が参加している募集

#やってみた

36,832件