PineBioML 1.2.0__py3-none-any.whl

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.
@@ -0,0 +1,989 @@
1
+ from . import Basic_tuner
2
+ from abc import abstractmethod
3
+ from typing import Literal
4
+
5
+ from joblib import parallel_backend
6
+
7
+ from sklearn.model_selection import StratifiedKFold
8
+ from sklearn.utils.class_weight import compute_sample_weight
9
+ from sklearn.svm import SVC
10
+ from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
11
+ from sklearn.tree import DecisionTreeClassifier
12
+ from statsmodels.discrete.discrete_model import Logit
13
+ from sklearn.linear_model import LogisticRegression
14
+ from xgboost import XGBClassifier
15
+ from lightgbm import LGBMClassifier
16
+ from catboost import CatBoostClassifier, Pool
17
+
18
+ import numpy as np
19
+ from pandas import DataFrame
20
+
21
+
22
+ class Classification_tuner(Basic_tuner):
23
+ """
24
+ A subclass of Basic_tuner for classification task.
25
+
26
+ """
27
+
28
+ def __init__(self,
29
+ n_try,
30
+ n_cv,
31
+ target,
32
+ validate_penalty,
33
+ kernel_seed=None,
34
+ valid_seed=None,
35
+ optuna_seed=None):
36
+ super().__init__(n_try=n_try,
37
+ n_cv=n_cv,
38
+ target=target,
39
+ validate_penalty=validate_penalty,
40
+ kernel_seed=kernel_seed,
41
+ valid_seed=valid_seed,
42
+ optuna_seed=optuna_seed)
43
+ """
44
+
45
+ Args:
46
+ n_try (int): The number of trials optuna should try.
47
+ n_cv (int): The number of folds to execute cross validation evaluation in iteration of optuna optimization.
48
+ target (str): The target of optuna optimization. Notice that is different from the training loss of model.
49
+ validate_penalty (bool): True to penalty the overfitting by difference between training score and cv score.
50
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
51
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
52
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
53
+ """
54
+
55
+ def is_regression(self) -> bool:
56
+ """
57
+ No, this is not a regresion tuner or for regresion task.
58
+ Returns:
59
+ bool: False
60
+ """
61
+
62
+ return False
63
+
64
+ def reference(self) -> dict[str, str]:
65
+ """
66
+ This function will return reference of this method in python dict.
67
+ If you want to access it in PineBioML api document, then click on the >Expand source code
68
+
69
+ Returns:
70
+ dict[str, str]: a dict of reference.
71
+ """
72
+ return super().reference()
73
+
74
+ @abstractmethod
75
+ def name(self) -> str:
76
+ """
77
+ To be determined.
78
+
79
+ Returns:
80
+ str: Name of this tuner.
81
+ """
82
+ pass
83
+
84
+ @abstractmethod
85
+ def create_model(self, trial, default):
86
+ """
87
+ Create model based on default setting or optuna trial
88
+
89
+ Args:
90
+ trial (optuna.trial.Trial): optuna trial in this call.
91
+ default (bool): To use default hyper parameter
92
+
93
+ Returns :
94
+ sklearn.base.BaseEstimator: A sklearn style model object.
95
+ """
96
+ pass
97
+
98
+ def predict_proba(self, x):
99
+ """
100
+ The sklearn.base.BaseEstimator predict_prob api.
101
+
102
+ Args:
103
+ x (pandas.DataFrame): feature to extract information from.
104
+
105
+ Returns:
106
+ pd.DataFrame: prediction in prob, an array with shape (n_samples, n_classes)
107
+ """
108
+ return DataFrame(self.best_model.predict_proba(x),
109
+ index=x.index,
110
+ columns=self.y_mapping.classes_)
111
+
112
+
113
+ # linear model, elasticnet
114
+ class ElasticLogit_tuner(Classification_tuner):
115
+ """
116
+ Tuning a elasic net logistic regression.
117
+ [sklearn.linear_model.LogisticRegression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression), reminds the choice of the algorithm depends on the penalty chosen and on (multinomial) multiclass support.
118
+ """
119
+
120
+ def __init__(self,
121
+ n_try=25,
122
+ n_cv=5,
123
+ target="mcc",
124
+ kernel_seed: int = None,
125
+ valid_seed: int = None,
126
+ optuna_seed: int = None,
127
+ validate_penalty=True):
128
+ """
129
+
130
+ Args:
131
+ n_try (int, optional): The number of trials optuna should try. Defaults to 25.
132
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
133
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
134
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
135
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
136
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
137
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
138
+ """
139
+ super().__init__(n_try=n_try,
140
+ n_cv=n_cv,
141
+ target=target,
142
+ kernel_seed=kernel_seed,
143
+ valid_seed=valid_seed,
144
+ optuna_seed=optuna_seed,
145
+ validate_penalty=validate_penalty)
146
+
147
+ # "saga" fast convergence is only guaranteed on features with approximately the same scale. You should do a feature-wise (between sample) normalization before fitting.
148
+ self.kernel = "saga"
149
+ self.penalty = "elasticnet"
150
+
151
+ def name(self):
152
+ return "ElasticNetLogisticRegression"
153
+
154
+ def reference(self) -> dict[str, str]:
155
+ """
156
+ This function will return reference of this method in python dict.
157
+ If you want to access it in PineBioML api document, then click on the >Expand source code
158
+
159
+ Returns:
160
+ dict[str, str]: a dict of reference.
161
+ """
162
+ refer = super().reference()
163
+ refer[
164
+ self.name() +
165
+ " document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html"
166
+ return refer
167
+
168
+ def create_model(self, trial, default=False):
169
+ if default:
170
+ parms = {
171
+ "C": 1.0,
172
+ "l1_ratio": 0.5,
173
+ "penalty": self.penalty,
174
+ "solver": self.kernel,
175
+ "random_state": self.kernel_seed_tape[trial.number],
176
+ "verbose": 0
177
+ }
178
+ else:
179
+ parms = {
180
+ "C": trial.suggest_float('C', 1e-6, 1e+2, log=True),
181
+ "l1_ratio": trial.suggest_float('l1_ratio', 0, 1),
182
+ "penalty": self.penalty,
183
+ "solver": self.kernel,
184
+ "random_state": self.kernel_seed_tape[trial.number],
185
+ "verbose": 0,
186
+ }
187
+ lg = LogisticRegression(**parms)
188
+ return lg
189
+
190
+ def summary(self):
191
+ """
192
+ It is the way I found to cram a sklearn regression result into the statsmodel regresion.
193
+ The only reason to do this is that statsmodel provides R-style summary.
194
+ """
195
+ if len(self.best_model.classes_) > 2:
196
+ # multi-class classification
197
+ # Todo
198
+ raise TypeError(
199
+ "multi-class classification summary not support yet. Please tell me why do you need that in a multi-class classification task"
200
+ )
201
+ else:
202
+ # binary classification
203
+ sm_logit = Logit(self.y, self.x).fit(
204
+ disp=False,
205
+ start_params=self.best_model.coef_.flatten(),
206
+ maxiter=0,
207
+ warn_convergence=False)
208
+ print(sm_logit.summary())
209
+
210
+
211
+ # RF
212
+ class RandomForest_tuner(Classification_tuner):
213
+ """
214
+ Tuning a random forest model.
215
+ [sklearn.ensemble.RandomForestClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
216
+ """
217
+
218
+ def __init__(self,
219
+ using_oob=True,
220
+ n_try=50,
221
+ n_cv=5,
222
+ target="mcc",
223
+ kernel_seed=None,
224
+ valid_seed=None,
225
+ optuna_seed=None,
226
+ validate_penalty=True):
227
+ """
228
+
229
+ Args:
230
+ using_oob (bool, optional): Using out of bag score as validation. Defaults to True.
231
+ n_try (int, optional): The number of trials optuna should try. Defaults to 50.
232
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
233
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
234
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
235
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
236
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
237
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
238
+ """
239
+ super().__init__(n_try=n_try,
240
+ n_cv=n_cv,
241
+ target=target,
242
+ kernel_seed=kernel_seed,
243
+ valid_seed=valid_seed,
244
+ optuna_seed=optuna_seed,
245
+ validate_penalty=validate_penalty)
246
+
247
+ self.using_oob = using_oob
248
+
249
+ def name(self):
250
+ return "RandomForest"
251
+
252
+ def reference(self) -> dict[str, str]:
253
+ """
254
+ This function will return reference of this method in python dict.
255
+ If you want to access it in PineBioML api document, then click on the >Expand source code
256
+
257
+ Returns:
258
+ dict[str, str]: a dict of reference.
259
+ """
260
+ refer = super().reference()
261
+ refer[
262
+ self.name() +
263
+ " document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html"
264
+ refer[
265
+ self.name() +
266
+ " publication"] = "https://www.stat.berkeley.edu/~breiman/randomforest2001.pdf"
267
+
268
+ return refer
269
+
270
+ def create_model(self, trial, default=False):
271
+ if default:
272
+ parms = {
273
+ "bootstrap": self.using_oob,
274
+ "oob_score": self.using_oob,
275
+ "n_jobs": -1,
276
+ "random_state": self.kernel_seed_tape[trial.number],
277
+ "verbose": 0,
278
+ }
279
+ else:
280
+ parms = {
281
+ "n_estimators":
282
+ trial.suggest_int('n_estimators', 32, 1024, log=True),
283
+ "min_samples_leaf":
284
+ trial.suggest_int('min_samples_leaf', 1, 32, log=True),
285
+ "ccp_alpha":
286
+ trial.suggest_float('ccp_alpha', 1e-4, 1e-1, log=True),
287
+ "max_samples":
288
+ trial.suggest_float('max_samples', 0.5, 0.9, log=True),
289
+ "bootstrap":
290
+ self.using_oob,
291
+ "oob_score":
292
+ self.using_oob,
293
+ "n_jobs":
294
+ -1,
295
+ "random_state":
296
+ self.kernel_seed_tape[trial.number],
297
+ "verbose":
298
+ 0,
299
+ }
300
+
301
+ rf = RandomForestClassifier(**parms)
302
+ return rf
303
+
304
+ def evaluate(self, trial, default=False):
305
+ """
306
+ RF needs oob and we have it.
307
+
308
+ Args:
309
+ trial (optuna.trial.Trial): optuna trial in this call.
310
+ default (bool): To use default hyper parameter. This argument will be passed to creat_model
311
+ Returns :
312
+ float: The score.
313
+ """
314
+ classifier_obj = self.create_model(trial, default)
315
+
316
+ if self.using_oob:
317
+ # oob predict
318
+ # there is a bug that default sklaern randomforest parallel_backend using thread where others use "loky", see joblib.parallel_backend.
319
+ with parallel_backend('loky'):
320
+ classifier_obj.fit(self.x,
321
+ self.y,
322
+ sample_weight=compute_sample_weight(
323
+ class_weight="balanced", y=self.y))
324
+
325
+ # oob prediction
326
+ y_pred = classifier_obj.oob_decision_function_
327
+ if self.metric_name == "roc_auc":
328
+ # roc_auc can only be used on binary classification. Do not try ovr, ovo. forget them.
329
+ y_pred = y_pred[:, 1]
330
+
331
+ # oob score
332
+ ### manual scorer wraper.
333
+ if self.metric_using_proba:
334
+ score = self.metric._score_func(self.y, y_pred,
335
+ **self.scorer_kargs)
336
+ else:
337
+ # revert to class symbols.
338
+ y_pred = classifier_obj.classes_[y_pred.argmax(axis=-1)]
339
+ score = self.metric._score_func(self.y, y_pred,
340
+ **self.scorer_kargs)
341
+ if not self.metric_great_better:
342
+ # if not great is better, then multiply -1
343
+ score *= -1
344
+ else:
345
+ # cv score
346
+ cv = StratifiedKFold(
347
+ n_splits=self.n_cv,
348
+ shuffle=True,
349
+ random_state=self.valid_seed_tape[trial.number])
350
+
351
+ score = []
352
+ for i, (train_ind, test_ind) in enumerate(cv.split(self.x,
353
+ self.y)):
354
+ x_train = self.x.iloc[train_ind]
355
+ y_train = self.y.iloc[train_ind]
356
+ x_test = self.x.iloc[test_ind]
357
+ y_test = self.y.iloc[test_ind]
358
+
359
+ with parallel_backend('loky'):
360
+ classifier_obj.fit(x_train,
361
+ y_train,
362
+ sample_weight=compute_sample_weight(
363
+ class_weight="balanced", y=y_train))
364
+
365
+ test_score = self.metric(classifier_obj, x_test, y_test)
366
+ train_score = self.metric(classifier_obj, x_train, y_train)
367
+ if self.validate_penalty:
368
+ score.append(test_score + 0.1 * (test_score - train_score))
369
+ else:
370
+ score.append(test_score)
371
+
372
+ score = sum(score) / self.n_cv
373
+ return score
374
+
375
+
376
+ # SVM
377
+ class SVM_tuner(Classification_tuner):
378
+ """
379
+ Tuning a support vector machine.
380
+ [sklearn.svm.SVC](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html)
381
+ """
382
+
383
+ def __init__(self,
384
+ kernel: Literal["linear", "poly", "rbf", "sigmoid"] = "rbf",
385
+ n_try=25,
386
+ n_cv=5,
387
+ target="mcc",
388
+ kernel_seed=None,
389
+ valid_seed=None,
390
+ optuna_seed=None,
391
+ validate_penalty=True):
392
+ """
393
+
394
+ Args:
395
+ kernel (Literal["linear", "poly", "rbf", "sigmoid"], optional): This will be passed to the attribute of SVC: "kernel". Defaults to "rbf".
396
+ n_try (int, optional): The number of trials optuna should try. Defaults to 25.
397
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
398
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
399
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
400
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
401
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
402
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
403
+ """
404
+ super().__init__(n_try=n_try,
405
+ n_cv=n_cv,
406
+ target=target,
407
+ kernel_seed=kernel_seed,
408
+ valid_seed=valid_seed,
409
+ optuna_seed=optuna_seed,
410
+ validate_penalty=validate_penalty)
411
+ self.kernel = kernel # rbf, linear, poly, sigmoid
412
+
413
+ def name(self):
414
+ return self.kernel + "-SVM"
415
+
416
+ def reference(self) -> dict[str, str]:
417
+ """
418
+ This function will return reference of this method in python dict.
419
+ If you want to access it in PineBioML api document, then click on the >Expand source code
420
+
421
+ Returns:
422
+ dict[str, str]: a dict of reference.
423
+ """
424
+ refer = super().reference()
425
+ refer[
426
+ self.name() +
427
+ " document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html"
428
+ refer[
429
+ self.name() +
430
+ " publication"] = "https://citeseerx.ist.psu.edu/doc_view/pid/42e5ed832d4310ce4378c44d05570439df28a393"
431
+
432
+ return refer
433
+
434
+ def create_model(self, trial, default=False):
435
+ if default:
436
+ parms = {
437
+ "kernel": self.kernel,
438
+ "random_state": self.kernel_seed_tape[trial.number],
439
+ "probability": True
440
+ }
441
+ else:
442
+ # scaling penalty: https://scikit-learn.org/stable/auto_examples/svm/plot_svm_scale_c.html#sphx-glr-auto-examples-svm-plot-svm-scale-c-py
443
+ parms = {
444
+ "C":
445
+ trial.suggest_float('C',
446
+ 1e-4 * np.sqrt(self.n_sample),
447
+ 1e+2 * np.sqrt(self.n_sample),
448
+ log=True),
449
+ "gamma":
450
+ "auto",
451
+ "kernel":
452
+ self.kernel,
453
+ "random_state":
454
+ self.kernel_seed_tape[trial.number],
455
+ "probability":
456
+ True
457
+ }
458
+ svm = SVC(**parms)
459
+ return svm
460
+
461
+
462
+ # Todo: learning rate and number of iteration adjustment
463
+ # XGboost
464
+ class XGBoost_tuner(Classification_tuner):
465
+ """
466
+ Tuning a XGBoost classifier model.
467
+ [xgboost.XGBClassifier](https://xgboost.readthedocs.io/en/stable/python/python_api.html)
468
+
469
+ ToDo:
470
+ 1. sample imbalance. (we have temporary solution)
471
+ 2. early stop.
472
+ 3. efficiency (optuna.integration.XGBoostPruningCallback).
473
+
474
+ """
475
+
476
+ def __init__(self,
477
+ n_try=75,
478
+ n_cv=5,
479
+ target="mcc",
480
+ kernel_seed=None,
481
+ valid_seed=None,
482
+ optuna_seed=None,
483
+ validate_penalty=True):
484
+ """
485
+
486
+ Args:
487
+ n_try (int, optional): The number of trials optuna should try. Defaults to 75.
488
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
489
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
490
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
491
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
492
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
493
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
494
+
495
+ """
496
+ super().__init__(n_try=n_try,
497
+ n_cv=n_cv,
498
+ target=target,
499
+ kernel_seed=kernel_seed,
500
+ valid_seed=valid_seed,
501
+ optuna_seed=optuna_seed,
502
+ validate_penalty=validate_penalty)
503
+
504
+ def name(self):
505
+ return "XGBoost"
506
+
507
+ def reference(self) -> dict[str, str]:
508
+ """
509
+ This function will return reference of this method in python dict.
510
+ If you want to access it in PineBioML api document, then click on the >Expand source code
511
+
512
+ Returns:
513
+ dict[str, str]: a dict of reference.
514
+ """
515
+ refer = super().reference()
516
+ refer[self.name() +
517
+ " document"] = "https://xgboost.readthedocs.io/en/stable/"
518
+ refer[
519
+ self.name() +
520
+ " publication"] = "https://dl.acm.org/doi/10.1145/2939672.2939785"
521
+
522
+ return refer
523
+
524
+ def create_model(self, trial, default=False):
525
+ if default:
526
+ parms = {
527
+ "n_jobs": None,
528
+ "random_state": self.kernel_seed_tape[trial.number],
529
+ "verbosity": 0
530
+ }
531
+ else:
532
+ if trial.suggest_categorical("use_subsample", [True, False]):
533
+ sampling_rate = trial.suggest_float('subsample',
534
+ 0.5,
535
+ 0.9,
536
+ log=True)
537
+ col_sampling_rate = trial.suggest_float(
538
+ 'colsample_bytree', 0.1, 0.9)
539
+ else:
540
+ sampling_rate = 1.
541
+ col_sampling_rate = 1.
542
+
543
+ parms = {
544
+ "n_estimators":
545
+ trial.suggest_int('n_estimators', 16, 256, log=True),
546
+ "max_depth":
547
+ trial.suggest_int('max_depth', 2, 16, log=True),
548
+ "gamma":
549
+ trial.suggest_float('gamma', 5e-2, 2e+1, log=True),
550
+ "learning_rate":
551
+ trial.suggest_float('learning_rate', 5e-2, 5e-1, log=True),
552
+ "subsample":
553
+ sampling_rate,
554
+ "colsample_bytree":
555
+ col_sampling_rate,
556
+ "reg_lambda":
557
+ trial.suggest_float('reg_lambda', 1e-2, 1e+1, log=True),
558
+ "n_jobs":
559
+ None,
560
+ "random_state":
561
+ self.kernel_seed_tape[trial.number],
562
+ "verbosity":
563
+ 0,
564
+ }
565
+
566
+ xgb = XGBClassifier(**parms)
567
+ return xgb
568
+
569
+
570
+ # lightGBM
571
+ class LighGBM_tuner(Classification_tuner):
572
+ """
573
+ Tuning a LighGBM classifier model.
574
+ [lightgbm.LGBMClassifier](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html)
575
+
576
+ ToDo:
577
+ 1. compare with optuna.integration.lightgbm.LightGBMTuner
578
+ """
579
+
580
+ def __init__(self,
581
+ n_try=75,
582
+ n_cv=5,
583
+ target="mcc",
584
+ kernel_seed=None,
585
+ valid_seed=None,
586
+ optuna_seed=None,
587
+ validate_penalty=True):
588
+ """
589
+
590
+ Args:
591
+ n_try (int, optional): The number of trials optuna should try. Defaults to 75.
592
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
593
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
594
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
595
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
596
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
597
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
598
+ """
599
+ super().__init__(n_try=n_try,
600
+ n_cv=n_cv,
601
+ target=target,
602
+ kernel_seed=kernel_seed,
603
+ valid_seed=valid_seed,
604
+ optuna_seed=optuna_seed,
605
+ validate_penalty=validate_penalty)
606
+
607
+ def name(self):
608
+ return "LightGBM"
609
+
610
+ def reference(self) -> dict[str, str]:
611
+ """
612
+ This function will return reference of this method in python dict.
613
+ If you want to access it in PineBioML api document, then click on the >Expand source code
614
+
615
+ Returns:
616
+ dict[str, str]: a dict of reference.
617
+ """
618
+ refer = super().reference()
619
+ refer[
620
+ self.name() +
621
+ " document"] = "https://lightgbm.readthedocs.io/en/latest/index.html"
622
+ refer[
623
+ self.name() +
624
+ " publication"] = "https://proceedings.neurips.cc/paper_files/paper/2017/file/6449f44a102fde848669bdd9eb6b76fa-Paper.pdf"
625
+
626
+ return refer
627
+
628
+ def create_model(self, trial, default=False):
629
+ if default:
630
+ parms = {
631
+ "n_jobs": None,
632
+ "random_state": self.kernel_seed,
633
+ "verbosity": -1
634
+ }
635
+ else:
636
+ depth = trial.suggest_float('max_depth', 3, 16, log=True)
637
+ leaves = trial.suggest_float('num_leaves',
638
+ depth * 2 / 3,
639
+ depth,
640
+ log=True)
641
+ depth = int(np.rint(depth))
642
+ leaves = int(np.floor(np.power(2, leaves)))
643
+
644
+ if trial.suggest_categorical("use_subsample", [True, False]):
645
+ sampling_rate = trial.suggest_float('subsample',
646
+ 0.5,
647
+ 0.9,
648
+ log=True)
649
+ col_sampling_rate = trial.suggest_float(
650
+ 'colsample_bytree', 0.1, 0.9)
651
+ else:
652
+ sampling_rate = 1.
653
+ col_sampling_rate = 1.
654
+
655
+ parms = {
656
+ "n_estimators":
657
+ trial.suggest_int('n_estimators', 16, 256, log=True),
658
+ "max_depth":
659
+ depth,
660
+ "num_leaves":
661
+ leaves,
662
+ "learning_rate":
663
+ trial.suggest_float('learning_rate', 1e-2, 1, log=True),
664
+ "subsample_freq":
665
+ 1,
666
+ "subsample":
667
+ sampling_rate,
668
+ "colsample_bytree":
669
+ col_sampling_rate,
670
+ "min_child_samples":
671
+ trial.suggest_int('min_child_samples', 2, 32, log=True),
672
+ "reg_lambda":
673
+ trial.suggest_float('reg_lambda', 5e-3, 1e+1, log=True),
674
+ "n_jobs":
675
+ None,
676
+ "random_state":
677
+ self.kernel_seed_tape[trial.number],
678
+ "verbosity":
679
+ -1,
680
+ }
681
+
682
+ lgbm = LGBMClassifier(**parms)
683
+ return lgbm
684
+
685
+
686
+ # Adaboost
687
+ class AdaBoost_tuner(Classification_tuner):
688
+ """
689
+ Tuning a AdaBoost calssifier.
690
+ [sklearn.ensemble.AdaBoostClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html)
691
+ """
692
+
693
+ def __init__(self,
694
+ n_try=25,
695
+ n_cv=5,
696
+ target="mcc",
697
+ kernel_seed=None,
698
+ valid_seed=None,
699
+ optuna_seed=None,
700
+ validate_penalty=True):
701
+ """
702
+ Args:
703
+ n_try (int, optional): The number of trials optuna should try. Defaults to 25.
704
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
705
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
706
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
707
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
708
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
709
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
710
+ """
711
+ super().__init__(n_try=n_try,
712
+ n_cv=n_cv,
713
+ target=target,
714
+ kernel_seed=kernel_seed,
715
+ valid_seed=valid_seed,
716
+ optuna_seed=optuna_seed,
717
+ validate_penalty=validate_penalty)
718
+
719
+ def name(self):
720
+ return "AdaBoost"
721
+
722
+ def reference(self) -> dict[str, str]:
723
+ """
724
+ This function will return reference of this method in python dict.
725
+ If you want to access it in PineBioML api document, then click on the >Expand source code
726
+
727
+ Returns:
728
+ dict[str, str]: a dict of reference.
729
+ """
730
+ refer = super().reference()
731
+ refer[
732
+ self.name() +
733
+ " document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html"
734
+ refer[
735
+ self.name() +
736
+ " publication: original version"] = "https://www.ee.columbia.edu/~sfchang/course/svia-F03/papers/freund95decisiontheoretic-adaboost.pdf"
737
+ refer[
738
+ self.name() +
739
+ " publication: implemented version"] = "https://doi.org/10.4310/SII.2009.v2.n3.a8"
740
+
741
+ return refer
742
+
743
+ def create_model(self, trial, default=False):
744
+ if default:
745
+ parms = {
746
+ "random_state": self.kernel_seed,
747
+ }
748
+ else:
749
+ # scaling penalty: https://scikit-learn.org/stable/auto_examples/svm/plot_svm_scale_c.html#sphx-glr-auto-examples-svm-plot-svm-scale-c-py
750
+ parms = {
751
+ "n_estimators":
752
+ trial.suggest_int('n_estimators', 8, 256, log=True),
753
+ "learning_rate":
754
+ trial.suggest_float('learning_rate', 1e-2, 1, log=True),
755
+ "algorithm":
756
+ "SAMME",
757
+ "random_state":
758
+ self.kernel_seed_tape[trial.number]
759
+ }
760
+ ada = AdaBoostClassifier(**parms)
761
+ return ada
762
+
763
+
764
+ # DT
765
+ class DecisionTree_tuner(Classification_tuner):
766
+ """
767
+ Tuning a DecisionTree calssifier.
768
+ [sklearn.tree.DecisionTreeClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html)
769
+ """
770
+
771
+ def __init__(self,
772
+ n_try=25,
773
+ n_cv=5,
774
+ target="mcc",
775
+ kernel_seed=None,
776
+ valid_seed=None,
777
+ optuna_seed=None,
778
+ validate_penalty=True):
779
+ """
780
+ Args:
781
+ n_try (int, optional): The number of trials optuna should try. Defaults to 25.
782
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
783
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
784
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
785
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
786
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
787
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
788
+ """
789
+ super().__init__(n_try=n_try,
790
+ n_cv=n_cv,
791
+ target=target,
792
+ kernel_seed=kernel_seed,
793
+ valid_seed=valid_seed,
794
+ optuna_seed=optuna_seed,
795
+ validate_penalty=validate_penalty)
796
+
797
+ def name(self):
798
+ return "DecisionTree"
799
+
800
+ def reference(self) -> dict[str, str]:
801
+ """
802
+ This function will return reference of this method in python dict.
803
+ If you want to access it in PineBioML api document, then click on the >Expand source code
804
+
805
+ Returns:
806
+ dict[str, str]: a dict of reference.
807
+ """
808
+ refer = super().reference()
809
+ refer[
810
+ self.name() +
811
+ " document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html"
812
+ refer[
813
+ self.name() +
814
+ " publication"] = "https://www.taylorfrancis.com/books/mono/10.1201/9781315139470/classification-regression-trees-leo-breiman-jerome-friedman-olshen-charles-stone"
815
+
816
+ return refer
817
+
818
+ def create_model(self, trial, default=False):
819
+ if default:
820
+ parms = {
821
+ "random_state": self.kernel_seed,
822
+ }
823
+ else:
824
+ parms = {
825
+ "max_depth":
826
+ trial.suggest_int('max_depth', 2, 16, log=True),
827
+ "min_samples_split":
828
+ trial.suggest_int('min_samples_split', 2, 32, log=True),
829
+ "min_samples_leaf":
830
+ trial.suggest_int('min_samples_leaf', 1, 16, log=True),
831
+ "ccp_alpha":
832
+ trial.suggest_float('ccp_alpha', 1e-3, 1e-1, log=True),
833
+ "random_state":
834
+ self.kernel_seed_tape[trial.number],
835
+ }
836
+ DT = DecisionTreeClassifier(**parms)
837
+ return DT
838
+
839
+
840
+ # catboost
841
+ class CatBoost_tuner(Classification_tuner):
842
+ """
843
+ Tuning a CatBoost classifier model.
844
+ [catboost.CatBoostClassifier](https://catboost.ai/en/docs/concepts/python-reference_catboostclassifier)
845
+
846
+ ToDo:
847
+ 1. compare with optuna.integration.CatBoost...
848
+ """
849
+
850
+ def __init__(self,
851
+ n_try=75,
852
+ n_cv=5,
853
+ target="mcc",
854
+ kernel_seed=None,
855
+ valid_seed=None,
856
+ optuna_seed=None,
857
+ validate_penalty=True):
858
+ """
859
+
860
+ Args:
861
+ n_try (int, optional): The number of trials optuna should try. Defaults to 75.
862
+ n_cv (int, optional): The number of folds to execute cross validation evaluation in iteration of optuna optimization. Defaults to 5.
863
+ target (str, optional): The target of optuna optimization. Notice that is different from the training loss of model. Defaults to "mcc".
864
+ kernel_seed (int, optional): Random seed for model. Defaults to None.
865
+ valid_seed (int, optional): Random seed for cross validation. Defaults to None.
866
+ optuna_seed (int, optional): Random seed for optuna's hyperparameter sampling. Defaults to None.
867
+ validate_penalty (bool, optional): True to penalty the overfitting by difference between training score and cv score. Defaults to True.
868
+
869
+ """
870
+ super().__init__(n_try=n_try,
871
+ n_cv=n_cv,
872
+ target=target,
873
+ kernel_seed=kernel_seed,
874
+ valid_seed=valid_seed,
875
+ optuna_seed=optuna_seed,
876
+ validate_penalty=validate_penalty)
877
+
878
+ def name(self):
879
+ return "CatBoost"
880
+
881
+ def reference(self) -> dict[str, str]:
882
+ """
883
+ This function will return reference of this method in python dict.
884
+ If you want to access it in PineBioML api document, then click on the >Expand source code
885
+
886
+ Returns:
887
+ dict[str, str]: a dict of reference.
888
+ """
889
+ refer = super().reference()
890
+ refer[self.name() + " document"] = "https://catboost.ai/en/docs/"
891
+ refer[
892
+ self.name() +
893
+ " publication"] = "https://proceedings.neurips.cc/paper_files/paper/2018/file/14491b756b3a51daac41c24863285549-Paper.pdf"
894
+
895
+ return refer
896
+
897
+ def create_model(self, trial, default=False):
898
+ if default:
899
+ parms = {
900
+ "random_seed": self.kernel_seed,
901
+ "verbose": False,
902
+ #"use_best_model": True
903
+ }
904
+ else:
905
+ depth = trial.suggest_float('max_depth', 3, 16, log=True)
906
+ leaves = trial.suggest_float('num_leaves',
907
+ depth * 2 / 3,
908
+ depth,
909
+ log=True)
910
+ depth = int(np.rint(depth))
911
+ leaves = int(np.floor(np.power(2, leaves)))
912
+
913
+ if trial.suggest_categorical("use_subsample", [True, False]):
914
+ sampling_rate = trial.suggest_float('subsample',
915
+ 0.5,
916
+ 0.9,
917
+ log=True)
918
+ col_sampling_rate = trial.suggest_float(
919
+ 'colsample_bytree', 0.1, 0.9)
920
+ else:
921
+ sampling_rate = 1.
922
+ col_sampling_rate = 1.
923
+
924
+ parms = {
925
+ "n_estimators":
926
+ trial.suggest_int('n_estimators', 16, 256, log=True),
927
+ "learning_rate":
928
+ trial.suggest_float('learning_rate', 1e-2, 1, log=True),
929
+ "max_depth":
930
+ depth,
931
+ "reg_lambda":
932
+ trial.suggest_float('reg_lambda', 5e-3, 1e+1, log=True),
933
+ "colsample_bylevel":
934
+ col_sampling_rate,
935
+ "subsample":
936
+ sampling_rate,
937
+ #"use_best_model": True,
938
+ "random_seed":
939
+ self.kernel_seed_tape[trial.number],
940
+ "verbose":
941
+ False,
942
+ }
943
+
944
+ cat = CatBoostClassifier(**parms)
945
+ return cat
946
+
947
+ def evaluate(self, trial, default=False):
948
+ """
949
+ To evaluate the score of this trial. you should call create_model instead of creating model manually in this function.
950
+ catboost need to be used with pool.
951
+
952
+ Args:
953
+ trial (optuna.trial.Trial): optuna trial in this call.
954
+ default (bool): To use default hyper parameter. This argument will be passed to creat_model
955
+ Returns :
956
+ float: The score.
957
+ """
958
+ classifier_obj = self.create_model(trial, default)
959
+
960
+ cv = StratifiedKFold(n_splits=self.n_cv,
961
+ shuffle=True,
962
+ random_state=self.valid_seed_tape[trial.number])
963
+
964
+ score = []
965
+ for i, (train_ind, test_ind) in enumerate(cv.split(self.x, self.y)):
966
+ x_train = self.x.iloc[train_ind]
967
+ y_train = self.y.iloc[train_ind]
968
+ pool_train = Pool(x_train, y_train)
969
+
970
+ x_test = self.x.iloc[test_ind]
971
+ y_test = self.y.iloc[test_ind]
972
+ #pool_test = Pool(x_test, y_test)
973
+
974
+ classifier_obj.fit(pool_train) #, eval_set=pool_test
975
+
976
+ train_score = self.metric(classifier_obj, x_train, y_train)
977
+ test_score = self.metric(classifier_obj, x_test, y_test)
978
+ if self.validate_penalty:
979
+ score.append(test_score + 0.1 * (test_score - train_score))
980
+ else:
981
+ score.append(test_score)
982
+
983
+ score = sum(score) / self.n_cv
984
+ return score
985
+
986
+
987
+ # Todo
988
+ # KNN
989
+ # KNN-Graph spectrum