以前このブログで LightGBM を使ってみる記事を書いた。 ただ、この記事で使っている Iris データセットにはカテゴリ変数が含まれていなかった。
そこで、今回はマッシュルームデータセットを使ってカテゴリ変数が含まれる場合を試してみる。
使った環境は次の通り。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.13.4 BuildVersion: 17E202 $ python -V Python 3.6.5
マッシュルームデータセットについて
マッシュルームデータセットはAgaricales Agaricaceae Lepiota という品種のキノコの外見的な特徴と毒の有無を記録したもの。 具体的には、次のような特徴が全てカテゴリ変数の形で入っている。 要するに、外見的な特徴から毒の有無を判定するモデルを作りたい、ということ。
- 毒の有無
- 傘の形
- 傘の表面
- 傘の色
- 部分的な変色の有無
- 匂い
- ひだの形状
- ひだの間隔
- ひだの大きさ
- ひだの色
- 柄の形
- 柄の根本
- 柄の表面 (つぼより上)
- 柄の表面 (つぼより下)
- 柄の色 (つぼより上)
- 柄の色 (つぼより下)
- 覆いの種類
- 覆いの色
- つぼの数
- つぼの種類
- 胞子の色
- 群生の仕方
- 生息地
データセットは次の Web サイトから得られる。
UCI Machine Learning Repository: Mushroom Data Set
まずは wget や curl を使ってデータセットをダウンロードしておこう。
$ wget https://archive.ics.uci.edu/ml/machine-learning-databases/mushroom/agaricus-lepiota.data
下準備
続いて、今回使う Python のパッケージをインストールしておく。
$ brew install cmake gcc@7 $ export CXX=g++-7 CC=gcc-7 $ pip install --no-binary lightgbm lightgbm pandas
インストールできたら Python のインタプリタを起動する。
$ python
データセットの CSV をパースする
続いてはダウンロードしてきた CSV を pandas の DataFrame
に変換する。
ここで注目すべきは dtype
として 'category'
を指定しているところ。
これによってパースされた各次元がカテゴリ型として扱われる。
>>> import pandas as pd >>> columns = [ ... 'poisonous', ... 'cap-shape', ... 'cap-surface', ... 'cap-color', ... 'bruises', ... 'odor', ... 'gill-attachment', ... 'gill-spacing', ... 'gill-size', ... 'gill-color', ... 'stalk-shape', ... 'stalk-root', ... 'stalk-surface-above-ring', ... 'stalk-surface-below-ring', ... 'stalk-color-above-ring', ... 'stalk-color-below-ring', ... 'veil-type', ... 'veil-color', ... 'ring-number', ... 'ring-type', ... 'spore-print-color', ... 'population', ... 'habitat', ... ] >>> df = pd.read_csv('agaricus-lepiota.data', header=None, names=columns, dtype='category')
上記でマッシュルームデータセットの 23 次元の特徴を持ったデータが読み込まれた。
>>> df.head() poisonous cap-shape cap-surface ... spore-print-color population habitat 0 p x s ... k s u 1 e x s ... n n g 2 e b s ... n n m 3 p x y ... k s u 4 e x s ... n a g [5 rows x 23 columns] >>> df.dtypes poisonous category cap-shape category cap-surface category cap-color category bruises category odor category gill-attachment category gill-spacing category gill-size category gill-color category stalk-shape category stalk-root category stalk-surface-above-ring category stalk-surface-below-ring category stalk-color-above-ring category stalk-color-below-ring category veil-type category veil-color category ring-number category ring-type category spore-print-color category population category habitat category dtype: object
pandas のカテゴリ型は Series#cat#categories
で水準を確認できたりする。
>>> df.poisonous.cat.categories Index(['e', 'p'], dtype='object')
整数値にラベルエンコードする
ただし、上記ではカテゴリ型の中身が 'object'
になっているため、そのままでは LightGBM に食べさせることができない。
LightGBM では int, float, boolean しか扱うことができないため。
そこで scikit-learn
の LabelEncoder
を使って対応する整数値に変換する。
>>> from sklearn import preprocessing >>> for column in df.columns: ... target_column = df[column] ... le = preprocessing.LabelEncoder() ... le.fit(target_column) ... label_encoded_column = le.transform(target_column) ... df[column] = pd.Series(label_encoded_column).astype('category') ...
これで DataFrame
がカテゴリ型のまま中身が整数になった。
>>> df.head() poisonous cap-shape ... population habitat 0 1 5 ... 3 5 1 0 5 ... 2 1 2 0 0 ... 2 3 3 1 5 ... 3 5 4 0 5 ... 0 1 [5 rows x 23 columns]
あとは普通に LightGBM で学習して汎化性能を検証するだけ。
まずはデータセットを説明変数と目的変数に分割する
>>> X, y = df.drop('poisonous', axis=1), df.poisonous
続いてデータセットを学習用、ハイパーパラメータ調整用、ホールドアウト検証用の 3 つに分割する。
>>> from sklearn.model_selection import train_test_split >>> # データセットを学習用と検証用に分割する ... X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.7, random_state=42) >>> # 検証用データセットをハイパーパラメータ調整用とバリデーション用に分割する ... X_eval, X_test, y_eval, y_test = train_test_split(X_val, y_val, test_size=0.5, random_state=42)
LightGBM を学習させる
続いて、分割したデータを元に LightGBM を学習させる。
学習に使う元ネタが pandas の DataFrame
で、適切にカテゴリ型になっていれば LightGBM はそれを識別してくれる。
>>> import lightgbm as lgb >>> lgb_train = lgb.Dataset(X_train, y_train) >>> lgb_eval = lgb.Dataset(X_eval, y_eval, reference=lgb_train) >>> # 学習用パラメータ ... lgbm_params = { ... # 二値分類問題 ... 'objective': 'binary', ... # 評価方法 ... 'metric': 'binary_error', ... } >>> # 学習 ... model = lgb.train(lgbm_params, lgb_train, valid_sets=lgb_eval)
ホールドアウト検証を用いた性能評価
学習させたモデルをホールドアウト検証用のデータセットで評価する。
>>> # バリデーション用データセットで汎化性能を確認する ... y_pred_proba = model.predict(X_test, num_iteration=model.best_iteration) >>> import numpy as np >>> # しきい値 0.5 で最尤なクラスに分類する ... y_pred = np.where(y_pred_proba > 0.5, 1, 0)
今回、精度で評価したところ全てのデータを正しく判別できた。 分割方法などにも依存するものの、このデータセットとモデルであればほぼ 100% に近い精度が得られるようだった。
>>> # 精度を確認する ... from sklearn.metrics import accuracy_score >>> accuracy_score(y_test, y_pred) 1.0
また、カテゴリ型が指定されていない pandas の DataFrame
や、それ以外をデータセットの元ネタにするときは、次のように categorical_feature
を指定すると良い。
>>> lgb_train = lgb.Dataset(X_train, y_train, categorical_feature=list(X.columns))
いじょう。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログ (19件) を見る

スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログ (1件) を見る