Skip to content

Commit 49834ac

Browse files
committed
Merge branch 'main' into model-parsing
2 parents a7266c9 + 75203a9 commit 49834ac

4 files changed

Lines changed: 108 additions & 41 deletions

File tree

.github/workflows/benchmark.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@ jobs:
2626
cache-name: benchmark-model-cache
2727
with:
2828
path: examples/benchmark_models
29-
key: models-${{ hashFiles('benchmark.py') }}
29+
key: models-${{ hashFiles('benchmark.py', '.github/workflows/benchmark.yml') }}
3030
- name: Run benchmark
3131
shell: bash -el {0}
3232
run: |
33-
echo "_(benchmark **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_" >> benchmark.md
34-
python benchmark.py >> benchmark.md
33+
python benchmark.py
34+
echo "_(benchmark **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_" >> result.md
35+
cat benchmark.md >> result.md
3536
- name: Comment on PR
3637
if: github.event_name == 'pull_request'
3738
uses: thollander/actions-comment-pull-request@v2
3839
with:
39-
filePath: benchmark.md
40+
filePath: result.md
4041
comment_tag: benchmark
4142
- name: Add benchmark to Job Summary
4243
run: cat benchmark.md >> "$GITHUB_STEP_SUMMARY"

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ jobs:
3131
name: artifact
3232
path: dist
3333
- name: Publish package on TestPyPi
34-
uses: pypa/gh-action-pypi-publish@a3a3bafbb3e5a75a854ae1bc53ae128cf22c4af4
34+
uses: pypa/gh-action-pypi-publish@48b317d84d5f59668bb13be49d1697e36b3ad009
3535
with:
3636
user: __token__
3737
password: ${{ secrets.TEST_PYPI_TOKEN }}
3838
repository_url: https://test.pypi.org/legacy/
3939
- name: Publish package on PyPi
40-
uses: pypa/gh-action-pypi-publish@a3a3bafbb3e5a75a854ae1bc53ae128cf22c4af4
40+
uses: pypa/gh-action-pypi-publish@48b317d84d5f59668bb13be49d1697e36b3ad009
4141
with:
4242
user: __token__
4343
password: ${{ secrets.PYPI_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
benchmark.md
2+
13
# Byte-compiled / optimized / DLL files
24
__pycache__/
35
*.py[cod]

benchmark.py

Lines changed: 99 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import io
2+
import itertools
23
import lzma
34
import pickle
45
import textwrap
@@ -11,6 +12,7 @@
1112

1213
from examples.utils import generate_dataset
1314
from slim_trees.lgbm_booster import dump_lgbm
15+
from slim_trees.pickling import get_pickled_size
1416
from slim_trees.sklearn_tree import dump_sklearn
1517

1618
MODELS_PATH = "examples/benchmark_models"
@@ -20,51 +22,93 @@ def load_model(model_name: str, generate: Callable) -> Any:
2022
model_path = Path(f"{MODELS_PATH}/{model_name}.pkl")
2123

2224
if model_path.exists():
25+
print(f"Loading model `{model_name}.pkl` from disk...")
2326
with open(model_path, "rb") as f:
2427
return pickle.load(f)
2528

29+
print(f"Training model `{model_name}`...")
2630
regressor = generate()
27-
regressor.fit(*generate_dataset(n_samples=10000))
31+
regressor.fit(
32+
*generate_dataset(n_samples=10000),
33+
)
34+
size = get_pickled_size(regressor, "no", pickle.dump)
35+
print(f"Trained model {model_name}. Size {size / 2**20:.2f} MB")
36+
2837
model_path.parent.mkdir(parents=True, exist_ok=True)
38+
2939
with open(model_path, "wb") as f:
3040
pickle.dump(regressor, f)
3141
return regressor
3242

3343

34-
def train_gb_sklearn() -> GradientBoostingRegressor:
44+
def train_sklearn_rf_20m() -> RandomForestRegressor:
45+
return load_model(
46+
"sklearn_rf_20m",
47+
lambda: RandomForestRegressor(
48+
n_estimators=100, max_leaf_nodes=1700, random_state=42, n_jobs=-1
49+
),
50+
)
51+
52+
53+
def train_sklearn_rf_200m() -> RandomForestRegressor:
54+
return load_model(
55+
"sklearn_rf_200m",
56+
lambda: RandomForestRegressor(n_estimators=275, random_state=42, n_jobs=-1),
57+
)
58+
59+
60+
def train_sklearn_rf_1g() -> RandomForestRegressor:
3561
return load_model(
36-
"gb_sklearn",
37-
lambda: GradientBoostingRegressor(n_estimators=2000, random_state=42),
62+
"sklearn_rf_1g",
63+
lambda: RandomForestRegressor(
64+
n_estimators=1500, max_leaf_nodes=10000, random_state=42, n_jobs=-1
65+
),
66+
)
67+
68+
69+
def train_sklearn_gb_2m() -> GradientBoostingRegressor:
70+
return load_model(
71+
"sklearn_gb_2m",
72+
lambda: GradientBoostingRegressor(
73+
n_estimators=2000, random_state=42, verbose=True
74+
),
3875
)
3976

4077

41-
def train_model_sklearn() -> RandomForestRegressor:
78+
def train_lgbm_gbdt_2m() -> lgb.LGBMRegressor:
4279
return load_model(
43-
"rf_sklearn",
44-
lambda: RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1),
80+
"lgbm_gbdt_2m", lambda: lgb.LGBMRegressor(n_estimators=1000, random_state=42)
4581
)
4682

4783

48-
def train_gbdt_lgbm() -> lgb.LGBMRegressor:
84+
def train_lgbm_gbdt_5m() -> lgb.LGBMRegressor:
4985
return load_model(
50-
"gbdt_lgbm", lambda: lgb.LGBMRegressor(n_estimators=2000, random_state=42)
86+
"lgbm_gbdt_5m",
87+
lambda: lgb.LGBMRegressor(n_estimators=2000, random_state=42),
5188
)
5289

5390

54-
def train_gbdt_large_lgbm() -> lgb.LGBMRegressor:
91+
def train_lgbm_gbdt_20m() -> lgb.LGBMRegressor:
5592
return load_model(
56-
"gbdt_large_lgbm",
57-
lambda: lgb.LGBMRegressor(n_estimators=20000, random_state=42),
93+
"lgbm_gbdt_20m",
94+
lambda: lgb.LGBMRegressor(n_estimators=8000, random_state=42),
5895
)
5996

6097

61-
def train_rf_lgbm() -> lgb.LGBMRegressor:
98+
def train_lgbm_gbdt_100m() -> lgb.LGBMRegressor:
6299
return load_model(
63-
"rg_lgbm",
100+
"lgbm_gbdt_100m",
101+
lambda: lgb.LGBMRegressor(n_estimators=35000, random_state=42),
102+
)
103+
104+
105+
def train_lgbm_rf_10m() -> lgb.LGBMRegressor:
106+
return load_model(
107+
"lgbm_rf_10m",
64108
lambda: lgb.LGBMRegressor(
65109
boosting_type="rf",
66-
n_estimators=100,
67-
num_leaves=1000,
110+
n_estimators=700,
111+
num_leaves=8000,
68112
random_state=42,
69113
bagging_freq=5,
70114
bagging_fraction=0.5,
@@ -74,12 +118,9 @@ def train_rf_lgbm() -> lgb.LGBMRegressor:
74118

75119

76120
def benchmark(func: Callable, *args, **kwargs) -> float:
77-
times = []
78-
for _ in range(5):
79-
start = time.perf_counter()
80-
func(*args, **kwargs)
81-
times.append(time.perf_counter() - start)
82-
return min(times)
121+
start = time.perf_counter()
122+
func(*args, **kwargs)
123+
return time.perf_counter() - start
83124

84125

85126
def benchmark_model( # noqa: PLR0913
@@ -99,11 +140,13 @@ def benchmark_model( # noqa: PLR0913
99140

100141
model = train_func()
101142

143+
print(f"Benchmarking naive implementation of `{name}`...")
102144
naive_dump_time = benchmark(base_dumps_func, model)
103145
naive_pickled = base_dumps_func(model)
104146
naive_pickled_size = len(naive_pickled)
105147
naive_load_time = benchmark(base_loads_func, naive_pickled)
106148

149+
print(f"Benchmarking our implementation of `{name}`...")
107150
our_dump_time = benchmark(dumps_func, model)
108151
our_pickled = dumps_func(model)
109152
our_pickled_size = len(our_pickled)
@@ -210,18 +253,39 @@ def loads_lzma(data):
210253
dumps_lzma,
211254
loads_lzma,
212255
)
213-
models_to_benchmark = [
214-
("sklearn rf", train_model_sklearn) + dumps_sklearn_args,
215-
("sklearn rf LZMA", train_model_sklearn) + dumps_sklearn_lzma_args,
216-
("sklearn gb", train_gb_sklearn) + dumps_sklearn_args,
217-
("sklearn gb LZMA", train_gb_sklearn) + dumps_sklearn_lzma_args,
218-
("LGBM gbdt", train_gbdt_lgbm) + dumps_lgbm_args,
219-
("LGBM gbdt LZMA", train_gbdt_lgbm) + dumps_lgbm_lzma_args,
220-
("LGBM gbdt large", train_gbdt_large_lgbm) + dumps_lgbm_args,
221-
("LGBM gbdt large LZMA", train_gbdt_large_lgbm) + dumps_lgbm_lzma_args,
222-
("LGBM rf", train_rf_lgbm) + dumps_lgbm_args,
223-
("LGBM rf LZMA", train_rf_lgbm) + dumps_lgbm_lzma_args,
256+
models = [
257+
("sklearn rf 20M", train_sklearn_rf_20m),
258+
("sklearn rf 200M", train_sklearn_rf_200m),
259+
("sklearn rf 1G", train_sklearn_rf_1g),
260+
("sklearn gb 2M", train_sklearn_gb_2m),
261+
("lgbm gbdt 2M", train_lgbm_gbdt_2m),
262+
("lgbm gbdt 5M", train_lgbm_gbdt_5m),
263+
("lgbm gbdt 20M", train_lgbm_gbdt_20m),
264+
("lgbm gbdt 100M", train_lgbm_gbdt_100m),
265+
("lgbm rf 10M", train_lgbm_rf_10m),
224266
]
267+
268+
def get_dumps_args(model_name, train_func):
269+
if "sklearn" in model_name:
270+
return (model_name, train_func) + dumps_sklearn_args
271+
elif "lgbm" in model_name:
272+
return (model_name, train_func) + dumps_lgbm_args
273+
else:
274+
raise ValueError(f"Unknown model name: {model_name}")
275+
276+
def get_dumps_args_lzma(model_name, train_func):
277+
if "sklearn" in model_name:
278+
return (model_name + " lzma", train_func) + dumps_sklearn_lzma_args
279+
elif "lgbm" in model_name:
280+
return (model_name + " lzma", train_func) + dumps_lgbm_lzma_args
281+
else:
282+
raise ValueError(f"Unknown model name: {model_name}")
283+
284+
models_to_benchmark = itertools.chain.from_iterable(
285+
[[get_dumps_args(*model), get_dumps_args_lzma(*model)] for model in models]
286+
)
225287
benchmark_results = [benchmark_model(*args) for args in models_to_benchmark]
226-
print("Base results / Our results / Change")
227-
print(format_benchmarks_results_table(benchmark_results))
288+
results_str = format_benchmarks_results_table(benchmark_results)
289+
with open("benchmark.md", "w") as f:
290+
f.write("Base results / Our results / Change\n")
291+
f.write(results_str)

0 commit comments

Comments
 (0)