1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package de.fu_berlin.ties.classify;
23
24 import java.io.File;
25 import java.util.Set;
26
27 import org.apache.commons.lang.ArrayUtils;
28 import org.apache.commons.lang.builder.ToStringBuilder;
29
30 import de.fu_berlin.ties.ContextMap;
31 import de.fu_berlin.ties.ProcessingException;
32 import de.fu_berlin.ties.TiesConfiguration;
33 import de.fu_berlin.ties.classify.feature.FeatureTransformer;
34 import de.fu_berlin.ties.classify.feature.FeatureVector;
35 import de.fu_berlin.ties.filter.TrainableFilter;
36 import de.fu_berlin.ties.util.Util;
37
38 /***
39 * A meta classifier combines several layers of classifiers. For each layer
40 * (except the last one), there is a "judge" that decides whether or not the
41 * decision of this classifier is likely to be correct. If the judge decides
42 * that it is likely to be wrong, the next layer is invoked to correct the
43 * decision.
44 *
45 * @author Christian Siefkes
46 * @version $Revision: 1.3 $, $Date: 2004/12/09 18:09:14 $, $Author: siefkes $
47 */
48 class MetaClassifier extends TrainableClassifier {
49
50 /***
51 * Key used to store the contexts of the inner classifiers.
52 */
53 private static final String KEY_INNER_CONTEXTS = "inner-context";
54
55 /***
56 * Key used to store the prediction distributions returned by the inner
57 * classifiers.
58 */
59 private static final String KEY_INNER_DISTS = "inner-dist";
60
61 /***
62 * Key used to store the contexts of the judges.
63 */
64 private static final String KEY_JUDGE_CONTEXTS = "judge-context";
65
66 /***
67 * Key used to store the prediction distributions returned by jthe udges.
68 */
69 private static final String KEY_JUDGE_DISTS = "judge-dist";
70
71 /***
72 * Key used to store the decisions made by the judges.
73 */
74 private static final String KEY_JUDGE_DECISIONS = "judge-decision";
75
76
77 /***
78 * The array of inner classifiers managed by this instance.
79 */
80 private final TrainableClassifier[] inner;
81
82 /***
83 * The array of judges managed by this instance.
84 */
85 private final TrainableClassifier[] judges;
86
87 /***
88 * Creates a new instance.
89 *
90 * @param allValidClasses the set of all valid classes; the first member
91 * of this set is considered as the "background" class, all further members
92 * are considered as "foreground" classes
93 * @param trans the last transformer in the transformer chain to use, or
94 * <code>null</code> if no feature transformers should be used
95 * @param runDirectory optional run directory passed to inner classifiers
96 * of the {@link ExternalClassifier} type
97 * @param innerSpec the specification used to initialize the inner
98 * classifiers, passed to the
99 * {@link TrainableClassifier#createClassifier(Set, File,
100 * FeatureTransformer, String[], TiesConfiguration)} factory method
101 * @param conf used to configure this instance and the inner classifiers
102 * @throws ProcessingException if an error occurred while creating this
103 * classifier or one of the wrapped classifiers
104 */
105 public MetaClassifier(Set<String> allValidClasses,
106 FeatureTransformer trans, final File runDirectory,
107 final String[] innerSpec, TiesConfiguration conf)
108 throws ProcessingException {
109 this(allValidClasses, trans, runDirectory, innerSpec,
110 conf.getInt("classifier.meta.layers"),
111 conf.getStringArray("classifier.meta.judge"), conf);
112 }
113
114 /***
115 * Creates a new instance.
116 *
117 * @param allValidClasses the set of all valid classes; the first member
118 * of this set is considered as the "background" class, all further members
119 * are considered as "foreground" classes
120 * @param trans the last transformer in the transformer chain to use, or
121 * <code>null</code> if no feature transformers should be used
122 * @param runDirectory optional run directory passed to inner classifiers
123 * of the {@link ExternalClassifier} type
124 * @param innerSpec the specification used to initialize the inner
125 * classifiers, passed to the
126 * {@link TrainableClassifier#createClassifier(Set, File,
127 * FeatureTransformer, String[], TiesConfiguration)} factory method
128 * @param layers the number of layers to use, must be at least one
129 * @param judgeSpec the specification used to initialize the judges,
130 * passed to the {@link TrainableClassifier#createClassifier(Set, File,
131 * FeatureTransformer, String[], TiesConfiguration)} factory method
132 * @param conf used to configure this instance as well as the inner
133 * classifiers and judges
134 * @throws ProcessingException if an error occurred while creating this
135 * classifier or one of the wrapped classifiers
136 */
137 public MetaClassifier(Set<String> allValidClasses,
138 FeatureTransformer trans, final File runDirectory,
139 final String[] innerSpec, final int layers,
140 final String[] judgeSpec, TiesConfiguration conf)
141 throws ProcessingException {
142 super(allValidClasses, trans, conf);
143
144 if (layers < 1) {
145 throw new IllegalArgumentException(
146 "MetaClassifier requires at least 1 layer instead of "
147 + layers);
148 }
149
150
151 inner = new TrainableClassifier[layers];
152 for (int i = 0; i < inner.length; i++) {
153
154
155 inner[i] = TrainableClassifier.createClassifier(allValidClasses,
156 runDirectory, null, innerSpec, conf);
157
158 }
159
160
161 judges = new TrainableClassifier[layers - 1];
162 for (int i = 0; i < judges.length; i++) {
163
164
165 judges[i] = TrainableClassifier.createClassifier(
166 TrainableFilter.BOOLEAN_CLASSES, runDirectory, null,
167 judgeSpec, conf);
168
169 }
170 }
171
172 /***
173 * {@inheritDoc}
174 */
175 protected PredictionDistribution doClassify(FeatureVector features,
176 Set candidateClasses, ContextMap context)
177 throws ProcessingException {
178
179 PredictionDistribution[] innerDists =
180 new PredictionDistribution[inner.length];
181 ContextMap[] innerContexts = new ContextMap[inner.length];
182 PredictionDistribution[] judgeDists =
183 new PredictionDistribution[judges.length];
184 ContextMap[] judgeContexts = new ContextMap[judges.length];
185 boolean[] judgements = new boolean[judges.length];
186
187 PredictionDistribution innerDist = null;
188 PredictionDistribution judgeDist;
189 ContextMap innerContext, judgeContext;
190 boolean judgement = false;
191 int i = 0;
192
193
194
195 while ((i < inner.length) || !judgement) {
196 if (i > 0) {
197 Util.LOG.debug("Invoking layer " + i
198 + " classifier since decision of previous layer was "
199 + "judged to be incorrect");
200 }
201
202
203 innerContext = new ContextMap();
204 innerDist = inner[i].doClassify(features, candidateClasses,
205 innerContext);
206 innerContexts[i] = innerContext;
207 innerDists[i] = innerDist;
208
209
210 if (i < judges.length) {
211 judgeContext = new ContextMap();
212 judgeDist = judges[i].doClassify(features,
213 judges[i].getAllClasses(), judgeContext);
214 judgeContexts[i] = judgeContext;
215 judgeDists[i] = judgeDist;
216
217
218 judgement = Util.asBoolean(judgeDist.best().getType());
219 judgements[i] = judgement;
220 }
221
222 i++;
223 }
224
225
226 context.put(KEY_INNER_CONTEXTS, innerContexts);
227 context.put(KEY_INNER_DISTS, innerDists);
228 context.put(KEY_JUDGE_CONTEXTS, judgeContexts);
229 context.put(KEY_JUDGE_DISTS, judgeDists);
230 context.put(KEY_JUDGE_DECISIONS, judgements);
231
232
233 return innerDist;
234 }
235
236 /***
237 * {@inheritDoc}
238 */
239 protected void doTrain(FeatureVector features, String targetClass,
240 ContextMap context) throws ProcessingException {
241
242 }
243
244 /***
245 * {@inheritDoc}
246 */
247 public void reset() throws ProcessingException {
248
249 }
250
251 /***
252 * Returns a string representation of this object.
253 *
254 * @return a textual representation
255 */
256 public String toString() {
257 return new ToStringBuilder(this)
258 .appendSuper(super.toString())
259 .append("inner classifiers", ArrayUtils.toString(inner))
260 .append("judges", ArrayUtils.toString(judges))
261 .toString();
262 }
263
264 /***
265 * {@inheritDoc}
266 */
267 protected boolean trainOnErrorHook(final PredictionDistribution predDist,
268 final FeatureVector features, final String targetClass,
269 final Set candidateClasses, final ContextMap context)
270 throws ProcessingException {
271
272 return false;
273 }
274
275 }