ツリー モデルの 3 つのシリアル化方法 (モデルの記憶域サイズ、シリアル化に使用されるメモリ、シリアル化速度) の違いについて説明し、要約します。

I.はじめに

この記事では、一般的に使用されるツリー モデル、つまり rf、xgboost、catboost、lightgbm などのモデルの保存と読み込み (シリアル化と逆シリアル化) のさまざまな方法を要約し、実行中のメモリからさまざまな方法の使用量とストレージ サイズを比較します。

2.モデル

2.1 設置環境

pip install xgboost
pip install lightgbm
pip install catboost
pip install scikit-learn

バージョンを指定するかどうかを指定でき、最新の pkg を直接ダウンロードして入手できます

2.2 モデルの実行例

アヤメデータセットの複数分類タスク

import xgboost as xgb
from catboost import CatBoostClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

import lightgbm as lgb
from sklearn.ensemble import RandomForestClassifier


iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

# lgb
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
params = {
    'boosting_type': 'gbdt',
    'objective': 'multiclass',
    'num_class': 3,
    'metric': 'multi_logloss',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9
}
gbm = lgb.train(params, lgb_train, num_boost_round=100, valid_sets=[lgb_eval], early_stopping_rounds=5)
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred = [list(x).index(max(x)) for x in y_pred]
lgb_acc = accuracy_score(y_test, y_pred)

# rf
rf = RandomForestClassifier()
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
rf_acc = accuracy_score(y_test, y_pred)

# catboost
cat_boost_model = CatBoostClassifier(depth=9, learning_rate=0.01,
                                     loss_function='MultiClass', custom_metric=['AUC'],
                                     eval_metric='MultiClass', random_seed=1996)

cat_boost_model.fit(X_train, y_train, eval_set=(X_test, y_test), use_best_model=True, early_stopping_rounds=1000)
y_pred = cat_boost_model.predict(X_test)
cat_acc = accuracy_score(y_test, y_pred)

print(xgb_acc, lgb_acc, rf_acc, cat_acc)

2.3 インメモリコンピューティングの実行

def cal_current_memory():
    # 获取当前进程内存占用。
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory_used = info.uss / 1024. / 1024. / 1024.
    return {
        'memoryUsed': memory_used
    }

現在のプロセスの PID を取得し、その PID を使用してメモリの使用状況をクエリします。

3. 保存とロード

主な方法は次の 3 つです。

  1. jsonpickle

  2. ピクルス

  3. モデルAPI

3.1 jsonpickle

jsonpickle は、Python オブジェクトを JSON 形式の文字列に、または JSON 形式の文字列を Python オブジェクトに変換する Python シリアル化および逆シリアル化ライブラリです。

jsonpickle.encode を呼び出してシリアル化し、デコードして逆シリアル化します。

xgbを例に挙げます

保存:

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

xgb_str = jsonpickle.encode(xgb_model)
with open(f'{save_dir}/xgb_model_jsonpickle.json', 'w') as f:
    f.write(xgb_str)

ロード:

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

with open(f'{save_dir}/xgb_model_jsonpickle.json', 'r') as f:
    xgb_model_jsonpickle = f.read()
xgb_model_jsonpickle = jsonpickle.decode(xgb_model_jsonpickle)
y_pred = xgb_model_jsonpickle.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

これでモデルの保存と読み込みが完了しました

アドバンテージ

  1. モデルの読み込みプロセスを再インスタンス化する必要はなく、モデル ファイルを jsonpickle.decode することでモデルを直接取得できます。

  2. 取得したモデルファイルはjson形式であるため、さまざまなプログラミング言語やプラットフォーム間のデータ交換が容易になり、異なるシステム間でのデータ送信や共有が容易になります。

不利益

  1. 大規模または複雑なモデルを扱う場合、シリアル化プロセスでパフォーマンスの問題が発生する可能性があります (より多くのメモリを占有する)。

  2. モデルファイルの保存スペースが比較的大きい

3.2 ピクルス

