0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

polarsでの機械学習モデル構築(LightGBM)

Posted at

はじめに

初投稿になります。最近使っているPolarsについての備忘録で、自己学習のアウトプットとしてまとめています。読みやすさ、内容については度外視しているので、ご了承ください。

やったこと

Pandasで行っていた機械学習モデル構築を、Polarsに切り替えて実装を行いました。
基本的な流れは以下の通りです。

  1. データ読み込み
  2. データ分布確認
  3. データ前処理(ワンホット化)
  4. モデル構築および予測

Polarsとは

Pandasとの比較

Pandasと同様、データ処理に特化したライブラリで、記述方法やAPIも似ているため、Pandasと同じような感覚で使うことができます。Pandasと比較して、ほぼすべての処理が、高速かつ省メモリで実行できるため、作業の効率化、マシンスペックを抑えた大規模データ処理が可能です。詳しい性能比較は、こちらを参照していただければと思いますが、Pandasとの性能差は圧倒的です。

私自身も使用していて、Pandasに比べて5倍~10倍ほどの処理速度であると感じます。特に、データ読み込みの速度は顕著に差が表れ、数ギガ単位のデータでも数秒程度で読み込めてしまいますし、メモリも食わないので、大規模データを扱う際も、快適に作業できます。

余談ですが、Google colabでは、デフォルトでPolarsがインストールされているので、天下のGoogleさんも認知していますね。

記述方法やAPIについて

記述については、前述したとおり、基本的にPandasと似ていますが、独特の記述もそこそこあるため、慣れるまでにやや時間を要するかと思います。自分の場合は、普段の業務でコードを書くことが多く、使い始めてから1~2週間ほどで、使用感に慣れました。自分が参考にした記事や公式ドキュメントで、すごく丁寧にまとめてくれているので、具体的な処理については、以下の記事を参考にしてください。

<参考記事>

自分も分からないときは、まとめ記事⇒公式ドキュメント⇒ChatGPTという順で調べて、今のところ100%解決してるので、適宜使い分けてもらえればと思います。

Polarsを用いたデータ処理及びモデル構築

前置きが長くなりましたが、本題になります。コード実行はJupyter notebook上で行いました。python, polarsのバージョンは以下の通りです。

  • python:3.10.14
  • polars:1.2.1

0. 事前準備、環境確認

Polarsを使用する際に注意したいのが、バージョンによって、引数の名称やAPI名が異なる場合がある点です。自分は業務で古いバージョンのPolars(0.18)を使用していたのですが、バージョン違いによる記述エラーに気づけず、混乱しました。。。

ネット記事を調べると、ほぼすべて1.0以上のバージョンで記述されており、コピペしてもエラーで動作しないことも幾度かありましたので、古いバージョンを利用する方は、公式ドキュメントを確認するのが一番正確です(公式ドキュメントもデフォルトで1.0のページが表示されるので、ページ内で古いドキュメントに切り替えてください)

polarsのバージョンは、pythonのバージョンに依存するっぽい(python 3.7.12で、polars 0.18.2でした)ので、polarsをインストールしたら、バージョン確認をおすすめします。

polarsインストール
!pip install polars
python, polarsのバージョン確認
import sys
import polars as pl

#polarsバージョン確認
print("polars", pl.__version__)

# Pythonバージョン確認 
sys.version

1. データ読み込み

Pandasと同様、csv,tsv等のデータを読み込めます。
今回使用するデータは以下になります。
https://www.kaggle.com/datasets/shubhambathwal/flight-price-prediction
3つのcsvファイルのうち、Clean_Dataset.csvを使用してます。

データ読み込み
df = pl.read_csv("Clean_Dataset.csv")

データフレームは以下のように表示されます。列の型も一緒に確認できるため、df.info()より見やすいですね。
image.png

上記の通り、polarsでは各カラムごとに、データ型が固定されます。そのため、1つのカラムに文字列と数値が混在するようなデータを読み込むとエラーが発生します。人によっては、面倒に思うかもしれませんが、事前に型混在に気付けるので、個人的にはありがたい仕様です。

