Add SVM low level machine learning algorithm, prepare for NLP text classification.
This commit is contained in:
parent
6bf552b20a
commit
a7e0280b1d
|
|
@ -13,9 +13,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.2.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
126
BotSharp.MachineLearning.UnitTest/SVM/ClassificationTests.cs
Normal file
126
BotSharp.MachineLearning.UnitTest/SVM/ClassificationTests.cs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SVM;
|
||||
using SVM.BotSharp.MachineLearning;
|
||||
using SVM.BotSharp.MachineLearningTest;
|
||||
|
||||
namespace BotSharp.MachineLearning.UnitTest.SVM
|
||||
{
|
||||
[TestClass]
|
||||
public class ClassificationTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestTwoClass()
|
||||
{
|
||||
SvmType[] svmTypes = new SvmType[]{SvmType.C_SVC, SvmType.NU_SVC};
|
||||
KernelType[] kernelTypes = new KernelType[]{KernelType.LINEAR, KernelType.POLY, KernelType.RBF, KernelType.SIGMOID};
|
||||
|
||||
foreach (SvmType svm in svmTypes)
|
||||
{
|
||||
foreach (KernelType kernel in kernelTypes)
|
||||
{
|
||||
double score = testTwoClassModel(100, svm, kernel);
|
||||
|
||||
Assert.AreEqual(1, score, .01, string.Format("SVM {0} with Kernel {1} did not train correctly", svm, kernel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestTwoClassProbability()
|
||||
{
|
||||
SvmType[] svmTypes = new SvmType[] { SvmType.C_SVC, SvmType.NU_SVC };
|
||||
KernelType[] kernelTypes = new KernelType[] { KernelType.LINEAR, KernelType.POLY, KernelType.RBF, KernelType.SIGMOID };
|
||||
|
||||
foreach (SvmType svm in svmTypes)
|
||||
{
|
||||
foreach (KernelType kernel in kernelTypes)
|
||||
{
|
||||
double score = testTwoClassModel(100, svm, kernel, true);
|
||||
|
||||
Assert.AreEqual(1, score, .01, string.Format("SVM {0} with Kernel {1} did not train correctly", svm, kernel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestMulticlass()
|
||||
{
|
||||
SvmType[] svmTypes = new SvmType[] { SvmType.C_SVC, SvmType.NU_SVC };
|
||||
KernelType[] kernelTypes = new KernelType[] { KernelType.LINEAR, KernelType.POLY, KernelType.RBF, KernelType.SIGMOID };
|
||||
|
||||
foreach (SvmType svm in svmTypes)
|
||||
{
|
||||
foreach (KernelType kernel in kernelTypes)
|
||||
{
|
||||
double score = testMulticlassModel(8, 100, svm, kernel);
|
||||
|
||||
Assert.AreEqual(1, score, .1, string.Format("SVM {0} with Kernel {1} did not train correctly", svm, kernel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestMulticlassProbability()
|
||||
{
|
||||
SvmType[] svmTypes = new SvmType[] { SvmType.C_SVC, SvmType.NU_SVC };
|
||||
KernelType[] kernelTypes = new KernelType[] { KernelType.LINEAR, KernelType.POLY, KernelType.RBF, KernelType.SIGMOID };
|
||||
|
||||
foreach (SvmType svm in svmTypes)
|
||||
{
|
||||
foreach (KernelType kernel in kernelTypes)
|
||||
{
|
||||
double score = testMulticlassModel(8, 100, svm, kernel, true);
|
||||
|
||||
Assert.AreEqual(1, score, .1, string.Format("SVM {0} with Kernel {1} did not train correctly", svm, kernel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double testTwoClassModel(int count, SvmType svm, KernelType kernel, bool probability = false, string outputFile = null)
|
||||
{
|
||||
Problem train = SVMUtilities.CreateTwoClassProblem(count);
|
||||
Parameter param = new Parameter();
|
||||
RangeTransform transform = RangeTransform.Compute(train);
|
||||
Problem scaled = transform.Scale(train);
|
||||
param.Gamma = .5;
|
||||
param.SvmType = svm;
|
||||
param.KernelType = kernel;
|
||||
param.Probability = probability;
|
||||
if (svm == SvmType.C_SVC)
|
||||
{
|
||||
param.Weights[-1] = 1;
|
||||
param.Weights[1] = 1;
|
||||
}
|
||||
|
||||
Model model = Training.Train(scaled, param);
|
||||
|
||||
Problem test = SVMUtilities.CreateTwoClassProblem(count, false);
|
||||
scaled = transform.Scale(test);
|
||||
return Prediction.Predict(scaled, outputFile, model, false);
|
||||
}
|
||||
|
||||
private double testMulticlassModel(int numberOfClasses, int count, SvmType svm, KernelType kernel, bool probability = false, string outputFile = null)
|
||||
{
|
||||
Problem train = SVMUtilities.CreateMulticlassProblem(numberOfClasses, count);
|
||||
Parameter param = new Parameter();
|
||||
RangeTransform transform = RangeTransform.Compute(train);
|
||||
Problem scaled = transform.Scale(train);
|
||||
param.Gamma = 1.0 / 3;
|
||||
param.SvmType = svm;
|
||||
param.KernelType = kernel;
|
||||
param.Probability = probability;
|
||||
if (svm == SvmType.C_SVC)
|
||||
{
|
||||
for (int i = 0; i < numberOfClasses; i++)
|
||||
param.Weights[i] = 1;
|
||||
}
|
||||
|
||||
Model model = Training.Train(scaled, param);
|
||||
|
||||
Problem test = SVMUtilities.CreateMulticlassProblem(numberOfClasses, count, false);
|
||||
scaled = transform.Scale(test);
|
||||
return Prediction.Predict(scaled, outputFile, model, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
76
BotSharp.MachineLearning.UnitTest/SVM/IOTests.cs
Normal file
76
BotSharp.MachineLearning.UnitTest/SVM/IOTests.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SVM;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SVM.BotSharp.MachineLearning;
|
||||
using SVM.BotSharp.MachineLearningTest;
|
||||
|
||||
namespace BotSharp.MachineLearning.UnitTest.SVM
|
||||
{
|
||||
[TestClass]
|
||||
public class IOTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ReadProblem()
|
||||
{
|
||||
Problem expected = SVMUtilities.CreateTwoClassProblem(100);
|
||||
Problem actual = Problem.Read("train0.problem");
|
||||
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteProblem()
|
||||
{
|
||||
Problem prob = SVMUtilities.CreateTwoClassProblem(100);
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
using (StreamReader input = new StreamReader("train0.problem"))
|
||||
{
|
||||
Problem.Write(stream, prob);
|
||||
string expected = input.ReadToEnd().Replace("\r\n", "\n");
|
||||
string actual = Encoding.ASCII.GetString(stream.ToArray());
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadModel()
|
||||
{
|
||||
Problem train = SVMUtilities.CreateTwoClassProblem(100);
|
||||
Parameter param = new Parameter();
|
||||
RangeTransform transform = RangeTransform.Compute(train);
|
||||
Problem scaled = transform.Scale(train);
|
||||
param.KernelType = KernelType.LINEAR;
|
||||
|
||||
Training.SetRandomSeed(SVMUtilities.TRAINING_SEED);
|
||||
Model expected = Training.Train(scaled, param);
|
||||
Model actual = Model.Read("svm0.model");
|
||||
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteModel()
|
||||
{
|
||||
Problem train = SVMUtilities.CreateTwoClassProblem(100);
|
||||
Parameter param = new Parameter();
|
||||
RangeTransform transform = RangeTransform.Compute(train);
|
||||
Problem scaled = transform.Scale(train);
|
||||
param.KernelType = KernelType.LINEAR;
|
||||
|
||||
Training.SetRandomSeed(SVMUtilities.TRAINING_SEED);
|
||||
Model model = Training.Train(scaled, param);
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
using (StreamReader input = new StreamReader("svm0.model"))
|
||||
{
|
||||
Model.Write(stream, model);
|
||||
string expected = input.ReadToEnd().Replace("\r\n", "\n");
|
||||
string actual = Encoding.ASCII.GetString(stream.ToArray());
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
BotSharp.MachineLearning.UnitTest/SVM/RegressionTests.cs
Normal file
48
BotSharp.MachineLearning.UnitTest/SVM/RegressionTests.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SVM;
|
||||
using SVM.BotSharp.MachineLearning;
|
||||
using SVM.BotSharp.MachineLearningTest;
|
||||
|
||||
namespace BotSharp.MachineLearning.UnitTest.SVM
|
||||
{
|
||||
[TestClass]
|
||||
public class RegressionTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestRegression()
|
||||
{
|
||||
SvmType[] svmTypes = new SvmType[] { SvmType.NU_SVR, SvmType.EPSILON_SVR };
|
||||
// LINEAR kernel is pretty horrible for regression
|
||||
KernelType[] kernelTypes = new KernelType[] { KernelType.LINEAR, KernelType.RBF, KernelType.SIGMOID };
|
||||
|
||||
foreach (SvmType svm in svmTypes)
|
||||
{
|
||||
foreach (KernelType kernel in kernelTypes)
|
||||
{
|
||||
double error = testRegressionModel(100, svm, kernel);
|
||||
|
||||
Assert.AreEqual(0, error, 2, string.Format("SVM {0} with Kernel {1} did not train correctly", svm, kernel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double testRegressionModel(int count, SvmType svm, KernelType kernel, string outputFile = null)
|
||||
{
|
||||
Problem train = SVMUtilities.CreateRegressionProblem(count);
|
||||
Parameter param = new Parameter();
|
||||
RangeTransform transform = RangeTransform.Compute(train);
|
||||
Problem scaled = transform.Scale(train);
|
||||
param.Gamma = 1.0 / 2;
|
||||
param.SvmType = svm;
|
||||
param.KernelType = kernel;
|
||||
param.Degree = 2;
|
||||
|
||||
Model model = Training.Train(scaled, param);
|
||||
|
||||
Problem test = SVMUtilities.CreateRegressionProblem(count, false);
|
||||
scaled = transform.Scale(test);
|
||||
return Prediction.Predict(scaled, outputFile, model, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,22 @@
|
|||
using BotSharp.MachineLearning.CRFLite.Decoder;
|
||||
/*
|
||||
* BotSharp.NLP Library
|
||||
* Copyright (C) 2018 Haiping Chen
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using BotSharp.MachineLearning.CRFLite.Decoder;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
|
@ -65,7 +83,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
if (inbuf.Count == 0)
|
||||
{
|
||||
//Empty input string
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
ret = tagger.reset();
|
||||
|
|
@ -94,7 +112,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
return ret;
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -109,7 +127,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
if (inbuf.Count == 0)
|
||||
{
|
||||
//Empty input string
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
ret = tagger.reset();
|
||||
|
|
@ -138,7 +156,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
return ret;
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,22 @@
|
|||
using BotSharp.MachineLearning.CRFLite.Encoder;
|
||||
/*
|
||||
* BotSharp.NLP Library
|
||||
* Copyright (C) 2018 Haiping Chen
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using BotSharp.MachineLearning.CRFLite.Encoder;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
}
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
public int initNbest()
|
||||
|
|
@ -164,10 +164,10 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
eos.next = null;
|
||||
if (BaseUtils.heap_insert(eos, heap_queue) < 0)
|
||||
{
|
||||
return BaseUtils.ERROR_INSERT_HEAP_FAILED;
|
||||
return BaseUtils.RETURN_INSERT_HEAP_FAILED;
|
||||
}
|
||||
}
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
public int next()
|
||||
|
|
@ -199,7 +199,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
n.next = top;
|
||||
if (BaseUtils.heap_insert(n, heap_queue) < 0)
|
||||
{
|
||||
return BaseUtils.ERROR_INSERT_HEAP_FAILED;
|
||||
return BaseUtils.RETURN_INSERT_HEAP_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -212,7 +212,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
Z_ = cost_ = 0.0;
|
||||
|
||||
BaseUtils.heap_reset(heap_queue);
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
|
@ -235,7 +235,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
}
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
public int add(List<List<string>> row_p)
|
||||
|
|
@ -243,7 +243,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
x_ = row_p;
|
||||
word_num = (short)x_.Count;
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
break;
|
||||
}
|
||||
}
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
//Label input string. The result is saved as result []
|
||||
|
|
@ -282,7 +282,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
//no word need to be labeled
|
||||
if (word_num == 0)
|
||||
{
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
//building feature set
|
||||
|
|
@ -326,7 +326,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -334,7 +334,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
{
|
||||
if (word_num <= 0)
|
||||
{
|
||||
return BaseUtils.ERROR_INVALIDATED_PARAMETER;
|
||||
return BaseUtils.RETURN_INVALIDATED_PARAMETER;
|
||||
}
|
||||
using (var v = _buildersPool.GetOrCreate())
|
||||
{
|
||||
|
|
@ -351,7 +351,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
var res = featureIndex.apply_rule(templ, cur, builder, this);
|
||||
if (res == null)
|
||||
{
|
||||
return BaseUtils.ERROR_EMPTY_FEATURE;
|
||||
return BaseUtils.RETURN_EMPTY_FEATURE;
|
||||
}
|
||||
id = featureIndex.get_id(res.ToString());
|
||||
if (id != -1)
|
||||
|
|
@ -373,7 +373,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
var strFeature = featureIndex.apply_rule(templ, cur, builder, this);
|
||||
if (strFeature == null)
|
||||
{
|
||||
return BaseUtils.ERROR_EMPTY_FEATURE;
|
||||
return BaseUtils.RETURN_EMPTY_FEATURE;
|
||||
}
|
||||
|
||||
id = featureIndex.get_id(strFeature.ToString());
|
||||
|
|
@ -387,7 +387,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
feature_cache_size++;
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +465,7 @@ namespace BotSharp.MachineLearning.CRFLite.Decoder
|
|||
}
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ namespace BotSharp.MachineLearning.CRFLite.Encoder
|
|||
return pair.Key;
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_INVALIDATED_FEATURE;
|
||||
return BaseUtils.RETURN_INVALIDATED_FEATURE;
|
||||
}
|
||||
|
||||
public long GetOrAddId(string key)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
|
||||
//build raw result at first
|
||||
var iRet = termbuf_build(term_buf);
|
||||
if (iRet != BaseUtils.ERROR_SUCCESS)
|
||||
if (iRet != BaseUtils.RETURN_SUCCESS)
|
||||
{
|
||||
return iRet;
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
}
|
||||
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
}
|
||||
}
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
|
||||
cost_ = -node_[s, result_[s]].bestCost;
|
||||
|
||||
return BaseUtils.ERROR_SUCCESS;
|
||||
return BaseUtils.RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
private void calcAlpha(int m, int n)
|
||||
|
|
|
|||
|
|
@ -32,16 +32,15 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
|
||||
public const int MODEL_TYPE_NORM = 100;
|
||||
|
||||
|
||||
public const int ERROR_INVALIDATED_FEATURE = -8;
|
||||
public const int ERROR_HEAP_SIZE_TOO_BIG = -7;
|
||||
public const int ERROR_INSERT_HEAP_FAILED = -6;
|
||||
public const int ERROR_EMPTY_FEATURE = -5;
|
||||
public const int ERROR_INVALIDATED_PARAMETER = -4;
|
||||
public const int ERROR_WRONG_STATUS = -3;
|
||||
public const int ERROR_TOO_LONG_WORD = -2;
|
||||
public const int ERROR_UNKNOWN = -1;
|
||||
public const int ERROR_SUCCESS = 0;
|
||||
public const int RETURN_INVALIDATED_FEATURE = -8;
|
||||
public const int RETURN_HEAP_SIZE_TOO_BIG = -7;
|
||||
public const int RETURN_INSERT_HEAP_FAILED = -6;
|
||||
public const int RETURN_EMPTY_FEATURE = -5;
|
||||
public const int RETURN_INVALIDATED_PARAMETER = -4;
|
||||
public const int RETURN_WRONG_STATUS = -3;
|
||||
public const int RETURN_TOO_LONG_WORD = -2;
|
||||
public const int RETURN_UNKNOWN = -1;
|
||||
public const int RETURN_SUCCESS = 0;
|
||||
|
||||
public static Heap heap_init(int max_size)
|
||||
{
|
||||
|
|
@ -82,7 +81,7 @@ namespace BotSharp.MachineLearning.CRFLite
|
|||
{
|
||||
if (H.size >= H.capacity)
|
||||
{
|
||||
return BaseUtils.ERROR_HEAP_SIZE_TOO_BIG;
|
||||
return BaseUtils.RETURN_HEAP_SIZE_TOO_BIG;
|
||||
}
|
||||
var i = ++H.size;
|
||||
while (i != 1 && H.elem_ptr_list[i / 2].fx > qe.fx)
|
||||
|
|
|
|||
161
BotSharp.MachineLearning/SVM/Cache.cs
Normal file
161
BotSharp.MachineLearning/SVM/Cache.cs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
internal class Cache
|
||||
{
|
||||
private int _count;
|
||||
private long _size;
|
||||
|
||||
private sealed class head_t
|
||||
{
|
||||
public head_t(Cache enclosingInstance)
|
||||
{
|
||||
_enclosingInstance = enclosingInstance;
|
||||
}
|
||||
private Cache _enclosingInstance;
|
||||
public Cache EnclosingInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _enclosingInstance;
|
||||
}
|
||||
}
|
||||
internal head_t prev, next; // a cicular list
|
||||
internal float[] data;
|
||||
internal int len; // data[0,len) is cached in this entry
|
||||
}
|
||||
|
||||
private head_t[] head;
|
||||
private head_t lru_head;
|
||||
|
||||
public Cache(int count, long size)
|
||||
{
|
||||
_count = count;
|
||||
_size = size;
|
||||
head = new head_t[_count];
|
||||
for (int i = 0; i < _count; i++)
|
||||
head[i] = new head_t(this);
|
||||
_size /= 4;
|
||||
_size -= _count * (16 / 4); // sizeof(head_t) == 16
|
||||
lru_head = new head_t(this);
|
||||
lru_head.next = lru_head.prev = lru_head;
|
||||
}
|
||||
|
||||
private void lru_delete(head_t h)
|
||||
{
|
||||
// delete from current location
|
||||
h.prev.next = h.next;
|
||||
h.next.prev = h.prev;
|
||||
}
|
||||
|
||||
private void lru_insert(head_t h)
|
||||
{
|
||||
// insert to last position
|
||||
h.next = lru_head;
|
||||
h.prev = lru_head.prev;
|
||||
h.prev.next = h;
|
||||
h.next.prev = h;
|
||||
}
|
||||
|
||||
private static void swap<T>(ref T lhs, ref T rhs)
|
||||
{
|
||||
T tmp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = tmp;
|
||||
}
|
||||
|
||||
// request data [0,len)
|
||||
// return some position p where [p,len) need to be filled
|
||||
// (p >= len if nothing needs to be filled)
|
||||
// java: simulate pointer using single-element array
|
||||
public int GetData(int index, out float[] data, int len)
|
||||
{
|
||||
head_t h = head[index];
|
||||
if (h.len > 0)
|
||||
lru_delete(h);
|
||||
int more = len - h.len;
|
||||
|
||||
if (more > 0)
|
||||
{
|
||||
// free old space
|
||||
while (_size < more)
|
||||
{
|
||||
head_t old = lru_head.next;
|
||||
lru_delete(old);
|
||||
_size += old.len;
|
||||
old.data = null;
|
||||
old.len = 0;
|
||||
}
|
||||
|
||||
// allocate new space
|
||||
float[] new_data = new float[len];
|
||||
if (h.data != null)
|
||||
Array.Copy(h.data, 0, new_data, 0, h.len);
|
||||
h.data = new_data;
|
||||
_size -= more;
|
||||
swap(ref h.len, ref len);
|
||||
}
|
||||
|
||||
lru_insert(h);
|
||||
data = h.data;
|
||||
return len;
|
||||
}
|
||||
|
||||
public void SwapIndex(int i, int j)
|
||||
{
|
||||
if (i == j)
|
||||
return;
|
||||
|
||||
if (head[i].len > 0)
|
||||
lru_delete(head[i]);
|
||||
if (head[j].len > 0)
|
||||
lru_delete(head[j]);
|
||||
swap(ref head[i].data, ref head[j].data);
|
||||
swap(ref head[i].len, ref head[j].len);
|
||||
if (head[i].len > 0)
|
||||
lru_insert(head[i]);
|
||||
if (head[j].len > 0)
|
||||
lru_insert(head[j]);
|
||||
|
||||
if (i > j)
|
||||
swap(ref i, ref j);
|
||||
|
||||
for (head_t h = lru_head.next; h != lru_head; h = h.next)
|
||||
{
|
||||
if (h.len > i)
|
||||
{
|
||||
if (h.len > j)
|
||||
swap(ref h.data[i], ref h.data[j]);
|
||||
else
|
||||
{
|
||||
// give up
|
||||
lru_delete(h);
|
||||
_size += h.len;
|
||||
h.data = null;
|
||||
h.len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
209
BotSharp.MachineLearning/SVM/GaussianTransform.cs
Normal file
209
BotSharp.MachineLearning/SVM/GaussianTransform.cs
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// A transform which learns the mean and variance of a sample set and uses these to transform new data
|
||||
/// so that it has zero mean and unit variance.
|
||||
/// </summary>
|
||||
public class GaussianTransform : IRangeTransform
|
||||
{
|
||||
private double[] _means;
|
||||
private double[] _stddevs;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the Gaussian transform for the provided problem.
|
||||
/// </summary>
|
||||
/// <param name="prob">The Problem to analyze</param>
|
||||
/// <returns>The Gaussian transform for the problem</returns>
|
||||
public static GaussianTransform Compute(Problem prob)
|
||||
{
|
||||
int[] counts = new int[prob.MaxIndex];
|
||||
double[] means = new double[prob.MaxIndex];
|
||||
foreach (Node[] sample in prob.X)
|
||||
{
|
||||
for (int i = 0; i < sample.Length; i++)
|
||||
{
|
||||
means[sample[i].Index-1] += sample[i].Value;
|
||||
counts[sample[i].Index-1]++;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < prob.MaxIndex; i++)
|
||||
{
|
||||
if (counts[i] == 0)
|
||||
counts[i] = 2;
|
||||
means[i] /= counts[i];
|
||||
}
|
||||
|
||||
double[] stddevs = new double[prob.MaxIndex];
|
||||
foreach (Node[] sample in prob.X)
|
||||
{
|
||||
for (int i = 0; i < sample.Length; i++)
|
||||
{
|
||||
double diff = sample[i].Value - means[sample[i].Index - 1];
|
||||
stddevs[sample[i].Index - 1] += diff * diff;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < prob.MaxIndex; i++)
|
||||
{
|
||||
if (stddevs[i] == 0)
|
||||
continue;
|
||||
stddevs[i] /= (counts[i] - 1);
|
||||
stddevs[i] = Math.Sqrt(stddevs[i]);
|
||||
}
|
||||
|
||||
return new GaussianTransform(means, stddevs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="means">Means in each dimension</param>
|
||||
/// <param name="stddevs">Standard deviation in each dimension</param>
|
||||
public GaussianTransform(double[] means, double[] stddevs)
|
||||
{
|
||||
_means = means;
|
||||
_stddevs = stddevs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the transform to the disk. The samples are not stored, only the
|
||||
/// statistics.
|
||||
/// </summary>
|
||||
/// <param name="stream">The destination stream</param>
|
||||
/// <param name="transform">The transform</param>
|
||||
public static void Write(Stream stream, GaussianTransform transform)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamWriter output = new StreamWriter(stream);
|
||||
output.WriteLine(transform._means.Length);
|
||||
for (int i = 0; i < transform._means.Length; i++)
|
||||
output.WriteLine("{0} {1}", transform._means[i], transform._stddevs[i]);
|
||||
output.Flush();
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a GaussianTransform from the provided stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The source stream</param>
|
||||
/// <returns>The transform</returns>
|
||||
public static GaussianTransform Read(Stream stream)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamReader input = new StreamReader(stream);
|
||||
int length = int.Parse(input.ReadLine(), CultureInfo.InvariantCulture);
|
||||
double[] means = new double[length];
|
||||
double[] stddevs = new double[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
string[] parts = input.ReadLine().Split();
|
||||
means[i] = double.Parse(parts[0], CultureInfo.InvariantCulture);
|
||||
stddevs[i] = double.Parse(parts[1], CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
|
||||
return new GaussianTransform(means, stddevs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the transform to the disk. The samples are not stored, only the
|
||||
/// statistics.
|
||||
/// </summary>
|
||||
/// <param name="filename">The destination filename</param>
|
||||
/// <param name="transform">The transform</param>
|
||||
public static void Write(string filename, GaussianTransform transform)
|
||||
{
|
||||
FileStream output = File.Open(filename, FileMode.Create);
|
||||
try
|
||||
{
|
||||
Write(output, transform);
|
||||
}
|
||||
finally
|
||||
{
|
||||
output.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a GaussianTransform from the provided stream.
|
||||
/// </summary>
|
||||
/// <param name="filename">The source filename</param>
|
||||
/// <returns>The transform</returns>
|
||||
public static GaussianTransform Read(string filename)
|
||||
{
|
||||
FileStream input = File.Open(filename, FileMode.Open);
|
||||
try
|
||||
{
|
||||
return Read(input);
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Close();
|
||||
}
|
||||
}
|
||||
|
||||
#region IRangeTransform Members
|
||||
|
||||
/// <summary>
|
||||
/// Transform the input value using the transform stored for the provided index.
|
||||
/// </summary>
|
||||
/// <param name="input">Input value</param>
|
||||
/// <param name="index">Index of the transform to use</param>
|
||||
/// <returns>The transformed value</returns>
|
||||
public double Transform(double input, int index)
|
||||
{
|
||||
index--;
|
||||
if (_stddevs[index] == 0)
|
||||
return 0;
|
||||
double diff = input - _means[index];
|
||||
diff /= _stddevs[index];
|
||||
return diff;
|
||||
}
|
||||
/// <summary>
|
||||
/// Transforms the input array.
|
||||
/// </summary>
|
||||
/// <param name="input">The array to transform</param>
|
||||
/// <returns>The transformed array</returns>
|
||||
public Node[] Transform(Node[] input)
|
||||
{
|
||||
Node[] output = new Node[input.Length];
|
||||
for (int i = 0; i < output.Length; i++)
|
||||
{
|
||||
int index = input[i].Index;
|
||||
double value = input[i].Value;
|
||||
output[i] = new Node(index, Transform(value, index));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
41
BotSharp.MachineLearning/SVM/IRangeTransform.cs
Normal file
41
BotSharp.MachineLearning/SVM/IRangeTransform.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface implemented by range transforms.
|
||||
/// </summary>
|
||||
public interface IRangeTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Transform the input value using the transform stored for the provided index.
|
||||
/// </summary>
|
||||
/// <param name="input">Input value</param>
|
||||
/// <param name="index">Index of the transform to use</param>
|
||||
/// <returns>The transformed value</returns>
|
||||
double Transform(double input, int index);
|
||||
/// <summary>
|
||||
/// Transforms the input array.
|
||||
/// </summary>
|
||||
/// <param name="input">The array to transform</param>
|
||||
/// <returns>The transformed array</returns>
|
||||
Node[] Transform(Node[] input);
|
||||
}
|
||||
}
|
||||
226
BotSharp.MachineLearning/SVM/Kernel.cs
Normal file
226
BotSharp.MachineLearning/SVM/Kernel.cs
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
internal interface IQMatrix
|
||||
{
|
||||
float[] GetQ(int column, int len);
|
||||
double[] GetQD();
|
||||
void SwapIndex(int i, int j);
|
||||
}
|
||||
|
||||
internal abstract class Kernel : IQMatrix
|
||||
{
|
||||
private Node[][] _x;
|
||||
private double[] _xSquare;
|
||||
|
||||
private KernelType _kernelType;
|
||||
private int _degree;
|
||||
private double _gamma;
|
||||
private double _coef0;
|
||||
|
||||
public abstract float[] GetQ(int column, int len);
|
||||
public abstract double[] GetQD();
|
||||
|
||||
public virtual void SwapIndex(int i, int j)
|
||||
{
|
||||
_x.SwapIndex(i, j);
|
||||
|
||||
if (_xSquare != null)
|
||||
{
|
||||
_xSquare.SwapIndex(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
private static double powi(double value, int times)
|
||||
{
|
||||
double tmp = value, ret = 1.0;
|
||||
|
||||
for (int t = times; t > 0; t /= 2)
|
||||
{
|
||||
if (t % 2 == 1) ret *= tmp;
|
||||
tmp = tmp * tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public double KernelFunction(int i, int j)
|
||||
{
|
||||
switch (_kernelType)
|
||||
{
|
||||
case KernelType.LINEAR:
|
||||
return dot(_x[i], _x[j]);
|
||||
case KernelType.POLY:
|
||||
return powi(_gamma * dot(_x[i], _x[j]) + _coef0, _degree);
|
||||
case KernelType.RBF:
|
||||
return Math.Exp(-_gamma * (_xSquare[i] + _xSquare[j] - 2 * dot(_x[i], _x[j])));
|
||||
case KernelType.SIGMOID:
|
||||
return Math.Tanh(_gamma * dot(_x[i], _x[j]) + _coef0);
|
||||
case KernelType.PRECOMPUTED:
|
||||
return _x[i][(int)(_x[j][0].Value)].Value;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Kernel(int l, Node[][] x_, Parameter param)
|
||||
{
|
||||
_kernelType = param.KernelType;
|
||||
_degree = param.Degree;
|
||||
_gamma = param.Gamma;
|
||||
_coef0 = param.Coefficient0;
|
||||
|
||||
_x = (Node[][])x_.Clone();
|
||||
|
||||
if (_kernelType == KernelType.RBF)
|
||||
{
|
||||
_xSquare = new double[l];
|
||||
for (int i = 0; i < l; i++)
|
||||
_xSquare[i] = dot(_x[i], _x[i]);
|
||||
}
|
||||
else _xSquare = null;
|
||||
}
|
||||
|
||||
private static double dot(Node[] xNodes, Node[] yNodes)
|
||||
{
|
||||
double sum = 0;
|
||||
int xlen = xNodes.Length;
|
||||
int ylen = yNodes.Length;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
Node x = xNodes[0];
|
||||
Node y = yNodes[0];
|
||||
while (true)
|
||||
{
|
||||
if (x._index == y._index)
|
||||
{
|
||||
sum += x._value * y._value;
|
||||
i++;
|
||||
j++;
|
||||
if (i < xlen && j < ylen)
|
||||
{
|
||||
x = xNodes[i];
|
||||
y = yNodes[j];
|
||||
}
|
||||
else if (i < xlen)
|
||||
{
|
||||
x = xNodes[i];
|
||||
break;
|
||||
}
|
||||
else if (j < ylen)
|
||||
{
|
||||
y = yNodes[j];
|
||||
break;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x._index > y._index)
|
||||
{
|
||||
++j;
|
||||
if (j < ylen)
|
||||
y = yNodes[j];
|
||||
else break;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
if (i < xlen)
|
||||
x = xNodes[i];
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static double computeSquaredDistance(Node[] xNodes, Node[] yNodes)
|
||||
{
|
||||
Node x = xNodes[0];
|
||||
Node y = yNodes[0];
|
||||
int xLength = xNodes.Length;
|
||||
int yLength = yNodes.Length;
|
||||
int xIndex = 0;
|
||||
int yIndex = 0;
|
||||
double sum = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (x._index == y._index)
|
||||
{
|
||||
double d = x._value - y._value;
|
||||
sum += d * d;
|
||||
xIndex++;
|
||||
yIndex++;
|
||||
if (xIndex < xLength && yIndex < yLength)
|
||||
{
|
||||
x = xNodes[xIndex];
|
||||
y = yNodes[yIndex];
|
||||
}
|
||||
else if(xIndex < xLength){
|
||||
x = xNodes[xIndex];
|
||||
break;
|
||||
}
|
||||
else if(yIndex < yLength){
|
||||
y = yNodes[yIndex];
|
||||
break;
|
||||
}else break;
|
||||
}
|
||||
else if (x._index > y._index)
|
||||
{
|
||||
sum += y._value * y._value;
|
||||
if (++yIndex < yLength)
|
||||
y = yNodes[yIndex];
|
||||
else break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum += x._value * x._value;
|
||||
if (++xIndex < xLength)
|
||||
x = xNodes[xIndex];
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
for (; xIndex < xLength; xIndex++)
|
||||
{
|
||||
double d = xNodes[xIndex]._value;
|
||||
sum += d * d;
|
||||
}
|
||||
|
||||
for (; yIndex < yLength; yIndex++)
|
||||
{
|
||||
double d = yNodes[yIndex]._value;
|
||||
sum += d * d;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static double KernelFunction(Node[] x, Node[] y, Parameter param)
|
||||
{
|
||||
switch (param.KernelType)
|
||||
{
|
||||
case KernelType.LINEAR:
|
||||
return dot(x, y);
|
||||
case KernelType.POLY:
|
||||
return powi(param.Degree * dot(x, y) + param.Coefficient0, param.Degree);
|
||||
case KernelType.RBF:
|
||||
{
|
||||
double sum = computeSquaredDistance(x, y);
|
||||
return Math.Exp(-param.Gamma * sum);
|
||||
}
|
||||
case KernelType.SIGMOID:
|
||||
return Math.Tanh(param.Gamma * dot(x, y) + param.Coefficient0);
|
||||
case KernelType.PRECOMPUTED:
|
||||
return x[(int)(y[0].Value)].Value;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
420
BotSharp.MachineLearning/SVM/Model.cs
Normal file
420
BotSharp.MachineLearning/SVM/Model.cs
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates an SVM Model.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Model
|
||||
{
|
||||
internal Model()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameter object.
|
||||
/// </summary>
|
||||
public Parameter Parameter{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Number of classes in the model.
|
||||
/// </summary>
|
||||
public int NumberOfClasses{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Total number of support vectors.
|
||||
/// </summary>
|
||||
public int SupportVectorCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The support vectors.
|
||||
/// </summary>
|
||||
public Node[][] SupportVectors{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// The coefficients for the support vectors.
|
||||
/// </summary>
|
||||
public double[][] SupportVectorCoefficients{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Values in [1,...,num_training_data] to indicate SVs in the training set
|
||||
/// </summary>
|
||||
public int[] SupportVectorIndices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constants in decision functions
|
||||
/// </summary>
|
||||
public double[] Rho{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// First pairwise probability.
|
||||
/// </summary>
|
||||
public double[] PairwiseProbabilityA{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Second pairwise probability.
|
||||
/// </summary>
|
||||
public double[] PairwiseProbabilityB{get;set;}
|
||||
|
||||
// for classification only
|
||||
|
||||
/// <summary>
|
||||
/// Class labels.
|
||||
/// </summary>
|
||||
public int[] ClassLabels{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Number of support vectors per class.
|
||||
/// </summary>
|
||||
public int[] NumberOfSVPerClass{get;set;}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Model test = obj as Model;
|
||||
if (test == null)
|
||||
return false;
|
||||
|
||||
bool same = ClassLabels.IsEqual(test.ClassLabels);
|
||||
same = same && NumberOfClasses == test.NumberOfClasses;
|
||||
same = same && NumberOfSVPerClass.IsEqual(test.NumberOfSVPerClass);
|
||||
if(PairwiseProbabilityA != null)
|
||||
same = same && PairwiseProbabilityA.IsEqual(test.PairwiseProbabilityA);
|
||||
if(PairwiseProbabilityB != null)
|
||||
same = same && PairwiseProbabilityB.IsEqual(test.PairwiseProbabilityB);
|
||||
same = same && Parameter.Equals(test.Parameter);
|
||||
same = same && Rho.IsEqual(test.Rho);
|
||||
same = same && SupportVectorCoefficients.IsEqual(test.SupportVectorCoefficients);
|
||||
same = same && SupportVectorCount == test.SupportVectorCount;
|
||||
same = same && SupportVectors.IsEqual(test.SupportVectors);
|
||||
return same;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ClassLabels.ComputeHashcode() +
|
||||
NumberOfClasses.GetHashCode() +
|
||||
NumberOfSVPerClass.ComputeHashcode() +
|
||||
PairwiseProbabilityA.ComputeHashcode() +
|
||||
PairwiseProbabilityB.ComputeHashcode() +
|
||||
Parameter.GetHashCode() +
|
||||
Rho.ComputeHashcode() +
|
||||
SupportVectorCoefficients.ComputeHashcode() +
|
||||
SupportVectorCount.GetHashCode() +
|
||||
SupportVectors.ComputeHashcode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Model from the provided file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The name of the file containing the Model</param>
|
||||
/// <returns>the Model</returns>
|
||||
public static Model Read(string filename)
|
||||
{
|
||||
FileStream input = File.OpenRead(filename);
|
||||
try
|
||||
{
|
||||
return Read(input);
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Model from the provided stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream from which to read the Model.</param>
|
||||
/// <returns>the Model</returns>
|
||||
public static Model Read(Stream stream)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamReader input = new StreamReader(stream);
|
||||
|
||||
// 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();
|
||||
|
||||
int i,n;
|
||||
switch(cmd){
|
||||
case "svm_type":
|
||||
param.SvmType = (SvmType)Enum.Parse(typeof(SvmType), arg.ToUpper());
|
||||
break;
|
||||
|
||||
case "kernel_type":
|
||||
if (arg == "polynomial")
|
||||
arg = "poly";
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a model to the provided filename. This will overwrite any previous data in the file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The desired file</param>
|
||||
/// <param name="model">The Model to write</param>
|
||||
public static void Write(string filename, Model model)
|
||||
{
|
||||
FileStream stream = File.Open(filename, FileMode.Create);
|
||||
try
|
||||
{
|
||||
Write(stream, model);
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a model to the provided stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The output stream</param>
|
||||
/// <param name="model">The model to write</param>
|
||||
public static void Write(Stream stream, Model model)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamWriter output = new StreamWriter(stream);
|
||||
|
||||
Parameter param = model.Parameter;
|
||||
|
||||
output.Write("svm_type {0}\n", param.SvmType);
|
||||
output.Write("kernel_type {0}\n", param.KernelType);
|
||||
|
||||
if (param.KernelType == KernelType.POLY)
|
||||
output.Write("degree {0}\n", param.Degree);
|
||||
|
||||
if (param.KernelType == KernelType.POLY || param.KernelType == KernelType.RBF || param.KernelType == KernelType.SIGMOID)
|
||||
output.Write("gamma {0:0.000000}\n", param.Gamma);
|
||||
|
||||
if (param.KernelType == KernelType.POLY || param.KernelType == KernelType.SIGMOID)
|
||||
output.Write("coef0 {0:0.000000}\n", param.Coefficient0);
|
||||
|
||||
int nr_class = model.NumberOfClasses;
|
||||
int l = model.SupportVectorCount;
|
||||
output.Write("nr_class {0}\n", nr_class);
|
||||
output.Write("total_sv {0}\n", l);
|
||||
|
||||
{
|
||||
output.Write("rho");
|
||||
for (int i = 0; i < nr_class * (nr_class - 1) / 2; i++)
|
||||
output.Write(" {0:0.000000}", model.Rho[i]);
|
||||
output.Write("\n");
|
||||
}
|
||||
|
||||
if (model.ClassLabels != null)
|
||||
{
|
||||
output.Write("label");
|
||||
for (int i = 0; i < nr_class; i++)
|
||||
output.Write(" {0}", model.ClassLabels[i]);
|
||||
output.Write("\n");
|
||||
}
|
||||
|
||||
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(" {0:0.000000}", model.PairwiseProbabilityA[i]);
|
||||
output.Write("\n");
|
||||
}
|
||||
if (model.PairwiseProbabilityB != null)
|
||||
{
|
||||
output.Write("probB");
|
||||
for (int i = 0; i < nr_class * (nr_class - 1) / 2; i++)
|
||||
output.Write(" {0:0.000000}", model.PairwiseProbabilityB[i]);
|
||||
output.Write("\n");
|
||||
}
|
||||
|
||||
if (model.NumberOfSVPerClass != null)
|
||||
{
|
||||
output.Write("nr_sv");
|
||||
for (int i = 0; i < nr_class; i++)
|
||||
output.Write(" {0}", model.NumberOfSVPerClass[i]);
|
||||
output.Write("\n");
|
||||
}
|
||||
|
||||
output.Write("SV\n");
|
||||
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("{0:0.000000} ", sv_coef[j][i]);
|
||||
|
||||
Node[] p = SV[i];
|
||||
if (p.Length == 0)
|
||||
{
|
||||
output.Write("\n");
|
||||
continue;
|
||||
}
|
||||
if (param.KernelType == KernelType.PRECOMPUTED)
|
||||
output.Write("0:{0:0.000000}", (int)p[0].Value);
|
||||
else
|
||||
{
|
||||
output.Write("{0}:{1:0.000000}", p[0].Index, p[0].Value);
|
||||
for (int j = 1; j < p.Length; j++)
|
||||
output.Write(" {0}:{1:0.000000}", p[j].Index, p[j].Value);
|
||||
}
|
||||
output.Write("\n");
|
||||
}
|
||||
|
||||
output.Flush();
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
117
BotSharp.MachineLearning/SVM/Node.cs
Normal file
117
BotSharp.MachineLearning/SVM/Node.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates a node in a Problem vector, with an index and a value (for more efficient representation
|
||||
/// of sparse data.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Node : IComparable<Node>
|
||||
{
|
||||
internal int _index;
|
||||
internal double _value;
|
||||
|
||||
/// <summary>
|
||||
/// Default Constructor.
|
||||
/// </summary>
|
||||
public Node()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the value.</param>
|
||||
/// <param name="value">The value to store.</param>
|
||||
public Node(int index, double value)
|
||||
{
|
||||
_index = index;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index of this Node.
|
||||
/// </summary>
|
||||
public int Index
|
||||
{
|
||||
get
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
set
|
||||
{
|
||||
_index = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Value at Index.
|
||||
/// </summary>
|
||||
public double Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String representation of this Node as {index}:{value}.
|
||||
/// </summary>
|
||||
/// <returns>{index}:{value}</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}:{1}", _index, _value.Truncate());
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Node other = obj as Node;
|
||||
if(other == null)
|
||||
return false;
|
||||
|
||||
return _index == other._index && _value.Truncate() == other._value.Truncate();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _index.GetHashCode() + _value.GetHashCode();
|
||||
}
|
||||
|
||||
#region IComparable<Node> Members
|
||||
|
||||
/// <summary>
|
||||
/// Compares this node with another.
|
||||
/// </summary>
|
||||
/// <param name="other">The node to compare to</param>
|
||||
/// <returns>A positive number if this node is greater, a negative number if it is less than, or 0 if equal</returns>
|
||||
public int CompareTo(Node other)
|
||||
{
|
||||
return _index.CompareTo(other._index);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
221
BotSharp.MachineLearning/SVM/Parameter.cs
Normal file
221
BotSharp.MachineLearning/SVM/Parameter.cs
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all of the types of SVM this library can model.
|
||||
/// </summary>
|
||||
public enum SvmType {
|
||||
/// <summary>
|
||||
/// C-SVC.
|
||||
/// </summary>
|
||||
C_SVC,
|
||||
/// <summary>
|
||||
/// nu-SVC.
|
||||
/// </summary>
|
||||
NU_SVC,
|
||||
/// <summary>
|
||||
/// one-class SVM
|
||||
/// </summary>
|
||||
ONE_CLASS,
|
||||
/// <summary>
|
||||
/// epsilon-SVR
|
||||
/// </summary>
|
||||
EPSILON_SVR,
|
||||
/// <summary>
|
||||
/// nu-SVR
|
||||
/// </summary>
|
||||
NU_SVR
|
||||
};
|
||||
/// <summary>
|
||||
/// Contains the various kernel types this library can use.
|
||||
/// </summary>
|
||||
public enum KernelType {
|
||||
/// <summary>
|
||||
/// Linear: u'*v
|
||||
/// </summary>
|
||||
LINEAR,
|
||||
/// <summary>
|
||||
/// Polynomial: (gamma*u'*v + coef0)^degree
|
||||
/// </summary>
|
||||
POLY,
|
||||
/// <summary>
|
||||
/// Radial basis function: exp(-gamma*|u-v|^2)
|
||||
/// </summary>
|
||||
RBF,
|
||||
/// <summary>
|
||||
/// Sigmoid: tanh(gamma*u'*v + coef0)
|
||||
/// </summary>
|
||||
SIGMOID,
|
||||
/// <summary>
|
||||
/// Precomputed kernel
|
||||
/// </summary>
|
||||
PRECOMPUTED,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This class contains the various parameters which can affect the way in which an SVM
|
||||
/// is learned. Unless you know what you are doing, chances are you are best off using
|
||||
/// the default values.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Parameter : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Default Constructor. Gives good default values to all parameters.
|
||||
/// </summary>
|
||||
public Parameter()
|
||||
{
|
||||
SvmType = SvmType.C_SVC;
|
||||
KernelType = KernelType.RBF;
|
||||
Degree = 3;
|
||||
Gamma = 0; // 1/k
|
||||
Coefficient0 = 0;
|
||||
Nu = 0.5;
|
||||
CacheSize = 40;
|
||||
C = 1;
|
||||
EPS = 1e-3;
|
||||
P = 0.1;
|
||||
Shrinking = true;
|
||||
Probability = false;
|
||||
Weights = new Dictionary<int, double>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of SVM (default C-SVC)
|
||||
/// </summary>
|
||||
public SvmType SvmType{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Type of kernel function (default Polynomial)
|
||||
/// </summary>
|
||||
public KernelType KernelType{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Degree in kernel function (default 3).
|
||||
/// </summary>
|
||||
public int Degree{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Gamma in kernel function (default 1/k)
|
||||
/// </summary>
|
||||
public double Gamma{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Zeroeth coefficient in kernel function (default 0)
|
||||
/// </summary>
|
||||
public double Coefficient0{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Cache memory size in MB (default 100)
|
||||
/// </summary>
|
||||
public double CacheSize{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Tolerance of termination criterion (default 0.001)
|
||||
/// </summary>
|
||||
public double EPS{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// The parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1)
|
||||
/// </summary>
|
||||
public double C{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Contains custom weights for class labels. Default weight value is 1.
|
||||
/// </summary>
|
||||
public Dictionary<int,double> Weights{get; private set;}
|
||||
|
||||
/// <summary>
|
||||
/// The parameter nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5)
|
||||
/// </summary>
|
||||
public double Nu{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// The epsilon in loss function of epsilon-SVR (default 0.1)
|
||||
/// </summary>
|
||||
public double P{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the shrinking heuristics, (default True)
|
||||
/// </summary>
|
||||
public bool Shrinking{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to train an SVC or SVR model for probability estimates, (default False)
|
||||
/// </summary>
|
||||
public bool Probability{get;set;}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Parameter other = obj as Parameter;
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return other.C == C &&
|
||||
other.CacheSize == CacheSize &&
|
||||
other.Coefficient0 == Coefficient0 &&
|
||||
other.Degree == Degree &&
|
||||
other.EPS == EPS &&
|
||||
other.Gamma == Gamma &&
|
||||
other.KernelType == KernelType &&
|
||||
other.Nu == Nu &&
|
||||
other.P == P &&
|
||||
other.Probability == Probability &&
|
||||
other.Shrinking == Shrinking &&
|
||||
other.SvmType == SvmType &&
|
||||
other.Weights.ToArray().IsEqual(Weights.ToArray());
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return C.GetHashCode() +
|
||||
CacheSize.GetHashCode() +
|
||||
Coefficient0.GetHashCode() +
|
||||
Degree.GetHashCode() +
|
||||
EPS.GetHashCode() +
|
||||
Gamma.GetHashCode() +
|
||||
KernelType.GetHashCode() +
|
||||
Nu.GetHashCode() +
|
||||
P.GetHashCode() +
|
||||
Probability.GetHashCode() +
|
||||
Shrinking.GetHashCode() +
|
||||
SvmType.GetHashCode() +
|
||||
Weights.ToArray().ComputeHashcode();
|
||||
}
|
||||
|
||||
|
||||
#region ICloneable Members
|
||||
/// <summary>
|
||||
/// Creates a memberwise clone of this parameters object.
|
||||
/// </summary>
|
||||
/// <returns>The clone (as type Parameter)</returns>
|
||||
public object Clone()
|
||||
{
|
||||
return base.MemberwiseClone();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
271
BotSharp.MachineLearning/SVM/ParameterSelection.cs
Normal file
271
BotSharp.MachineLearning/SVM/ParameterSelection.cs
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Class representing a grid square result.
|
||||
/// </summary>
|
||||
public class GridSquare
|
||||
{
|
||||
/// <summary>
|
||||
/// The C value
|
||||
/// </summary>
|
||||
public double C;
|
||||
/// <summary>
|
||||
/// The Gamma value
|
||||
/// </summary>
|
||||
public double Gamma;
|
||||
/// <summary>
|
||||
/// The cross validation score
|
||||
/// </summary>
|
||||
public double Score;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} {1} {2}", C, Gamma, Score);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class contains routines which perform parameter selection for a model which uses C-SVC and
|
||||
/// an RBF kernel.
|
||||
/// </summary>
|
||||
public static class ParameterSelection
|
||||
{
|
||||
/// <summary>
|
||||
/// Default number of times to divide the data.
|
||||
/// </summary>
|
||||
public const int NFOLD = 5;
|
||||
/// <summary>
|
||||
/// Default minimum power of 2 for the C value (-5)
|
||||
/// </summary>
|
||||
public const int MIN_C = -5;
|
||||
/// <summary>
|
||||
/// Default maximum power of 2 for the C value (15)
|
||||
/// </summary>
|
||||
public const int MAX_C = 15;
|
||||
/// <summary>
|
||||
/// Default power iteration step for the C value (2)
|
||||
/// </summary>
|
||||
public const int C_STEP = 2;
|
||||
/// <summary>
|
||||
/// Default minimum power of 2 for the Gamma value (-15)
|
||||
/// </summary>
|
||||
public const int MIN_G = -15;
|
||||
/// <summary>
|
||||
/// Default maximum power of 2 for the Gamma Value (3)
|
||||
/// </summary>
|
||||
public const int MAX_G = 3;
|
||||
/// <summary>
|
||||
/// Default power iteration step for the Gamma value (2)
|
||||
/// </summary>
|
||||
public const int G_STEP = 2;
|
||||
/// <summary>
|
||||
/// Used to control the degree of parallelism used in grid exploration. Default value is the number of processors.
|
||||
/// </summary>
|
||||
public static int Threads = Environment.ProcessorCount;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a logarithmic list of values from minimum power of 2 to the maximum power of 2 using the provided iteration size.
|
||||
/// </summary>
|
||||
/// <param name="minPower">The minimum power of 2</param>
|
||||
/// <param name="maxPower">The maximum power of 2</param>
|
||||
/// <param name="iteration">The iteration size to use in powers</param>
|
||||
/// <returns></returns>
|
||||
public static List<double> GetList(double minPower, double maxPower, double iteration)
|
||||
{
|
||||
List<double> list = new List<double>();
|
||||
for (double d = minPower; d <= maxPower; d += iteration)
|
||||
list.Add(Math.Pow(2, d));
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a Grid parameter selection, trying all possible combinations of the two lists and returning the
|
||||
/// combination which performed best. The default ranges of C and Gamma values are used. Use this method if there is no validation data available, and it will
|
||||
/// divide it 5 times to allow 5-fold validation (training on 4/5 and validating on 1/5, 5 times).
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="createParams">The parameters to use when optimizing</param>
|
||||
/// <param name="report">Function used to report results</param>
|
||||
/// <param name="C">The optimal C value will be put into this variable</param>
|
||||
/// <param name="Gamma">The optimal Gamma value will be put into this variable</param>
|
||||
/// <returns>A list of grid squares and their results</returns>
|
||||
public static List<GridSquare> Grid(
|
||||
Problem problem,
|
||||
Func<Parameter> createParams,
|
||||
Action<GridSquare> report,
|
||||
out double C,
|
||||
out double Gamma)
|
||||
{
|
||||
return Grid(problem, createParams, GetList(MIN_C, MAX_C, C_STEP), GetList(MIN_G, MAX_G, G_STEP), report, NFOLD, out C, out Gamma);
|
||||
}
|
||||
/// <summary>
|
||||
/// Performs a Grid parameter selection, trying all possible combinations of the two lists and returning the
|
||||
/// combination which performed best. Use this method if there is no validation data available, and it will
|
||||
/// divide it 5 times to allow 5-fold validation (training on 4/5 and validating on 1/5, 5 times).
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="createParams">The parameters to use when optimizing</param>
|
||||
/// <param name="CValues">The set of C values to use</param>
|
||||
/// <param name="GammaValues">The set of Gamma values to use</param>
|
||||
/// <param name="report">Function used to report results</param>
|
||||
/// <param name="C">The optimal C value will be put into this variable</param>
|
||||
/// <param name="Gamma">The optimal Gamma value will be put into this variable</param>
|
||||
/// <returns>A list of grid squares and their results</returns>
|
||||
public static List<GridSquare> Grid(
|
||||
Problem problem,
|
||||
Func<Parameter> createParams,
|
||||
List<double> CValues,
|
||||
List<double> GammaValues,
|
||||
Action<GridSquare> report,
|
||||
out double C,
|
||||
out double Gamma)
|
||||
{
|
||||
return Grid(problem, createParams, CValues, GammaValues, report, NFOLD, out C, out Gamma);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a Grid parameter selection, trying all possible combinations of the two lists and returning the
|
||||
/// combination which performed best. Use this method if validation data isn't available, as it will
|
||||
/// divide the training data and train on a portion of it and test on the rest.
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="createParams">The parameters to use when optimizing</param>
|
||||
/// <param name="CValues">The set of C values to use</param>
|
||||
/// <param name="GammaValues">The set of Gamma values to use</param>
|
||||
/// <param name="report">Function used to report results</param>
|
||||
/// <param name="nrfold">The number of times the data should be divided for validation</param>
|
||||
/// <param name="C">The optimal C value will be placed in this variable</param>
|
||||
/// <param name="Gamma">The optimal Gamma value will be placed in this variable</param>
|
||||
/// <returns>A list of grid squares and their results</returns>
|
||||
public static List<GridSquare> Grid(
|
||||
Problem problem,
|
||||
Func<Parameter> createParams,
|
||||
List<double> CValues,
|
||||
List<double> GammaValues,
|
||||
Action<GridSquare> report,
|
||||
int nrfold,
|
||||
out double C,
|
||||
out double Gamma)
|
||||
{
|
||||
C = 0;
|
||||
Gamma = 0;
|
||||
|
||||
List<GridSquare> squares = new List<GridSquare>();
|
||||
foreach (double testC in CValues)
|
||||
foreach (double testGamma in GammaValues)
|
||||
squares.Add(new GridSquare { C = testC, Gamma = testGamma });
|
||||
|
||||
ThreadLocal<Parameter> parameters = new ThreadLocal<Parameter>(() => createParams());
|
||||
|
||||
Parallel.ForEach(squares, new ParallelOptions{MaxDegreeOfParallelism=Threads}, square =>
|
||||
{
|
||||
parameters.Value.C = square.C;
|
||||
parameters.Value.Gamma = square.Gamma;
|
||||
square.Score = Training.PerformCrossValidation(problem, parameters.Value, nrfold);
|
||||
if(report != null)
|
||||
report(square);
|
||||
});
|
||||
|
||||
GridSquare best = squares.OrderByDescending(o => o.Score).First();
|
||||
C = best.C;
|
||||
Gamma = best.Gamma;
|
||||
|
||||
return squares.OrderBy(o => o.C).ThenBy(o => o.Gamma).ToList();
|
||||
}
|
||||
/// <summary>
|
||||
/// Performs a Grid parameter selection, trying all possible combinations of the two lists and returning the
|
||||
/// combination which performed best. Uses the default values of C and Gamma.
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="validation">The validation data</param>
|
||||
/// <param name="createParams">The parameters to use when optimizing</param>
|
||||
/// <param name="report">Function used to report results</param>
|
||||
/// <param name="C">The optimal C value will be placed in this variable</param>
|
||||
/// <param name="Gamma">The optimal Gamma value will be placed in this variable</param>
|
||||
/// <returns>A list of grid squares and their results</returns>
|
||||
public static List<GridSquare> Grid(
|
||||
Problem problem,
|
||||
Problem validation,
|
||||
Func<Parameter> createParams,
|
||||
Action<GridSquare> report,
|
||||
out double C,
|
||||
out double Gamma)
|
||||
{
|
||||
return Grid(problem, validation, createParams, GetList(MIN_C, MAX_C, C_STEP), GetList(MIN_G, MAX_G, G_STEP), report, out C, out Gamma);
|
||||
}
|
||||
/// <summary>
|
||||
/// Performs a Grid parameter selection, trying all possible combinations of the two lists and returning the
|
||||
/// combination which performed best.
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="validation">The validation data</param>
|
||||
/// <param name="createParams">The parameters to use when optimizing</param>
|
||||
/// <param name="CValues">The C values to use</param>
|
||||
/// <param name="GammaValues">The Gamma values to use</param>
|
||||
/// <param name="report">Function used to report results</param>
|
||||
/// <param name="C">The optimal C value will be placed in this variable</param>
|
||||
/// <param name="Gamma">The optimal Gamma value will be placed in this variable</param>
|
||||
/// <returns>A list of grid squares and their results</returns>
|
||||
public static List<GridSquare> Grid(
|
||||
Problem problem,
|
||||
Problem validation,
|
||||
Func<Parameter> createParams,
|
||||
List<double> CValues,
|
||||
List<double> GammaValues,
|
||||
Action<GridSquare> report,
|
||||
out double C,
|
||||
out double Gamma)
|
||||
{
|
||||
C = 0;
|
||||
Gamma = 0;
|
||||
|
||||
List<GridSquare> squares = new List<GridSquare>();
|
||||
foreach (double testC in CValues)
|
||||
foreach (double testGamma in GammaValues)
|
||||
squares.Add(new GridSquare { C = testC, Gamma = testGamma });
|
||||
ThreadLocal<Parameter> parameters = new ThreadLocal<Parameter>(() => createParams());
|
||||
|
||||
Parallel.ForEach(squares, new ParallelOptions { MaxDegreeOfParallelism = Threads }, square =>
|
||||
{
|
||||
parameters.Value.C = square.C;
|
||||
parameters.Value.Gamma = square.Gamma;
|
||||
Model model = Training.Train(problem, parameters.Value);
|
||||
square.Score = Prediction.Predict(validation, null, model, false);
|
||||
if (report != null)
|
||||
report(square);
|
||||
});
|
||||
|
||||
GridSquare best = squares.OrderByDescending(o => o.Score).First();
|
||||
C = best.C;
|
||||
Gamma = best.Gamma;
|
||||
|
||||
return squares.OrderBy(o=>o.C).ThenBy(o=>o.Gamma).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
415
BotSharp.MachineLearning/SVM/PerformanceEvaluator.cs
Normal file
415
BotSharp.MachineLearning/SVM/PerformanceEvaluator.cs
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Class encoding a member of a ranked set of labels.
|
||||
/// </summary>
|
||||
public class RankPair : IComparable<RankPair>
|
||||
{
|
||||
private double _score, _label;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="score">Score for this pair</param>
|
||||
/// <param name="label">Label associated with the given score</param>
|
||||
public RankPair(double score, double label)
|
||||
{
|
||||
_score = score;
|
||||
_label = label;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The score for this pair.
|
||||
/// </summary>
|
||||
public double Score
|
||||
{
|
||||
get
|
||||
{
|
||||
return _score;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Label for this pair.
|
||||
/// </summary>
|
||||
public double Label
|
||||
{
|
||||
get
|
||||
{
|
||||
return _label;
|
||||
}
|
||||
}
|
||||
|
||||
#region IComparable<RankPair> Members
|
||||
|
||||
/// <summary>
|
||||
/// Compares this pair to another. It will end up in a sorted list in decending score order.
|
||||
/// </summary>
|
||||
/// <param name="other">The pair to compare to</param>
|
||||
/// <returns>Whether this should come before or after the argument</returns>
|
||||
public int CompareTo(RankPair other)
|
||||
{
|
||||
return other.Score.CompareTo(Score);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this pair.
|
||||
/// </summary>
|
||||
/// <returns>A string in the for Score:Label</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}:{1}", Score, Label);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class encoding the point on a 2D curve.
|
||||
/// </summary>
|
||||
public class CurvePoint
|
||||
{
|
||||
private float _x, _y;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate</param>
|
||||
/// <param name="y">Y coordinate</param>
|
||||
public CurvePoint(float x, float y)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate
|
||||
/// </summary>
|
||||
public float X
|
||||
{
|
||||
get
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Y coordinate
|
||||
/// </summary>
|
||||
public float Y
|
||||
{
|
||||
get
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a string representation of this point.
|
||||
/// </summary>
|
||||
/// <returns>string in the form (x, y)</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1})", _x, _y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class which evaluates an SVM model using several standard techniques.
|
||||
/// </summary>
|
||||
public class PerformanceEvaluator
|
||||
{
|
||||
private class ChangePoint
|
||||
{
|
||||
public ChangePoint(int tp, int fp, int tn, int fn)
|
||||
{
|
||||
TP = tp;
|
||||
FP = fp;
|
||||
TN = tn;
|
||||
FN = fn;
|
||||
}
|
||||
|
||||
public int TP, FP, TN, FN;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}:{1}:{2}:{3}", TP, FP, TN, FN);
|
||||
}
|
||||
}
|
||||
|
||||
private List<CurvePoint> _prCurve;
|
||||
private double _ap;
|
||||
|
||||
private List<CurvePoint> _rocCurve;
|
||||
private double _auc;
|
||||
|
||||
private List<RankPair> _data;
|
||||
private List<ChangePoint> _changes;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="set">A pre-computed ranked pair set</param>
|
||||
public PerformanceEvaluator(List<RankPair> set)
|
||||
{
|
||||
_data = set;
|
||||
computeStatistics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="model">Model to evaluate</param>
|
||||
/// <param name="problem">Problem to evaluate</param>
|
||||
/// <param name="category">Label to be evaluate for</param>
|
||||
public PerformanceEvaluator(Model model, Problem problem, double category) : this(model, problem, category, "tmp.results") { }
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="model">Model to evaluate</param>
|
||||
/// <param name="problem">Problem to evaluate</param>
|
||||
/// <param name="resultsFile">Results file for output</param>
|
||||
/// <param name="category">Category to evaluate for</param>
|
||||
public PerformanceEvaluator(Model model, Problem problem, double category, string resultsFile)
|
||||
{
|
||||
Prediction.Predict(problem, resultsFile, model, true);
|
||||
parseResultsFile(resultsFile, problem.Y, category);
|
||||
|
||||
computeStatistics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="resultsFile">Results file</param>
|
||||
/// <param name="correctLabels">The correct labels of each data item</param>
|
||||
/// <param name="category">The category to evaluate for</param>
|
||||
public PerformanceEvaluator(string resultsFile, double[] correctLabels, double category)
|
||||
{
|
||||
parseResultsFile(resultsFile, correctLabels, category);
|
||||
computeStatistics();
|
||||
}
|
||||
|
||||
private void parseResultsFile(string resultsFile, double[] labels, double category)
|
||||
{
|
||||
StreamReader input = new StreamReader(resultsFile);
|
||||
string[] parts = input.ReadLine().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
int confidenceIndex = -1;
|
||||
for (int i = 1; i < parts.Length; i++)
|
||||
if (double.Parse(parts[i], CultureInfo.InvariantCulture) == category)
|
||||
{
|
||||
confidenceIndex = i;
|
||||
break;
|
||||
}
|
||||
_data = new List<RankPair>();
|
||||
for (int i = 0; i < labels.Length; i++)
|
||||
{
|
||||
parts = input.ReadLine().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
double confidence = double.Parse(parts[confidenceIndex], CultureInfo.InvariantCulture);
|
||||
_data.Add(new RankPair(confidence, labels[i] == category ? 1 : 0));
|
||||
}
|
||||
input.Close();
|
||||
}
|
||||
|
||||
private void computeStatistics()
|
||||
{
|
||||
_data.Sort();
|
||||
|
||||
findChanges();
|
||||
computePR();
|
||||
computeRoC();
|
||||
}
|
||||
|
||||
private void findChanges()
|
||||
{
|
||||
int tp, fp, tn, fn;
|
||||
tp = fp = tn = fn = 0;
|
||||
for (int i = 0; i < _data.Count; i++)
|
||||
{
|
||||
if (_data[i].Label == 1)
|
||||
fn++;
|
||||
else tn++;
|
||||
}
|
||||
_changes = new List<ChangePoint>();
|
||||
for (int i = 0; i < _data.Count; i++)
|
||||
{
|
||||
if (_data[i].Label == 1)
|
||||
{
|
||||
tp++;
|
||||
fn--;
|
||||
}
|
||||
else
|
||||
{
|
||||
fp++;
|
||||
tn--;
|
||||
}
|
||||
_changes.Add(new ChangePoint(tp, fp, tn, fn));
|
||||
}
|
||||
}
|
||||
|
||||
private float computePrecision(ChangePoint p)
|
||||
{
|
||||
return (float)p.TP / (p.TP + p.FP);
|
||||
}
|
||||
|
||||
private float computeRecall(ChangePoint p)
|
||||
{
|
||||
return (float)p.TP / (p.TP + p.FN);
|
||||
}
|
||||
|
||||
private void computePR()
|
||||
{
|
||||
_prCurve = new List<CurvePoint>();
|
||||
_prCurve.Add(new CurvePoint(0, 1));
|
||||
float precision = computePrecision(_changes[0]);
|
||||
float recall = computeRecall(_changes[0]);
|
||||
float precisionSum = 0;
|
||||
if (_changes[0].TP > 0)
|
||||
{
|
||||
precisionSum += precision;
|
||||
_prCurve.Add(new CurvePoint(recall, precision));
|
||||
}
|
||||
for (int i = 1; i < _changes.Count; i++)
|
||||
{
|
||||
precision = computePrecision(_changes[i]);
|
||||
recall = computeRecall(_changes[i]);
|
||||
if (_changes[i].TP > _changes[i - 1].TP)
|
||||
{
|
||||
precisionSum += precision;
|
||||
_prCurve.Add(new CurvePoint(recall, precision));
|
||||
}
|
||||
}
|
||||
_prCurve.Add(new CurvePoint(1, (float)(_changes[0].TP + _changes[0].FN) / (_changes[0].FP + _changes[0].TN)));
|
||||
_ap = precisionSum / (_changes[0].FN + _changes[0].TP);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the Precision-Recall curve to a tab-delimited file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename for output</param>
|
||||
public void WritePRCurve(string filename)
|
||||
{
|
||||
StreamWriter output = new StreamWriter(filename);
|
||||
output.WriteLine(_ap);
|
||||
for (int i = 0; i < _prCurve.Count; i++)
|
||||
output.WriteLine("{0}\t{1}", _prCurve[i].X, _prCurve[i].Y);
|
||||
output.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the Receiver Operating Characteristic curve to a tab-delimited file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename for output</param>
|
||||
public void WriteROCCurve(string filename)
|
||||
{
|
||||
StreamWriter output = new StreamWriter(filename);
|
||||
output.WriteLine(_auc);
|
||||
for (int i = 0; i < _rocCurve.Count; i++)
|
||||
output.WriteLine("{0}\t{1}", _rocCurve[i].X, _rocCurve[i].Y);
|
||||
output.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receiver Operating Characteristic curve
|
||||
/// </summary>
|
||||
public List<CurvePoint> ROCCurve
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rocCurve;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the area under the ROC Curve
|
||||
/// </summary>
|
||||
public double AuC
|
||||
{
|
||||
get
|
||||
{
|
||||
return _auc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Precision-Recall curve
|
||||
/// </summary>
|
||||
public List<CurvePoint> PRCurve
|
||||
{
|
||||
get
|
||||
{
|
||||
return _prCurve;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The average precision
|
||||
/// </summary>
|
||||
public double AP
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ap;
|
||||
}
|
||||
}
|
||||
|
||||
private float computeTPR(ChangePoint cp)
|
||||
{
|
||||
return computeRecall(cp);
|
||||
}
|
||||
|
||||
private float computeFPR(ChangePoint cp)
|
||||
{
|
||||
return (float)cp.FP / (cp.FP + cp.TN);
|
||||
}
|
||||
|
||||
private void computeRoC()
|
||||
{
|
||||
_rocCurve = new List<CurvePoint>();
|
||||
_rocCurve.Add(new CurvePoint(0, 0));
|
||||
float tpr = computeTPR(_changes[0]);
|
||||
float fpr = computeFPR(_changes[0]);
|
||||
_rocCurve.Add(new CurvePoint(fpr, tpr));
|
||||
_auc = 0;
|
||||
for (int i = 1; i < _changes.Count; i++)
|
||||
{
|
||||
float newTPR = computeTPR(_changes[i]);
|
||||
float newFPR = computeFPR(_changes[i]);
|
||||
if (_changes[i].TP > _changes[i - 1].TP)
|
||||
{
|
||||
_auc += tpr * (newFPR - fpr) + .5 * (newTPR - tpr) * (newFPR - fpr);
|
||||
tpr = newTPR;
|
||||
fpr = newFPR;
|
||||
_rocCurve.Add(new CurvePoint(fpr, tpr));
|
||||
}
|
||||
}
|
||||
_rocCurve.Add(new CurvePoint(1, 1));
|
||||
_auc += tpr * (1 - fpr) + .5 * (1 - tpr) * (1 - fpr);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
117
BotSharp.MachineLearning/SVM/PrecomputedKernel.cs
Normal file
117
BotSharp.MachineLearning/SVM/PrecomputedKernel.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Class encapsulating a precomputed kernel, where each position indicates the similarity score for two items in the training data.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class PrecomputedKernel
|
||||
{
|
||||
private float[,] _similarities;
|
||||
private int _rows;
|
||||
private int _columns;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="similarities">The similarity scores between all items in the training data</param>
|
||||
public PrecomputedKernel(float[,] similarities)
|
||||
{
|
||||
_similarities = similarities;
|
||||
_rows = _similarities.GetLength(0);
|
||||
_columns = _similarities.GetLength(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="nodes">Nodes for self-similarity analysis</param>
|
||||
/// <param name="param">Parameters to use when computing similarities</param>
|
||||
public PrecomputedKernel(List<Node[]> nodes, Parameter param)
|
||||
{
|
||||
_rows = nodes.Count;
|
||||
_columns = _rows;
|
||||
_similarities = new float[_rows, _columns];
|
||||
for (int r = 0; r < _rows; r++)
|
||||
{
|
||||
for (int c = 0; c < r; c++)
|
||||
_similarities[r, c] = _similarities[c, r];
|
||||
_similarities[r, r] = 1;
|
||||
for (int c = r + 1; c < _columns; c++)
|
||||
_similarities[r, c] = (float)Kernel.KernelFunction(nodes[r], nodes[c], param);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="rows">Nodes to use as the rows of the matrix</param>
|
||||
/// <param name="columns">Nodes to use as the columns of the matrix</param>
|
||||
/// <param name="param">Parameters to use when compute similarities</param>
|
||||
public PrecomputedKernel(List<Node[]> rows, List<Node[]> columns, Parameter param)
|
||||
{
|
||||
_rows = rows.Count;
|
||||
_columns = columns.Count;
|
||||
_similarities = new float[_rows, _columns];
|
||||
for (int r = 0; r < _rows; r++)
|
||||
for (int c = 0; c < _columns; c++)
|
||||
_similarities[r, c] = (float)Kernel.KernelFunction(rows[r], columns[c], param);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Problem"/> object using the labels provided. If a label is set to "0" that item is ignored.
|
||||
/// </summary>
|
||||
/// <param name="rowLabels">The labels for the row items</param>
|
||||
/// <param name="columnLabels">The labels for the column items</param>
|
||||
/// <returns>A <see cref="Problem"/> object</returns>
|
||||
public Problem Compute(double[] rowLabels, double[] columnLabels)
|
||||
{
|
||||
List<Node[]> X = new List<Node[]>();
|
||||
List<double> Y = new List<double>();
|
||||
int maxIndex = 0;
|
||||
for (int i = 0; i < columnLabels.Length; i++)
|
||||
if (columnLabels[i] != 0)
|
||||
maxIndex++;
|
||||
maxIndex++;
|
||||
for (int r = 0; r < _rows; r++)
|
||||
{
|
||||
if (rowLabels[r] == 0)
|
||||
continue;
|
||||
List<Node> nodes = new List<Node>();
|
||||
nodes.Add(new Node(0, X.Count + 1));
|
||||
for (int c = 0; c < _columns; c++)
|
||||
{
|
||||
if (columnLabels[c] == 0)
|
||||
continue;
|
||||
double value = _similarities[r, c];
|
||||
nodes.Add(new Node(nodes.Count, value));
|
||||
}
|
||||
X.Add(nodes.ToArray());
|
||||
Y.Add(rowLabels[r]);
|
||||
}
|
||||
return new Problem(X.Count, Y.ToArray(), X.ToArray(), maxIndex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
214
BotSharp.MachineLearning/SVM/Prediction.cs
Normal file
214
BotSharp.MachineLearning/SVM/Prediction.cs
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Class containing the routines to perform class membership prediction using a trained SVM.
|
||||
/// </summary>
|
||||
public static class Prediction
|
||||
{
|
||||
/// <summary>
|
||||
/// Predicts the class memberships of all the vectors in the problem.
|
||||
/// </summary>
|
||||
/// <param name="problem">The SVM Problem to solve</param>
|
||||
/// <param name="outputFile">File for result output</param>
|
||||
/// <param name="model">The Model to use</param>
|
||||
/// <param name="predict_probability">Whether to output a distribution over the classes</param>
|
||||
/// <returns>Percentage correctly labelled</returns>
|
||||
public static double Predict(
|
||||
Problem problem,
|
||||
string outputFile,
|
||||
Model model,
|
||||
bool predict_probability)
|
||||
{
|
||||
int correct = 0;
|
||||
int total = 0;
|
||||
double error = 0;
|
||||
double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0;
|
||||
StreamWriter output = outputFile != null ? new StreamWriter(outputFile) : null;
|
||||
|
||||
SvmType svm_type = Procedures.svm_get_svm_type(model);
|
||||
int nr_class = Procedures.svm_get_nr_class(model);
|
||||
int[] labels = new int[nr_class];
|
||||
double[] prob_estimates = null;
|
||||
|
||||
if (predict_probability)
|
||||
{
|
||||
if (svm_type == SvmType.EPSILON_SVR || svm_type == SvmType.NU_SVR)
|
||||
{
|
||||
Console.WriteLine("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma=" + Procedures.svm_get_svr_probability(model));
|
||||
}
|
||||
else
|
||||
{
|
||||
Procedures.svm_get_labels(model, labels);
|
||||
prob_estimates = new double[nr_class];
|
||||
if (output != null)
|
||||
{
|
||||
output.Write("labels");
|
||||
for (int j = 0; j < nr_class; j++)
|
||||
{
|
||||
output.Write(" " + labels[j]);
|
||||
}
|
||||
output.Write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < problem.Count; i++)
|
||||
{
|
||||
double target = problem.Y[i];
|
||||
Node[] x = problem.X[i];
|
||||
|
||||
double v;
|
||||
if (predict_probability && (svm_type == SvmType.C_SVC || svm_type == SvmType.NU_SVC))
|
||||
{
|
||||
v = Procedures.svm_predict_probability(model, x, prob_estimates);
|
||||
if (output != null)
|
||||
{
|
||||
output.Write(v + " ");
|
||||
for (int j = 0; j < nr_class; j++)
|
||||
{
|
||||
output.Write(prob_estimates[j] + " ");
|
||||
}
|
||||
output.Write("\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
v = Procedures.svm_predict(model, x);
|
||||
if(output != null)
|
||||
output.Write(v + "\n");
|
||||
}
|
||||
|
||||
if (v == target)
|
||||
++correct;
|
||||
error += (v - target) * (v - target);
|
||||
sumv += v;
|
||||
sumy += target;
|
||||
sumvv += v * v;
|
||||
sumyy += target * target;
|
||||
sumvy += v * target;
|
||||
++total;
|
||||
}
|
||||
if(output != null)
|
||||
output.Close();
|
||||
|
||||
if (model.Parameter.SvmType == SvmType.EPSILON_SVR || model.Parameter.SvmType == SvmType.NU_SVR)
|
||||
return error / total;
|
||||
else return (double)correct / total;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predict the labels for all the data points in a problem.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to use</param>
|
||||
/// <param name="problem">The problem to solve</param>
|
||||
/// <returns>The predicted labels</returns>
|
||||
public static double[] PredictLabels(this Model model, Problem problem)
|
||||
{
|
||||
return problem.X.Select(o => model.Predict(o)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predict the probability distributions over all labels for each data point in a problem.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to use</param>
|
||||
/// <param name="problem">The problem to solve</param>
|
||||
/// <returns>A distribution over labels for each data point</returns>
|
||||
public static double[][] PredictLabelsProbability(this Model model, Problem problem)
|
||||
{
|
||||
return problem.X.Select(o => model.PredictProbability(o)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predict the class for a single input vector.
|
||||
/// </summary>
|
||||
/// <param name="model">The Model to use for prediction</param>
|
||||
/// <param name="x">The vector for which to predict class</param>
|
||||
/// <returns>The result</returns>
|
||||
public static double Predict(this Model model, Node[] x)
|
||||
{
|
||||
return Procedures.svm_predict(model, x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts a class distribution for the single input vector.
|
||||
/// </summary>
|
||||
/// <param name="model">Model to use for prediction</param>
|
||||
/// <param name="x">The vector for which to predict the class distribution</param>
|
||||
/// <returns>A probability distribtion over classes</returns>
|
||||
public static double[] PredictProbability(this Model model, Node[] x)
|
||||
{
|
||||
SvmType svm_type = Procedures.svm_get_svm_type(model);
|
||||
if (svm_type != SvmType.C_SVC && svm_type != SvmType.NU_SVC)
|
||||
throw new Exception("Model type " + svm_type + " unable to predict probabilities.");
|
||||
int nr_class = Procedures.svm_get_nr_class(model);
|
||||
double[] probEstimates = new double[nr_class];
|
||||
Procedures.svm_predict_probability(model, x, probEstimates);
|
||||
return probEstimates;
|
||||
}
|
||||
|
||||
private static void exit_with_help()
|
||||
{
|
||||
Debug.Write("usage: svm_predict [options] test_file model_file output_file\n" + "options:\n" + "-b probability_estimates: whether to predict probability estimates, 0 or 1 (default 0); one-class SVM not supported yet\n");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy method, provided to allow usage as though this were the command line version of libsvm.
|
||||
/// </summary>
|
||||
/// <param name="args">Standard arguments passed to the svm_predict exectutable. See libsvm documentation for details.</param>
|
||||
[Obsolete("Use the other version of Predict() instead")]
|
||||
public static void Predict(params string[] args)
|
||||
{
|
||||
int i = 0;
|
||||
bool predictProbability = false;
|
||||
|
||||
// parse options
|
||||
for (i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i][0] != '-')
|
||||
break;
|
||||
++i;
|
||||
switch (args[i - 1][1])
|
||||
{
|
||||
|
||||
case 'b':
|
||||
predictProbability = int.Parse(args[i]) == 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unknown option");
|
||||
|
||||
}
|
||||
}
|
||||
if (i >= args.Length)
|
||||
throw new ArgumentException("No input, model and output files provided");
|
||||
|
||||
Problem problem = Problem.Read(args[i]);
|
||||
Model model = Model.Read(args[i + 1]);
|
||||
Predict(problem, args[i + 2], model, predictProbability);
|
||||
}
|
||||
}
|
||||
}
|
||||
190
BotSharp.MachineLearning/SVM/Problem.cs
Normal file
190
BotSharp.MachineLearning/SVM/Problem.cs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates a problem, or set of vectors which must be classified.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Problem
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="count">Number of vectors</param>
|
||||
/// <param name="y">The class labels</param>
|
||||
/// <param name="x">Vector data.</param>
|
||||
/// <param name="maxIndex">Maximum index for a vector</param>
|
||||
public Problem(int count, double[] y, Node[][] x, int maxIndex)
|
||||
{
|
||||
Count = count;
|
||||
Y = y;
|
||||
X = x;
|
||||
MaxIndex = maxIndex;
|
||||
}
|
||||
/// <summary>
|
||||
/// Empty Constructor. Nothing is initialized.
|
||||
/// </summary>
|
||||
public Problem()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Number of vectors.
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Class labels.
|
||||
/// </summary>
|
||||
public double[] Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vector data.
|
||||
/// </summary>
|
||||
public Node[][] X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum index for a vector.
|
||||
/// </summary>
|
||||
public int MaxIndex { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Problem other = obj as Problem;
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return other.Count == Count &&
|
||||
other.MaxIndex == MaxIndex &&
|
||||
other.X.IsEqual(X) &&
|
||||
other.Y.IsEqual(Y);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Count.GetHashCode() +
|
||||
MaxIndex.GetHashCode() +
|
||||
X.ComputeHashcode() +
|
||||
Y.ComputeHashcode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a problem from a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream to read from</param>
|
||||
/// <returns>The problem</returns>
|
||||
public static Problem Read(Stream stream)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamReader input = new StreamReader(stream);
|
||||
List<double> vy = new List<double>();
|
||||
List<Node[]> vx = new List<Node[]>();
|
||||
int max_index = 0;
|
||||
|
||||
while (input.Peek() > -1)
|
||||
{
|
||||
string[] parts = input.ReadLine().Trim().Split();
|
||||
|
||||
vy.Add(double.Parse(parts[0]));
|
||||
int m = parts.Length - 1;
|
||||
Node[] x = new Node[m];
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
x[j] = new Node();
|
||||
string[] nodeParts = parts[j + 1].Split(':');
|
||||
x[j].Index = int.Parse(nodeParts[0]);
|
||||
x[j].Value = double.Parse(nodeParts[1]);
|
||||
}
|
||||
if (m > 0)
|
||||
max_index = Math.Max(max_index, x[m - 1].Index);
|
||||
vx.Add(x);
|
||||
}
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
|
||||
return new Problem(vy.Count, vy.ToArray(), vx.ToArray(), max_index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a problem to a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to write the problem to.</param>
|
||||
/// <param name="problem">The problem to write.</param>
|
||||
public static void Write(Stream stream, Problem problem)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamWriter output = new StreamWriter(stream);
|
||||
for (int i = 0; i < problem.Count; i++)
|
||||
{
|
||||
output.Write(problem.Y[i]);
|
||||
for (int j = 0; j < problem.X[i].Length; j++)
|
||||
output.Write(" {0}:{1:0.000000}", problem.X[i][j].Index, problem.X[i][j].Value);
|
||||
output.Write("\n");
|
||||
}
|
||||
output.Flush();
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Problem from a file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The file to read from.</param>
|
||||
/// <returns>the Probem</returns>
|
||||
public static Problem Read(string filename)
|
||||
{
|
||||
FileStream input = File.OpenRead(filename);
|
||||
try
|
||||
{
|
||||
return Read(input);
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a problem to a file. This will overwrite any previous data in the file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The file to write to</param>
|
||||
/// <param name="problem">The problem to write</param>
|
||||
public static void Write(string filename, Problem problem)
|
||||
{
|
||||
FileStream output = File.Open(filename, FileMode.Create);
|
||||
try
|
||||
{
|
||||
Write(output, problem);
|
||||
}
|
||||
finally
|
||||
{
|
||||
output.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
245
BotSharp.MachineLearning/SVM/RangeTransform.cs
Normal file
245
BotSharp.MachineLearning/SVM/RangeTransform.cs
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Class which encapsulates a range transformation.
|
||||
/// </summary>
|
||||
public class RangeTransform : IRangeTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Default lower bound for scaling (-1).
|
||||
/// </summary>
|
||||
public const int DEFAULT_LOWER_BOUND = -1;
|
||||
/// <summary>
|
||||
/// Default upper bound for scaling (1).
|
||||
/// </summary>
|
||||
public const int DEFAULT_UPPER_BOUND = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the Range transform for the provided problem. Uses the default lower and upper bounds.
|
||||
/// </summary>
|
||||
/// <param name="prob">The Problem to analyze</param>
|
||||
/// <returns>The Range transform for the problem</returns>
|
||||
public static RangeTransform Compute(Problem prob)
|
||||
{
|
||||
return Compute(prob, DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines the Range transform for the provided problem.
|
||||
/// </summary>
|
||||
/// <param name="prob">The Problem to analyze</param>
|
||||
/// <param name="lowerBound">The lower bound for scaling</param>
|
||||
/// <param name="upperBound">The upper bound for scaling</param>
|
||||
/// <returns>The Range transform for the problem</returns>
|
||||
public static RangeTransform Compute(Problem prob, double lowerBound, double upperBound)
|
||||
{
|
||||
double[] minVals = new double[prob.MaxIndex];
|
||||
double[] maxVals = new double[prob.MaxIndex];
|
||||
for (int i = 0; i < prob.MaxIndex; i++)
|
||||
{
|
||||
minVals[i] = double.MaxValue;
|
||||
maxVals[i] = double.MinValue;
|
||||
}
|
||||
for (int i = 0; i < prob.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < prob.X[i].Length; j++)
|
||||
{
|
||||
int index = prob.X[i][j].Index - 1;
|
||||
double value = prob.X[i][j].Value;
|
||||
minVals[index] = Math.Min(minVals[index], value);
|
||||
maxVals[index] = Math.Max(maxVals[index], value);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < prob.MaxIndex; i++)
|
||||
{
|
||||
if (minVals[i] == double.MaxValue || maxVals[i] == double.MinValue)
|
||||
{
|
||||
minVals[i] = 0;
|
||||
maxVals[i] = 0;
|
||||
}
|
||||
}
|
||||
return new RangeTransform(minVals, maxVals, lowerBound, upperBound);
|
||||
}
|
||||
|
||||
private double[] _inputStart;
|
||||
private double[] _inputScale;
|
||||
private double _outputStart;
|
||||
private double _outputScale;
|
||||
private int _length;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="minValues">The minimum values in each dimension.</param>
|
||||
/// <param name="maxValues">The maximum values in each dimension.</param>
|
||||
/// <param name="lowerBound">The desired lower bound for all dimensions.</param>
|
||||
/// <param name="upperBound">The desired upper bound for all dimensions.</param>
|
||||
public RangeTransform(double[] minValues, double[] maxValues, double lowerBound, double upperBound)
|
||||
{
|
||||
_length = minValues.Length;
|
||||
if(maxValues.Length != _length)
|
||||
throw new Exception("Number of max and min values must be equal.");
|
||||
_inputStart = new double[_length];
|
||||
_inputScale = new double[_length];
|
||||
for (int i = 0; i < _length; i++)
|
||||
{
|
||||
_inputStart[i] = minValues[i];
|
||||
_inputScale[i] = maxValues[i] - minValues[i];
|
||||
}
|
||||
_outputStart = lowerBound;
|
||||
_outputScale = upperBound - lowerBound;
|
||||
}
|
||||
private RangeTransform(double[] inputStart, double[] inputScale, double outputStart, double outputScale, int length)
|
||||
{
|
||||
_inputStart = inputStart;
|
||||
_inputScale = inputScale;
|
||||
_outputStart = outputStart;
|
||||
_outputScale = outputScale;
|
||||
_length = length;
|
||||
}
|
||||
/// <summary>
|
||||
/// Transforms the input array based upon the values provided.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array</param>
|
||||
/// <returns>A scaled array</returns>
|
||||
public Node[] Transform(Node[] input)
|
||||
{
|
||||
Node[] output = new Node[input.Length];
|
||||
for (int i = 0; i < output.Length; i++)
|
||||
{
|
||||
int index = input[i].Index;
|
||||
double value = input[i].Value;
|
||||
output[i] = new Node(index, Transform(value, index));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms this an input value using the scaling transform for the provided dimension.
|
||||
/// </summary>
|
||||
/// <param name="input">The input value to transform</param>
|
||||
/// <param name="index">The dimension whose scaling transform should be used</param>
|
||||
/// <returns>The scaled value</returns>
|
||||
public double Transform(double input, int index)
|
||||
{
|
||||
index--;
|
||||
double tmp = input - _inputStart[index];
|
||||
if (_inputScale[index] == 0)
|
||||
return 0;
|
||||
tmp /= _inputScale[index];
|
||||
tmp *= _outputScale;
|
||||
return tmp + _outputStart;
|
||||
}
|
||||
/// <summary>
|
||||
/// Writes this Range transform to a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to write to</param>
|
||||
/// <param name="r">The range to write</param>
|
||||
public static void Write(Stream stream, RangeTransform r)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamWriter output = new StreamWriter(stream);
|
||||
output.WriteLine(r._length);
|
||||
output.Write(r._inputStart[0]);
|
||||
for(int i=1; i<r._inputStart.Length; i++)
|
||||
output.Write(" " + r._inputStart[i]);
|
||||
output.WriteLine();
|
||||
output.Write(r._inputScale[0]);
|
||||
for (int i = 1; i < r._inputScale.Length; i++)
|
||||
output.Write(" " + r._inputScale[i]);
|
||||
output.WriteLine();
|
||||
output.WriteLine("{0} {1}", r._outputStart, r._outputScale);
|
||||
output.Flush();
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes this Range transform to a file. This will overwrite any previous data in the file.
|
||||
/// </summary>
|
||||
/// <param name="outputFile">The file to write to</param>
|
||||
/// <param name="r">The Range to write</param>
|
||||
public static void Write(string outputFile, RangeTransform r)
|
||||
{
|
||||
FileStream s = File.Open(outputFile, FileMode.Create);
|
||||
try
|
||||
{
|
||||
Write(s, r);
|
||||
}
|
||||
finally
|
||||
{
|
||||
s.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Range transform from a file.
|
||||
/// </summary>
|
||||
/// <param name="inputFile">The file to read from</param>
|
||||
/// <returns>The Range transform</returns>
|
||||
public static RangeTransform Read(string inputFile)
|
||||
{
|
||||
FileStream s = File.OpenRead(inputFile);
|
||||
try
|
||||
{
|
||||
return Read(s);
|
||||
}
|
||||
finally
|
||||
{
|
||||
s.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Range transform from a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <returns>The Range transform</returns>
|
||||
public static RangeTransform Read(Stream stream)
|
||||
{
|
||||
TemporaryCulture.Start();
|
||||
|
||||
StreamReader input = new StreamReader(stream);
|
||||
int length = int.Parse(input.ReadLine());
|
||||
double[] inputStart = new double[length];
|
||||
double[] inputScale = new double[length];
|
||||
string[] parts = input.ReadLine().Split();
|
||||
for (int i = 0; i < length; i++)
|
||||
inputStart[i] = double.Parse(parts[i]);
|
||||
parts = input.ReadLine().Split();
|
||||
for (int i = 0; i < length; i++)
|
||||
inputScale[i] = double.Parse(parts[i]);
|
||||
parts = input.ReadLine().Split();
|
||||
double outputStart = double.Parse(parts[0]);
|
||||
double outputScale = double.Parse(parts[1]);
|
||||
|
||||
TemporaryCulture.Stop();
|
||||
|
||||
return new RangeTransform(inputStart, inputScale, outputStart, outputScale, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
BotSharp.MachineLearning/SVM/SVMExtensions.cs
Normal file
82
BotSharp.MachineLearning/SVM/SVMExtensions.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
internal static class SVMExtensions
|
||||
{
|
||||
private const double PRECISION = 1000000.0;
|
||||
|
||||
public static double Truncate(this double x)
|
||||
{
|
||||
return Math.Round(x * PRECISION) / PRECISION;
|
||||
}
|
||||
|
||||
public static void SwapIndex<T>(this T[] list, int i, int j)
|
||||
{
|
||||
T tmp = list[i];
|
||||
list[i] = list[j];
|
||||
list[j] = tmp;
|
||||
}
|
||||
|
||||
public static bool IsEqual<T>(this T[][] lhs, T[][] rhs)
|
||||
{
|
||||
if (lhs.Length != rhs.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < lhs.Length; i++)
|
||||
{
|
||||
if (!lhs[i].IsEqual(rhs[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsEqual<T>(this T[] lhs, T[] rhs)
|
||||
{
|
||||
if (lhs.Length != rhs.Length)
|
||||
return false;
|
||||
for (int i = 0; i < lhs.Length; i++)
|
||||
if (!lhs[i].Equals(rhs[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsEqual(this double[] lhs, double[] rhs)
|
||||
{
|
||||
if (lhs.Length != rhs.Length)
|
||||
return false;
|
||||
for (int i = 0; i < lhs.Length; i++)
|
||||
{
|
||||
double x = lhs[i].Truncate();
|
||||
double y = rhs[i].Truncate();
|
||||
if (x != y)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsEqual(this double[][] lhs, double[][] rhs)
|
||||
{
|
||||
if (lhs.Length != rhs.Length)
|
||||
return false;
|
||||
for (int i = 0; i < lhs.Length; i++)
|
||||
if (!lhs[i].IsEqual(rhs[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int ComputeHashcode<T>(this T[] array)
|
||||
{
|
||||
return array.Sum(o => o.GetHashCode());
|
||||
}
|
||||
|
||||
public static int ComputeHashcode<T>(this T[][] array)
|
||||
{
|
||||
return array.Sum(o => o.ComputeHashcode());
|
||||
}
|
||||
}
|
||||
}
|
||||
116
BotSharp.MachineLearning/SVM/SVMUtilities.cs
Normal file
116
BotSharp.MachineLearning/SVM/SVMUtilities.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
using SVM;
|
||||
using SVM.BotSharp.MachineLearning;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearningTest
|
||||
{
|
||||
public static class SVMUtilities
|
||||
{
|
||||
private const double SCALE = 100;
|
||||
public const int TRAINING_SEED = 20080524;
|
||||
public const int TESTING_SEED = 20140407;
|
||||
|
||||
public static Problem CreateTwoClassProblem(int count, bool isTraining = true)
|
||||
{
|
||||
Problem prob = new Problem();
|
||||
prob.Count = count;
|
||||
prob.MaxIndex = 2;
|
||||
|
||||
Random rand = new Random(isTraining ? TRAINING_SEED : TESTING_SEED);
|
||||
// create points on either side of the vertical axis
|
||||
int positive = count / 2;
|
||||
List<double> labels = new List<double>();
|
||||
List<Node[]> data = new List<Node[]>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
double x = rand.NextDouble() * SCALE + 10;
|
||||
double y = rand.NextDouble() * SCALE - (SCALE * .5);
|
||||
x = i < positive ? x : -x;
|
||||
data.Add(new Node[] { new Node(1, x), new Node(2, y) });
|
||||
labels.Add(i < positive ? 1 : -1);
|
||||
}
|
||||
prob.X = data.ToArray();
|
||||
prob.Y = labels.ToArray();
|
||||
|
||||
return prob;
|
||||
}
|
||||
|
||||
public static Problem CreateMulticlassProblem(int numberOfClasses, int count, bool isTraining = true)
|
||||
{
|
||||
if (numberOfClasses > 8)
|
||||
throw new ArgumentException("Number of classes must be < 8");
|
||||
|
||||
Problem prob = new Problem();
|
||||
prob.Count = count;
|
||||
prob.MaxIndex = 3;
|
||||
|
||||
int[] samplesPerClass = new int[numberOfClasses];
|
||||
double countPerClass = (double)count / numberOfClasses;
|
||||
double current = countPerClass;
|
||||
for (int i = 1; i < samplesPerClass.Length; i++)
|
||||
{
|
||||
samplesPerClass[i] = (int)current;
|
||||
current += countPerClass;
|
||||
samplesPerClass[i - 1] = samplesPerClass[i] - samplesPerClass[i - 1];
|
||||
}
|
||||
samplesPerClass[samplesPerClass.Length - 1] = count - samplesPerClass.Last();
|
||||
|
||||
int[] xSigns = new int[8] { -1, 1, 1, -1, -1, 1, 1, -1 };
|
||||
int[] ySigns = new int[8] { 1, 1, -1, -1, 1, 1, -1, -1 };
|
||||
int[] zSigns = new int[8] { 1, 1, 1, 1, -1, -1, -1, -1 };
|
||||
|
||||
Random rand = new Random(isTraining ? TRAINING_SEED : TESTING_SEED);
|
||||
|
||||
List<double> labels = new List<double>();
|
||||
List<Node[]> data = new List<Node[]>();
|
||||
for (int i = 0; i < numberOfClasses; i++)
|
||||
{
|
||||
for (int j = 0; j < samplesPerClass[i]; j++)
|
||||
{
|
||||
double x = rand.NextDouble() * SCALE + 10;
|
||||
double y = rand.NextDouble() * SCALE + 10;
|
||||
double z = rand.NextDouble() * SCALE + 10;
|
||||
x *= xSigns[i];
|
||||
y *= ySigns[i];
|
||||
z *= zSigns[i];
|
||||
|
||||
data.Add(new Node[] { new Node(1, x), new Node(2, y), new Node(3, z) });
|
||||
labels.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
prob.X = data.ToArray();
|
||||
prob.Y = labels.ToArray();
|
||||
|
||||
return prob;
|
||||
}
|
||||
|
||||
public static Problem CreateRegressionProblem(int count, bool isTraining = true)
|
||||
{
|
||||
Problem prob = new Problem();
|
||||
prob.Count = count;
|
||||
prob.MaxIndex = 2;
|
||||
|
||||
Random rand = new Random(isTraining ? TRAINING_SEED : TESTING_SEED);
|
||||
|
||||
List<double> labels = new List<double>();
|
||||
List<Node[]> data = new List<Node[]>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
double y = rand.NextDouble() * 10 - 5;
|
||||
double z = rand.NextDouble() * 10 - 5;
|
||||
double x = 2 * y + z;
|
||||
data.Add(new Node[] { new Node(1, y), new Node(2, z) });
|
||||
labels.Add(x);
|
||||
}
|
||||
prob.X = data.ToArray();
|
||||
prob.Y = labels.ToArray();
|
||||
|
||||
return prob;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
BotSharp.MachineLearning/SVM/Scaling.cs
Normal file
49
BotSharp.MachineLearning/SVM/Scaling.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Deals with the scaling of Problems so they have uniform ranges across all dimensions in order to
|
||||
/// result in better SVM performance.
|
||||
/// </summary>
|
||||
public static class Scaling
|
||||
{
|
||||
/// <summary>
|
||||
/// Scales a problem using the provided range. This will not affect the parameter.
|
||||
/// </summary>
|
||||
/// <param name="prob">The problem to scale</param>
|
||||
/// <param name="range">The Range transform to use in scaling</param>
|
||||
/// <returns>The Scaled problem</returns>
|
||||
public static Problem Scale(this IRangeTransform range, Problem prob)
|
||||
{
|
||||
Problem scaledProblem = new Problem(prob.Count, new double[prob.Count], new Node[prob.Count][], prob.MaxIndex);
|
||||
for (int i = 0; i < scaledProblem.Count; i++)
|
||||
{
|
||||
scaledProblem.X[i] = new Node[prob.X[i].Length];
|
||||
for (int j = 0; j < scaledProblem.X[i].Length; j++)
|
||||
scaledProblem.X[i][j] = new Node(prob.X[i][j].Index, range.Transform(prob.X[i][j].Value, prob.X[i][j].Index));
|
||||
scaledProblem.Y[i] = prob.Y[i];
|
||||
}
|
||||
return scaledProblem;
|
||||
}
|
||||
}
|
||||
}
|
||||
2298
BotSharp.MachineLearning/SVM/Solver.cs
Normal file
2298
BotSharp.MachineLearning/SVM/Solver.cs
Normal file
File diff suppressed because it is too large
Load diff
25
BotSharp.MachineLearning/SVM/TemporaryCulture.cs
Normal file
25
BotSharp.MachineLearning/SVM/TemporaryCulture.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
internal static class TemporaryCulture
|
||||
{
|
||||
private static CultureInfo _culture;
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
_culture = Thread.CurrentThread.CurrentCulture;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = _culture;
|
||||
}
|
||||
}
|
||||
}
|
||||
232
BotSharp.MachineLearning/SVM/Training.cs
Normal file
232
BotSharp.MachineLearning/SVM/Training.cs
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SVM.BotSharp.MachineLearning
|
||||
{
|
||||
/// <summary>
|
||||
/// Class containing the routines to train SVM models.
|
||||
/// </summary>
|
||||
public static class Training
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the system will output information to the console during the training process.
|
||||
/// </summary>
|
||||
public static bool IsVerbose
|
||||
{
|
||||
get
|
||||
{
|
||||
return Procedures.IsVerbose;
|
||||
}
|
||||
set
|
||||
{
|
||||
Procedures.IsVerbose = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static double doCrossValidation(Problem problem, Parameter parameters, int nr_fold)
|
||||
{
|
||||
int i;
|
||||
double[] target = new double[problem.Count];
|
||||
Procedures.svm_cross_validation(problem, parameters, nr_fold, target);
|
||||
int total_correct = 0;
|
||||
double total_error = 0;
|
||||
double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0;
|
||||
if (parameters.SvmType == SvmType.EPSILON_SVR || parameters.SvmType == SvmType.NU_SVR)
|
||||
{
|
||||
for (i = 0; i < problem.Count; i++)
|
||||
{
|
||||
double y = problem.Y[i];
|
||||
double v = target[i];
|
||||
total_error += (v - y) * (v - y);
|
||||
sumv += v;
|
||||
sumy += y;
|
||||
sumvv += v * v;
|
||||
sumyy += y * y;
|
||||
sumvy += v * y;
|
||||
}
|
||||
return(problem.Count * sumvy - sumv * sumy) / (Math.Sqrt(problem.Count * sumvv - sumv * sumv) * Math.Sqrt(problem.Count * sumyy - sumy * sumy));
|
||||
}
|
||||
else
|
||||
for (i = 0; i < problem.Count; i++)
|
||||
if (target[i] == problem.Y[i])
|
||||
++total_correct;
|
||||
return (double)total_correct / problem.Count;
|
||||
}
|
||||
|
||||
public static void SetRandomSeed(int seed)
|
||||
{
|
||||
Procedures.setRandomSeed(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy. Allows use as if this was svm_train. See libsvm documentation for details on which arguments to pass.
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
[Obsolete("Provided only for legacy compatibility, use the other Train() methods")]
|
||||
public static void Train(params string[] args)
|
||||
{
|
||||
Parameter parameters;
|
||||
Problem problem;
|
||||
bool crossValidation;
|
||||
int nrfold;
|
||||
string modelFilename;
|
||||
parseCommandLine(args, out parameters, out problem, out crossValidation, out nrfold, out modelFilename);
|
||||
if (crossValidation)
|
||||
PerformCrossValidation(problem, parameters, nrfold);
|
||||
else Model.Write(modelFilename, Train(problem, parameters));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs cross validation.
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="parameters">The parameters to test</param>
|
||||
/// <param name="nrfold">The number of cross validations to use</param>
|
||||
/// <returns>The cross validation score</returns>
|
||||
public static double PerformCrossValidation(Problem problem, Parameter parameters, int nrfold)
|
||||
{
|
||||
string error = Procedures.svm_check_parameter(problem, parameters);
|
||||
if (error == null)
|
||||
return doCrossValidation(problem, parameters, nrfold);
|
||||
else throw new Exception(error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trains a model using the provided training data and parameters.
|
||||
/// </summary>
|
||||
/// <param name="problem">The training data</param>
|
||||
/// <param name="parameters">The parameters to use</param>
|
||||
/// <returns>A trained SVM Model</returns>
|
||||
public static Model Train(Problem problem, Parameter parameters)
|
||||
{
|
||||
string error = Procedures.svm_check_parameter(problem, parameters);
|
||||
|
||||
if (error == null)
|
||||
return Procedures.svm_train(problem, parameters);
|
||||
else throw new Exception(error);
|
||||
}
|
||||
|
||||
private static void parseCommandLine(string[] args, out Parameter parameters, out Problem problem, out bool crossValidation, out int nrfold, out string modelFilename)
|
||||
{
|
||||
int i;
|
||||
|
||||
parameters = new Parameter();
|
||||
// default values
|
||||
|
||||
crossValidation = false;
|
||||
nrfold = 0;
|
||||
|
||||
// parse options
|
||||
for (i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i][0] != '-')
|
||||
break;
|
||||
++i;
|
||||
switch (args[i - 1][1])
|
||||
{
|
||||
|
||||
case 's':
|
||||
parameters.SvmType = (SvmType)int.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
parameters.KernelType = (KernelType)int.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
parameters.Degree = int.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
parameters.Gamma = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
parameters.Coefficient0 = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
parameters.Nu = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
parameters.CacheSize = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
parameters.C = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
parameters.EPS = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
parameters.P = double.Parse(args[i]);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
parameters.Shrinking = int.Parse(args[i]) == 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
parameters.Probability = int.Parse(args[i]) == 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
crossValidation = true;
|
||||
nrfold = int.Parse(args[i]);
|
||||
if (nrfold < 2)
|
||||
{
|
||||
throw new ArgumentException("n-fold cross validation: n must >= 2");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
parameters.Weights[int.Parse(args[i - 1].Substring(2))] = double.Parse(args[1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unknown Parameter");
|
||||
}
|
||||
}
|
||||
|
||||
// determine filenames
|
||||
|
||||
if (i >= args.Length)
|
||||
throw new ArgumentException("No input file specified");
|
||||
|
||||
problem = Problem.Read(args[i]);
|
||||
|
||||
if (parameters.Gamma == 0)
|
||||
parameters.Gamma = 1.0 / problem.MaxIndex;
|
||||
|
||||
if (i < args.Length - 1)
|
||||
modelFilename = args[i + 1];
|
||||
else
|
||||
{
|
||||
int p = args[i].LastIndexOf('/') + 1;
|
||||
modelFilename = args[i].Substring(p) + ".model";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DevExpress.Xpo" Version="18.1.4" />
|
||||
<ProjectReference Include="..\BotSharp.MachineLearning\BotSharp.MachineLearning.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,22 @@
|
|||
using System;
|
||||
/*
|
||||
* BotSharp.NLP Library
|
||||
* Copyright (C) 2018 Haiping Chen
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,22 @@
|
|||
using System;
|
||||
/*
|
||||
* BotSharp.NLP Library
|
||||
* Copyright (C) 2018 Haiping Chen
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,22 @@
|
|||
using System;
|
||||
/*
|
||||
* BotSharp.NLP Library
|
||||
* Copyright (C) 2018 Haiping Chen
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
.. image:: https://raw.githubusercontent.com/Oceania2018/BotSharp/master/BotSharp.WebHost/wwwroot/images/BotSharp.png
|
||||
:height: 30px
|
||||
|
||||
The Open Source AI Bot Platform Builder for Enterprise
|
||||
The Open Source AI Bot Platform Builder for Enterprise
|
||||
======================================================
|
||||
|
||||
*"Conversation as a platform (CaaP) is the future, so it's perfect that we're already offering the whole toolkits to our enterprise developers using the BotSharp Bot Platform Builder to build a CaaP. It opens up as much learning power as possible for your enterprise robots and precisely control every step of the AI processing pipeline."*
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to BotSharp's documentation!
|
||||
====================================
|
||||
.. image:: https://raw.githubusercontent.com/Oceania2018/BotSharp/master/BotSharp.WebHost/wwwroot/images/BotSharp.png
|
||||
:height: 30px
|
||||
|
||||
.. include:: ../README.rst
|
||||
|
||||
.. toctree::
|
||||
|
|
|
|||
Loading…
Reference in a new issue