Pickle は Python のシリアル化および逆シリアル化モジュールであり、Python オブジェクトをバイト ストリームに変換し、バイト ストリームを Python オブジェクトに変換して、Python オブジェクトの永続的なストレージと回復を実現します。(モデルもオブジェクトです)

pickle.dump/dumps を呼び出してシリアル化し、pickle.load/loads を呼び出してシリアル化解除します (dump はシリアル化されたファイルを直接保存し、dump はシリアル化されたバイト ファイルを返します。load とロードも同様です)。

ここで、他の Python メソッドとの比較を確認できます: https://docs.python.org/zh-cn/3/library/pickle.html

xgbを例に挙げます

保存:

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

with open(f'{save_dir}/xgb_model_pickle.pkl', 'wb') as f:
    pickle.dump(xgb_model, f)

ロード:

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

with open(f'{save_dir}/xgb_model_pickle.pkl', 'rb') as f:
    xgb_model_pickle = pickle.load(f)
y_pred = xgb_model_pickle.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

アドバンテージ

  1. モデルの読み込みプロセスも、jsonpickle と同様に再インスタンス化を必要としません。

  2. シリアル化されたファイルは jsonpickle よりもはるかに小さく、読み取りと保存が高速になります。

不利益

  1. 大きなオブジェクトや複雑なオブジェクトを扱う場合、パフォーマンスの問題が発生する可能性があります (より多くのメモリを消費する)

  2. json形式ではないため、プラットフォームや言語を超えて使用するのは困難です

3.3 モデルには以下が付属します

xgbを例に挙げます

保存

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)

model_path = f'{save_dir}/xgb_model_self.bin' #也可以是json格式,但最终文件大小有区别 
xgb_model.save_model(model_path)

ロード

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

xgb_model_self = xgb.Booster()
xgb_model_self.load_model(f'{save_dir}/xgb_model_self.bin')
y_pred = xgb_model_self.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

その他のドキュメント参照: https://xgboost.readthedocs.io/en/stable/tutorials/ Saving_model.html

アドバンテージ

  1. モデルのパラメータ ファイルのみが保存されます (ツリー構造と目的関数などの必要なモデル パラメータを含む)。モデル ファイルのサイズは小さいです

  2. シリアル化プロセス中の実行メモリはそれほど多くありません

  3. json形式で保存することもできます(XGBoost 1.0.0以降はjson形式で保存することを推奨します)

不利益

  1. モデルをロードする前に、モデルのインスタンスを作成する必要があります。

4. 実験

以下は主に小型モデルの実験です

4.1 モデルストレージサイズの比較実験

28dac749994c9c45614a4aeeaa59d6ec.png
  1. _jsonpickle は、jsonpickle メソッドでシリアル化されたモデル ファイルです。

  2. _pickle は、pickle メソッドでシリアル化されたモデル ファイルです。

  3. _self は、独自のモデル保存メソッドによって保存されたモデル ファイルです。

jsonpickle > pickle > self という関係があることがわかります。

4.2 メモリ比較実験の実行

xgb などのシリアル化の前後のメモリを監視することによって (シリアル化のみを考慮し、ファイルの書き込みに必要なメモリを削除します):

print("before:", cal_current_memory())
model_path = f'{save_dir}/xgb_model_self.bin'
xgb_model.save_model(model_path)
print("after:", cal_current_memory())

実行して次のものを取得します。

before: {'memoryUsed': 0.1490936279296875}
after: {'memoryUsed': 0.14911270141601562}
print("before:", cal_current_memory())
pickle.dumps(xgb_model)
print("after:", cal_current_memory())

実行して次のものを取得します。

before: {'memoryUsed': 0.1498260498046875}
after: {'memoryUsed': 0.14990234375}
print("before:", cal_current_memory())
xgb_str = jsonpickle.encode(xgb_model)
print("after:", cal_current_memory())

実行して次のものを取得します。

before: {'memoryUsed': 0.14917755126953125}
after: {'memoryUsed': 0.15140914916992188}

xgb モデルの場合、picklejson が必要とするメモリは他の 2 つのメソッドの数十倍であり、他の 2 つのメソッドは非常に似ていることがわかります。

lgbの結果:

