/* * SVM.NET Library * Copyright (C) 2008 Matthew Johnson * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.IO; namespace SVM { /// /// Encapsulates an SVM Model. /// [Serializable] public class Model { private Parameter _parameter; private int _numberOfClasses; private int _supportVectorCount; private Node[][] _supportVectors; private double[][] _supportVectorCoefficients; private double[] _rho; private double[] _pairwiseProbabilityA; private double[] _pairwiseProbabilityB; private int[] _classLabels; private int[] _numberOfSVPerClass; internal Model() { } /// /// Parameter object. /// public Parameter Parameter { get { return _parameter; } set { _parameter = value; } } /// /// Number of classes in the model. /// public int NumberOfClasses { get { return _numberOfClasses; } set { _numberOfClasses = value; } } /// /// Total number of support vectors. /// public int SupportVectorCount { get { return _supportVectorCount; } set { _supportVectorCount = value; } } /// /// The support vectors. /// public Node[][] SupportVectors { get { return _supportVectors; } set { _supportVectors = value; } } /// /// The coefficients for the support vectors. /// public double[][] SupportVectorCoefficients { get { return _supportVectorCoefficients; } set { _supportVectorCoefficients = value; } } /// /// Rho values. /// public double[] Rho { get { return _rho; } set { _rho = value; } } /// /// First pairwise probability. /// public double[] PairwiseProbabilityA { get { return _pairwiseProbabilityA; } set { _pairwiseProbabilityA = value; } } /// /// Second pairwise probability. /// public double[] PairwiseProbabilityB { get { return _pairwiseProbabilityB; } set { _pairwiseProbabilityB = value; } } // for classification only /// /// Class labels. /// public int[] ClassLabels { get { return _classLabels; } set { _classLabels = value; } } /// /// Number of support vectors per class. /// public int[] NumberOfSVPerClass { get { return _numberOfSVPerClass; } set { _numberOfSVPerClass = value; } } /// /// Reads a Model from the provided file. /// /// The name of the file containing the Model /// the Model public static Model Read(string filename) { FileStream input = File.OpenRead(filename); try { return Read(input); } finally { input.Close(); } } public static Model Read(Stream stream) { return Read(new StreamReader(stream)); } /// /// Reads a Model from the provided stream. /// /// The stream from which to read the Model. /// the Model public static Model Read(TextReader input) { TemporaryCulture.Start(); // read parameters Model model = new Model(); Parameter param = new Parameter(); model.Parameter = param; model.Rho = null; model.PairwiseProbabilityA = null; model.PairwiseProbabilityB = null; model.ClassLabels = null; model.NumberOfSVPerClass = null; bool headerFinished = false; while (!headerFinished) { string line = input.ReadLine(); string cmd, arg; int splitIndex = line.IndexOf(' '); if (splitIndex >= 0) { cmd = line.Substring(0, splitIndex); arg = line.Substring(splitIndex + 1); } else { cmd = line; arg = ""; } // arg = arg.ToLower(); (transforms double NaN or Infinity values to incorrect format [gkronber]) int i, n; switch (cmd) { case "svm_type": param.SvmType = (SvmType)Enum.Parse(typeof(SvmType), arg.ToUpper()); break; case "kernel_type": param.KernelType = (KernelType)Enum.Parse(typeof(KernelType), arg.ToUpper()); break; case "degree": param.Degree = int.Parse(arg); break; case "gamma": param.Gamma = double.Parse(arg); break; case "coef0": param.Coefficient0 = double.Parse(arg); break; case "nr_class": model.NumberOfClasses = int.Parse(arg); break; case "total_sv": model.SupportVectorCount = int.Parse(arg); break; case "rho": n = model.NumberOfClasses * (model.NumberOfClasses - 1) / 2; model.Rho = new double[n]; string[] rhoParts = arg.Split(); for (i = 0; i < n; i++) model.Rho[i] = double.Parse(rhoParts[i]); break; case "label": n = model.NumberOfClasses; model.ClassLabels = new int[n]; string[] labelParts = arg.Split(); for (i = 0; i < n; i++) model.ClassLabels[i] = int.Parse(labelParts[i]); break; case "probA": n = model.NumberOfClasses * (model.NumberOfClasses - 1) / 2; model.PairwiseProbabilityA = new double[n]; string[] probAParts = arg.Split(); for (i = 0; i < n; i++) model.PairwiseProbabilityA[i] = double.Parse(probAParts[i]); break; case "probB": n = model.NumberOfClasses * (model.NumberOfClasses - 1) / 2; model.PairwiseProbabilityB = new double[n]; string[] probBParts = arg.Split(); for (i = 0; i < n; i++) model.PairwiseProbabilityB[i] = double.Parse(probBParts[i]); break; case "nr_sv": n = model.NumberOfClasses; model.NumberOfSVPerClass = new int[n]; string[] nrsvParts = arg.Split(); for (i = 0; i < n; i++) model.NumberOfSVPerClass[i] = int.Parse(nrsvParts[i]); break; case "SV": headerFinished = true; break; default: throw new Exception("Unknown text in model file"); } } // read sv_coef and SV int m = model.NumberOfClasses - 1; int l = model.SupportVectorCount; model.SupportVectorCoefficients = new double[m][]; for (int i = 0; i < m; i++) { model.SupportVectorCoefficients[i] = new double[l]; } model.SupportVectors = new Node[l][]; for (int i = 0; i < l; i++) { string[] parts = input.ReadLine().Trim().Split(); for (int k = 0; k < m; k++) model.SupportVectorCoefficients[k][i] = double.Parse(parts[k]); int n = parts.Length - m; model.SupportVectors[i] = new Node[n]; for (int j = 0; j < n; j++) { string[] nodeParts = parts[m + j].Split(':'); model.SupportVectors[i][j] = new Node(); model.SupportVectors[i][j].Index = int.Parse(nodeParts[0]); model.SupportVectors[i][j].Value = double.Parse(nodeParts[1]); } } TemporaryCulture.Stop(); return model; } /// /// Writes a model to the provided filename. This will overwrite any previous data in the file. /// /// The desired file /// The Model to write public static void Write(string filename, Model model) { FileStream stream = File.Open(filename, FileMode.Create); try { Write(stream, model); } finally { stream.Close(); } } /// /// Writes a model to the provided stream. /// /// The output stream /// The model to write public static void Write(Stream stream, Model model) { TemporaryCulture.Start(); StreamWriter output = new StreamWriter(stream); Parameter param = model.Parameter; output.Write("svm_type " + param.SvmType + Environment.NewLine); output.Write("kernel_type " + param.KernelType + Environment.NewLine); if (param.KernelType == KernelType.POLY) output.Write("degree " + param.Degree.ToString("r") + Environment.NewLine); if (param.KernelType == KernelType.POLY || param.KernelType == KernelType.RBF || param.KernelType == KernelType.SIGMOID) output.Write("gamma " + param.Gamma.ToString("r") + Environment.NewLine); if (param.KernelType == KernelType.POLY || param.KernelType == KernelType.SIGMOID) output.Write("coef0 " + param.Coefficient0.ToString("r") + Environment.NewLine); int nr_class = model.NumberOfClasses; int l = model.SupportVectorCount; output.Write("nr_class " + nr_class + Environment.NewLine); output.Write("total_sv " + l + Environment.NewLine); { output.Write("rho"); for (int i = 0; i < nr_class * (nr_class - 1) / 2; i++) output.Write(" " + model.Rho[i].ToString("r")); output.Write(Environment.NewLine); } if (model.ClassLabels != null) { output.Write("label"); for (int i = 0; i < nr_class; i++) output.Write(" " + model.ClassLabels[i]); output.Write(Environment.NewLine); } if (model.PairwiseProbabilityA != null) // regression has probA only { output.Write("probA"); for (int i = 0; i < nr_class * (nr_class - 1) / 2; i++) output.Write(" " + model.PairwiseProbabilityA[i].ToString("r")); output.Write(Environment.NewLine); } if (model.PairwiseProbabilityB != null) { output.Write("probB"); for (int i = 0; i < nr_class * (nr_class - 1) / 2; i++) output.Write(" " + model.PairwiseProbabilityB[i].ToString("r")); output.Write(Environment.NewLine); } if (model.NumberOfSVPerClass != null) { output.Write("nr_sv"); for (int i = 0; i < nr_class; i++) output.Write(" " + model.NumberOfSVPerClass[i]); output.Write(Environment.NewLine); } output.WriteLine("SV"); double[][] sv_coef = model.SupportVectorCoefficients; Node[][] SV = model.SupportVectors; for (int i = 0; i < l; i++) { for (int j = 0; j < nr_class - 1; j++) output.Write(sv_coef[j][i].ToString("r") + " "); Node[] p = SV[i]; if (p.Length == 0) { output.WriteLine(); continue; } if (param.KernelType == KernelType.PRECOMPUTED) output.Write("0:{0}", (int)p[0].Value); else { output.Write("{0}:{1}", p[0].Index, p[0].Value.ToString("r")); for (int j = 1; j < p.Length; j++) output.Write(" {0}:{1}", p[j].Index, p[j].Value.ToString("r")); } output.WriteLine(); } output.Flush(); TemporaryCulture.Stop(); } } }