001/*
002 * Copyright (c) 2015-2020, Oracle and/or its affiliates. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.tribuo.common.tree;
018
019import com.oracle.labs.mlrg.olcut.config.PropertyException;
020import org.tribuo.Output;
021import org.tribuo.ensemble.BaggingTrainer;
022import org.tribuo.ensemble.EnsembleCombiner;
023
024import java.util.logging.Logger;
025
026/**
027 * A trainer which produces an Extremely Randomized Tree Ensemble.
028 * <p>
029 * Extremely Randomized Trees are similar to Random Forests, but they add an extra element of randomness in that
030 * the split points for features are also chosen randomly. As with Random Forests, feature subsampling is available at
031 * each of the nodes.
032 * An exception will be thrown if the inner trainer is not a decision tree trainer or if random splitting is turned off.
033 * <p>
034 * See:
035 * <pre>
036 * P. Geurts, D. Ernst, L. Wehenkel.
037 * "Extremely Randomized Trees"
038 * March 2006. <a href="https://link.springer.com/article/10.1007/s10994-006-6226-1">PDF</a>
039 * </pre>
040 *
041 */
042public class ExtraTreesTrainer<T extends Output<T>> extends BaggingTrainer<T> {
043
044    private static final Logger logger = Logger.getLogger(ExtraTreesTrainer.class.getName());
045
046    /**
047     * For the configuration system.
048     */
049    private ExtraTreesTrainer() { }
050
051    /**
052     * Constructs an ExtraTreesTrainer with the default seed {@link org.tribuo.Trainer#DEFAULT_SEED}.
053     * <p>
054     * Throws {@link PropertyException} if the trainer is not set to use random split points.
055     * @param trainer The tree trainer.
056     * @param combiner The combining function for the ensemble.
057     * @param numMembers The number of ensemble members to train.
058     */
059    public ExtraTreesTrainer(DecisionTreeTrainer<T> trainer, EnsembleCombiner<T> combiner, int numMembers) {
060        super(trainer,combiner,numMembers);
061        postConfig();
062    }
063
064    /**
065     * Constructs an ExtraTreesTrainer with the supplied seed, trainer, combining function and number of members.
066     * <p>
067     * Throws {@link PropertyException} if the trainer is not set to use random split points.
068     * @param trainer The tree trainer.
069     * @param combiner The combining function for the ensemble.
070     * @param numMembers The number of ensemble members to train.
071     * @param seed The RNG seed.
072     */
073    public ExtraTreesTrainer(DecisionTreeTrainer<T> trainer, EnsembleCombiner<T> combiner, int numMembers, long seed) {
074        super(trainer,combiner,numMembers,seed);
075        postConfig();
076    }
077
078    @Override
079    public void postConfig() {
080        super.postConfig();
081        if (!(innerTrainer instanceof DecisionTreeTrainer)) {
082            throw new PropertyException("","innerTrainer","ExtraTreesTrainer requires a decision tree innerTrainer");
083        }
084        DecisionTreeTrainer<T> t = (DecisionTreeTrainer<T>) innerTrainer;
085        if (!t.getUseRandomSplitPoints()) {
086            throw new PropertyException("","innerTrainer","ExtraTreesTrainer requires that the decision tree " +
087                    "innerTrainer have random split points turned on.");
088        }
089    }
090
091    @Override
092    protected String ensembleName() {
093        return "extra-trees-ensemble";
094    }
095
096    @Override
097    public String toString() {
098        StringBuilder buffer = new StringBuilder();
099
100        buffer.append("ExtraTreesTrainer(");
101        buffer.append("innerTrainer=");
102        buffer.append(innerTrainer.toString());
103        buffer.append(",combiner=");
104        buffer.append(combiner.toString());
105        buffer.append(",numMembers=");
106        buffer.append(numMembers);
107        buffer.append(",seed=");
108        buffer.append(seed);
109        buffer.append(")");
110
111        return buffer.toString();
112    }
113
114}
115