001    package org.maltparser.ml.lib;
002    
003    import java.io.BufferedOutputStream;
004    import java.io.BufferedReader;
005    import java.io.FileOutputStream;
006    import java.io.IOException;
007    import java.io.InputStream;
008    import java.io.InputStreamReader;
009    import java.io.ObjectInputStream;
010    import java.io.ObjectOutputStream;
011    import java.io.PrintStream;
012    import java.util.LinkedHashMap;
013    
014    import liblinear.FeatureNode;
015    import liblinear.Linear;
016    import liblinear.Model;
017    import liblinear.Parameter;
018    import liblinear.Problem;
019    import liblinear.SolverType;
020    
021    import org.maltparser.core.exception.MaltChainedException;
022    import org.maltparser.core.feature.FeatureVector;
023    import org.maltparser.core.helper.NoPrintStream;
024    import org.maltparser.parser.guide.instance.InstanceModel;
025    
026    public class LibLinear extends Lib {
027            
028            public LibLinear(InstanceModel owner, Integer learnerMode) throws MaltChainedException {
029                    super(owner, learnerMode, "liblinear");
030                    if (learnerMode == CLASSIFY) {
031                            try {
032                                ObjectInputStream input = new ObjectInputStream(getInputStreamFromConfigFileEntry(".moo"));
033                                try {
034                                    model = (MaltLibModel)input.readObject();
035                                } finally {
036                                    input.close();
037                                }
038                            } catch (ClassNotFoundException e) {
039                                    throw new LibException("Couldn't load the liblinear model", e);
040                            } catch (Exception e) {
041                                    throw new LibException("Couldn't load the liblinear model", e);
042                            }
043                    }
044    
045            }
046            
047            protected void trainInternal(FeatureVector featureVector) throws MaltChainedException {
048                    try {
049                            if (configLogger.isInfoEnabled()) {
050                                    configLogger.info("Creating Liblinear model "+getFile(".moo").getName()+"\n");
051                            }
052                            Problem problem = readProblem(getInstanceInputStreamReader(".ins"));
053                            final PrintStream out = System.out;
054                            final PrintStream err = System.err;
055                            System.setOut(NoPrintStream.NO_PRINTSTREAM);
056                            System.setErr(NoPrintStream.NO_PRINTSTREAM);
057                            Parameter parameter = getLiblinearParameters();
058                            Model model = Linear.train(problem, parameter);
059                            System.setOut(err);
060                            System.setOut(out);
061                            MaltLiblinearModel xmodel = new MaltLiblinearModel(model, parameter.getSolverType());
062                        ObjectOutputStream output = new ObjectOutputStream (new BufferedOutputStream(new FileOutputStream(getFile(".moo").getAbsolutePath())));
063                    try{
064                      output.writeObject(xmodel);
065                    } finally {
066                      output.close();
067                    }
068                            if (!saveInstanceFiles) {
069                                    getFile(".ins").delete();
070                            }
071                    } catch (OutOfMemoryError e) {
072                            throw new LibException("Out of memory. Please increase the Java heap size (-Xmx<size>). ", e);
073                    } catch (IllegalArgumentException e) {
074                            throw new LibException("The Liblinear learner was not able to redirect Standard Error stream. ", e);
075                    } catch (SecurityException e) {
076                            throw new LibException("The Liblinear learner cannot remove the instance file. ", e);
077                    } catch (IOException e) {
078                            throw new LibException("The Liblinear learner cannot save the model file '"+getFile(".mod").getAbsolutePath()+"'. ", e);
079                    }
080            }
081            
082            protected void trainExternal(FeatureVector featureVector) throws MaltChainedException {
083                    try {           
084                            
085                            if (configLogger.isInfoEnabled()) {
086                                    owner.getGuide().getConfiguration().getConfigLogger().info("Creating liblinear model (external) "+getFile(".mod").getName());
087                            }
088                            binariesInstances2SVMFileFormat(getInstanceInputStreamReader(".ins"), getInstanceOutputStreamWriter(".ins.tmp"));
089                            final String[] params = getLibParamStringArray();
090                            String[] arrayCommands = new String[params.length+3];
091                            int i = 0;
092                            arrayCommands[i++] = pathExternalTrain;
093                            for (; i <= params.length; i++) {
094                                    arrayCommands[i] = params[i-1];
095                            }
096                            arrayCommands[i++] = getFile(".ins.tmp").getAbsolutePath();
097                            arrayCommands[i++] = getFile(".mod").getAbsolutePath();
098                            
099                    if (verbosity == Verbostity.ALL) {
100                            owner.getGuide().getConfiguration().getConfigLogger().info('\n');
101                    }
102                            final Process child = Runtime.getRuntime().exec(arrayCommands);
103                    final InputStream in = child.getInputStream();
104                    final InputStream err = child.getErrorStream();
105                    int c;
106                    while ((c = in.read()) != -1){
107                            if (verbosity == Verbostity.ALL) {
108                                    owner.getGuide().getConfiguration().getConfigLogger().info((char)c);
109                            }
110                    }
111                    while ((c = err.read()) != -1){
112                            if (verbosity == Verbostity.ALL || verbosity == Verbostity.ERROR) {
113                                    owner.getGuide().getConfiguration().getConfigLogger().info((char)c);
114                            }
115                    }
116                if (child.waitFor() != 0) {
117                    owner.getGuide().getConfiguration().getConfigLogger().info(" FAILED ("+child.exitValue()+")");
118                }
119                    in.close();
120                    err.close();
121                            if (configLogger.isInfoEnabled()) {
122                                    configLogger.info("\nSaving Liblinear model "+getFile(".moo").getName()+"\n");
123                            }
124                            MaltLiblinearModel xmodel = new MaltLiblinearModel(getFile(".mod"));
125                        ObjectOutputStream output = new ObjectOutputStream (new BufferedOutputStream(new FileOutputStream(getFile(".moo").getAbsolutePath())));
126                    try{
127                      output.writeObject(xmodel);
128                    } finally {
129                      output.close();
130                    }
131                    if (!saveInstanceFiles) {
132                                    getFile(".ins").delete();
133                                    getFile(".mod").delete();
134                                    getFile(".ins.tmp").delete();
135                    }
136                    if (configLogger.isInfoEnabled()) {
137                            configLogger.info('\n');
138                    }
139                    } catch (InterruptedException e) {
140                             throw new LibException("Learner is interrupted. ", e);
141                    } catch (IllegalArgumentException e) {
142                            throw new LibException("The learner was not able to redirect Standard Error stream. ", e);
143                    } catch (SecurityException e) {
144                            throw new LibException("The learner cannot remove the instance file. ", e);
145                    } catch (IOException e) {
146                            throw new LibException("The learner cannot save the model file '"+getFile(".mod").getAbsolutePath()+"'. ", e);
147                    } catch (OutOfMemoryError e) {
148                            throw new LibException("Out of memory. Please increase the Java heap size (-Xmx<size>). ", e);
149                    }
150            }
151            
152            public void terminate() throws MaltChainedException { 
153                    super.terminate();
154            }
155    
156            public void initLibOptions() {
157                    libOptions = new LinkedHashMap<String, String>();
158                    libOptions.put("s", "4"); // type = SolverType.L2LOSS_SVM_DUAL (default)
159                    libOptions.put("c", "0.1"); // cost = 1 (default)
160                    libOptions.put("e", "0.1"); // epsilon = 0.1 (default)
161                    libOptions.put("B", "-1"); // bias = -1 (default)
162            }
163            
164            public void initAllowedLibOptionFlags() {
165                    allowedLibOptionFlags = "sceB";
166            }
167            
168            private Problem readProblem(InputStreamReader isr) throws MaltChainedException {
169                    Problem problem = new Problem();
170                    final FeatureList featureList = new FeatureList();
171                    
172                    try {
173                            final BufferedReader fp = new BufferedReader(isr);
174                            
175                            problem.bias = -1;
176                            problem.l = getNumberOfInstances();
177                            problem.x = new FeatureNode[problem.l][];
178                            problem.y = new int[problem.l];
179                            int i = 0;
180    
181                            while(true) {
182                                    String line = fp.readLine();
183                                    if(line == null) break;
184                                    int y = binariesInstance(line, featureList);
185                                    if (y == -1) {
186                                            continue;
187                                    }
188                                    try {
189                                            problem.y[i] = y;
190                                            problem.x[i] = new FeatureNode[featureList.size()];
191                                            int p = 0;
192                                    for (int k=0; k < featureList.size(); k++) {
193                                            MaltFeatureNode x = featureList.get(k);
194                                                    problem.x[i][p++] = new FeatureNode(x.getIndex(), x.getValue());
195                                            }
196                                            i++;
197                                    } catch (ArrayIndexOutOfBoundsException e) {
198                                            throw new LibException("Couldn't read liblinear problem from the instance file. ", e);
199                                    }
200    
201                            }
202                            fp.close();
203                            problem.n = featureMap.size();
204                    } catch (IOException e) {
205                            throw new LibException("Cannot read from the instance file. ", e);
206                    }
207                    return problem;
208            }
209            
210            private Parameter getLiblinearParameters() throws MaltChainedException {
211                    Parameter param = new Parameter(SolverType.MCSVM_CS, 0.1, 0.1);
212                    String type = libOptions.get("s");
213                    
214                    if (type.equals("0")) {
215                            param.setSolverType(SolverType.L2R_LR);
216                    } else if (type.equals("1")) {
217                            param.setSolverType(SolverType.L2R_L2LOSS_SVC_DUAL);
218                    } else if (type.equals("2")) {
219                            param.setSolverType(SolverType.L2R_L2LOSS_SVC);
220                    } else if (type.equals("3")) {
221                            param.setSolverType(SolverType.L2R_L1LOSS_SVC_DUAL);
222                    } else if (type.equals("4")) {
223                            param.setSolverType(SolverType.MCSVM_CS);
224                    } else if (type.equals("5")) {
225                            param.setSolverType(SolverType.L1R_L2LOSS_SVC); 
226                    } else if (type.equals("6")) {
227                            param.setSolverType(SolverType.L1R_LR); 
228                    } else if (type.equals("7")) {
229                            param.setSolverType(SolverType.L2R_LR_DUAL);    
230                    } else {
231                            throw new LibException("The liblinear type (-s) is not an integer value between 0 and 4. ");
232                    }
233                    try {
234                            param.setC(Double.valueOf(libOptions.get("c")).doubleValue());
235                    } catch (NumberFormatException e) {
236                            throw new LibException("The liblinear cost (-c) value is not numerical value. ", e);
237                    }
238                    try {
239                            param.setEps(Double.valueOf(libOptions.get("e")).doubleValue());
240                    } catch (NumberFormatException e) {
241                            throw new LibException("The liblinear epsilon (-e) value is not numerical value. ", e);
242                    }
243                    return param;
244            }
245    }