読み込み時にエラーが出る際は、引数オプションで型指定をしたり、エラーが出る箇所を無視するなどの処理が必要です。自分がよく使用する引数は以下になります。

引数 説明
infer_schema_length int 読み込み時のデータ型推定に使用する行数。デフォルト値は100で、先頭から100行のデータを先に読み込んで型推定を行う。
schema_overrides(旧:dtypes) dict 列ごとに読み込むデータ型を指定可能。指定してない列に関しては、infer_schema_lengthをもとに型推定を行う。polars 0.18, 0.19では、引数名が"dtypes"なので注意。
columns list 使用する列を指定。リストはインデックスもしくは列名で指定
ignore_errors bool Trueにすると、エラーが出る箇所をnullに置き換える。例えば、intを指定して読み込んだ列に、文字列が混じっていた場合、文字列がnullに置換されます。置換前の値を知りたい場合は、schema_overridesにて、該当列をstrで読み込み、個別に処理する必要があります。

他にも便利な引数があるので、詳しくは公式ドキュメントや前述の参考記事を参照してください。

データ読み込み(引数例)
df = pl.read_csv("data.csv",
                 infer_schema_length = 100, #カラムの型推定に使用する行数
                 schema_overrides = {"column_1": pl.Int64, #Int型で読み込み
                                     "column_2": pl.Float64, #Float型で読み込み
                                     "column_3": pl.String, #Str型で読み込み
                                    },
                 columns = ["column_1","column_2","column_3","column_4"],
                 # columns = [0,1,2,3], #インデックス指定も可能 
                 ignore_errors = True
                 )

2. データ分布確認

2.1 基本統計量の確認

基本統計量は、pandasと同様の記述で確認できます。欠損値(null_count)も確認できるので便利です。

基本統計量の確認
df.describe()

image.png

2.2 データの可視化

polarsでは、df.hist()のような可視化メソッドはないですが、以下のようにpandasに変換することでpandasの機能も利用できます。polarsの処理で困ったら、とりあえずpandasに変換すれば何とかなります。

ヒストグラム出力
df.to_pandas().hist()

一方、matplotlib, seabornでは、polarsのデータ型のままで動くので、pandasへの変換は不要で、pandasと同じコードで可視化できます。

matplotlibでの可視化
import matplotlib.pyplot as plt

# matplotlibでのでのヒストグラム出力
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(df["price"], bins=50)
plt.show()

image.png

seabornでの可視化
import seaborn as sns

# seabornでのヒストグラム出力
sns.histplot(df['price'], bins = 50)

image.png

2.3 半自動可視化ライブラリへの対応(sweetviz)

こちら余談ですが、自分の場合は、sweetvizのような半自動化ライブラリで可視化することが多いので、試してみました。結論、sweetvizではpolarsのデータ型のままだとエラーになるので、pandasに変換してから渡す必要があります。

sweetvizでの可視化
import sweetviz as sv

# Buisness,Economyのデータを比較
report = sv.compare([df.filter(pl.col("class") == "Business").to_pandas(),'Business'],
                    [df.filter(pl.col("class") == "Economy").to_pandas(),'Economy'],
                    target_feat='price') 
report.show_html("Business_Economy_compare.html")

ちなみに、sweetvizを使用すると以下のようなレポートをhtmlファイルで出力してくれます。
分析初期段階で網羅的に特徴を把握したいときに便利なので、気になる方はぜひ使用してみてください!
参考:個人的に最もおすすめなEDAツール Sweetvizの使い方

image.png

3. データ前処理

今回は、機械学習モデル構築の確認が目的ですので、最低限の処理のみ実施してます。
データ抽出、集計、文字列処理などの、データ加工に関する処理は、以下の記事ですごく丁寧にまとめられているので、こちらを確認してください。

今回使用するLightGBMは、欠損値があっても大丈夫な機械学習モデルなので、ワンホット化のみ実施します。

文字列のみワンホット化
import polars.selectors as cs # int,strなどのデータ型で、列選択が可能

# チケット情報を削除
df = df.drop("flight")

# ワンホット化
df_dummy = df.to_dummies(cs.string())
df_dummy

