ココンの情報をいつでも、どこでも。ココントコ。

エンジニア 2018.09.20

アンサンブル学習と粗行列とメモリ問題 -前編-

AI戦略室の坂本です。
これまでの記事では主に、ニューラルネットワークに対する機械学習、一般的にディープラーニングと呼ばれる技術について書いてきましたが、機械学習アルゴリズムは何も、ニューラルネットワークに対して適応されるものばかりではありません。
最近ではアンサンブル学習(http://scikit-learn.org/stable/modules/classes.html#module-sklearn.ensemble)と呼ばれる学習手法において新しいアルゴリズムが登場していて、単純な回帰やクラス分類ならばもうディープラーニング要らないよね、という雰囲気なのですが、今回はアンサンブル学習の手法と、その使用上の注意点などについて書いてゆこうと思います。

目次

アンサンブル学習とは

アンサンブル学習とは一口に言ってしまえば、異なる複数の学習モデルを使用して、それらの結果を組み合わせて最終的な結果とする機械学習の手法です。
昔ながらのものとしては、ランダムに作成された複数の決定木による多数決/平均を使用する、ランダムフォレストというアルゴリズムがあります。
ランダムフォレストは、内部的に複数の決定木を用意するのでアンサンブル学習の一つとして扱われますが、あくまでランダムフォレストというアルゴリズムに過ぎません。
しかし最近流行しているアンサンブル学習は、異なる木分割アルゴリズムを使用して複数のモデルを作成し、それらの結果を組み合わせようというものです。
最近流行しているアンサンブル学習用のアルゴリズムには、LightGBMやXGBoost、CatBoost等があり、全て決定木を作成するアルゴリズムであることが特徴です。
最近のトレンドとして、画像や複雑な処理に対してはニューラルネットワーク、回帰やクラス分類に関してはアンサンブル学習と、利用される機械学習の手法がシーンに応じて二分されるようになってきたように感じられますね。

アンサンブル学習のメリットとしては、主に学習速度と結果の精度が良いという点が挙げられますが、意外な利点としてメモリ効率を良くすることが出来るという側面もあります。
機械学習用による分析では、CPUやGPUの速度の他に物理メモリのサイズが問題になることがあります。
もちろんニューラルネットワークの学習にも大量のメモリが必要になりますが、ディープラーニングではデータを複数のミニバッチに分割して学習させるので、メモリサイズの問題よりもCPUやGPUの速度の方が問題になりがちです。
しかし、アルゴリズムの実装次第ではありますが決定木による分析では、分析対象となるデータを一旦全てメモリ上に展開する必要がある場合がほとんどなので、CPUの速度よりもメモリの量が問題になりがちです。
そこで、全てのデータを一度に学習させることが出来ない場合、データ内からある程度の大きさのサブサンプルを取り出し、それらに対して個別に学習を行うことになります。
そして、全ての学習モデルの結果から、多数決や平均でもって最終的な結果を作成します。

実はこのロジックこそ、ランダムフォレスト法の原理でもあって、アンサンブル学習はメモリ効率という点でも有効であることが解ります。

また、決定木を作成するアルゴリズムにおけるその他の利点として、決定木の分岐するノードを見れば、入力データのうち、どの要素が結果に対して重要なファクターであるかを知ることが出来る、という面もあります。
下のサンプルでは、ランダムフォレスト法で配列([[1,2,3],[1,4,5],[1,6,0],[1,1,9]])を学習し、その結果作成された決定木から、入力データの重要度を取得しています。
学習データの内最初の項目は全ての要素で値が1と共通なので、結果には全く影響しません。そのため、ランダムフォレスト法で作成した決定木においても、重要度=0とされていることが解ります。

>>> from sklearn.ensemble import RandomForestClassifier
>>> clf = RandomForestClassifier()
>>> clf.fit([[1,2,3],[1,4,5],[1,6,0],[1,1,9]],[0,1,1,0])
>>> clf.feature_importances_  # データ内の重要度を得る
array([0.        , 0.66666667, 0.33333333])
# 重要度のリスト - 1番目の要素は全データで同じなので重要度は0となる

この、決定木から入力データ内の重要度を取得できるという特徴は重要な役目があり、この後の解説で再び登場します。

粗行列とは

粗行列とは、大規模なデータを使用する際に利用される、プログラム上のテクニックで、ほとんどの項目が0となるベクトルや行列データに対して、値のある位置を保持することで効率よくメモリを使用するための方法です。
例えば、以下のリストがあったとします。

[0.0, 0.0, 2.5, 0.0, 0.0, 0.0, 5.7, 0.0, 0.0, 0.0]

このリストのサイズは10なので、普通の配列を使用する場合、このリストのデータを保存するには10個の数値をメモリ上に配置する必要があります。
しかし、このリストの中身を見てみると、10個中8個のデータが0.0であり、3番目と7番目の要素にのみ値が含まれていることが解ります。
そこで、全く同じデータに対して、「インデックス2の位置に2.5の値が、インデックス6の位置に5.7がある、10次元のベクトル」という表現でデータを保存することも出来ます。

[(2, 2.5), (6, 5.7), 10]

この表現にすることで、数値10個分必要だったメモリが、半分の5個で済むようになることが解ります。さらに言えば、位置を表現するインデックスについては実数値ではなく整数値なため、必要となるメモリサイズはさらに小さくなります。
Pythonでは粗行列は、scipyのライブラリを使用して扱う事が出来ます。

>>> from scipy.sparse import coo_matrix
>>> matrix = coo_matrix([0.0, 0.0, 2.5, 0.0, 0.0, 0.0, 5.7, 0.0, 0.0, 0.0])  # 粗行列を作成
>>> matrix
<1x10 sparse matrix of type’ with 2 stored elements in COOrdinate format>

粗行列を使用するデータ形式としては、文章解析で使用するTF-IDFベクトルやCountベクトルが代表例でしょう。

これらのベクトルでは、データ中の語彙数に応じた次元数のベクトルが作成されますが、実際のデータ一つには、全体の語彙数のうち僅かの単語しか含まれていないため、ベクトル中のほとんどの要素が0となってしまいます。
Countベクトルについては文章の他にもログデータの解析などで多用しますが、解析対象となるデータのサイズによっては、粗行列を使用することがほぼ必須になります。

CatBoostアルゴリズムのメモリ問題

以上の話をまとめると、
・アンサンブル学習では色々なアルゴリズムを組み合わせたい
・ビッグデータ解析ではメモリ効率から粗行列を使用したい
という事になるのですが、ここで一つ問題があります。
というのは、決定木分析の有力なアルゴリズムで、アンサンブル学習でもぜひ使いたいアルゴリズムであるCatBoostが、粗行列によるデータ入力に対応していないのです。
少し、その様子を見てみましょう。

from sklearn.ensemble import RandomForestClassifier
import lightgbm as lgb
import xgboost as xgb
import catboost as cb
from catboost import CatBoostClassifier
from scipy.sparse import csc_matrix

X = csc_matrix([[1,2,3],[1,4,5],[1,6,0],[1,1,9]], dtype=float)
Y = [0,1,1,0]

lgb_clf = lgb.train({'objective': 'multiclass','num_class': 3, 'min_data':1, 'min_data_in_bin':1}, lgb.Dataset(X, Y))

xgb_clf = xgb.train({'objective': 'multi:softmax','num_class': 3}, xgb.DMatrix(X, Y))

cb_clf = cb.train(cb.Pool(X, label=Y), params={'loss_function':'MultiClass', 'classes_count':3})
#_catboost.CatboostError: Invalid data type=: data must be list(), np.ndarray(), DataFrame(), Series() or filename str().

上のコードは、圧縮粗行列のクラスであるcsc_matrixのデータを、LightGBM、XGBoost、CatBoostのアルゴリズムに学習させてみる内容ですが、LightGBMとXGBoostは動作しますが、CatBoostではエラーが発生します。
PandasのSeriesが使えるので、SparseSeriesならばどうかと思って試してみても・・・

x = pd.SparseSeries.from_coo(X.tocoo(), dense_index=True)
cb_clf = cb.train(cb.Pool(x, label=Y), params={'loss_function':'MultiClass', 'classes_count':3})
#TypeError: object of type 'numpy.float64' has no len()

やはりエラーが出ます。
どうやらPythonのCatBoostでは、粗行列をそのまま扱う事は出来ないようです。

ではアンサンブル学習において、粗行列を使用しなければならないほどデータサイズが大きい場合、CatBoostアルゴリズムを諦めるのかというとそんなことはありません。
ちょっとしたテクニックを使用して、学習用データの次元数を削減してやれば、普通にCatBoostアルゴリズムを使用してアンサンブル学習を行うことが出来ます。

メモリ的に実現可能なアンサンブル学習

ここでは事例として、文章データを含むビッグデータから、TF-IDFベクトルを含めた粗行列のデータセットを作成し、それに対してアンサンブル学習を行う事を想定します。
さらに、TF-IDFベクトルの次元数とデータセットのサイズは非常に大きく、密な行列のまま学習アルゴリズムに投入するのは不可能で、次元削減を使用してCatBoostアルゴリズムを使用する、という状況を前提とします。
まずは、粗行列をそのまま扱う事が出来るアルゴリズムで学習を行い、入力データの重要度から重要性の高い要素のみを取り出したサブセットのデータを作成します。

文章データから作成したTF-IDFベクトルなどでは、語彙数のうちほとんどの単語が実際の分析には影響しない単語になるので、この方法を使用して入力データの次元数を大幅に削減することが出来ます。また、アンサンブル学習に使用する結果の一つが同時に取得できるので、一石二鳥でもあります。
さらに、次元削減といえばPCA(http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html#sklearn.decomposition.PCA)やGaussianProjection(http://scikit-learn.org/stable/modules/generated/sklearn.random_projection.GaussianRandomProjection.html#sklearn.random_projection.GaussianRandomProjection)といったアルゴリズムを使用して、入力データの次元数を削減したベクトルデータを作成し、それを学習に使用する事も出来ます。
アンサンブル学習では次元削減のアルゴリズムもアンサンブル的に、複数のアルゴリズムを組み合わせて使用したりします。

それらのテクニックを組み合わせるとどうなるかというと、まずは学習データを粗行列のまま扱えるアルゴリズム(LightGBMやXGBoostなど)を使用して機械学習を行い、結果1と結果に対する重要度の高いサブセットのデータを作成します。
次に、PCA等のアルゴリズムを使用して入力データの次元数を削減したベクトルデータを作成し、サブセットのデータと結合して、密な行列として学習するデータ(次元数は元の粗行列より大幅に小さい)を作成します。
そして、粗行列のまま学習できないアルゴリズムについて機械学習を行い、結果2を作成します。
最後に、結果1と結果2(それぞれアルゴリズムの数だけある)を組み合わせて(アンサンブル)、最終結果を作成します。
結果の組合せ方は、回帰分析であれば平均値であったり、重み付けを使用したり、線形カーネルによるSVMを使用したりします。

一見すると、このような複雑なことをやっても、よく調整された単一のモデルより精度が悪くなるだけでは無いかと感じてしまいますが、アンサンブル学習に限って言えば、結構良い結果に繋がっているようで面白いものです。

次回は、実際のプログラムコードを元にして、アンサンブル学習のサンプルを紹介します。

イベントインタビュー広報 2020.01.31

「OpenID Summit Tokyo 2020」参加レポート

広報の馬場です。4年に一度のOpenIDの祭典「OpenID Summit Tokyo 2020」が渋谷ストリーム・ホールにて1月2…

イベントインタビュー広報 2019.11.11

世界のハッカーが競う「DEF CON CTF Finals 2019」参加者インタビュー

広報の馬場です。 DEF CONの人気コンテンツの一つである「DEF CON CTF」は、熾烈な予選を勝ち抜いたチーム…

エンジニア 2019.08.23

危険すぎると話題の文章生成モデル「GPT-2」で会社紹介文を生成してみました

ココングループのAI TOKYO LABの細川です。AI、特にディープラーニング系のホットな技術を実際に動かすと…