protected void backfit(Instances trainSet, double alpha, RegressionTree[] grove, double[][] rtPreds,
			double[] residualTrain) {
		Map<Integer, Integer> bagIndices = new HashMap<>();
		List<Integer> oobIndices = new ArrayList<>();
		Sampling.createBootstrapSample(trainSet, bagIndices, oobIndices);
		Instances bag = new Instances(trainSet.getAttributes(), trainSet.getTargetAttribute(), bagIndices.size());
		for (Integer idx : bagIndices.keySet()) {
			int weight = bagIndices.get(idx);
			Instance instance = trainSet.get(idx).clone();
			instance.setWeight(weight);
			bag.add(instance);
		}

		RegressionTreeLearner rtLearner = new RegressionTreeLearner();
		rtLearner.setConstructionMode(Mode.ALPHA_LIMITED);
		rtLearner.setAlpha(alpha);

		double prevRMSE = evalRMSE(oobIndices, residualTrain);
		for (;;) {
			for (int iter = 0; iter < grove.length; iter++) {
				int treeIdx = (iter + grove.length - 1) % grove.length;

				double[] treePreds = rtPreds[treeIdx];
				for (int i = 0; i < residualTrain.length; i++) {
					residualTrain[i] += treePreds[i];
					trainSet.get(i).setTarget(residualTrain[i]);
				}

				RegressionTree rt = rtLearner.build(bag);
				grove[treeIdx] = rt;

				for (int i = 0; i < residualTrain.length; i++) {
					double pred = rt.regress(trainSet.get(i));
					treePreds[i] = pred;
					residualTrain[i] -= pred;
				}
			}

			double currRMSE = evalRMSE(oobIndices, residualTrain);
			if (currRMSE == 0 || (prevRMSE - currRMSE) / prevRMSE <= 0.002) {
				break;
			} else {
				prevRMSE = currRMSE;
			}
		}

	}