image.png

polarsのデータフレームでは、データ型の指定されているため、データ抽出や、列選択時に、データ型で指定できます。pandasで同じことをやろうとすると、少し手間ですし、データ型が混同しているとエラーを吐かれたりもするので、データ型の指定により、処理が安定するのは、個人的に気に入っている点です。

特徴量が増えてしまうので、flight(チケット情報)を削除してますが、番号部分は特徴量として有効に働き、精度が上がるので、興味ある方は試してみてください。

4. モデル構築および予測

4.1 データ分割

scikit-learnのtrain_test_splitは普通に使えます。

データ分割
import lightgbm as lgb
from sklearn.model_selection import train_test_split

# train, validに分割
train, valid = train_test_split(df_dummy, test_size=0.2, random_state=1234)
print("train_size:{}, valid_size:{},".format(len(train), len(valid)))
display(train,valid)

4.2 LightGBMでの学習モデル作成

LightGBMでの学習で、データを渡す際にエラーが出てしまうので、pandasに変換してからLightGBMに渡しています。pandasに変換してからは、今まで通りモデルを定義して学習するだけです。polarsからpandasへの変換も、ほとんど時間はかかりません。

モデル作成、学習
# polars -> pandasに変換
train_pd = train.to_pandas()
valid_pd = valid.to_pandas() 

#LightGBM用にデータセットを作成
lgb_train = lgb.Dataset(train_pd.drop("price",axis=1), train_pd["price"])
lgb_valid = lgb.Dataset(valid_pd.drop("price",axis=1), valid_pd["price"])

#ハイパーパラメタの設定
hyper_params = {     
    'boosting_type':'gbdt',#ブースティングの種類。ここでは勾配ブースティング
    'objective':'regression',#機械学習タスクの種類。ここでは回帰分類     
    'metric':'rmse',#モデルの評価指標 
    'num_leaves':100,#葉(ノード)の数。大きいほど複雑になる
    'max_depth':-1, #木の深さ、デフォルト-1で、負の数は制限なしを意味する
    'learning_rate':0.1,#学習率 
    'random_state':1234, #乱数のシード値
    }

# この数字を1にすると学習スコア推移が表示される、表示しない場合は0にする
verbose_eval = 1  

#モデル作成、学習回数、学習打ち切り回数の設定
model = lgb.train(params = hyper_params,
                train_set = lgb_train,
                valid_sets = [lgb_train, lgb_valid],
                valid_names=['train','val'], # 学習経過で表示する名称

                #学習数と学習打ち切り回数のみ自分で自由に決める。
                num_boost_round=200,  #学習数
                callbacks=[lgb.early_stopping(stopping_rounds=10, #学習打ち切り回数 
                                              verbose=True), 
                          lgb.log_evaluation(verbose_eval)]
                )

せっかくなので、Feature importanceも確認してみましたが、class_Businessが非常に高い値になってました。お試しでのモデル作成なので、特に深く言及しません。

Feature importanceの確認
# 特徴量の重要度を(Feature importance)を確認する
lgb.plot_importance(model, figsize=(8,4), max_num_features=10, importance_type='gain')

image.png

以上でpolarsを使用した機械学習モデル構築のパイプラインが組めたので、無事に目標達成できました!!

まとめ

Pandasからpolarsへの切り替えということで、可視化、データ処理、機械学習モデル作成までを実践してみました。とにかく処理が早くて快適です(特にデータ読み込み)。今回使用したデータ以外にも、5,000,000×17ほどのデータで、集計・加工を試していますが、pandasだと数十秒かかる処理も、「えっ、もう終わったの?」というくらい一瞬で終わるので、びっくりしてます。

はじめは、polarsの記述に戸惑う部分もありましたが、pandasと似ているので、Pandasを触ったことがあれば、すんなり移行できる印象です。
pandasにしか対応してないpythonライブラリもあるため、その場合は、to_pandas()でpandasに変換すれば問題ないかと思います。

個人的には、polarsでの記述の方がしっくりきますし、処理も早く快適なので、今後もデータ処理においては、polarsメインで使用していきたいと思います。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?