트리 모델의 세 가지 직렬화 방법(모델 저장 크기, 직렬화에 사용되는 메모리, 직렬화 속도) 간의 차이점을 논의하고 요약합니다...

I. 소개

이 기사에서는 일반적으로 사용되는 트리 모델을 요약합니다. rf, xgboost, catboost 및 lightgbm과 같은 모델을 저장 및 로드(직렬화 및 역직렬화)하는 다양한 방법과 실행 중인 메모리에서 다양한 방법의 사용량 및 저장 크기를 비교합니다.

2. 모델

2.1 설치환경

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

버전을 지정할 수 있으며 직접 다운로드하여 최신 패키지를 얻을 수 있습니다.

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. 저장 및 불러오기

세 가지 주요 방법이 있습니다.

  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는 로드 및 로드와 마찬가지로 직렬화된 바이트 파일을 반환합니다).

여기에서 다른 파이썬 방법과의 비교를 볼 수 있습니다: 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 모델 스토리지 크기 비교 실험

28dac749994c9c45614a4aeaa59d6ec.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이 필요로 하는 메모리가 다른 두 방식의 수십 배임을 알 수 있으며, 나머지 두 방식은 매우 유사함

성소수자 결과:

위의 순서에 해당:

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이 더 크지만 배수는 더 작습니다.

catboost의 결과:

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은 시간이 더 걸립니다

다섯, 전체 코드

기차:

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. 공간 절약 및 메모리 실행을 위해 모델 자체의 저장 방법을 선택할 수 있지만(주로 모델 매개변수 파일만 저장) 이 방법의 경우 일반 클래스에서 직렬화 및 역직렬화 방법을 구현해야 할 수 있습니다. model (하위 모델은 구현되어야 하며 각각 모델의 savemodel 및 loadmodel 메서드를 호출합니다.)

3. 파이썬에서 크로스 플랫폼 언어 직렬화와 역직렬화를 고려하지 않는다면 역시 비교적 쉬운 피클의 직렬화 방법을 직접 고려할 수 있습니다.

推荐阅读:

我的2022届互联网校招分享

我的2021总结

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

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

公众号:AI蜗牛车

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

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

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

추천

출처blog.csdn.net/qq_33431368/article/details/131039421