int[] evaluateModel(EnsembleModel<Label> randomForest, AddressSet testPositive,
			AddressSet testNegative, AddressSet errors, int preBytes, int initialBytes,
			TaskMonitor monitor) throws CancelledException {
		GThreadPool threadPool = GThreadPool.getSharedThreadPool(RANDOM_FOREST_TRAINING_THREADPOOL);
		long start = System.nanoTime();
		monitor.setMessage(
			"Evaluating model (step 1 of 2; " + testPositive.getNumAddresses() + " addresses)");
		ConcurrentQBuilder<Address, Boolean> evalBuilder = new ConcurrentQBuilder<>();
		ConcurrentQ<Address, Boolean> evalQ = evalBuilder.setThreadPool(threadPool)
				.setCollectResults(true)
				.setMonitor(monitor)
				.build(new EnsembleEvaluatorCallback(randomForest, program, preBytes, initialBytes,
					params.getIncludeBitFeatures(), RandomForestFunctionFinderPlugin.FUNC_START));
		evalQ.add(testPositive.getAddresses(true));
		int[] confusionMatrix = new int[4];
		try {
			Collection<QResult<Address, Boolean>> results = evalQ.waitForResults();
			updateConfusionMatrix(results, confusionMatrix,
				RandomForestFunctionFinderPlugin.FUNC_START, errors);
		}
		catch (Exception e) {
			monitor.checkCancelled();
			Msg.error(this,
				"Exception while evaluating model on known function starts: " + e.getMessage());
		}
		monitor.setMessage(
			"Evaluating model (step 2 of 2; " + testNegative.getNumAddresses() + " addresses)");
		evalQ = evalBuilder.setThreadPool(threadPool)
				.setCollectResults(true)
				.setMonitor(monitor)
				.build(new EnsembleEvaluatorCallback(randomForest, program, preBytes, initialBytes,
					params.getIncludeBitFeatures(), RandomForestFunctionFinderPlugin.NON_START));

		evalQ.add(testNegative.getAddresses(true));
		try {
			Collection<QResult<Address, Boolean>> results = evalQ.waitForResults();
			updateConfusionMatrix(results, confusionMatrix,
				RandomForestFunctionFinderPlugin.NON_START, errors);
		}
		catch (Exception e) {
			monitor.checkCancelled();
			Msg.error(this,
				"Exception while evaluating model on known function interiors: " + e.getMessage());
		}
		long end = System.nanoTime();
		Msg.info(this,
			String.format("Evaluation time: %g seconds", (end - start) / NANOSECONDS_PER_SECOND));
		return confusionMatrix;
	}
Evaluates a model

@param randomForest model to evaluate
@param testPositive test set of function entries
@param testNegative test set of function interiors
@param errors set to place addresses with classifier errors
@param preBytes number of bytes before entries
@param initialBytes number of bytes before interiors
@param monitor task monitor
@return confusion matrix
@throws CancelledException if monitor is canceled
Ghidra/Extensions/MachineLearning/src/main/java/ghidra/machinelearning/functionfinding/RandomForestTrainingTask.java