【Python】カテゴリカル変数のone-hot化(sparse)

処女作です。勉強したいので変なところがあればご指摘くださいmm

機械学習でよく使う前処理であるone-hot化をなるべく省メモリで実行する方法をメモしたいと思い書いてみました。

想定しているのは、1つのカラムに複数のラベルが立つトランザクション形式のデータをone-hot化することで、カテゴリ数は10000個あるとします。

このときぱっと思い浮かぶのはsklearnのMultiLabelBinalizerを使うことですが、sparse_output=Trueを指定することでメモリサイズを節約できます。

実際にどれくらい差が出るか試してみました。

まずは適当にデータを作成します。1つのデータにつき、10000種類のカテゴリから10個をランダムサンプリングします。これを10万データ作成します。

import random
import pandas as pd

n_categories = 10000 # カテゴリ数
n_data = 100000 # データ数

feature_list = ['特徴_{}'.format(i) for i in range(n_categories)]
data_list = [[random.sample(feature_list, 10)] for i in range(n_data)]

data = pd.DataFrame(data_list, columns=['特徴量'])

こんなかんじのデータができました。

無題

そのままMultiLabelBinarizerを使う場合とsparse_output=Trueを指定する場合とでメモリ使用量をプロファイルしてみました。Jupyter notebookを使用したので以下のようなコードを書きました。

from memory_profiler import profile
from sklearn.preprocessing import MultiLabelBinarizer
import sys

%load_ext memory_profiler

def MLB_sparse(data, col):
   mlb = MultiLabelBinarizer(sparse_output=True)
   mlb.fit(data[col].values)
   array = mlb.transform(data[col].values)
   
   return array, mlb.classes_

def MLB_normal(data, col):
   mlb = MultiLabelBinarizer()
   mlb.fit(data[col].values)
   array = mlb.transform(data[col].values)
   
   return array, mlb.classes_

print("sparse")
%memit array_sparse, columns = MLB_sparse(data, '特徴量')
print(sys.getsizeof(array_sparse), 'byte')

%memit array, columns = MLB_normal(data, '特徴量')
print(sys.getsizeof(array), 'byte')

出力結果は以下のようになりました。

無題

incrementで約1/330, 生成された配列のメモリサイズは約1/70,000,000になりました。

normalのほうはデータ型がnp.int32になっていたので、np.int8に直すことで約1/4にメモリサイズを抑えることができます。






この記事が気に入ったらサポートをしてみませんか?