Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ We will be handling all ai algorithms using the same methods.
Here is an example to show how easy it is to use this library to get a trained model, get metrics for the trained model, and generate new predictions:

```cs
using AiDotNet;
using AiDotNet.Regression;

var inputs = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var outputs = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Expand All @@ -26,12 +26,13 @@ var predictions = simpleRegression.Predictions;
Here is an example for more advanced users to customize everything used in the algorithm:

```cs
using AiDotNet;
using AiDotNet.Regression;
using AiDotNet.Normalization;

var inputs = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var outputs = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var advancedSimpleRegression = new SimpleRegression(inputs, outputs, trainingPctSize: 20);
var advancedSimpleRegression = new SimpleRegression(inputs, outputs, trainingPctSize: 20, normalization: new LogNormalization());
var metrics = advancedSimpleRegression.Metrics;
var predictions = advancedSimpleRegression.Predictions;
```
2 changes: 1 addition & 1 deletion src/AiDotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>0.0.1-preview</Version>
<Version>0.0.3-preview</Version>
<Title>Ai for .Net</Title>
<Description>This is a preview library that will eventually showcase the latest and greatest in ai breakthroughs and bring them to the .net community</Description>
<Company>Ooples Finance</Company>
Expand Down
11 changes: 11 additions & 0 deletions src/Helpers/NormalizationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace AiDotNet.Helpers;

internal static class NormalizationHelper
{
public static (double[] trainingInputs, double[] trainingOutputs, double[] oosInputs, double[] oosOutputs) SplitData(double[] inputs, double[] outputs,
int trainingSize)
{
return (inputs.Take(trainingSize).ToArray(), outputs.Take(trainingSize).ToArray(),
inputs.Skip(trainingSize).ToArray(), outputs.Skip(trainingSize).ToArray());
}
}
2 changes: 2 additions & 0 deletions src/Helpers/UsingsHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using AiDotNet.Helpers;
global using AiDotNet.Interfaces;
6 changes: 5 additions & 1 deletion src/Interfaces/INormalization.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
namespace AiDotNet.Interfaces;

public interface INormalization
public abstract class INormalization
{
internal abstract double[] Normalize(double[] rawValues);

internal abstract (double[] trainingInputs, double[] trainingOutputs, double[] oosInputs, double[] oosOutputs)
PrepareData(double[] inputs, double[] outputs, int trainingSize);
}
3 changes: 3 additions & 0 deletions src/Interfaces/IRegression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

public abstract class IRegression
{
internal abstract (double[] trainingInputs, double[] trainingOutputs, double[] oosInputs, double[] oosOutputs)
PrepareData(double[] inputs, double[] outputs, int trainingSize, INormalization normalization);

internal abstract void Fit(double[] inputs, double[] outputs);

internal abstract double[] Transform(double[] inputs);
Expand Down
63 changes: 30 additions & 33 deletions src/Metrics/Metrics.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using AiDotNet.Interfaces;

namespace AiDotNet;
namespace AiDotNet;

public sealed class Metrics : IMetrics
{
Expand All @@ -14,24 +12,23 @@ public sealed class Metrics : IMetrics
public double AverageStandardDeviation { get; private set; }
public int DegreesOfFreedom { get; private set; }

private double[] _oosPredictions { get; }
private double _oosPredictionsAvg { get; }
private double[] _oosActualValues { get; }
private double _oosActualValuesAvg { get; }
private int _paramsCount { get; }
private int _sampleSize { get; }
private double _residualSumOfSquares { get; set; }
private double _totalSumOfSquares { get; set; }

public Metrics(double[] OosPredictions, double[] OosActualValues, int paramCount)
private double[] OosPredictions { get; }
private double OosPredictionsAvg { get; }
private double[] OosActualValues { get; }
private double OosActualValuesAvg { get; }
private int ParamsCount { get; }
private int SampleSize { get; }
private double ResidualSumOfSquares { get; set; }
private double TotalSumOfSquares { get; set; }

public Metrics(double[] oosPredictions, double[] oosActualValues, int paramCount)
{
_oosPredictions = OosPredictions;
_oosPredictionsAvg = _oosPredictions.Average();
_oosActualValues = OosActualValues;
_oosActualValuesAvg = _oosActualValues.Average();
_paramsCount = paramCount;
_sampleSize = _oosPredictions.Length;

OosPredictions = oosPredictions;
OosPredictionsAvg = oosPredictions.Average();
OosActualValues = oosActualValues;
OosActualValuesAvg = oosActualValues.Average();
ParamsCount = paramCount;
SampleSize = oosPredictions.Length;
DegreesOfFreedom = CalculateDegreesOfFreedom();
R2 = CalculateR2();
AdjustedR2 = CalculateAdjustedR2(R2);
Expand All @@ -45,7 +42,7 @@ public Metrics(double[] OosPredictions, double[] OosActualValues, int paramCount

internal override double CalculateMeanSquaredError()
{
return _residualSumOfSquares / _sampleSize;
return ResidualSumOfSquares / SampleSize;
}

internal override double CalculateRootMeanSquaredError()
Expand All @@ -56,32 +53,32 @@ internal override double CalculateRootMeanSquaredError()
internal override double CalculateR2()
{
double residualSumSquares = 0, totalSumSquares = 0;
for (int i = 0; i < _sampleSize; i++)
for (int i = 0; i < SampleSize; i++)
{
residualSumSquares += Math.Pow(_oosActualValues[i] - _oosPredictions[i], 2);
totalSumSquares += Math.Pow(_oosActualValues[i] - _oosActualValuesAvg, 2);
residualSumSquares += Math.Pow(OosActualValues[i] - OosPredictions[i], 2);
totalSumSquares += Math.Pow(OosActualValues[i] - OosActualValuesAvg, 2);
}

// We are saving these values for later reuse
_residualSumOfSquares = residualSumSquares;
_totalSumOfSquares = totalSumSquares;
ResidualSumOfSquares = residualSumSquares;
TotalSumOfSquares = totalSumSquares;

return _totalSumOfSquares != 0 ? 1 - (_residualSumOfSquares / _totalSumOfSquares) : 0;
return TotalSumOfSquares != 0 ? 1 - (ResidualSumOfSquares / TotalSumOfSquares) : 0;
}

internal override double CalculateAdjustedR2(double r2)
{
return _sampleSize != 1 && DegreesOfFreedom != 1 ? 1 - (1 - Math.Pow(r2, 2)) * (_sampleSize - 1) / (DegreesOfFreedom - 1) : 0;
return SampleSize != 1 && DegreesOfFreedom != 1 ? 1 - (1 - Math.Pow(r2, 2)) * (SampleSize - 1) / (DegreesOfFreedom - 1) : 0;
}

internal override double CalculateAverageStandardError()
{
return AverageStandardDeviation / Math.Sqrt(_sampleSize);
return AverageStandardDeviation / Math.Sqrt(SampleSize);
}

internal override double CalculatePredictionStandardError()
{
return PredictionsStandardDeviation / Math.Sqrt(_sampleSize);
return PredictionsStandardDeviation / Math.Sqrt(SampleSize);
}

private static double CalculateStandardDeviation(double avgSumSquares)
Expand All @@ -91,20 +88,20 @@ private static double CalculateStandardDeviation(double avgSumSquares)

internal override double CalculateAverageStandardDeviation()
{
var avgSumSquares = _totalSumOfSquares / _sampleSize;
var avgSumSquares = TotalSumOfSquares / SampleSize;

return CalculateStandardDeviation(avgSumSquares);
}

internal override double CalculatePredictionStandardDeviation()
{
var avgSumSquares = _residualSumOfSquares / _sampleSize;
var avgSumSquares = ResidualSumOfSquares / SampleSize;

return CalculateStandardDeviation(avgSumSquares);
}

internal override int CalculateDegreesOfFreedom()
{
return _sampleSize - _paramsCount;
return SampleSize - ParamsCount;
}
}
38 changes: 38 additions & 0 deletions src/Normalization/DecimalNormalization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace AiDotNet.Normalization;

public class DecimalNormalization : INormalization
{
internal override double[] Normalize(double[] rawValues)
{
var normalizedValues = new double[rawValues.Length];
var maxValue = rawValues.Max(Math.Abs);

var smallestMult = 0;
for (var i = 1; i < 100; i++)
{
if (!(maxValue / Math.Pow(10, i) <= 1)) continue;
smallestMult = i;
break;
}

if (smallestMult == 0)
{
throw new ArgumentException(
"There are either too many decimals in the raw values or there are invalid values such as NaN or Infinity in the raw values", nameof(rawValues));
}

for (var i = 0; i < rawValues.Length; i++)
{
normalizedValues.SetValue(rawValues[i] / Math.Pow(10, smallestMult), i);
}

return normalizedValues;
}

internal override (double[], double[], double[], double[]) PrepareData(double[] inputs, double[] outputs, int trainingSize)
{
var (trainingInputs, trainingOutputs, oosInputs, oosOutputs) = NormalizationHelper.SplitData(inputs, outputs, trainingSize);

return (Normalize(trainingInputs), Normalize(trainingOutputs), Normalize(oosInputs), Normalize(oosOutputs));
}
}
48 changes: 48 additions & 0 deletions src/Normalization/LogNormalization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace AiDotNet.Normalization;

public class LogNormalization : INormalization
{
internal override double[] Normalize(double[] rawValues)
{
var normalizedValues = new double[rawValues.Length];

for (var i = 0; i < rawValues.Length; i++)
{
normalizedValues.SetValue(Math.Log(rawValues[i]), i);
}

return normalizedValues;
}

internal override (double[], double[], double[], double[]) PrepareData(double[] inputs, double[] outputs, int trainingSize)
{
var preparedInputs = Normalize(inputs);
var preparedOutputs = Normalize(outputs);

if (preparedInputs.Contains(double.NaN))
{
throw new ArgumentException("Normalized Inputs can't contain NaN values. " +
"Log Normalization creates NaN values when a raw input value is negative.", nameof(inputs));
}

if (preparedOutputs.Contains(double.NaN))
{
throw new ArgumentException("Normalized Outputs can't contain NaN values. " +
"Log Normalization creates NaN values when a raw output value is negative.", nameof(outputs));
}

if (preparedInputs.Contains(double.PositiveInfinity) || preparedInputs.Contains(double.NegativeInfinity))
{
throw new ArgumentException("Normalized Inputs can't contain Infinity values. " +
"Log Normalization creates Infinity values when a raw input value is 0 or infinity.", nameof(inputs));
}

if (preparedOutputs.Contains(double.PositiveInfinity) || preparedOutputs.Contains(double.NegativeInfinity))
{
throw new ArgumentException("Normalized Outputs can't contain Infinity values. " +
"Log Normalization creates Infinity values when a raw output value is 0 or infinity.", nameof(outputs));
}

return NormalizationHelper.SplitData(preparedInputs, preparedOutputs, trainingSize);
}
}
Loading