CVParameterSelection.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 20k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    CVParameterSelection.java
  18.  *    Copyright (C) 1999 Len Trigg
  19.  *
  20.  */
  21. package weka.classifiers.meta;
  22. import weka.classifiers.Evaluation;
  23. import weka.classifiers.Classifier;
  24. import weka.classifiers.rules.ZeroR;
  25. import java.io.*;
  26. import java.util.*;
  27. import weka.core.*;
  28. /**
  29.  * Class for performing parameter selection by cross-validation for any
  30.  * classifier. For more information, see<p>
  31.  *
  32.  * R. Kohavi (1995). <i>Wrappers for Performance
  33.  * Enhancement and Oblivious Decision Graphs</i>. PhD
  34.  * Thesis. Department of Computer Science, Stanford University. <p>
  35.  *
  36.  * Valid options are:<p>
  37.  *
  38.  * -D <br>
  39.  * Turn on debugging output.<p>
  40.  *
  41.  * -W classname <br>
  42.  * Specify the full class name of classifier to perform cross-validation
  43.  * selection on.<p>
  44.  *
  45.  * -X num <br>
  46.  * Number of folds used for cross validation (default 10). <p>
  47.  *
  48.  * -S seed <br>
  49.  * Random number seed (default 1).<p>
  50.  *
  51.  * -P "N 1 5 10" <br>
  52.  * Sets an optimisation parameter for the classifier with name -N,
  53.  * lower bound 1, upper bound 5, and 10 optimisation steps.
  54.  * The upper bound may be the character 'A' or 'I' to substitute 
  55.  * the number of attributes or instances in the training data,
  56.  * respectively.
  57.  * This parameter may be supplied more than once to optimise over
  58.  * several classifier options simultaneously. <p>
  59.  *
  60.  * Options after -- are passed to the designated sub-classifier. <p>
  61.  *
  62.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  63.  * @version $Revision: 1.16 $ 
  64. */
  65. public class CVParameterSelection extends Classifier 
  66.   implements OptionHandler, Summarizable {
  67.   /*
  68.    * A data structure to hold values associated with a single
  69.    * cross-validation search parameter
  70.    */
  71.   protected class CVParameter {
  72.     /**  Char used to identify the option of interest */
  73.     private char m_ParamChar;    
  74.     /**  Lower bound for the CV search */
  75.     private double m_Lower;      
  76.     /**  Upper bound for the CV search */
  77.     private double m_Upper;      
  78.     /**  Increment during the search */
  79.     private double m_Steps;      
  80.     /**  The parameter value with the best performance */
  81.     private double m_ParamValue; 
  82.     /**  True if the parameter should be added at the end of the argument list */
  83.     private boolean m_AddAtEnd;  
  84.     /**  True if the parameter should be rounded to an integer */
  85.     private boolean m_RoundParam;
  86.     /**
  87.      * Constructs a CVParameter.
  88.      */
  89.     public CVParameter(String param) throws Exception {
  90.      
  91.       // Tokenize the string into it's parts
  92.       StreamTokenizer st = new StreamTokenizer(new StringReader(param));
  93.       if (st.nextToken() != StreamTokenizer.TT_WORD) {
  94. throw new Exception("CVParameter " + param 
  95.     + ": Character parameter identifier expected");
  96.       }
  97.       m_ParamChar = st.sval.charAt(0);
  98.       if (st.nextToken() != StreamTokenizer.TT_NUMBER) {
  99. throw new Exception("CVParameter " + param 
  100.     + ": Numeric lower bound expected");
  101.       }
  102.       m_Lower = st.nval;
  103.       if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
  104. m_Upper = st.nval;
  105. if (m_Upper < m_Lower) {
  106.   throw new Exception("CVParameter " + param
  107.       + ": Upper bound is less than lower bound");
  108. }
  109.       } else if (st.ttype == StreamTokenizer.TT_WORD) {
  110. if (st.sval.toUpperCase().charAt(0) == 'A') {
  111.   m_Upper = m_Lower - 1;
  112. } else if (st.sval.toUpperCase().charAt(0) == 'I') {
  113.   m_Upper = m_Lower - 2;
  114. } else {
  115.   throw new Exception("CVParameter " + param 
  116.       + ": Upper bound must be numeric, or 'A' or 'N'");
  117. }
  118.       } else {
  119. throw new Exception("CVParameter " + param 
  120.       + ": Upper bound must be numeric, or 'A' or 'N'");
  121.       }
  122.       if (st.nextToken() != StreamTokenizer.TT_NUMBER) {
  123. throw new Exception("CVParameter " + param 
  124.     + ": Numeric number of steps expected");
  125.       }
  126.       m_Steps = st.nval;
  127.       if (st.nextToken() == StreamTokenizer.TT_WORD) {
  128. if (st.sval.toUpperCase().charAt(0) == 'R') {
  129.   m_RoundParam = true;
  130. }
  131.       }
  132.     }
  133.     /**
  134.      * Returns a CVParameter as a string.
  135.      */
  136.     public String toString() {
  137.       String result = m_ParamChar + " " + m_Lower + " ";
  138.       switch ((int)(m_Lower - m_Upper + 0.5)) {
  139.       case 1:
  140. result += "A";
  141. break;
  142.       case 2:
  143. result += "I";
  144. break;
  145.       default:
  146. result += m_Upper;
  147. break;
  148.       }
  149.       result += " " + m_Steps;
  150.       if (m_RoundParam) {
  151. result += " R";
  152.       }
  153.       return result;
  154.     }
  155.   }
  156.   /** The generated base classifier */
  157.   protected Classifier m_Classifier = new weka.classifiers.rules.ZeroR();
  158.   /**
  159.    * The base classifier options (not including those being set
  160.    * by cross-validation)
  161.    */
  162.   protected String [] m_ClassifierOptions;
  163.   /** The set of all classifier options as determined by cross-validation */
  164.   protected String [] m_BestClassifierOptions;
  165.   /** The cross-validated performance of the best options */
  166.   protected double m_BestPerformance;
  167.   /** The set of parameters to cross-validate over */
  168.   protected FastVector m_CVParams;
  169.   /** The number of attributes in the data */
  170.   protected int m_NumAttributes;
  171.   /** The number of instances in a training fold */
  172.   protected int m_TrainFoldSize;
  173.   
  174.   /** The number of folds used in cross-validation */
  175.   protected int m_NumFolds = 10;
  176.   /** Random number seed */
  177.   protected int m_Seed = 1;
  178.   /** Debugging mode, gives extra output if true */
  179.   protected boolean m_Debug;
  180.   /**
  181.    * Create the options array to pass to the classifier. The parameter
  182.    * values and positions are taken from m_ClassifierOptions and
  183.    * m_CVParams.
  184.    *
  185.    * @return the options array
  186.    */
  187.   protected String [] createOptions() {
  188.     
  189.     String [] options = new String [m_ClassifierOptions.length 
  190.    + 2 * m_CVParams.size()];
  191.     int start = 0, end = options.length;
  192.     // Add the cross-validation parameters and their values
  193.     for (int i = 0; i < m_CVParams.size(); i++) {
  194.       CVParameter cvParam = (CVParameter)m_CVParams.elementAt(i);
  195.       double paramValue = cvParam.m_ParamValue;
  196.       if (cvParam.m_RoundParam) {
  197. paramValue = (double)((int) (paramValue + 0.5));
  198.       }
  199.       if (cvParam.m_AddAtEnd) {
  200. options[--end] = "" + 
  201. Utils.doubleToString(paramValue,4);
  202. options[--end] = "-" + cvParam.m_ParamChar;
  203.       } else {
  204. options[start++] = "-" + cvParam.m_ParamChar;
  205. options[start++] = "" 
  206. + Utils.doubleToString(paramValue,4);
  207.       }
  208.     }
  209.     // Add the static parameters
  210.     System.arraycopy(m_ClassifierOptions, 0,
  211.      options, start,
  212.      m_ClassifierOptions.length);
  213.     return options;
  214.   }
  215.   /**
  216.    * Finds the best parameter combination. (recursive for each parameter
  217.    * being optimised).
  218.    *
  219.    * @param depth the index of the parameter to be optimised at this level
  220.    * @exception Exception if an error occurs
  221.    */
  222.   protected void findParamsByCrossValidation(int depth, Instances trainData)
  223.     throws Exception {
  224.     if (depth < m_CVParams.size()) {
  225.       CVParameter cvParam = (CVParameter)m_CVParams.elementAt(depth);
  226.       double upper;
  227.       switch ((int)(cvParam.m_Lower - cvParam.m_Upper + 0.5)) {
  228.       case 1:
  229. upper = m_NumAttributes;
  230. break;
  231.       case 2:
  232. upper = m_TrainFoldSize;
  233. break;
  234.       default:
  235. upper = cvParam.m_Upper;
  236. break;
  237.       }
  238.       double increment = (upper - cvParam.m_Lower) / (cvParam.m_Steps - 1);
  239.       for(cvParam.m_ParamValue = cvParam.m_Lower; 
  240.   cvParam.m_ParamValue <= upper; 
  241.   cvParam.m_ParamValue += increment) {
  242. findParamsByCrossValidation(depth + 1, trainData);
  243.       }
  244.     } else {
  245.       
  246.       Evaluation evaluation = new Evaluation(trainData);
  247.       // Set the classifier options
  248.       String [] options = createOptions();
  249.       if (m_Debug) {
  250. System.err.print("Setting options for " 
  251.  + m_Classifier.getClass().getName() + ":");
  252. for (int i = 0; i < options.length; i++) {
  253.   System.err.print(" " + options[i]);
  254. }
  255. System.err.println("");
  256.       }
  257.       ((OptionHandler)m_Classifier).setOptions(options);
  258.       for (int j = 0; j < m_NumFolds; j++) {
  259. Instances train = trainData.trainCV(m_NumFolds, j);
  260. Instances test = trainData.testCV(m_NumFolds, j);
  261. m_Classifier.buildClassifier(train);
  262. evaluation.setPriors(train);
  263. evaluation.evaluateModel(m_Classifier, test);
  264.       }
  265.       double error = evaluation.errorRate();
  266.       if (m_Debug) {
  267. System.err.println("Cross-validated error rate: " 
  268.    + Utils.doubleToString(error, 6, 4));
  269.       }
  270.       if ((m_BestPerformance == -99) || (error < m_BestPerformance)) {
  271. m_BestPerformance = error;
  272. m_BestClassifierOptions = createOptions();
  273.       }
  274.     }
  275.   }
  276.   /**
  277.    * Returns an enumeration describing the available options.
  278.    *
  279.    * @return an enumeration of all the available options.
  280.    */
  281.   public Enumeration listOptions() {
  282.     Vector newVector = new Vector(5);
  283.     newVector.addElement(new Option(
  284.       "tTurn on debugging output.",
  285.       "D", 0, "-D"));
  286.     newVector.addElement(new Option(
  287.       "tFull name of classifier to perform parameter selection on.n"
  288.       + "teg: weka.classifiers.bayes.NaiveBayes",
  289.       "W", 1, "-W <classifier class name>"));
  290.     newVector.addElement(new Option(
  291.       "tNumber of folds used for cross validation (default 10).",
  292.       "X", 1, "-X <number of folds>"));
  293.     newVector.addElement(new Option(
  294.       "tClassifier parameter options.n"
  295.       + "teg: "N 1 5 10" Sets an optimisation parameter for then"
  296.       + "tclassifier with name -N, with lower bound 1, upper boundn"
  297.       + "t5, and 10 optimisation steps. The upper bound may be then"
  298.       + "tcharacter 'A' or 'I' to substitute the number ofn"
  299.       + "tattributes or instances in the training data,n"
  300.       + "trespectively. This parameter may be supplied more thann"
  301.       + "tonce to optimise over several classifier optionsn"
  302.       + "tsimultaneously.",
  303.       "P", 1, "-P <classifier parameter>"));
  304.     newVector.addElement(new Option(
  305.       "tSets the random number seed (default 1).",
  306.       "S", 1, "-S <random number seed>"));
  307.     if ((m_Classifier != null) &&
  308. (m_Classifier instanceof OptionHandler)) {
  309.       newVector.addElement(new Option("",
  310.         "", 0,
  311. "nOptions specific to sub-classifier "
  312.         + m_Classifier.getClass().getName()
  313. + ":n(use -- to signal start of sub-classifier options)"));
  314.       Enumeration enum = ((OptionHandler)m_Classifier).listOptions();
  315.       while (enum.hasMoreElements()) {
  316. newVector.addElement(enum.nextElement());
  317.       }
  318.     }
  319.     return newVector.elements();
  320.   }
  321.   /**
  322.    * Parses a given list of options. Valid options are:<p>
  323.    *
  324.    * -D <br>
  325.    * Turn on debugging output.<p>
  326.    *
  327.    * -W classname <br>
  328.    * Specify the full class name of classifier to perform cross-validation
  329.    * selection on.<p>
  330.    *
  331.    * -X num <br>
  332.    * Number of folds used for cross validation (default 10). <p>
  333.    *
  334.    * -S seed <br>
  335.    * Random number seed (default 1).<p>
  336.    *
  337.    * -P "N 1 5 10" <br>
  338.    * Sets an optimisation parameter for the classifier with name -N,
  339.    * lower bound 1, upper bound 5, and 10 optimisation steps.
  340.    * The upper bound may be the character 'A' or 'I' to substitute 
  341.    * the number of attributes or instances in the training data,
  342.    * respectively.
  343.    * This parameter may be supplied more than once to optimise over
  344.    * several classifier options simultaneously. <p>
  345.    *
  346.    * Options after -- are passed to the designated sub-classifier. <p>
  347.    *
  348.    * @param options the list of options as an array of strings
  349.    * @exception Exception if an option is not supported
  350.    */
  351.   public void setOptions(String[] options) throws Exception {
  352.     
  353.     setDebug(Utils.getFlag('D', options));
  354.     String foldsString = Utils.getOption('X', options);
  355.     if (foldsString.length() != 0) {
  356.       setNumFolds(Integer.parseInt(foldsString));
  357.     } else {
  358.       setNumFolds(10);
  359.     }
  360.     String randomString = Utils.getOption('S', options);
  361.     if (randomString.length() != 0) {
  362.       setSeed(Integer.parseInt(randomString));
  363.     } else {
  364.       setSeed(1);
  365.     }
  366.     String cvParam;
  367.     m_CVParams = new FastVector();
  368.     do {
  369.       cvParam = Utils.getOption('P', options);
  370.       if (cvParam.length() != 0) {
  371. addCVParameter(cvParam);
  372.       }
  373.     } while (cvParam.length() != 0);
  374.     if (m_CVParams.size() == 0) {
  375.       throw new Exception("A parameter specifier must be given with"
  376.   + " the -P option.");
  377.     }
  378.     String classifierName = Utils.getOption('W', options);
  379.     if (classifierName.length() == 0) {
  380.       throw new Exception("A classifier must be specified with"
  381.   + " the -W option.");
  382.     }
  383.     setClassifier(Classifier.forName(classifierName,
  384.      Utils.partitionOptions(options)));
  385.     if (!(m_Classifier instanceof OptionHandler)) {
  386.       throw new Exception("Base classifier must accept options");
  387.     }
  388.   }
  389.   /**
  390.    * Gets the current settings of the Classifier.
  391.    *
  392.    * @return an array of strings suitable for passing to setOptions
  393.    */
  394.   public String [] getOptions() {
  395.     String [] classifierOptions = new String [0];
  396.     if ((m_Classifier != null) && 
  397. (m_Classifier instanceof OptionHandler)) {
  398.       classifierOptions = ((OptionHandler)m_Classifier).getOptions();
  399.     }
  400.     int current = 0;
  401.     String [] options = new String [classifierOptions.length + 8];
  402.     if (m_CVParams != null) {
  403.       options = new String [m_CVParams.size() * 2 + options.length];
  404.       for (int i = 0; i < m_CVParams.size(); i++) {
  405. options[current++] = "-P"; options[current++] = "" + getCVParameter(i);
  406.       }
  407.     }
  408.     if (getDebug()) {
  409.       options[current++] = "-D";
  410.     }
  411.     options[current++] = "-X"; options[current++] = "" + getNumFolds();
  412.     options[current++] = "-S"; options[current++] = "" + getSeed();
  413.     if (getClassifier() != null) {
  414.       options[current++] = "-W";
  415.       options[current++] = getClassifier().getClass().getName();
  416.     }
  417.     options[current++] = "--";
  418.     System.arraycopy(classifierOptions, 0, options, current, 
  419.      classifierOptions.length);
  420.     current += classifierOptions.length;
  421.     while (current < options.length) {
  422.       options[current++] = "";
  423.     }
  424.     return options;
  425.   }
  426.   /**
  427.    * Generates the classifier.
  428.    *
  429.    * @param instances set of instances serving as training data 
  430.    * @exception Exception if the classifier has not been generated successfully
  431.    */
  432.   public void buildClassifier(Instances instances) throws Exception {
  433.     if (instances.checkForStringAttributes()) {
  434.       throw new UnsupportedAttributeTypeException("Cannot handle string attributes!");
  435.     }
  436.     Instances trainData = new Instances(instances);
  437.     trainData.deleteWithMissingClass();
  438.     if (trainData.numInstances() == 0) {
  439.       throw new Exception("No training instances without missing class.");
  440.     }
  441.     if (trainData.numInstances() < m_NumFolds) {
  442.       throw new Exception("Number of training instances smaller than number of folds.");
  443.     }
  444.     // Check whether there are any parameters to optimize
  445.     if (m_CVParams == null) {
  446.        m_Classifier.buildClassifier(trainData);
  447.        return;
  448.     }
  449.     trainData.randomize(new Random(m_Seed));
  450.     if (trainData.classAttribute().isNominal()) {
  451.       trainData.stratify(m_NumFolds);
  452.     }
  453.     m_BestPerformance = -99;
  454.     m_BestClassifierOptions = null;
  455.     m_NumAttributes = trainData.numAttributes();
  456.     m_TrainFoldSize = trainData.trainCV(m_NumFolds, 0).numInstances();
  457.     
  458.     // Set up m_ClassifierOptions -- take getOptions() and remove
  459.     // those being optimised.
  460.     m_ClassifierOptions = ((OptionHandler)m_Classifier).getOptions();
  461.     for (int i = 0; i < m_CVParams.size(); i++) {
  462.       Utils.getOption(((CVParameter)m_CVParams.elementAt(i)).m_ParamChar,
  463.       m_ClassifierOptions);
  464.     }
  465.     findParamsByCrossValidation(0, trainData);
  466.     String [] options = (String [])m_BestClassifierOptions.clone();
  467.     ((OptionHandler)m_Classifier).setOptions(options);
  468.     m_Classifier.buildClassifier(trainData);
  469.   }
  470.   /**
  471.    * Predicts the class value for the given test instance.
  472.    *
  473.    * @param instance the instance to be classified
  474.    * @return the predicted class value
  475.    * @exception Exception if an error occurred during the prediction
  476.    */
  477.   public double classifyInstance(Instance instance) throws Exception {
  478.     
  479.     return m_Classifier.classifyInstance(instance);
  480.   }
  481.   /**
  482.    * Sets the seed for random number generation.
  483.    *
  484.    * @param seed the random number seed
  485.    */
  486.   public void setSeed(int seed) {
  487.     
  488.     m_Seed = seed;;
  489.   }
  490.   /**
  491.    * Gets the random number seed.
  492.    * 
  493.    * @return the random number seed
  494.    */
  495.   public int getSeed() {
  496.     return m_Seed;
  497.   }
  498.   /**
  499.    * Adds a scheme parameter to the list of parameters to be set
  500.    * by cross-validation
  501.    *
  502.    * @param cvParam the string representation of a scheme parameter. The
  503.    * format is: <br>
  504.    * param_char lower_bound upper_bound increment <br>
  505.    * eg to search a parameter -P from 1 to 10 by increments of 2: <br>
  506.    * P 1 10 2 <br>
  507.    * @exception Exception if the parameter specifier is of the wrong format
  508.    */
  509.   public void addCVParameter(String cvParam) throws Exception {
  510.     CVParameter newCV = new CVParameter(cvParam);
  511.     
  512.     m_CVParams.addElement(newCV);
  513.   }
  514.   /**
  515.    * Gets the scheme paramter with the given index.
  516.    */
  517.   public String getCVParameter(int index) {
  518.     if (m_CVParams.size() <= index) {
  519.       return "";
  520.     }
  521.     return ((CVParameter)m_CVParams.elementAt(index)).toString();
  522.   }
  523.   /**
  524.    * Sets debugging mode
  525.    *
  526.    * @param debug true if debug output should be printed
  527.    */
  528.   public void setDebug(boolean debug) {
  529.     m_Debug = debug;
  530.   }
  531.   /**
  532.    * Gets whether debugging is turned on
  533.    *
  534.    * @return true if debugging output is on
  535.    */
  536.   public boolean getDebug() {
  537.     return m_Debug;
  538.   }
  539.   /**
  540.    * Get the number of folds used for cross-validation.
  541.    *
  542.    * @return the number of folds used for cross-validation.
  543.    */
  544.   public int getNumFolds() {
  545.     
  546.     return m_NumFolds;
  547.   }
  548.   
  549.   /**
  550.    * Set the number of folds used for cross-validation.
  551.    *
  552.    * @param newNumFolds the number of folds used for cross-validation.
  553.    */
  554.   public void setNumFolds(int newNumFolds) {
  555.     
  556.     m_NumFolds = newNumFolds;
  557.   }
  558.   /**
  559.    * Set the classifier for boosting. 
  560.    *
  561.    * @param newClassifier the Classifier to use.
  562.    */
  563.   public void setClassifier(Classifier newClassifier) {
  564.     m_Classifier = newClassifier;
  565.   }
  566.   /**
  567.    * Get the classifier used as the classifier
  568.    *
  569.    * @return the classifier used as the classifier
  570.    */
  571.   public Classifier getClassifier() {
  572.     return m_Classifier;
  573.   }
  574.  
  575.   /**
  576.    * Returns description of the cross-validated classifier.
  577.    *
  578.    * @return description of the cross-validated classifier as a string
  579.    */
  580.   public String toString() {
  581.     if (m_BestClassifierOptions == null)
  582.       return "CVParameterSelection: No model built yet.";
  583.     String result = "Cross-validated Parameter selection.n"
  584.     + "Classifier: " + m_Classifier.getClass().getName() + "n";
  585.     try {
  586.       for (int i = 0; i < m_CVParams.size(); i++) {
  587. CVParameter cvParam = (CVParameter)m_CVParams.elementAt(i);
  588. result += "Cross-validation Parameter: '-" 
  589.   + cvParam.m_ParamChar + "'"
  590.   + " ranged from " + cvParam.m_Lower 
  591.   + " to ";
  592. switch ((int)(cvParam.m_Lower - cvParam.m_Upper + 0.5)) {
  593. case 1:
  594.   result += m_NumAttributes;
  595.   break;
  596. case 2:
  597.   result += m_TrainFoldSize;
  598.   break;
  599. default:
  600.   result += cvParam.m_Upper;
  601.   break;
  602. }
  603. result += " with " + cvParam.m_Steps + " stepsn";
  604.       }
  605.     } catch (Exception ex) {
  606.       result += ex.getMessage();
  607.     }
  608.     result += "Classifier Options: "
  609.       + Utils.joinOptions(m_BestClassifierOptions)
  610.       + "nn" + m_Classifier.toString();
  611.     return result;
  612.   }
  613.   public String toSummaryString() {
  614.     String result = "Selected values: "
  615.       + Utils.joinOptions(m_BestClassifierOptions);
  616.     return result + 'n';
  617.   }
  618.   
  619.   /**
  620.    * Main method for testing this class.
  621.    *
  622.    * @param argv the options
  623.    */
  624.   public static void main(String [] argv) {
  625.     try {
  626.       System.out.println(Evaluation.evaluateModel(new CVParameterSelection(), 
  627.   argv));
  628.     } catch (Exception e) {
  629.       System.err.println(e.getMessage());
  630.     }
  631.   }
  632. }
  633.