1  /*


2  * SVM.NET Library


3  * Copyright (C) 2008 Matthew Johnson


4  *


5  * This program is free software: you can redistribute it and/or modify


6  * it under the terms of the GNU General Public License as published by


7  * the Free Software Foundation, either version 3 of the License, or


8  * (at your option) any later version.


9  *


10  * This program is distributed in the hope that it will be useful,


11  * but WITHOUT ANY WARRANTY; without even the implied warranty of


12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the


13  * GNU General Public License for more details.


14  *


15  * You should have received a copy of the GNU General Public License


16  * along with this program. If not, see <http://www.gnu.org/licenses/>.


17  */


18 


19 


20 


21  using System;


22  using System.IO;


23 


24  namespace SVM {


25  /// <summary>


26  /// Encapsulates an SVM Model.


27  /// </summary>


28  [Serializable]


29  public class Model {


30  private Parameter _parameter;


31  private int _numberOfClasses;


32  private int _supportVectorCount;


33  private int[] _supportVectorIndizes;


34  private Node[][] _supportVectors;


35  private double[][] _supportVectorCoefficients;


36  private double[] _rho;


37  private double[] _pairwiseProbabilityA;


38  private double[] _pairwiseProbabilityB;


39 


40  private int[] _classLabels;


41  private int[] _numberOfSVPerClass;


42 


43  internal Model() {


44  }


45 


46  /// <summary>


47  /// Parameter object.


48  /// </summary>


49  public Parameter Parameter {


50  get {


51  return _parameter;


52  }


53  set {


54  _parameter = value;


55  }


56  }


57 


58  /// <summary>


59  /// Number of classes in the model.


60  /// </summary>


61  public int NumberOfClasses {


62  get {


63  return _numberOfClasses;


64  }


65  set {


66  _numberOfClasses = value;


67  }


68  }


69 


70  /// <summary>


71  /// Total number of support vectors.


72  /// </summary>


73  public int SupportVectorCount {


74  get {


75  return _supportVectorCount;


76  }


77  set {


78  _supportVectorCount = value;


79  }


80  }


81 


82  /// <summary>


83  /// Indizes of support vectors identified in the training.


84  /// </summary>


85  public int[] SupportVectorIndizes {


86  get {


87  return _supportVectorIndizes;


88  }


89  set {


90  _supportVectorIndizes = value;


91  }


92  }


93 


94  /// <summary>


95  /// The support vectors.


96  /// </summary>


97  public Node[][] SupportVectors {


98  get {


99  return _supportVectors;


100  }


101  set {


102  _supportVectors = value;


103  }


104  }


105 


106  /// <summary>


107  /// The coefficients for the support vectors.


108  /// </summary>


109  public double[][] SupportVectorCoefficients {


110  get {


111  return _supportVectorCoefficients;


112  }


113  set {


114  _supportVectorCoefficients = value;


115  }


116  }


117 


118  /// <summary>


119  /// Rho values.


120  /// </summary>


121  public double[] Rho {


122  get {


123  return _rho;


124  }


125  set {


126  _rho = value;


127  }


128  }


129 


130  /// <summary>


131  /// First pairwise probability.


132  /// </summary>


133  public double[] PairwiseProbabilityA {


134  get {


135  return _pairwiseProbabilityA;


136  }


137  set {


138  _pairwiseProbabilityA = value;


139  }


140  }


141 


142  /// <summary>


143  /// Second pairwise probability.


144  /// </summary>


145  public double[] PairwiseProbabilityB {


146  get {


147  return _pairwiseProbabilityB;


148  }


149  set {


150  _pairwiseProbabilityB = value;


151  }


152  }


153 


154  // for classification only


155 


156  /// <summary>


157  /// Class labels.


158  /// </summary>


159  public int[] ClassLabels {


160  get {


161  return _classLabels;


162  }


163  set {


164  _classLabels = value;


165  }


166  }


167 


168  /// <summary>


169  /// Number of support vectors per class.


170  /// </summary>


171  public int[] NumberOfSVPerClass {


172  get {


173  return _numberOfSVPerClass;


174  }


175  set {


176  _numberOfSVPerClass = value;


177  }


178  }


179 


180  /// <summary>


181  /// Reads a Model from the provided file.


182  /// </summary>


183  /// <param name="filename">The name of the file containing the Model</param>


184  /// <returns>the Model</returns>


185  public static Model Read(string filename) {


186  FileStream input = File.OpenRead(filename);


187  try {


188  return Read(input);


189  }


190  finally {


191  input.Close();


192  }


193  }


194 


195  public static Model Read(Stream stream) {


196  return Read(new StreamReader(stream));


197  }


198 


199  /// <summary>


200  /// Reads a Model from the provided stream.


201  /// </summary>


202  /// <param name="input">The stream from which to read the Model.</param>


203  /// <returns>the Model</returns>


204  public static Model Read(TextReader input) {


205  TemporaryCulture.Start();


206 


207  // read parameters


208 


209  Model model = new Model();


210  Parameter param = new Parameter();


211  model.Parameter = param;


212  model.Rho = null;


213  model.PairwiseProbabilityA = null;


214  model.PairwiseProbabilityB = null;


215  model.ClassLabels = null;


216  model.NumberOfSVPerClass = null;


217  model.SupportVectorIndizes = new int[0];


218 


219  bool headerFinished = false;


220  while (!headerFinished) {


221  string line = input.ReadLine();


222  string cmd, arg;


223  int splitIndex = line.IndexOf(' ');


224  if (splitIndex >= 0) {


225  cmd = line.Substring(0, splitIndex);


226  arg = line.Substring(splitIndex + 1);


227  } else {


228  cmd = line;


229  arg = "";


230  }


231  // arg = arg.ToLower(); (transforms double NaN or Infinity values to incorrect format [gkronber])


232 


233  int i, n;


234  switch (cmd) {


235  case "svm_type":


236  param.SvmType = (SvmType)Enum.Parse(typeof(SvmType), arg.ToUpper());


237  break;


238 


239  case "kernel_type":


240  param.KernelType = (KernelType)Enum.Parse(typeof(KernelType), arg.ToUpper());


241  break;


242 


243  case "degree":


244  param.Degree = int.Parse(arg);


245  break;


246 


247  case "gamma":


248  param.Gamma = double.Parse(arg);


249  break;


250 


251  case "coef0":


252  param.Coefficient0 = double.Parse(arg);


253  break;


254 


255  case "nr_class":


256  model.NumberOfClasses = int.Parse(arg);


257  break;


258 


259  case "total_sv":


260  model.SupportVectorCount = int.Parse(arg);


261  break;


262 


263  case "rho":


264  n = model.NumberOfClasses * (model.NumberOfClasses  1) / 2;


265  model.Rho = new double[n];


266  string[] rhoParts = arg.Split();


267  for (i = 0; i < n; i++)


268  model.Rho[i] = double.Parse(rhoParts[i]);


269  break;


270 


271  case "label":


272  n = model.NumberOfClasses;


273  model.ClassLabels = new int[n];


274  string[] labelParts = arg.Split();


275  for (i = 0; i < n; i++)


276  model.ClassLabels[i] = int.Parse(labelParts[i]);


277  break;


278 


279  case "probA":


280  n = model.NumberOfClasses * (model.NumberOfClasses  1) / 2;


281  model.PairwiseProbabilityA = new double[n];


282  string[] probAParts = arg.Split();


283  for (i = 0; i < n; i++)


284  model.PairwiseProbabilityA[i] = double.Parse(probAParts[i]);


285  break;


286 


287  case "probB":


288  n = model.NumberOfClasses * (model.NumberOfClasses  1) / 2;


289  model.PairwiseProbabilityB = new double[n];


290  string[] probBParts = arg.Split();


291  for (i = 0; i < n; i++)


292  model.PairwiseProbabilityB[i] = double.Parse(probBParts[i]);


293  break;


294 


295  case "nr_sv":


296  n = model.NumberOfClasses;


297  model.NumberOfSVPerClass = new int[n];


298  string[] nrsvParts = arg.Split();


299  for (i = 0; i < n; i++)


300  model.NumberOfSVPerClass[i] = int.Parse(nrsvParts[i]);


301  break;


302 


303  case "SV":


304  headerFinished = true;


305  break;


306 


307  default:


308  throw new Exception("Unknown text in model file");


309  }


310  }


311 


312  // read sv_coef and SV


313 


314  int m = model.NumberOfClasses  1;


315  int l = model.SupportVectorCount;


316  model.SupportVectorCoefficients = new double[m][];


317  for (int i = 0; i < m; i++) {


318  model.SupportVectorCoefficients[i] = new double[l];


319  }


320  model.SupportVectors = new Node[l][];


321 


322  for (int i = 0; i < l; i++) {


323  string[] parts = input.ReadLine().Trim().Split();


324 


325  for (int k = 0; k < m; k++)


326  model.SupportVectorCoefficients[k][i] = double.Parse(parts[k]);


327  int n = parts.Length  m;


328  model.SupportVectors[i] = new Node[n];


329  for (int j = 0; j < n; j++) {


330  string[] nodeParts = parts[m + j].Split(':');


331  model.SupportVectors[i][j] = new Node();


332  model.SupportVectors[i][j].Index = int.Parse(nodeParts[0]);


333  model.SupportVectors[i][j].Value = double.Parse(nodeParts[1]);


334  }


335  }


336 


337  TemporaryCulture.Stop();


338 


339  return model;


340  }


341 


342  /// <summary>


343  /// Writes a model to the provided filename. This will overwrite any previous data in the file.


344  /// </summary>


345  /// <param name="filename">The desired file</param>


346  /// <param name="model">The Model to write</param>


347  public static void Write(string filename, Model model) {


348  FileStream stream = File.Open(filename, FileMode.Create);


349  try {


350  Write(stream, model);


351  }


352  finally {


353  stream.Close();


354  }


355  }


356 


357  /// <summary>


358  /// Writes a model to the provided stream.


359  /// </summary>


360  /// <param name="stream">The output stream</param>


361  /// <param name="model">The model to write</param>


362  public static void Write(Stream stream, Model model) {


363  TemporaryCulture.Start();


364 


365  StreamWriter output = new StreamWriter(stream);


366 


367  Parameter param = model.Parameter;


368 


369  output.Write("svm_type " + param.SvmType + Environment.NewLine);


370  output.Write("kernel_type " + param.KernelType + Environment.NewLine);


371 


372  if (param.KernelType == KernelType.POLY)


373  output.Write("degree " + param.Degree.ToString("r") + Environment.NewLine);


374 


375  if (param.KernelType == KernelType.POLY  param.KernelType == KernelType.RBF  param.KernelType == KernelType.SIGMOID)


376  output.Write("gamma " + param.Gamma.ToString("r") + Environment.NewLine);


377 


378  if (param.KernelType == KernelType.POLY  param.KernelType == KernelType.SIGMOID)


379  output.Write("coef0 " + param.Coefficient0.ToString("r") + Environment.NewLine);


380 


381  int nr_class = model.NumberOfClasses;


382  int l = model.SupportVectorCount;


383  output.Write("nr_class " + nr_class + Environment.NewLine);


384  output.Write("total_sv " + l + Environment.NewLine);


385 


386  {


387  output.Write("rho");


388  for (int i = 0; i < nr_class * (nr_class  1) / 2; i++)


389  output.Write(" " + model.Rho[i].ToString("r"));


390  output.Write(Environment.NewLine);


391  }


392 


393  if (model.ClassLabels != null) {


394  output.Write("label");


395  for (int i = 0; i < nr_class; i++)


396  output.Write(" " + model.ClassLabels[i]);


397  output.Write(Environment.NewLine);


398  }


399 


400  if (model.PairwiseProbabilityA != null)


401  // regression has probA only


402  {


403  output.Write("probA");


404  for (int i = 0; i < nr_class * (nr_class  1) / 2; i++)


405  output.Write(" " + model.PairwiseProbabilityA[i].ToString("r"));


406  output.Write(Environment.NewLine);


407  }


408  if (model.PairwiseProbabilityB != null) {


409  output.Write("probB");


410  for (int i = 0; i < nr_class * (nr_class  1) / 2; i++)


411  output.Write(" " + model.PairwiseProbabilityB[i].ToString("r"));


412  output.Write(Environment.NewLine);


413  }


414 


415  if (model.NumberOfSVPerClass != null) {


416  output.Write("nr_sv");


417  for (int i = 0; i < nr_class; i++)


418  output.Write(" " + model.NumberOfSVPerClass[i].ToString("r"));


419  output.Write(Environment.NewLine);


420  }


421 


422  output.WriteLine("SV");


423  double[][] sv_coef = model.SupportVectorCoefficients;


424  Node[][] SV = model.SupportVectors;


425 


426  for (int i = 0; i < l; i++) {


427  for (int j = 0; j < nr_class  1; j++)


428  output.Write(sv_coef[j][i].ToString("r") + " ");


429 


430  Node[] p = SV[i];


431  if (p.Length == 0) {


432  output.WriteLine();


433  continue;


434  }


435  if (param.KernelType == KernelType.PRECOMPUTED)


436  output.Write("0:{0}", (int)p[0].Value);


437  else {


438  output.Write("{0}:{1}", p[0].Index, p[0].Value.ToString("r"));


439  for (int j = 1; j < p.Length; j++)


440  output.Write(" {0}:{1}", p[j].Index, p[j].Value.ToString("r"));


441  }


442  output.WriteLine();


443  }


444 


445  output.Flush();


446 


447  TemporaryCulture.Stop();


448  }


449  }


450  } 
