[c++][python-package] add int8 input dtype for pre-discretized features#7246
[c++][python-package] add int8 input dtype for pre-discretized features#7246kyo219 wants to merge 5 commits into
Conversation
Allow passing int8 numpy arrays directly to LightGBM without float32 conversion, reducing Python-side memory by 4x for datasets with small discrete integer features.
jameslamb
left a comment
There was a problem hiding this comment.
Thanks for your interest in LightGBM.
Before we consider this, please add some unit tests covering this new code.
Cover the new int8 path added in 5984f8f: - extend test_no_copy_in_dataset_from_numpy_2d with int8 to assert _np2d_to_np1d forwards int8 arrays without copying - add test_c_float_array_accepts_int8 to verify _c_float_array returns _C_API_DTYPE_INT8 and reuses the underlying buffer - add test_c_float_array_rejects_unsupported_int_dtypes to confirm the updated TypeError message still rejects int16/int32/int64/uint8 - add test_dataset_from_int8_matches_float32 for end-to-end equivalence via byte-identical _dump_text output
|
Thanks for the review, @jameslamb! I've added unit tests in
All 119 tests in Let me know if you'd like additional coverage (e.g. CSR/CSC paths or prediction-time int8 inputs). |
|
Thanks, I've started a CI run. Let's see how that goes before we review further. |
This comment was marked as spam.
This comment was marked as spam.
|
@BelixRogner |
_c_float_array now returns a POINTER(c_int8) for int8 input, but the _ctypes_float_ptr type alias only listed c_float/c_double pointers, causing mypy to fail on the assignment at basic.py:767.
|
@jameslamb Thanks in advance for taking a look! I pushed a small fix here ( The two remaining red checks both look like unrelated infrastructure flakes, not something caused by my one-line type-annotation change:
Both of these jobs passed on the previous run of this PR, so they don't seem deterministic. Since I don't have permission to re-run jobs on the upstream repo, would you mind re-running them (or the whole workflow) when you get a chance? Happy to push an empty commit to re-trigger everything if you'd prefer. Thanks! |
Problem
When feature values are small discrete integers (e.g. 0–4), LightGBM's
Python wrapper forces a copy to float32 before passing them to the C++
layer. This quadruples memory usage for the feature matrix even though
LightGBM immediately bins the values into uint8/16/32 indices internally.
Solution
Add
C_API_DTYPE_INT8so thatnp.int8arrays flow through toRowFunctionFromDenseMatrixwithout any intermediate copy.Changes
include/LightGBM/c_api.h— newC_API_DTYPE_INT8 (4)constantsrc/c_api.cpp—int8_tcase inRowFunctionFromDenseMatrixpython-package/lightgbm/basic.py— acceptnp.int8in_np2d_to_np1d()and_c_float_array()Benchmark (500 K rows × 2 000 cols, int8 values 0–4)
Training / prediction speed is unchanged — the saving is in the
Python → C++ data hand-off, not in the tree-building itself.