binboost 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- binboost-0.1.0/LICENSE +21 -0
- binboost-0.1.0/PKG-INFO +104 -0
- binboost-0.1.0/README.md +80 -0
- binboost-0.1.0/binboost/__init__.py +5 -0
- binboost-0.1.0/binboost/binarizer.py +95 -0
- binboost-0.1.0/binboost/binboost.py +568 -0
- binboost-0.1.0/binboost/loss.py +99 -0
- binboost-0.1.0/binboost/rule.py +168 -0
- binboost-0.1.0/binboost.egg-info/PKG-INFO +104 -0
- binboost-0.1.0/binboost.egg-info/SOURCES.txt +14 -0
- binboost-0.1.0/binboost.egg-info/dependency_links.txt +1 -0
- binboost-0.1.0/binboost.egg-info/requires.txt +2 -0
- binboost-0.1.0/binboost.egg-info/top_level.txt +1 -0
- binboost-0.1.0/pyproject.toml +26 -0
- binboost-0.1.0/setup.cfg +4 -0
- binboost-0.1.0/setup.py +25 -0
binboost-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 BinBoost Authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
binboost-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: binboost
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner
|
|
5
|
+
Home-page: https://github.com/username/binboost
|
|
6
|
+
Author: BinBoost Authors
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/username/binboost
|
|
9
|
+
Keywords: gradient boosting,logical rules,binary features,interpretable machine learning
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=1.21.0
|
|
19
|
+
Requires-Dist: pandas>=1.3.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-python
|
|
24
|
+
|
|
25
|
+
# BinBoost
|
|
26
|
+
|
|
27
|
+
**Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner**
|
|
28
|
+
|
|
29
|
+
BinBoost adalah algoritma klasifikasi gradient boosting yang membangun ensemble aturan logika murni (AND, OR, XOR) dengan binarisasi fitur numerik adaptif berbasis gradien pada setiap iterasi boosting. Setiap weak learner berupa aturan yang dapat dibaca langsung oleh manusia tanpa memerlukan alat bantu penjelasan pasca-pelatihan.
|
|
30
|
+
|
|
31
|
+
## Kebaruan Utama
|
|
32
|
+
|
|
33
|
+
- **Binarisasi adaptif berbasis gradien**: nilai ambang batas fitur numerik dicari per iterasi untuk memaksimalkan korelasi dengan gradien saat ini
|
|
34
|
+
- **Ensemble aturan logika murni**: tidak ada pohon keputusan, setiap weak learner adalah aturan seperti `(A AND B)` atau `(C OR D)`
|
|
35
|
+
|
|
36
|
+
## Instalasi
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install binboost
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Penggunaan Dasar
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import numpy as np
|
|
46
|
+
from binboost import BinBoost
|
|
47
|
+
|
|
48
|
+
X = np.array([[1, 0, 1], [0, 1, 0], [1, 1, 0], [0, 0, 1]], dtype=float)
|
|
49
|
+
y = np.array([1, 0, 1, 0])
|
|
50
|
+
|
|
51
|
+
model = BinBoost(n_estimators=50, learning_rate=0.1, max_rule_length=2)
|
|
52
|
+
model.fit(X, y)
|
|
53
|
+
|
|
54
|
+
print(model.predict(X))
|
|
55
|
+
print(model.predict_proba(X))
|
|
56
|
+
print(model.rules_)
|
|
57
|
+
|
|
58
|
+
import pandas as pd
|
|
59
|
+
print(pd.DataFrame(model.rule_summary_))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Hyperparameter Utama
|
|
63
|
+
|
|
64
|
+
| Parameter | Bawaan | Keterangan |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `n_estimators` | 100 | Jumlah iterasi boosting |
|
|
67
|
+
| `learning_rate` | 0.1 | Faktor penyusutan tiap aturan |
|
|
68
|
+
| `loss` | `'logistic'` | Fungsi loss: `'logistic'`, `'focal'`, `'poly'` |
|
|
69
|
+
| `max_rule_length` | 2 | Jumlah maksimum fitur dalam satu aturan |
|
|
70
|
+
| `operators` | `['AND','OR']` | Operator logika yang digunakan |
|
|
71
|
+
| `beam_width` | 5 | Lebar beam search |
|
|
72
|
+
| `binarize_strategy` | `'gradient'` | Strategi binarisasi: `'gradient'`, `'quantile'`, `'uniform'`, `'kmeans'` |
|
|
73
|
+
| `subsample` | 0.8 | Fraksi data per iterasi |
|
|
74
|
+
| `feature_selection_threshold` | 0.01 | Ambang batas seleksi fitur berbasis gradien |
|
|
75
|
+
|
|
76
|
+
## Fitur yang Didukung
|
|
77
|
+
|
|
78
|
+
- Fitur biner (0/1): langsung diproses
|
|
79
|
+
- Fitur numerik (int/float): dibinarisasi otomatis
|
|
80
|
+
- Fitur kategorikal 3+ kelas: wajib OneHotEncode terlebih dahulu
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
84
|
+
enc = OneHotEncoder(sparse_output=False, drop='first')
|
|
85
|
+
X_encoded = enc.fit_transform(X[['kolom_kategorikal']])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
BinBoost secara otomatis mendeteksi kelompok fitur OneHotEncoding dan mencegah aturan yang tidak masuk akal seperti `(Warna_Merah AND Warna_Biru)`.
|
|
89
|
+
|
|
90
|
+
## Atribut Setelah Pelatihan
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
model.rules_ # daftar teks aturan
|
|
94
|
+
model.rule_weights_ # bobot setiap aturan
|
|
95
|
+
model.feature_importances_ # skor kepentingan fitur
|
|
96
|
+
model.train_score_ # loss per iterasi
|
|
97
|
+
model.rule_summary_ # ringkasan lengkap atau konversi ke DataFrame
|
|
98
|
+
model.n_rules_ # jumlah aturan aktif
|
|
99
|
+
model.feature_usage_ # frekuensi penggunaan tiap fitur
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Lisensi
|
|
103
|
+
|
|
104
|
+
MIT
|
binboost-0.1.0/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# BinBoost
|
|
2
|
+
|
|
3
|
+
**Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner**
|
|
4
|
+
|
|
5
|
+
BinBoost adalah algoritma klasifikasi gradient boosting yang membangun ensemble aturan logika murni (AND, OR, XOR) dengan binarisasi fitur numerik adaptif berbasis gradien pada setiap iterasi boosting. Setiap weak learner berupa aturan yang dapat dibaca langsung oleh manusia tanpa memerlukan alat bantu penjelasan pasca-pelatihan.
|
|
6
|
+
|
|
7
|
+
## Kebaruan Utama
|
|
8
|
+
|
|
9
|
+
- **Binarisasi adaptif berbasis gradien**: nilai ambang batas fitur numerik dicari per iterasi untuk memaksimalkan korelasi dengan gradien saat ini
|
|
10
|
+
- **Ensemble aturan logika murni**: tidak ada pohon keputusan, setiap weak learner adalah aturan seperti `(A AND B)` atau `(C OR D)`
|
|
11
|
+
|
|
12
|
+
## Instalasi
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install binboost
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Penggunaan Dasar
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import numpy as np
|
|
22
|
+
from binboost import BinBoost
|
|
23
|
+
|
|
24
|
+
X = np.array([[1, 0, 1], [0, 1, 0], [1, 1, 0], [0, 0, 1]], dtype=float)
|
|
25
|
+
y = np.array([1, 0, 1, 0])
|
|
26
|
+
|
|
27
|
+
model = BinBoost(n_estimators=50, learning_rate=0.1, max_rule_length=2)
|
|
28
|
+
model.fit(X, y)
|
|
29
|
+
|
|
30
|
+
print(model.predict(X))
|
|
31
|
+
print(model.predict_proba(X))
|
|
32
|
+
print(model.rules_)
|
|
33
|
+
|
|
34
|
+
import pandas as pd
|
|
35
|
+
print(pd.DataFrame(model.rule_summary_))
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Hyperparameter Utama
|
|
39
|
+
|
|
40
|
+
| Parameter | Bawaan | Keterangan |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `n_estimators` | 100 | Jumlah iterasi boosting |
|
|
43
|
+
| `learning_rate` | 0.1 | Faktor penyusutan tiap aturan |
|
|
44
|
+
| `loss` | `'logistic'` | Fungsi loss: `'logistic'`, `'focal'`, `'poly'` |
|
|
45
|
+
| `max_rule_length` | 2 | Jumlah maksimum fitur dalam satu aturan |
|
|
46
|
+
| `operators` | `['AND','OR']` | Operator logika yang digunakan |
|
|
47
|
+
| `beam_width` | 5 | Lebar beam search |
|
|
48
|
+
| `binarize_strategy` | `'gradient'` | Strategi binarisasi: `'gradient'`, `'quantile'`, `'uniform'`, `'kmeans'` |
|
|
49
|
+
| `subsample` | 0.8 | Fraksi data per iterasi |
|
|
50
|
+
| `feature_selection_threshold` | 0.01 | Ambang batas seleksi fitur berbasis gradien |
|
|
51
|
+
|
|
52
|
+
## Fitur yang Didukung
|
|
53
|
+
|
|
54
|
+
- Fitur biner (0/1): langsung diproses
|
|
55
|
+
- Fitur numerik (int/float): dibinarisasi otomatis
|
|
56
|
+
- Fitur kategorikal 3+ kelas: wajib OneHotEncode terlebih dahulu
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
60
|
+
enc = OneHotEncoder(sparse_output=False, drop='first')
|
|
61
|
+
X_encoded = enc.fit_transform(X[['kolom_kategorikal']])
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
BinBoost secara otomatis mendeteksi kelompok fitur OneHotEncoding dan mencegah aturan yang tidak masuk akal seperti `(Warna_Merah AND Warna_Biru)`.
|
|
65
|
+
|
|
66
|
+
## Atribut Setelah Pelatihan
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
model.rules_ # daftar teks aturan
|
|
70
|
+
model.rule_weights_ # bobot setiap aturan
|
|
71
|
+
model.feature_importances_ # skor kepentingan fitur
|
|
72
|
+
model.train_score_ # loss per iterasi
|
|
73
|
+
model.rule_summary_ # ringkasan lengkap atau konversi ke DataFrame
|
|
74
|
+
model.n_rules_ # jumlah aturan aktif
|
|
75
|
+
model.feature_usage_ # frekuensi penggunaan tiap fitur
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Lisensi
|
|
79
|
+
|
|
80
|
+
MIT
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GradientBinarizer:
|
|
5
|
+
# Binarisasi fitur numerik berbasis korelasi dengan gradien saat ini
|
|
6
|
+
|
|
7
|
+
def __init__(self, strategy='gradient', n_thresholds='auto'):
|
|
8
|
+
self.strategy = strategy
|
|
9
|
+
self.n_thresholds = n_thresholds
|
|
10
|
+
# Ambang batas tetap per fitur disimpan setelah fit pertama untuk strategi non-gradien
|
|
11
|
+
self._fixed_thresholds = {}
|
|
12
|
+
|
|
13
|
+
def _get_candidates(self, values):
|
|
14
|
+
# Kembalikan daftar kandidat ambang batas untuk satu fitur
|
|
15
|
+
unique = np.unique(values)
|
|
16
|
+
if len(unique) <= 1:
|
|
17
|
+
return np.array([])
|
|
18
|
+
if self.n_thresholds == 'auto':
|
|
19
|
+
max_t = 256
|
|
20
|
+
else:
|
|
21
|
+
max_t = int(self.n_thresholds)
|
|
22
|
+
if len(unique) - 1 <= max_t:
|
|
23
|
+
return (unique[:-1] + unique[1:]) / 2
|
|
24
|
+
idx = np.linspace(0, len(unique) - 2, max_t, dtype=int)
|
|
25
|
+
return (unique[idx] + unique[idx + 1]) / 2
|
|
26
|
+
|
|
27
|
+
def _best_threshold_gradient(self, values, gradients):
|
|
28
|
+
# 1. Cari ambang batas t* yang memaksimalkan korelasi dengan gradien
|
|
29
|
+
candidates = self._get_candidates(values)
|
|
30
|
+
if len(candidates) == 0:
|
|
31
|
+
return 0.0, np.zeros(len(values), dtype=np.float64)
|
|
32
|
+
best_t = candidates[0]
|
|
33
|
+
best_score = -np.inf
|
|
34
|
+
for t in candidates:
|
|
35
|
+
binary = (values > t).astype(np.float64)
|
|
36
|
+
score = np.abs(np.dot(gradients, binary))
|
|
37
|
+
if score > best_score:
|
|
38
|
+
best_score = score
|
|
39
|
+
best_t = t
|
|
40
|
+
return best_t, (values > best_t).astype(np.float64)
|
|
41
|
+
|
|
42
|
+
def _best_threshold_quantile(self, col_idx, values):
|
|
43
|
+
# 1. Gunakan ambang batas tetap dari kuantil distribusi data
|
|
44
|
+
if col_idx not in self._fixed_thresholds:
|
|
45
|
+
candidates = self._get_candidates(values)
|
|
46
|
+
if len(candidates) == 0:
|
|
47
|
+
self._fixed_thresholds[col_idx] = 0.0
|
|
48
|
+
else:
|
|
49
|
+
self._fixed_thresholds[col_idx] = np.median(candidates)
|
|
50
|
+
t = self._fixed_thresholds[col_idx]
|
|
51
|
+
return t, (values > t).astype(np.float64)
|
|
52
|
+
|
|
53
|
+
def _best_threshold_uniform(self, col_idx, values):
|
|
54
|
+
# 1. Gunakan ambang batas tengah dari rentang nilai fitur
|
|
55
|
+
if col_idx not in self._fixed_thresholds:
|
|
56
|
+
self._fixed_thresholds[col_idx] = (values.min() + values.max()) / 2
|
|
57
|
+
t = self._fixed_thresholds[col_idx]
|
|
58
|
+
return t, (values > t).astype(np.float64)
|
|
59
|
+
|
|
60
|
+
def _best_threshold_kmeans(self, col_idx, values):
|
|
61
|
+
# 1. Gunakan titik tengah dua centroid K-Means satu dimensi sebagai ambang batas
|
|
62
|
+
if col_idx not in self._fixed_thresholds:
|
|
63
|
+
v = values.reshape(-1, 1)
|
|
64
|
+
c1 = np.percentile(values, 25)
|
|
65
|
+
c2 = np.percentile(values, 75)
|
|
66
|
+
for _ in range(100):
|
|
67
|
+
labels = (np.abs(values - c1) > np.abs(values - c2)).astype(int)
|
|
68
|
+
new_c1 = values[labels == 0].mean() if (labels == 0).any() else c1
|
|
69
|
+
new_c2 = values[labels == 1].mean() if (labels == 1).any() else c2
|
|
70
|
+
if np.abs(new_c1 - c1) < 1e-8 and np.abs(new_c2 - c2) < 1e-8:
|
|
71
|
+
break
|
|
72
|
+
c1, c2 = new_c1, new_c2
|
|
73
|
+
self._fixed_thresholds[col_idx] = (c1 + c2) / 2
|
|
74
|
+
t = self._fixed_thresholds[col_idx]
|
|
75
|
+
return t, (values > t).astype(np.float64)
|
|
76
|
+
|
|
77
|
+
def transform(self, X, gradients, numeric_cols):
|
|
78
|
+
# Binarisasi semua fitur numerik dan kembalikan matriks biner serta ambang batas iterasi ini
|
|
79
|
+
X_bin = X.copy().astype(np.float64)
|
|
80
|
+
thresholds_this_iter = {}
|
|
81
|
+
for col_idx in numeric_cols:
|
|
82
|
+
values = X[:, col_idx].astype(np.float64)
|
|
83
|
+
if self.strategy == 'gradient':
|
|
84
|
+
t, binary = self._best_threshold_gradient(values, gradients)
|
|
85
|
+
elif self.strategy == 'quantile':
|
|
86
|
+
t, binary = self._best_threshold_quantile(col_idx, values)
|
|
87
|
+
elif self.strategy == 'uniform':
|
|
88
|
+
t, binary = self._best_threshold_uniform(col_idx, values)
|
|
89
|
+
elif self.strategy == 'kmeans':
|
|
90
|
+
t, binary = self._best_threshold_kmeans(col_idx, values)
|
|
91
|
+
else:
|
|
92
|
+
raise ValueError(f"Strategi '{self.strategy}' tidak dikenali.")
|
|
93
|
+
X_bin[:, col_idx] = binary
|
|
94
|
+
thresholds_this_iter[col_idx] = t
|
|
95
|
+
return X_bin, thresholds_this_iter
|
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .loss import get_loss, sigmoid
|
|
4
|
+
from .binarizer import GradientBinarizer
|
|
5
|
+
from .rule import Rule, BeamSearchRuleFinder
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BinBoost:
|
|
9
|
+
"""
|
|
10
|
+
BinBoost: Gradient Boosting Berbasis Aturan Logika Adaptif.
|
|
11
|
+
|
|
12
|
+
Algoritma klasifikasi biner yang membangun ensemble aturan logika
|
|
13
|
+
murni (AND, OR, XOR) dengan binarisasi fitur numerik adaptif
|
|
14
|
+
berbasis gradien pada setiap iterasi boosting.
|
|
15
|
+
|
|
16
|
+
Parameter
|
|
17
|
+
----------
|
|
18
|
+
n_estimators : int, default=100
|
|
19
|
+
Jumlah iterasi boosting.
|
|
20
|
+
|
|
21
|
+
learning_rate : float, default=0.1
|
|
22
|
+
Faktor penyusutan kontribusi setiap aturan.
|
|
23
|
+
|
|
24
|
+
loss : str, default='logistic'
|
|
25
|
+
Fungsi loss. Pilihan: 'logistic', 'focal', 'poly'.
|
|
26
|
+
|
|
27
|
+
poly_epsilon : float, default=1.0
|
|
28
|
+
Koefisien epsilon pada PolyLoss. Hanya berlaku saat loss='poly'.
|
|
29
|
+
|
|
30
|
+
focal_gamma : float, default=2.0
|
|
31
|
+
Parameter gamma pada Focal Loss. Hanya berlaku saat loss='focal'.
|
|
32
|
+
|
|
33
|
+
focal_alpha : float, default=0.25
|
|
34
|
+
Parameter alpha pada Focal Loss. Hanya berlaku saat loss='focal'.
|
|
35
|
+
|
|
36
|
+
max_rule_length : int, default=2
|
|
37
|
+
Jumlah maksimum fitur dalam satu aturan.
|
|
38
|
+
|
|
39
|
+
operators : list, default=['AND', 'OR']
|
|
40
|
+
Daftar operator logika yang digunakan.
|
|
41
|
+
|
|
42
|
+
use_xor : bool, default=False
|
|
43
|
+
Jika True maka XOR ditambahkan ke daftar operators.
|
|
44
|
+
|
|
45
|
+
rule_complexity_penalty : float, default=0.0
|
|
46
|
+
Penalti bobot proporsional dengan panjang aturan.
|
|
47
|
+
|
|
48
|
+
feature_selection_threshold : float, default=0.01
|
|
49
|
+
Ambang batas skor korelasi gradien minimum agar fitur masuk kandidat.
|
|
50
|
+
|
|
51
|
+
beam_width : int, default=5
|
|
52
|
+
Jumlah kandidat aturan terbaik yang dipertahankan per langkah beam search.
|
|
53
|
+
|
|
54
|
+
subsample : float, default=0.8
|
|
55
|
+
Fraksi data yang digunakan per iterasi boosting.
|
|
56
|
+
|
|
57
|
+
min_samples_rule : int or float, default=10
|
|
58
|
+
Jumlah minimum sampel yang harus memenuhi sebuah aturan.
|
|
59
|
+
|
|
60
|
+
binarize_strategy : str, default='gradient'
|
|
61
|
+
Strategi binarisasi fitur numerik. Pilihan: 'gradient', 'quantile',
|
|
62
|
+
'uniform', 'kmeans'.
|
|
63
|
+
|
|
64
|
+
n_thresholds : int or str, default='auto'
|
|
65
|
+
Jumlah kandidat ambang batas per fitur numerik.
|
|
66
|
+
|
|
67
|
+
n_iter_no_change : int or None, default=None
|
|
68
|
+
Jumlah iterasi tanpa peningkatan sebelum pelatihan dihentikan lebih awal.
|
|
69
|
+
|
|
70
|
+
tol : float, default=1e-4
|
|
71
|
+
Ambang batas peningkatan minimum untuk early stopping.
|
|
72
|
+
|
|
73
|
+
random_state : int or None, default=None
|
|
74
|
+
Nilai acak untuk reprodusibilitas.
|
|
75
|
+
|
|
76
|
+
warm_start : bool, default=False
|
|
77
|
+
Jika True maka pelatihan dilanjutkan dari kondisi model sebelumnya.
|
|
78
|
+
|
|
79
|
+
Atribut yang tersedia Setelah fit
|
|
80
|
+
--------------------------------
|
|
81
|
+
estimators_ : list of Rule
|
|
82
|
+
Daftar aturan yang dipelajari.
|
|
83
|
+
|
|
84
|
+
rule_weights_ : ndarray of float
|
|
85
|
+
Bobot optimal setiap aturan.
|
|
86
|
+
|
|
87
|
+
rules_ : list of str
|
|
88
|
+
Representasi teks setiap aturan.
|
|
89
|
+
|
|
90
|
+
feature_importances_ : ndarray of float
|
|
91
|
+
Skor kepentingan setiap fitur.
|
|
92
|
+
|
|
93
|
+
thresholds_ : dict
|
|
94
|
+
Ambang batas optimal per fitur numerik per iterasi.
|
|
95
|
+
|
|
96
|
+
train_score_ : ndarray of float
|
|
97
|
+
Nilai loss per iterasi selama pelatihan.
|
|
98
|
+
|
|
99
|
+
n_features_in_ : int
|
|
100
|
+
Jumlah fitur saat fit dipanggil.
|
|
101
|
+
|
|
102
|
+
feature_names_in_ : ndarray of str or None
|
|
103
|
+
Nama fitur jika input berupa DataFrame.
|
|
104
|
+
|
|
105
|
+
n_estimators_ : int
|
|
106
|
+
Jumlah iterasi aktual setelah pelatihan.
|
|
107
|
+
|
|
108
|
+
ohe_groups_ : dict or None
|
|
109
|
+
Pemetaan kelompok fitur hasil OneHotEncoding.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
n_estimators=100,
|
|
115
|
+
learning_rate=0.1,
|
|
116
|
+
loss='logistic',
|
|
117
|
+
poly_epsilon=1.0,
|
|
118
|
+
focal_gamma=2.0,
|
|
119
|
+
focal_alpha=0.25,
|
|
120
|
+
max_rule_length=2,
|
|
121
|
+
operators=None,
|
|
122
|
+
use_xor=False,
|
|
123
|
+
rule_complexity_penalty=0.0,
|
|
124
|
+
feature_selection_threshold=0.01,
|
|
125
|
+
beam_width=5,
|
|
126
|
+
subsample=0.8,
|
|
127
|
+
min_samples_rule=10,
|
|
128
|
+
binarize_strategy='gradient',
|
|
129
|
+
n_thresholds='auto',
|
|
130
|
+
n_iter_no_change=None,
|
|
131
|
+
tol=1e-4,
|
|
132
|
+
random_state=None,
|
|
133
|
+
warm_start=False,
|
|
134
|
+
):
|
|
135
|
+
self.n_estimators = n_estimators
|
|
136
|
+
self.learning_rate = learning_rate
|
|
137
|
+
self.loss = loss
|
|
138
|
+
self.poly_epsilon = poly_epsilon
|
|
139
|
+
self.focal_gamma = focal_gamma
|
|
140
|
+
self.focal_alpha = focal_alpha
|
|
141
|
+
self.max_rule_length = max_rule_length
|
|
142
|
+
self.operators = operators if operators is not None else ['AND', 'OR']
|
|
143
|
+
self.use_xor = use_xor
|
|
144
|
+
self.rule_complexity_penalty = rule_complexity_penalty
|
|
145
|
+
self.feature_selection_threshold = feature_selection_threshold
|
|
146
|
+
self.beam_width = beam_width
|
|
147
|
+
self.subsample = subsample
|
|
148
|
+
self.min_samples_rule = min_samples_rule
|
|
149
|
+
self.binarize_strategy = binarize_strategy
|
|
150
|
+
self.n_thresholds = n_thresholds
|
|
151
|
+
self.n_iter_no_change = n_iter_no_change
|
|
152
|
+
self.tol = tol
|
|
153
|
+
self.random_state = random_state
|
|
154
|
+
self.warm_start = warm_start
|
|
155
|
+
|
|
156
|
+
def _validate_input(self, X, y=None):
|
|
157
|
+
# Ubah input ke numpy array dan pastikan dimensinya benar
|
|
158
|
+
if hasattr(X, 'values'):
|
|
159
|
+
if hasattr(X, 'columns'):
|
|
160
|
+
self._input_feature_names = list(X.columns)
|
|
161
|
+
X = X.values
|
|
162
|
+
X = np.array(X, dtype=np.float64)
|
|
163
|
+
if X.ndim != 2:
|
|
164
|
+
raise ValueError("X harus berupa matriks dua dimensi.")
|
|
165
|
+
if y is not None:
|
|
166
|
+
y = np.array(y, dtype=np.float64)
|
|
167
|
+
unique_y = np.unique(y)
|
|
168
|
+
if not np.all(np.isin(unique_y, [0, 1])):
|
|
169
|
+
raise ValueError("Label y harus bernilai 0 atau 1.")
|
|
170
|
+
if len(y) != X.shape[0]:
|
|
171
|
+
raise ValueError("Jumlah baris X dan y harus sama.")
|
|
172
|
+
return X, y
|
|
173
|
+
|
|
174
|
+
def _detect_numeric_cols(self, X):
|
|
175
|
+
# Identifikasi kolom fitur yang bukan biner murni sebagai fitur numerik
|
|
176
|
+
numeric = []
|
|
177
|
+
for col in range(X.shape[1]):
|
|
178
|
+
unique_vals = np.unique(X[:, col])
|
|
179
|
+
if not np.all(np.isin(unique_vals, [0.0, 1.0])):
|
|
180
|
+
numeric.append(col)
|
|
181
|
+
return numeric
|
|
182
|
+
|
|
183
|
+
def _detect_ohe_groups(self, X):
|
|
184
|
+
# 1. Deteksi kelompok fitur OneHotEncoding berdasarkan sifat mutually exclusive
|
|
185
|
+
n_features = X.shape[1]
|
|
186
|
+
groups = {}
|
|
187
|
+
used = set()
|
|
188
|
+
group_id = 0
|
|
189
|
+
for i in range(n_features):
|
|
190
|
+
if i in used:
|
|
191
|
+
continue
|
|
192
|
+
group = [i]
|
|
193
|
+
for j in range(i + 1, n_features):
|
|
194
|
+
if j in used:
|
|
195
|
+
continue
|
|
196
|
+
# a. Dua fitur bersifat mutually exclusive jika tidak pernah bernilai 1 bersamaan
|
|
197
|
+
both_one = np.sum((X[:, i] == 1) & (X[:, j] == 1))
|
|
198
|
+
if both_one == 0:
|
|
199
|
+
# b. Pastikan keduanya memang fitur biner
|
|
200
|
+
if np.all(np.isin(np.unique(X[:, i]), [0.0, 1.0])) and \
|
|
201
|
+
np.all(np.isin(np.unique(X[:, j]), [0.0, 1.0])):
|
|
202
|
+
group.append(j)
|
|
203
|
+
used.add(j)
|
|
204
|
+
if len(group) > 1:
|
|
205
|
+
groups[group_id] = group
|
|
206
|
+
group_id += 1
|
|
207
|
+
used.add(i)
|
|
208
|
+
return groups if groups else None
|
|
209
|
+
|
|
210
|
+
def _subsample_indices(self, n_samples, rng):
|
|
211
|
+
# Kembalikan indeks subsample acak tanpa pengembalian
|
|
212
|
+
if self.subsample >= 1.0:
|
|
213
|
+
return np.arange(n_samples)
|
|
214
|
+
n_sub = max(1, int(n_samples * self.subsample))
|
|
215
|
+
return rng.choice(n_samples, size=n_sub, replace=False)
|
|
216
|
+
|
|
217
|
+
def _build_operators(self):
|
|
218
|
+
# Gabungkan daftar operator dan tambahkan XOR jika use_xor aktif
|
|
219
|
+
ops = list(self.operators)
|
|
220
|
+
if self.use_xor and 'XOR' not in ops:
|
|
221
|
+
ops.append('XOR')
|
|
222
|
+
return ops
|
|
223
|
+
|
|
224
|
+
def fit(self, X, y, sample_weight=None):
|
|
225
|
+
"""
|
|
226
|
+
Latih BinBoost pada data X dan label y.
|
|
227
|
+
|
|
228
|
+
Parameter
|
|
229
|
+
----------
|
|
230
|
+
X : array-like of shape (n_samples, n_features)
|
|
231
|
+
y : array-like of shape (n_samples,), nilai 0 atau 1
|
|
232
|
+
sample_weight : array-like of shape (n_samples,), opsional
|
|
233
|
+
|
|
234
|
+
Kembalian
|
|
235
|
+
----------
|
|
236
|
+
self
|
|
237
|
+
"""
|
|
238
|
+
self._input_feature_names = None
|
|
239
|
+
X, y = self._validate_input(X, y)
|
|
240
|
+
n_samples, n_features = X.shape
|
|
241
|
+
|
|
242
|
+
self.n_features_in_ = n_features
|
|
243
|
+
self.feature_names_in_ = (
|
|
244
|
+
np.array(self._input_feature_names)
|
|
245
|
+
if self._input_feature_names is not None else None
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
rng = np.random.RandomState(self.random_state)
|
|
249
|
+
ops = self._build_operators()
|
|
250
|
+
|
|
251
|
+
loss_fn = get_loss(
|
|
252
|
+
self.loss,
|
|
253
|
+
poly_epsilon=self.poly_epsilon,
|
|
254
|
+
focal_gamma=self.focal_gamma,
|
|
255
|
+
focal_alpha=self.focal_alpha,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
numeric_cols = self._detect_numeric_cols(X)
|
|
259
|
+
self.ohe_groups_ = self._detect_ohe_groups(X)
|
|
260
|
+
|
|
261
|
+
binarizer = GradientBinarizer(
|
|
262
|
+
strategy=self.binarize_strategy,
|
|
263
|
+
n_thresholds=self.n_thresholds
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
rule_finder = BeamSearchRuleFinder(
|
|
267
|
+
operators=ops,
|
|
268
|
+
max_rule_length=self.max_rule_length,
|
|
269
|
+
beam_width=self.beam_width,
|
|
270
|
+
feature_selection_threshold=self.feature_selection_threshold,
|
|
271
|
+
min_samples_rule=self.min_samples_rule,
|
|
272
|
+
rule_complexity_penalty=self.rule_complexity_penalty,
|
|
273
|
+
ohe_groups=self.ohe_groups_,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if not self.warm_start or not hasattr(self, 'estimators_'):
|
|
277
|
+
self.estimators_ = []
|
|
278
|
+
self.rule_weights_ = []
|
|
279
|
+
self.thresholds_ = {col: [] for col in numeric_cols}
|
|
280
|
+
self.train_score_ = []
|
|
281
|
+
self._F = loss_fn.init_F(y)
|
|
282
|
+
|
|
283
|
+
no_improve_count = 0
|
|
284
|
+
best_score = np.inf
|
|
285
|
+
|
|
286
|
+
for m in range(self.n_estimators):
|
|
287
|
+
# 1. Hitung gradien dari fungsi loss yang dipilih
|
|
288
|
+
gradients = loss_fn.gradient(y, self._F)
|
|
289
|
+
|
|
290
|
+
# 2. Ambil subsample data untuk iterasi ini
|
|
291
|
+
idx = self._subsample_indices(n_samples, rng)
|
|
292
|
+
X_sub = X[idx]
|
|
293
|
+
g_sub = gradients[idx]
|
|
294
|
+
|
|
295
|
+
# 3. Binarisasi fitur numerik menggunakan strategi yang dipilih
|
|
296
|
+
X_bin, thresholds_iter = binarizer.transform(X_sub, g_sub, numeric_cols)
|
|
297
|
+
|
|
298
|
+
# 4. Cari aturan terbaik menggunakan beam search
|
|
299
|
+
rule = rule_finder.find_best_rule(X_bin, g_sub)
|
|
300
|
+
if rule is None:
|
|
301
|
+
break
|
|
302
|
+
|
|
303
|
+
rule.feature_names = (
|
|
304
|
+
list(self.feature_names_in_) if self.feature_names_in_ is not None else None
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# 5. Perbarui prediksi model untuk seluruh data
|
|
308
|
+
X_bin_full, thresholds_full = binarizer.transform(X, gradients, numeric_cols)
|
|
309
|
+
h_full = rule.evaluate(X_bin_full)
|
|
310
|
+
self._F = self._F + self.learning_rate * rule.weight * h_full
|
|
311
|
+
|
|
312
|
+
# 6. Simpan aturan dan statistik iterasi ini
|
|
313
|
+
self.estimators_.append(rule)
|
|
314
|
+
self.rule_weights_.append(rule.weight)
|
|
315
|
+
for col, t in thresholds_full.items():
|
|
316
|
+
self.thresholds_[col].append(t)
|
|
317
|
+
|
|
318
|
+
current_score = loss_fn.loss(y, self._F)
|
|
319
|
+
self.train_score_.append(current_score)
|
|
320
|
+
|
|
321
|
+
# 7. Periksa kondisi early stopping jika diaktifkan
|
|
322
|
+
if self.n_iter_no_change is not None:
|
|
323
|
+
if current_score < best_score - self.tol:
|
|
324
|
+
best_score = current_score
|
|
325
|
+
no_improve_count = 0
|
|
326
|
+
else:
|
|
327
|
+
no_improve_count += 1
|
|
328
|
+
if no_improve_count >= self.n_iter_no_change:
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
self.n_estimators_ = len(self.estimators_)
|
|
332
|
+
self.train_score_ = np.array(self.train_score_)
|
|
333
|
+
self.rule_weights_ = np.array(self.rule_weights_)
|
|
334
|
+
self.rules_ = [r.to_string(self.feature_names_in_) for r in self.estimators_]
|
|
335
|
+
self._compute_feature_importances(n_features)
|
|
336
|
+
self.is_fitted_ = True
|
|
337
|
+
return self
|
|
338
|
+
|
|
339
|
+
def _compute_feature_importances(self, n_features):
|
|
340
|
+
# Hitung skor kepentingan fitur dari frekuensi kemunculan dikali rata-rata bobot absolut
|
|
341
|
+
importances = np.zeros(n_features)
|
|
342
|
+
for rule in self.estimators_:
|
|
343
|
+
gain = np.abs(rule.weight)
|
|
344
|
+
for f in rule.features:
|
|
345
|
+
importances[f] += gain
|
|
346
|
+
total = importances.sum()
|
|
347
|
+
self.feature_importances_ = importances / total if total > 0 else importances
|
|
348
|
+
|
|
349
|
+
def _check_is_fitted(self):
|
|
350
|
+
if not getattr(self, 'is_fitted_', False):
|
|
351
|
+
raise RuntimeError("Model belum dilatih. Panggil fit terlebih dahulu.")
|
|
352
|
+
|
|
353
|
+
def _decision_function(self, X):
|
|
354
|
+
# Hitung nilai F akhir untuk seluruh sampel
|
|
355
|
+
self._check_is_fitted()
|
|
356
|
+
X, _ = self._validate_input(X)
|
|
357
|
+
F = np.zeros(X.shape[0])
|
|
358
|
+
binarizer = GradientBinarizer(
|
|
359
|
+
strategy=self.binarize_strategy,
|
|
360
|
+
n_thresholds=self.n_thresholds
|
|
361
|
+
)
|
|
362
|
+
numeric_cols = self._detect_numeric_cols(X)
|
|
363
|
+
dummy_grad = np.ones(X.shape[0])
|
|
364
|
+
X_bin, _ = binarizer.transform(X, dummy_grad, numeric_cols)
|
|
365
|
+
for rule, w in zip(self.estimators_, self.rule_weights_):
|
|
366
|
+
h = rule.evaluate(X_bin)
|
|
367
|
+
F += self.learning_rate * w * h
|
|
368
|
+
return F
|
|
369
|
+
|
|
370
|
+
def predict_proba(self, X):
|
|
371
|
+
"""
|
|
372
|
+
Kembalikan probabilitas kelas untuk setiap sampel.
|
|
373
|
+
|
|
374
|
+
Kembalian
|
|
375
|
+
----------
|
|
376
|
+
ndarray of shape (n_samples, 2)
|
|
377
|
+
"""
|
|
378
|
+
F = self._decision_function(X)
|
|
379
|
+
p = sigmoid(F)
|
|
380
|
+
return np.column_stack([1 - p, p])
|
|
381
|
+
|
|
382
|
+
def predict_log_proba(self, X):
|
|
383
|
+
"""
|
|
384
|
+
Kembalikan logaritma probabilitas kelas untuk setiap sampel.
|
|
385
|
+
|
|
386
|
+
Kembalian
|
|
387
|
+
----------
|
|
388
|
+
ndarray of shape (n_samples, 2)
|
|
389
|
+
"""
|
|
390
|
+
return np.log(np.clip(self.predict_proba(X), 1e-15, None))
|
|
391
|
+
|
|
392
|
+
def predict(self, X):
|
|
393
|
+
"""
|
|
394
|
+
Prediksi label kelas (0 atau 1) untuk setiap sampel.
|
|
395
|
+
|
|
396
|
+
Kembalian
|
|
397
|
+
----------
|
|
398
|
+
ndarray of shape (n_samples,)
|
|
399
|
+
"""
|
|
400
|
+
proba = self.predict_proba(X)
|
|
401
|
+
return (proba[:, 1] >= 0.5).astype(int)
|
|
402
|
+
|
|
403
|
+
def score(self, X, y):
|
|
404
|
+
"""
|
|
405
|
+
Hitung akurasi klasifikasi pada data X dan label y.
|
|
406
|
+
|
|
407
|
+
Kembalian
|
|
408
|
+
----------
|
|
409
|
+
float
|
|
410
|
+
"""
|
|
411
|
+
_, y = self._validate_input(X, y)
|
|
412
|
+
preds = self.predict(X)
|
|
413
|
+
return np.mean(preds == y)
|
|
414
|
+
|
|
415
|
+
def staged_predict_proba(self, X):
|
|
416
|
+
"""
|
|
417
|
+
Generator probabilitas per iterasi boosting.
|
|
418
|
+
|
|
419
|
+
Hasil
|
|
420
|
+
----------
|
|
421
|
+
ndarray of shape (n_samples, 2) per iterasi
|
|
422
|
+
"""
|
|
423
|
+
self._check_is_fitted()
|
|
424
|
+
X, _ = self._validate_input(X)
|
|
425
|
+
binarizer = GradientBinarizer(
|
|
426
|
+
strategy=self.binarize_strategy,
|
|
427
|
+
n_thresholds=self.n_thresholds
|
|
428
|
+
)
|
|
429
|
+
numeric_cols = self._detect_numeric_cols(X)
|
|
430
|
+
dummy_grad = np.ones(X.shape[0])
|
|
431
|
+
X_bin, _ = binarizer.transform(X, dummy_grad, numeric_cols)
|
|
432
|
+
F = np.zeros(X.shape[0])
|
|
433
|
+
for rule, w in zip(self.estimators_, self.rule_weights_):
|
|
434
|
+
h = rule.evaluate(X_bin)
|
|
435
|
+
F += self.learning_rate * w * h
|
|
436
|
+
p = sigmoid(F)
|
|
437
|
+
yield np.column_stack([1 - p, p])
|
|
438
|
+
|
|
439
|
+
def staged_predict(self, X):
|
|
440
|
+
"""
|
|
441
|
+
Generator prediksi kelas per iterasi boosting.
|
|
442
|
+
|
|
443
|
+
Hasil
|
|
444
|
+
----------
|
|
445
|
+
ndarray of shape (n_samples,) per iterasi
|
|
446
|
+
"""
|
|
447
|
+
for proba in self.staged_predict_proba(X):
|
|
448
|
+
yield (proba[:, 1] >= 0.5).astype(int)
|
|
449
|
+
|
|
450
|
+
def apply(self, X):
|
|
451
|
+
"""
|
|
452
|
+
Kembalikan nilai aktivasi setiap aturan untuk setiap sampel.
|
|
453
|
+
|
|
454
|
+
Kembalian
|
|
455
|
+
----------
|
|
456
|
+
ndarray of shape (n_samples, n_estimators_)
|
|
457
|
+
"""
|
|
458
|
+
self._check_is_fitted()
|
|
459
|
+
X, _ = self._validate_input(X)
|
|
460
|
+
binarizer = GradientBinarizer(
|
|
461
|
+
strategy=self.binarize_strategy,
|
|
462
|
+
n_thresholds=self.n_thresholds
|
|
463
|
+
)
|
|
464
|
+
numeric_cols = self._detect_numeric_cols(X)
|
|
465
|
+
dummy_grad = np.ones(X.shape[0])
|
|
466
|
+
X_bin, _ = binarizer.transform(X, dummy_grad, numeric_cols)
|
|
467
|
+
results = []
|
|
468
|
+
for rule in self.estimators_:
|
|
469
|
+
results.append(rule.evaluate(X_bin))
|
|
470
|
+
return np.column_stack(results)
|
|
471
|
+
|
|
472
|
+
def get_params(self, deep=True):
|
|
473
|
+
"""
|
|
474
|
+
Kembalikan semua hyperparameter sebagai kamus.
|
|
475
|
+
|
|
476
|
+
Kembalian
|
|
477
|
+
----------
|
|
478
|
+
dict
|
|
479
|
+
"""
|
|
480
|
+
return {
|
|
481
|
+
'n_estimators': self.n_estimators,
|
|
482
|
+
'learning_rate': self.learning_rate,
|
|
483
|
+
'loss': self.loss,
|
|
484
|
+
'poly_epsilon': self.poly_epsilon,
|
|
485
|
+
'focal_gamma': self.focal_gamma,
|
|
486
|
+
'focal_alpha': self.focal_alpha,
|
|
487
|
+
'max_rule_length': self.max_rule_length,
|
|
488
|
+
'operators': self.operators,
|
|
489
|
+
'use_xor': self.use_xor,
|
|
490
|
+
'rule_complexity_penalty': self.rule_complexity_penalty,
|
|
491
|
+
'feature_selection_threshold': self.feature_selection_threshold,
|
|
492
|
+
'beam_width': self.beam_width,
|
|
493
|
+
'subsample': self.subsample,
|
|
494
|
+
'min_samples_rule': self.min_samples_rule,
|
|
495
|
+
'binarize_strategy': self.binarize_strategy,
|
|
496
|
+
'n_thresholds': self.n_thresholds,
|
|
497
|
+
'n_iter_no_change': self.n_iter_no_change,
|
|
498
|
+
'tol': self.tol,
|
|
499
|
+
'random_state': self.random_state,
|
|
500
|
+
'warm_start': self.warm_start,
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
def set_params(self, **params):
|
|
504
|
+
"""
|
|
505
|
+
Tetapkan nilai hyperparameter.
|
|
506
|
+
|
|
507
|
+
Kembalian
|
|
508
|
+
----------
|
|
509
|
+
self
|
|
510
|
+
"""
|
|
511
|
+
for key, val in params.items():
|
|
512
|
+
if not hasattr(self, key):
|
|
513
|
+
raise ValueError(f"Parameter '{key}' tidak dikenali.")
|
|
514
|
+
setattr(self, key, val)
|
|
515
|
+
return self
|
|
516
|
+
|
|
517
|
+
@property
|
|
518
|
+
def rule_summary_(self):
|
|
519
|
+
"""
|
|
520
|
+
Tabel ringkasan semua aturan beserta bobot, cakupan, dan dukungan.
|
|
521
|
+
|
|
522
|
+
Kembalian
|
|
523
|
+
----------
|
|
524
|
+
list of dict yang dapat dikonversi ke DataFrame
|
|
525
|
+
"""
|
|
526
|
+
self._check_is_fitted()
|
|
527
|
+
summary = []
|
|
528
|
+
for i, rule in enumerate(self.estimators_):
|
|
529
|
+
summary.append({
|
|
530
|
+
'iterasi': i + 1,
|
|
531
|
+
'aturan': self.rules_[i],
|
|
532
|
+
'bobot': round(float(self.rule_weights_[i]), 6),
|
|
533
|
+
'panjang_aturan': len(rule.features),
|
|
534
|
+
})
|
|
535
|
+
return summary
|
|
536
|
+
|
|
537
|
+
@property
|
|
538
|
+
def n_rules_(self):
|
|
539
|
+
"""
|
|
540
|
+
Jumlah aturan aktif dalam model.
|
|
541
|
+
|
|
542
|
+
Kembalian
|
|
543
|
+
----------
|
|
544
|
+
int
|
|
545
|
+
"""
|
|
546
|
+
self._check_is_fitted()
|
|
547
|
+
return self.n_estimators_
|
|
548
|
+
|
|
549
|
+
@property
|
|
550
|
+
def feature_usage_(self):
|
|
551
|
+
"""
|
|
552
|
+
Frekuensi kemunculan setiap fitur di dalam aturan.
|
|
553
|
+
|
|
554
|
+
Kembalian
|
|
555
|
+
----------
|
|
556
|
+
dict
|
|
557
|
+
"""
|
|
558
|
+
self._check_is_fitted()
|
|
559
|
+
usage = {}
|
|
560
|
+
for rule in self.estimators_:
|
|
561
|
+
for f in rule.features:
|
|
562
|
+
name = (
|
|
563
|
+
self.feature_names_in_[f]
|
|
564
|
+
if self.feature_names_in_ is not None
|
|
565
|
+
else f"X{f}"
|
|
566
|
+
)
|
|
567
|
+
usage[name] = usage.get(name, 0) + 1
|
|
568
|
+
return usage
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def sigmoid(x):
|
|
5
|
+
# Fungsi sigmoid dengan penjagaan numerik agar tidak overflow
|
|
6
|
+
return np.where(x >= 0, 1 / (1 + np.exp(-x)), np.exp(x) / (1 + np.exp(x)))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LogisticLoss:
|
|
10
|
+
# Loss biner standar berbasis log-likelihood
|
|
11
|
+
|
|
12
|
+
def loss(self, y, F):
|
|
13
|
+
p = sigmoid(F)
|
|
14
|
+
p = np.clip(p, 1e-15, 1 - 1e-15)
|
|
15
|
+
return -np.mean(y * np.log(p) + (1 - y) * np.log(1 - p))
|
|
16
|
+
|
|
17
|
+
def gradient(self, y, F):
|
|
18
|
+
# 1. Gradien negatif sebagai arah penurunan loss
|
|
19
|
+
p = sigmoid(F)
|
|
20
|
+
return y - p
|
|
21
|
+
|
|
22
|
+
def init_F(self, y):
|
|
23
|
+
# Inisialisasi F0 dari proporsi kelas positif
|
|
24
|
+
p = np.clip(np.mean(y), 1e-15, 1 - 1e-15)
|
|
25
|
+
return np.full(len(y), np.log(p / (1 - p)))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FocalLoss:
|
|
29
|
+
# Focal Loss untuk data tidak seimbang
|
|
30
|
+
|
|
31
|
+
def __init__(self, gamma=2.0, alpha=0.25):
|
|
32
|
+
self.gamma = gamma
|
|
33
|
+
self.alpha = alpha
|
|
34
|
+
|
|
35
|
+
def loss(self, y, F):
|
|
36
|
+
p = sigmoid(F)
|
|
37
|
+
p = np.clip(p, 1e-15, 1 - 1e-15)
|
|
38
|
+
pt = np.where(y == 1, p, 1 - p)
|
|
39
|
+
alpha_t = np.where(y == 1, self.alpha, 1 - self.alpha)
|
|
40
|
+
return -np.mean(alpha_t * ((1 - pt) ** self.gamma) * np.log(pt))
|
|
41
|
+
|
|
42
|
+
def gradient(self, y, F):
|
|
43
|
+
# 1. Gradien Focal Loss mengikuti turunan dari Lin et al. 2017
|
|
44
|
+
# a. Hitung probabilitas dan faktor modulasi
|
|
45
|
+
p = sigmoid(F)
|
|
46
|
+
p = np.clip(p, 1e-15, 1 - 1e-15)
|
|
47
|
+
pt = np.where(y == 1, p, 1 - p)
|
|
48
|
+
alpha_t = np.where(y == 1, self.alpha, 1 - self.alpha)
|
|
49
|
+
# b. Gradien lengkap dengan faktor fokus
|
|
50
|
+
weight = alpha_t * (1 - pt) ** self.gamma
|
|
51
|
+
grad = weight * (y - p) + self.gamma * weight * pt * np.log(pt) * np.where(y == 1, -(1 - p), p)
|
|
52
|
+
return grad
|
|
53
|
+
|
|
54
|
+
def init_F(self, y):
|
|
55
|
+
p = np.clip(np.mean(y), 1e-15, 1 - 1e-15)
|
|
56
|
+
return np.full(len(y), np.log(p / (1 - p)))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PolyLoss:
|
|
60
|
+
# PolyLoss yang berbasis ekspansi polinomial
|
|
61
|
+
def __init__(self, epsilon=1.0):
|
|
62
|
+
self.epsilon = epsilon
|
|
63
|
+
|
|
64
|
+
def loss(self, y, F):
|
|
65
|
+
p = sigmoid(F)
|
|
66
|
+
p = np.clip(p, 1e-15, 1 - 1e-15)
|
|
67
|
+
pt = np.where(y == 1, p, 1 - p)
|
|
68
|
+
ce = -np.log(pt)
|
|
69
|
+
return np.mean(ce + self.epsilon * (1 - pt))
|
|
70
|
+
|
|
71
|
+
def gradient(self, y, F):
|
|
72
|
+
# 1. Gradien PolyLoss adalah gradien CE ditambah suku koreksi polinomial
|
|
73
|
+
p = sigmoid(F)
|
|
74
|
+
p = np.clip(p, 1e-15, 1 - 1e-15)
|
|
75
|
+
pt = np.where(y == 1, p, 1 - p)
|
|
76
|
+
# a. Gradien cross-entropy standar
|
|
77
|
+
grad_ce = y - p
|
|
78
|
+
# b. Suku koreksi dari ekspansi polinomial pertama
|
|
79
|
+
grad_poly = self.epsilon * np.where(y == 1, p * (1 - p), -p * (1 - p))
|
|
80
|
+
return grad_ce + grad_poly
|
|
81
|
+
|
|
82
|
+
def init_F(self, y):
|
|
83
|
+
p = np.clip(np.mean(y), 1e-15, 1 - 1e-15)
|
|
84
|
+
return np.full(len(y), np.log(p / (1 - p)))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_loss(name, **kwargs):
|
|
88
|
+
# Kembalikan objek loss sesuai nama yang dipilih pengguna
|
|
89
|
+
if name == 'logistic':
|
|
90
|
+
return LogisticLoss()
|
|
91
|
+
elif name == 'focal':
|
|
92
|
+
return FocalLoss(
|
|
93
|
+
gamma=kwargs.get('focal_gamma', 2.0),
|
|
94
|
+
alpha=kwargs.get('focal_alpha', 0.25)
|
|
95
|
+
)
|
|
96
|
+
elif name == 'poly':
|
|
97
|
+
return PolyLoss(epsilon=kwargs.get('poly_epsilon', 1.0))
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError(f"Loss '{name}' tidak dikenali. Pilihan: 'logistic', 'focal', 'poly'.")
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from itertools import combinations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Rule:
|
|
6
|
+
# Satu aturan logika dalam ensemble BinBoost
|
|
7
|
+
|
|
8
|
+
def __init__(self, features, operators, weight, feature_names=None):
|
|
9
|
+
# 1. Simpan komponen aturan
|
|
10
|
+
# a. Indeks fitur yang terlibat
|
|
11
|
+
self.features = features
|
|
12
|
+
# b. Daftar operator antar fitur
|
|
13
|
+
self.operators = operators
|
|
14
|
+
# c. Bobot optimal hasil perhitungan w_m*
|
|
15
|
+
self.weight = weight
|
|
16
|
+
self.feature_names = feature_names
|
|
17
|
+
|
|
18
|
+
def evaluate(self, X_bin):
|
|
19
|
+
# Hitung keluaran aturan untuk setiap sampel dan kembalikan larik biner
|
|
20
|
+
result = X_bin[:, self.features[0]].astype(bool)
|
|
21
|
+
for i, op in enumerate(self.operators):
|
|
22
|
+
next_col = X_bin[:, self.features[i + 1]].astype(bool)
|
|
23
|
+
if op == 'AND':
|
|
24
|
+
result = result & next_col
|
|
25
|
+
elif op == 'OR':
|
|
26
|
+
result = result | next_col
|
|
27
|
+
elif op == 'XOR':
|
|
28
|
+
result = result ^ next_col
|
|
29
|
+
return result.astype(np.float64)
|
|
30
|
+
|
|
31
|
+
def to_string(self, feature_names=None):
|
|
32
|
+
# Kembalikan representasi teks aturan yang dapat dibaca manusia
|
|
33
|
+
names = feature_names if feature_names is not None else self.feature_names
|
|
34
|
+
if names is None:
|
|
35
|
+
parts = [f"X{f}" for f in self.features]
|
|
36
|
+
else:
|
|
37
|
+
parts = [names[f] for f in self.features]
|
|
38
|
+
if len(parts) == 1:
|
|
39
|
+
return parts[0]
|
|
40
|
+
expr = parts[0]
|
|
41
|
+
for i, op in enumerate(self.operators):
|
|
42
|
+
expr = f"({expr} {op} {parts[i + 1]})"
|
|
43
|
+
return expr
|
|
44
|
+
|
|
45
|
+
def __repr__(self):
|
|
46
|
+
return f"Rule({self.to_string()}, weight={self.weight:.4f})"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class BeamSearchRuleFinder:
|
|
50
|
+
# Pencari aturan terbaik menggunakan beam search berbasis gradien
|
|
51
|
+
|
|
52
|
+
def __init__(self, operators, max_rule_length, beam_width,
|
|
53
|
+
feature_selection_threshold, min_samples_rule,
|
|
54
|
+
rule_complexity_penalty, ohe_groups=None):
|
|
55
|
+
self.operators = operators
|
|
56
|
+
self.max_rule_length = max_rule_length
|
|
57
|
+
self.beam_width = beam_width
|
|
58
|
+
self.feature_selection_threshold = feature_selection_threshold
|
|
59
|
+
self.min_samples_rule = min_samples_rule
|
|
60
|
+
self.rule_complexity_penalty = rule_complexity_penalty
|
|
61
|
+
self.ohe_groups = ohe_groups
|
|
62
|
+
|
|
63
|
+
def _select_features(self, X_bin, gradients):
|
|
64
|
+
# 1. Pilih fitur kandidat berdasarkan skor korelasi gradien
|
|
65
|
+
n_samples, n_features = X_bin.shape
|
|
66
|
+
scores = []
|
|
67
|
+
for f in range(n_features):
|
|
68
|
+
col = X_bin[:, f]
|
|
69
|
+
score = np.abs(np.dot(gradients, col)) / (np.sum(col) + 1e-10)
|
|
70
|
+
scores.append(score)
|
|
71
|
+
scores = np.array(scores)
|
|
72
|
+
selected = np.where(scores >= self.feature_selection_threshold)[0]
|
|
73
|
+
if len(selected) == 0:
|
|
74
|
+
selected = np.array([np.argmax(scores)])
|
|
75
|
+
return selected
|
|
76
|
+
|
|
77
|
+
def _same_ohe_group(self, f1, f2):
|
|
78
|
+
# Periksa apakah dua fitur berasal dari grup OneHotEncoding yang sama
|
|
79
|
+
if self.ohe_groups is None:
|
|
80
|
+
return False
|
|
81
|
+
for group in self.ohe_groups.values():
|
|
82
|
+
if f1 in group and f2 in group:
|
|
83
|
+
return True
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
def _compute_score(self, h, gradients):
|
|
87
|
+
# 1. Hitung bobot optimal w_m* dan skor galat kuadrat terkecil
|
|
88
|
+
denom = np.dot(h, h)
|
|
89
|
+
if denom < 1e-10:
|
|
90
|
+
return None, np.inf
|
|
91
|
+
w = np.dot(gradients, h) / denom
|
|
92
|
+
penalty = self.rule_complexity_penalty
|
|
93
|
+
score = np.sum((gradients - w * h) ** 2)
|
|
94
|
+
return w, score
|
|
95
|
+
|
|
96
|
+
def _is_valid(self, h, n_samples):
|
|
97
|
+
# Periksa apakah jumlah sampel yang memenuhi aturan mencukupi min_samples_rule
|
|
98
|
+
support = int(np.sum(h))
|
|
99
|
+
if isinstance(self.min_samples_rule, float) and self.min_samples_rule < 1.0:
|
|
100
|
+
min_s = int(self.min_samples_rule * n_samples)
|
|
101
|
+
else:
|
|
102
|
+
min_s = int(self.min_samples_rule)
|
|
103
|
+
return support >= min_s
|
|
104
|
+
|
|
105
|
+
def find_best_rule(self, X_bin, gradients):
|
|
106
|
+
# 1. Jalankan beam search untuk menemukan aturan terbaik
|
|
107
|
+
n_samples = X_bin.shape[0]
|
|
108
|
+
candidate_features = self._select_features(X_bin, gradients)
|
|
109
|
+
|
|
110
|
+
# a. Inisialisasi beam dengan aturan panjang satu
|
|
111
|
+
beam = []
|
|
112
|
+
for f in candidate_features:
|
|
113
|
+
h = X_bin[:, f].astype(np.float64)
|
|
114
|
+
if not self._is_valid(h, n_samples):
|
|
115
|
+
continue
|
|
116
|
+
w, score = self._compute_score(h, gradients)
|
|
117
|
+
if w is not None:
|
|
118
|
+
beam.append(([f], [], w, score))
|
|
119
|
+
|
|
120
|
+
if not beam:
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
beam.sort(key=lambda x: x[3])
|
|
124
|
+
beam = beam[:self.beam_width]
|
|
125
|
+
best_candidate = beam[0]
|
|
126
|
+
|
|
127
|
+
# b. Perluas aturan hingga panjang maksimum
|
|
128
|
+
for length in range(2, self.max_rule_length + 1):
|
|
129
|
+
new_beam = []
|
|
130
|
+
for (feats, ops, w_prev, score_prev) in beam:
|
|
131
|
+
for f in candidate_features:
|
|
132
|
+
if f in feats:
|
|
133
|
+
continue
|
|
134
|
+
# c. Terapkan constraint mutual exclusivity OHE
|
|
135
|
+
skip = False
|
|
136
|
+
for existing_f in feats:
|
|
137
|
+
if self._same_ohe_group(existing_f, f):
|
|
138
|
+
skip = True
|
|
139
|
+
break
|
|
140
|
+
if skip:
|
|
141
|
+
continue
|
|
142
|
+
for op in self.operators:
|
|
143
|
+
new_feats = feats + [f]
|
|
144
|
+
new_ops = ops + [op]
|
|
145
|
+
tmp_rule = Rule(new_feats, new_ops, 0.0)
|
|
146
|
+
h = tmp_rule.evaluate(X_bin)
|
|
147
|
+
if not self._is_valid(h, n_samples):
|
|
148
|
+
continue
|
|
149
|
+
w, score = self._compute_score(h, gradients)
|
|
150
|
+
# d. Terapkan penalti kompleksitas berdasarkan panjang aturan
|
|
151
|
+
penalized_score = score * (1 + self.rule_complexity_penalty * length)
|
|
152
|
+
if w is not None:
|
|
153
|
+
new_beam.append((new_feats, new_ops, w, penalized_score))
|
|
154
|
+
|
|
155
|
+
if not new_beam:
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
new_beam.sort(key=lambda x: x[3])
|
|
159
|
+
new_beam = new_beam[:self.beam_width]
|
|
160
|
+
|
|
161
|
+
if new_beam[0][3] < best_candidate[3]:
|
|
162
|
+
best_candidate = new_beam[0]
|
|
163
|
+
beam = new_beam
|
|
164
|
+
else:
|
|
165
|
+
break
|
|
166
|
+
|
|
167
|
+
feats, ops, w, score = best_candidate
|
|
168
|
+
return Rule(feats, ops, w)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: binboost
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner
|
|
5
|
+
Home-page: https://github.com/username/binboost
|
|
6
|
+
Author: BinBoost Authors
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/username/binboost
|
|
9
|
+
Keywords: gradient boosting,logical rules,binary features,interpretable machine learning
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=1.21.0
|
|
19
|
+
Requires-Dist: pandas>=1.3.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-python
|
|
24
|
+
|
|
25
|
+
# BinBoost
|
|
26
|
+
|
|
27
|
+
**Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner**
|
|
28
|
+
|
|
29
|
+
BinBoost adalah algoritma klasifikasi gradient boosting yang membangun ensemble aturan logika murni (AND, OR, XOR) dengan binarisasi fitur numerik adaptif berbasis gradien pada setiap iterasi boosting. Setiap weak learner berupa aturan yang dapat dibaca langsung oleh manusia tanpa memerlukan alat bantu penjelasan pasca-pelatihan.
|
|
30
|
+
|
|
31
|
+
## Kebaruan Utama
|
|
32
|
+
|
|
33
|
+
- **Binarisasi adaptif berbasis gradien**: nilai ambang batas fitur numerik dicari per iterasi untuk memaksimalkan korelasi dengan gradien saat ini
|
|
34
|
+
- **Ensemble aturan logika murni**: tidak ada pohon keputusan, setiap weak learner adalah aturan seperti `(A AND B)` atau `(C OR D)`
|
|
35
|
+
|
|
36
|
+
## Instalasi
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install binboost
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Penggunaan Dasar
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import numpy as np
|
|
46
|
+
from binboost import BinBoost
|
|
47
|
+
|
|
48
|
+
X = np.array([[1, 0, 1], [0, 1, 0], [1, 1, 0], [0, 0, 1]], dtype=float)
|
|
49
|
+
y = np.array([1, 0, 1, 0])
|
|
50
|
+
|
|
51
|
+
model = BinBoost(n_estimators=50, learning_rate=0.1, max_rule_length=2)
|
|
52
|
+
model.fit(X, y)
|
|
53
|
+
|
|
54
|
+
print(model.predict(X))
|
|
55
|
+
print(model.predict_proba(X))
|
|
56
|
+
print(model.rules_)
|
|
57
|
+
|
|
58
|
+
import pandas as pd
|
|
59
|
+
print(pd.DataFrame(model.rule_summary_))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Hyperparameter Utama
|
|
63
|
+
|
|
64
|
+
| Parameter | Bawaan | Keterangan |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `n_estimators` | 100 | Jumlah iterasi boosting |
|
|
67
|
+
| `learning_rate` | 0.1 | Faktor penyusutan tiap aturan |
|
|
68
|
+
| `loss` | `'logistic'` | Fungsi loss: `'logistic'`, `'focal'`, `'poly'` |
|
|
69
|
+
| `max_rule_length` | 2 | Jumlah maksimum fitur dalam satu aturan |
|
|
70
|
+
| `operators` | `['AND','OR']` | Operator logika yang digunakan |
|
|
71
|
+
| `beam_width` | 5 | Lebar beam search |
|
|
72
|
+
| `binarize_strategy` | `'gradient'` | Strategi binarisasi: `'gradient'`, `'quantile'`, `'uniform'`, `'kmeans'` |
|
|
73
|
+
| `subsample` | 0.8 | Fraksi data per iterasi |
|
|
74
|
+
| `feature_selection_threshold` | 0.01 | Ambang batas seleksi fitur berbasis gradien |
|
|
75
|
+
|
|
76
|
+
## Fitur yang Didukung
|
|
77
|
+
|
|
78
|
+
- Fitur biner (0/1): langsung diproses
|
|
79
|
+
- Fitur numerik (int/float): dibinarisasi otomatis
|
|
80
|
+
- Fitur kategorikal 3+ kelas: wajib OneHotEncode terlebih dahulu
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
84
|
+
enc = OneHotEncoder(sparse_output=False, drop='first')
|
|
85
|
+
X_encoded = enc.fit_transform(X[['kolom_kategorikal']])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
BinBoost secara otomatis mendeteksi kelompok fitur OneHotEncoding dan mencegah aturan yang tidak masuk akal seperti `(Warna_Merah AND Warna_Biru)`.
|
|
89
|
+
|
|
90
|
+
## Atribut Setelah Pelatihan
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
model.rules_ # daftar teks aturan
|
|
94
|
+
model.rule_weights_ # bobot setiap aturan
|
|
95
|
+
model.feature_importances_ # skor kepentingan fitur
|
|
96
|
+
model.train_score_ # loss per iterasi
|
|
97
|
+
model.rule_summary_ # ringkasan lengkap atau konversi ke DataFrame
|
|
98
|
+
model.n_rules_ # jumlah aturan aktif
|
|
99
|
+
model.feature_usage_ # frekuensi penggunaan tiap fitur
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Lisensi
|
|
103
|
+
|
|
104
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
binboost/__init__.py
|
|
6
|
+
binboost/binarizer.py
|
|
7
|
+
binboost/binboost.py
|
|
8
|
+
binboost/loss.py
|
|
9
|
+
binboost/rule.py
|
|
10
|
+
binboost.egg-info/PKG-INFO
|
|
11
|
+
binboost.egg-info/SOURCES.txt
|
|
12
|
+
binboost.egg-info/dependency_links.txt
|
|
13
|
+
binboost.egg-info/requires.txt
|
|
14
|
+
binboost.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
binboost
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "binboost"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
dependencies = [
|
|
13
|
+
"numpy>=1.21.0",
|
|
14
|
+
"pandas>=1.3.0",
|
|
15
|
+
]
|
|
16
|
+
keywords = ["gradient boosting", "logical rules", "binary features", "interpretable machine learning"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Intended Audience :: Science/Research",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/username/binboost"
|
binboost-0.1.0/setup.cfg
ADDED
binboost-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="binboost",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
author="BinBoost Authors",
|
|
7
|
+
description="Gradient Boosting Berbasis Aturan Logika Adaptif untuk Fitur Biner",
|
|
8
|
+
long_description=open("README.md", encoding="utf-8").read(),
|
|
9
|
+
long_description_content_type="text/markdown",
|
|
10
|
+
url="https://github.com/username/binboost",
|
|
11
|
+
packages=find_packages(),
|
|
12
|
+
python_requires=">=3.8",
|
|
13
|
+
install_requires=[
|
|
14
|
+
"numpy>=1.21.0",
|
|
15
|
+
"pandas>=1.3.0",
|
|
16
|
+
],
|
|
17
|
+
classifiers=[
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Intended Audience :: Science/Research",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
],
|
|
24
|
+
keywords="gradient boosting, logical rules, binary features, interpretable machine learning",
|
|
25
|
+
)
|