/// <summary> /// Trains the model to recognise the specified input as the specified symbol /// </summary> public void Train(WrappedBitmap inputData, TSymbol representedValue, uint rounds) { var initialGeneration = Enumerable.Range(0, (int)rounds) .Select(i => new Scorer(new TuningParams.Scoring())) // TODO nested constructors is smelly .Select(s => new ScorerScorePair(s, s.Score(inputData))) .AsParallel() .OrderByDescending(s => s.Score); // Having acquired the initial set of sampling pixels via sheer randomness, we'll work with // the best we've got and try and massage them into something better. var mutParams = new TuningParams.Mutation(); var initialBestCandidates = initialGeneration.Take(100); // TODO parameterise magic value (which was decided totally arbitrarily anyway) var initialBestCandidatesWithMutatedVariants = from p in initialBestCandidates let mutatedOffspring = p.Scorer.MutateMany(mutParams) let mutatedScores = from m in mutatedOffspring let mutatedScore = m.Score(inputData) select new ScorerScorePair(m, mutatedScore) select new ParentChildScoreCollection(p, mutatedScores); var bestOftheMutantStrains = initialBestCandidatesWithMutatedVariants .SelectMany(x => x.Children) .OrderByDescending(s => s.Score) .First(); // Compute some stats for these mutations. These stats are for interest only, they don't affect any computation. Benchmarking.Mutation.AddDataPoints(initialBestCandidatesWithMutatedVariants); var rec = new Recogniser <TSymbol>(representedValue, bestOftheMutantStrains.Scorer); RegisterRecogniser(rec); }
/// <summary> /// Randomises the sampled pixels, but keeps them relatively close to their existing locations. /// </summary> public IScorer Mutate(TuningParams.Mutation tuningParams) { var newPixels = new List <Point>(_pixelsToSample); int n = _randomNumGenerator.Next(tuningParams.PixelRandomChurn); for (int i = 0; i < n; i++) { // Remove one of the pixels at random newPixels.RemoveAt(_randomNumGenerator.Next(TuningParams.ImageSize)); } // Skew some of the remaining ones n = _randomNumGenerator.Next(tuningParams.PixelRandomSkewNumber); for (int i = 0; i < n; i++) { // TODO the same pixels could be skewed more than once (does that even matter?) int xOffset = _randomNumGenerator.Next(tuningParams.PixelRandomSkewOffset) - tuningParams.PixelRandomSkewOffset; int yOffset = _randomNumGenerator.Next(tuningParams.PixelRandomSkewOffset) - tuningParams.PixelRandomSkewOffset; int pixIndex = _randomNumGenerator.Next(newPixels.Count - 1); newPixels[pixIndex] = ConstrainPixelToImage( newPixels[pixIndex].X + xOffset, newPixels[pixIndex].Y + yOffset); } // Add some more random new ones (approx the same number to replace those that were removed or "churned") n = _randomNumGenerator.Next(tuningParams.PixelRandomChurn); for (int i = 0; i < n; i++) { // NOTE - it's possible to get duplicates, in which case the same pixel is counted twice // TODO check for off-by-ones (can we hit the image edges?) newPixels.Add( new Point( _randomNumGenerator.Next(TuningParams.ImageSize), _randomNumGenerator.Next(TuningParams.ImageSize) ) ); } // modify the negative/positive offset randomly //_positiveScoreOffset = (byte)(_positiveScoreOffset + _randomNumGenerator.Next(tuningParams.PositiveRandOffset) - TuningParams.PositiveRandOffset / 2); //_negativeScoreOffset = (byte)(_negativeScoreOffset + _randomNumGenerator.Next(TuningParams.Scoring.NegativeRandOffset) - TuningParams.NegativeRandOffset / 2); // TODO keeping these params the same as our parent for now, and only randomoising on the sampled pixels. the randomisation of varying parameters could go on forever!!! return(new Scorer(_tuningParams, newPixels)); }
public IEnumerable <IScorer> MutateMany(TuningParams.Mutation tuningParams) { return(Enumerable.Range(0, tuningParams.SpawnedDescendants) .Select(i => Mutate(tuningParams))); }