'Iteratively fits the stages.
For each stage it computes the progress (OOB, train score)
and delegates to ``_fit_stage``.
Returns the number of stages fit; might differ from ``n_estimators``
due to early stopping.'

def _fit_stages(self, X, y, y_pred, sample_weight, random_state, X_val, y_val, sample_weight_val, begin_at_stage=0, monitor=None, X_idx_sorted=None):

    n_samples = X.shape[0]
    do_oob = (self.subsample < 1.0)
    sample_mask = np.ones((n_samples,), dtype=np.bool)
    n_inbag = max(1, int((self.subsample * n_samples)))
    loss_ = self.loss_
    if ((self.min_weight_fraction_leaf != 0.0) and (sample_weight is not None)):
        min_weight_leaf = (self.min_weight_fraction_leaf * np.sum(sample_weight))
    else:
        min_weight_leaf = 0.0
    if self.verbose:
        verbose_reporter = VerboseReporter(self.verbose)
        verbose_reporter.init(self, begin_at_stage)
    X_csc = (csc_matrix(X) if issparse(X) else None)
    X_csr = (csr_matrix(X) if issparse(X) else None)
    if (self.n_iter_no_change is not None):
        loss_history = (np.ones(self.n_iter_no_change) * np.inf)
        y_val_pred_iter = self._staged_decision_function(X_val)
    i = begin_at_stage
    for i in range(begin_at_stage, self.n_estimators):
        if do_oob:
            sample_mask = _random_sample_mask(n_samples, n_inbag, random_state)
            old_oob_score = loss_(y[(~ sample_mask)], y_pred[(~ sample_mask)], sample_weight[(~ sample_mask)])
        y_pred = self._fit_stage(i, X, y, y_pred, sample_weight, sample_mask, random_state, X_idx_sorted, X_csc, X_csr)
        if do_oob:
            self.train_score_[i] = loss_(y[sample_mask], y_pred[sample_mask], sample_weight[sample_mask])
            self.oob_improvement_[i] = (old_oob_score - loss_(y[(~ sample_mask)], y_pred[(~ sample_mask)], sample_weight[(~ sample_mask)]))
        else:
            self.train_score_[i] = loss_(y, y_pred, sample_weight)
        if (self.verbose > 0):
            verbose_reporter.update(i, self)
        if (monitor is not None):
            early_stopping = monitor(i, self, locals())
            if early_stopping:
                break
        if (self.n_iter_no_change is not None):
            validation_loss = loss_(y_val, next(y_val_pred_iter), sample_weight_val)
            if np.any(((validation_loss + self.tol) < loss_history)):
                loss_history[(i % len(loss_history))] = validation_loss
            else:
                break
    return (i + 1)