上記のシーケンスに対応します。

self:
before: {'memoryUsed': 0.14953994750976562}
after {'memoryUsed': 0.14959716796875}
pickle:
before: {'memoryUsed': 0.14938735961914062}
after {'memoryUsed': 0.14946746826171875}
jsonpickle:
before: {'memoryUsed': 0.14945602416992188}
after {'memoryUsed': 0.14974594116210938}

ここではまだ jsonpickle が大きくなっていますが、倍数は小さくなっています

キャットブーストの結果:

self:
before: {'memoryUsed': 0.24615478515625}
after {'memoryUsed': 0.25492095947265625}
pickle:
before: {'memoryUsed': 0.2300567626953125}
after {'memoryUsed': 0.25820159912109375}
jsonpickle:
before: {'memoryUsed': 0.2452239990234375}
after {'memoryUsed': 0.272674560546875}

4.3 シリアル化時間の比較

catboost の全体的なモデル サイズが大きいため、catboost を通じてシリアル化の速度がよりよく反映されます。

self:
0.02413797378540039 s
pickle:
0.04681825637817383 s
jsonpickle:
0.3211638927459717  s

jsonpickleにはさらに時間がかかります

5、全体のコード

訓練:

import base64
import json
import os
import pickle
import time
import jsonpickle
import psutil
import xgboost as xgb
from catboost import CatBoostClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

import lightgbm as lgb
from sklearn.ensemble import RandomForestClassifier

save_dir = "./models"


def cal_current_memory():
    # 获取当前进程内存占用。
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory_used = info.uss / 1024. / 1024. / 1024.
    return {
        'memoryUsed': memory_used
    }


iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

# xgb
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
xgb_params = {'objective': 'multi:softmax', 'eval_metric': 'mlogloss', 'num_class': 3, 'verbosity': 0}
xgb_model = xgb.train(xgb_params, xgb_train)
y_pred = xgb_model.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
#
# print("before:", cal_current_memory())
# model_path = f'{save_dir}/xgb_model_self.bin'
# xgb_model.save_model(model_path)
# print("after", cal_current_memory())
with open(f'{save_dir}/xgb_model_pickle.pkl', 'wb') as f:
    pickle.dump(xgb_model, f)
print(cal_current_memory())
xgb_str = jsonpickle.encode(xgb_model)
with open(f'{save_dir}/xgb_model_jsonpickle.json', 'w') as f:
    f.write(xgb_str)
print(cal_current_memory())


# lgb
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
params = {
    'boosting_type': 'gbdt',
    'objective': 'multiclass',
    'num_class': 3,
    'metric': 'multi_logloss',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9
}
gbm = lgb.train(params, lgb_train, num_boost_round=100, valid_sets=[lgb_eval], early_stopping_rounds=5)
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred = [list(x).index(max(x)) for x in y_pred]
lgb_acc = accuracy_score(y_test, y_pred)
#
# print("before:", cal_current_memory())
# model_path = f'{save_dir}/lgb_model_self.bin'
# gbm.save_model(model_path)
# print("after", cal_current_memory())

with open(f'{save_dir}/lgb_model_pickle.pkl', 'wb') as f:
    pickle.dump(gbm, f)

lgb_str = jsonpickle.encode(gbm)
with open(f'{save_dir}/lgb_model_jsonpickle.json', 'w') as f:
    f.write(lgb_str)


# rf
rf = RandomForestClassifier()
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
rf_acc = accuracy_score(y_test, y_pred)


with open(f'{save_dir}/rf_model_pickle.pkl', 'wb') as f:
    pickle.dump(rf, f)

rf_str = jsonpickle.encode(rf)
with open(f'{save_dir}/rf_model_jsonpickle.json', 'w') as f:
    f.write(rf_str)



# catboost
cat_boost_model = CatBoostClassifier(depth=9, learning_rate=0.01,
                                     loss_function='MultiClass', custom_metric=['AUC'],
                                     eval_metric='MultiClass', random_seed=1996)

