class_docstr: Tree-based GAM classifier.
  Uses cyclical boosting to fit a GAM with small trees.
  Simplified version of the explainable boosting machine described in https://github.com/interpretml/interpret
  Only works for binary classification.
  Fits a scalar bias to the mean.
docstr: Apply cyclic boosting, storing trees in self.estimators_
docstr_tok: ["Apply","cyclic","boosting",",","storing","trees","in","self.estimators_"]
code: def _cyclic_boost(
      self,
      X_train,
      y_train,
      sample_weight_train,
      X_val,
      y_val,
      sample_weight_val,
  ):
      """Apply cyclic boosting, storing trees in self.estimators_"""
  
      residuals_train = y_train - self.predict_proba(X_train)[:, 1]
      mse_val = self._calc_mse(X_val, y_val, sample_weight_val)
      for _ in range(self.n_boosting_rounds):
          boosting_round_ests = []
          boosting_round_mses = []
          for feature_num in range(X_train.shape[1]):
              X_ = np.zeros_like(X_train)
              X_[:, feature_num] = X_train[:, feature_num]
              est = DecisionTreeRegressor(
                  max_leaf_nodes=self.max_leaf_nodes,
                  random_state=self.random_state,
              )
              est.fit(
                  X_, residuals_train, sample_weight=sample_weight_train
              )
              succesfully_split_on_feature = np.all(
                  (est.tree_.feature[0] == feature_num)
                  | (est.tree_.feature[0] == -2)
              )
              if not succesfully_split_on_feature:
                  continue
              self.estimators_.append(est)
              residuals_train_new = (
                  residuals_train
                  - self.learning_rate * est.predict(X_train)
              )
              if self.boosting_strategy == "cyclic":
                  residuals_train = residuals_train_new
              elif self.boosting_strategy == "greedy":
                  mse_train_new = self._calc_mse(
                      X_train, y_train, sample_weight_train
                  )
                  # don't add each estimator for greedy
                  boosting_round_ests.append(
                      deepcopy(self.estimators_.pop())
                  )
                  boosting_round_mses.append(mse_train_new)
  
          if self.boosting_strategy == "greedy":
              best_est = boosting_round_ests[
                  np.argmin(boosting_round_mses)
              ]
              self.estimators_.append(best_est)
              residuals_train = (
                  residuals_train
                  - self.learning_rate * best_est.predict(X_train)
              )
  
          # early stopping if validation error does not decrease
          mse_val_new = self._calc_mse(X_val, y_val, sample_weight_val)
          if mse_val_new >= mse_val:
              return
          else:
              mse_val = mse_val_new
  
imports: from copy import deepcopy
  import numpy
  import pandas
  from sklearn.base import BaseEstimator
  from sklearn.tree import DecisionTreeRegressor
  from sklearn.utils.validation import check_is_fitted
  from sklearn.utils import check_array
  from sklearn.utils.multiclass import check_classification_targets
  from sklearn.utils.validation import check_X_y
  from sklearn.utils.validation import _check_sample_weight
  from sklearn.model_selection import train_test_split
  from sklearn.metrics import accuracy_score, roc_auc_score
  from tqdm import tqdm
  import imodels
  from sklearn.base import RegressorMixin, ClassifierMixin
label_desc: Use image node_id 3 for calling the TreeGAMSimple obj's underlying member method code with example usage: obj._cyclic_boost(X_train, y_train, sample_weight_train, X_val, y_val, sample_weight_val) without return types