public double Verify(double[] features, string identifier) { // trying to match on an identifier that hasn't been trained wouldn't make sense if (!trainedIdentifiers.Contains(identifier)) { throw new ArgumentException("Provided identifier does not match any trained identifiers"); } // scale/shift features to fit within standard range double[] standardizedFeatures = StandardizationHelpers.GenerateTransformedData(features, (value, i) => (value * featureScaling[i]) + featureShift[i]); return(classifier.Verify(standardizedFeatures, identifier)); }
public List <Tuple <double, double[]> > GetScaledDimensions(ISample sample) { return(StandardizationHelpers.GenerateTransformedData(sample.GetDimensions(), (value, i) => (value * featureScaling[i]) + featureShift[i]) .Select((value, i) => new Tuple <double, double[]>(i, new double[] { value })).ToList()); }
public override P Train <P>(List <ISample> trainingSamples) { InitializeTimeAndCountProperties(trainingSamples, columns); // get the aligned training samples Dictionary <int, List <AlignedRegressionColumn> > csAlignedSamples = FindApproximateDTWAlignment(trainingSamples, base.trainingColumnCount, base.trainingRowCount, bandwidth); // resample back to original number of training rows // the dtw path in the alignment for the sample with the lowest total cost (marked as IsTemplate) Templates = csAlignedSamples.Select(column => new { column = column.Key, template = column.Value.First(row => row.IsTemplate) }).ToDictionary(col => col.column, col => StandardizationHelpers.Resample(col.template.GetRows(), trainingRowCount)); // calculate the regression slopes for the set of aligned signals (key = time slopes, value = force slopes) AlignedRegressor regressor = GenerateRegressionSlopes <AlignedRegressionColumn, AlignedRegressor>(csAlignedSamples); AmplitudeSlopes = regressor.GetAmplitudeSlopes().ToDictionary(slopes => slopes.Key, slopes => StandardizationHelpers.Resample(slopes.Value, trainingRowCount)); // resample back to original number of training rows PhaseSlopes = regressor.GetPhaseSlopes().ToDictionary(slopes => slopes.Key, slopes => StandardizationHelpers.Resample(slopes.Value, trainingRowCount)); return(this as P); }
protected override List <Tuple <double, double[]> > GetTransformedRows(ISample sample, int[] columns) { if (!sample.GetDuration().HasValue) { throw new ArgumentException("Sample does not have duration"); } // find the dtw path between the sample and template for each column, use it to get the slopes mapping, then apply the amplitude/phase transformation for each feature var pathsToTemplates = Templates.Where(template => columns.Contains(template.Key)).Select(template => new { column = template.Key, pathToTemplate = FindDTWPath(template.Value, sample.GetDataRows(template.Key), bandwidth).Select(s => s.Item2).ToArray() }); Dictionary <int, double[]> dtwAmplitudeSlopes = pathsToTemplates.Select(path => new { path.column, slopes = DynamicTimeWarping.GenerateFlatPath(path.pathToTemplate, StandardizationHelpers.Resample(AmplitudeSlopes[path.column], path.pathToTemplate.Length)) }) .ToDictionary(path => path.column, path => path.slopes); Dictionary <int, double[]> dtwPhaseSlopes = pathsToTemplates.Select(path => new { path.column, slopes = DynamicTimeWarping.GenerateFlatPath(path.pathToTemplate, StandardizationHelpers.Resample(AmplitudeSlopes[path.column], path.pathToTemplate.Length)) }) .ToDictionary(path => path.column, path => path.slopes); // create amplitude warping var amplitudeWarp = dtwAmplitudeSlopes.Select(amplitude => new { column = amplitude.Key, amplitudeWarp = sample.GetDataRows(amplitude.Key).Select((value, j) => value + dtwAmplitudeSlopes[amplitude.Key][j] * (standardTime - sample.GetDuration().Value)) }); // create phase warping Dictionary <int, double[]> phaseWarp = dtwPhaseSlopes.Select(phase => new { column = phase.Key, phaseWarp = StandardizationHelpers.GenerateL1NormalizedValues(sample.GetDataRows().Select((data, i) => phase.Value[i] * (standardTime - sample.GetDuration().Value)).ToArray()) }) .ToDictionary(warp => warp.column, warp => warp.phaseWarp); // get phase warping average Dictionary <int, double> phaseWarpAverage = phaseWarp.ToDictionary(warp => warp.Key, warp => warp.Value.Average()); // normalize the phase warp to have a mean value of 1 then multiply it by the respective amplitude slopes to scale the phase accordingly double amplitudeSum = amplitudeWarp.SelectMany(warp => warp.amplitudeWarp).Sum(); // TODO - make this configurable so can be disabled for performance Dictionary <int, double[]> phaseAmplitudeWarp = amplitudeWarp.Select(warp => new { warp.column, phaseWarped = warp.amplitudeWarp.Select((value, j) => value * (phaseWarp[warp.column][j] - phaseWarpAverage[warp.column] + 1)).ToArray() }) .ToDictionary(warp => warp.column, warp => warp.phaseWarped); double phaseSum = phaseAmplitudeWarp.SelectMany(warp => warp.Value).Sum(); // TODO - make this configurable so can be disabled for performance // warn if difference is beyond tolerance (TODO - make configurable) if (Math.Abs(amplitudeSum - phaseSum) > (Math.Abs(amplitudeSum) * 0.05)) { Console.WriteLine("Potentially weak phase normalization. Amplitude Norm Sum: " + amplitudeSum + ", Phase Norm Sum: " + phaseSum); } // NOTE - the lambda expression references an outer variable - this will be stored in the lambda and not garbaged collected until the lambda is (see Variable Scope in Lambda Expressions) return(TransformData(sample, (interval, i) => i * (standardTime / sample.GetDataRows().Count), (value, i, j) => phaseAmplitudeWarp[j][i], columns)); }
protected static double FindDTWCost(double[] d1, double[] d2, double bandwidth) { // normalize each signal to the same height to ensure transformation only affects phase of the signals return(DynamicTimeWarping.FindDTWCost(StandardizationHelpers.GenerateLinfNormalizedValues(d1), StandardizationHelpers.GenerateLinfNormalizedValues(d2), bandwidth)); }
protected static List <Tuple <int, int> > FindDTWPath(double[] template, double[] sample, double bandwidth) { // normalize each signal to the same height to ensure transformation only affects phase of the signals return(DynamicTimeWarping.FindDTWPath(StandardizationHelpers.GenerateLinfNormalizedValues(template), StandardizationHelpers.GenerateLinfNormalizedValues(sample), bandwidth)); }