cat_boost_model.fit(X_train, y_train, eval_set=(X_test, y_test), use_best_model=True, early_stopping_rounds=1000)
y_pred = cat_boost_model.predict(X_test)
cat_acc = accuracy_score(y_test, y_pred)

# t = time.time()
# model_path = f'{save_dir}/cat_boost_model_self.bin'
# cat_boost_model.save_model(model_path)
# print("after", time.time() - t)

# print("before:", cal_current_memory())
# model_path = f'{save_dir}/cat_boost_model_self.bin'
# cat_boost_model.save_model(model_path)
# print("after", cal_current_memory())
with open(f'{save_dir}/cat_boost_model_pickle.pkl', 'wb') as f:
    pickle.dump(cat_boost_model, f)

cat_boost_model_str = jsonpickle.encode(cat_boost_model)
with open(f'{save_dir}/cat_boost_model_jsonpickle.json', 'w') as f:
    f.write(cat_boost_model_str)

print(xgb_acc, lgb_acc, rf_acc, cat_acc)

テスト

import pickle

import jsonpickle
import psutil
import xgboost as xgb
from catboost import CatBoostClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
import lightgbm as lgb
from sklearn.ensemble import RandomForestClassifier

save_dir = './models'

iris = load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1996)

xgb_test = xgb.DMatrix(X_test, y_test)

xgb_model_self = xgb.Booster()
xgb_model_self.load_model(f'{save_dir}/xgb_model_self.bin')
y_pred = xgb_model_self.predict(xgb_test)
xgb_acc = accuracy_score(y_test, y_pred)
print(xgb_acc)

# with open(f'{save_dir}/xgb_model_pickle.pkl', 'rb') as f:
#     xgb_model_pickle = pickle.load(f)
# y_pred = xgb_model_pickle.predict(xgb_test)
# xgb_acc = accuracy_score(y_test, y_pred)
# print(xgb_acc)

#
# with open(f'{save_dir}/xgb_model_jsonpickle.json', 'r') as f:
#     xgb_model_jsonpickle = f.read()
# xgb_model_jsonpickle = jsonpickle.decode(xgb_model_jsonpickle)
# y_pred = xgb_model_jsonpickle.predict(xgb_test)
# xgb_acc = accuracy_score(y_test, y_pred)
# print(xgb_acc)

ps: すべてのコードはここに記載されています。コードの数はそれほど多くありませんが、比較的大まかにまとめて書かれています。実験ごとに他の対応するコードをコメントアウトすることを忘れないでください。

6. まとめ

上記の実験は複数の実験結果の平均です。より説得力を持たせたい場合は、さらに多くの実験の平均を参考にしてください。全体的な結果は基本的に同じです。(より大きなモデルから始めて議論することもできます)

1. 手間を省き、クロスプラットフォーム言語を使用したい場合は、picklejson を選択できますが、モデルが複雑で大きい場合 (モデル クラスに他のモデルの複数のオブジェクトが含まれている可能性があります)、特定のメモリ見積もりが必要です。大量のメモリを消費し、モデル ファイルは非常に大きくなりますが、個々のサブモデルをシリアル化する必要はなく、直接デコードするだけです。

2. スペースと実行メモリを節約するために、モデル自体の保存方法 (主にモデル パラメーター ファイルの保存のみ) を選択できますが、この方法では、モデル (サブモデルを実装する必要があり、それぞれがモデルの savemodel メソッドと loadmodel メソッドを呼び出します)

3. Python でのクロスプラットフォーム言語のシリアル化と逆シリアル化を考慮しない場合は、pickle のシリアル化方法を直接検討できます。これも比較的簡単です。

推荐阅读:

我的2022届互联网校招分享

我的2021总结

浅谈算法岗和开发岗的区别

互联网校招研发薪资汇总
2022届互联网求职现状,金9银10快变成铜9铁10!!

公众号:AI蜗牛车

保持谦逊、保持自律、保持进步

发送【蜗牛】获取一份《手把手AI项目》(AI蜗牛车著)
发送【1222】获取一份不错的leetcode刷题笔记

发送【AI四大名著】获取四本经典AI电子书

おすすめ

転載: blog.csdn.net/qq_33431368/article/details/131039421