Created
January 22, 2016 09:03
-
-
Save sudipto80/9d152161130cfcd82a89 to your computer and use it in GitHub Desktop.
Identifying Digits with kNN
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System.IO | |
open System | |
open System.Windows.Forms | |
open System.Drawing | |
//The type that represents each row of the training | |
//or the test dataset | |
type Entry = {Label :string; Values : int list} | |
//Calculates Squared Euclidean distance between pixel | |
//values of two images | |
let distance ( values1 : int list , values2 : int list) = | |
values1 | |
|> List.zip values2 | |
|> List.map ( fun it -> Math.Pow( float (fst it) - float (snd it),2.0)) | |
|> List.sum | |
//Loading values from the training/test data. | |
//This assumes that the first one is the | |
//label/class/category of the data | |
let loadValues (filename : string) = | |
File.ReadAllLines(filename) | |
|> Seq.ofArray | |
// leave the first row as that's the column headers | |
|> Seq.skip (1) | |
|> Seq.map ( fun line -> | |
{ | |
Label = line.Substring(0,line.IndexOf(',')); | |
Values = line.Split(',') | |
|> Seq.ofArray | |
//the first token is the label. So skip it | |
|> Seq.skip (1) | |
|> Seq.map( fun n -> Convert.ToInt32(n)) | |
|> Seq.toList | |
}) | |
|>Seq.toList | |
//A generic k-nearest neighbor algorithm | |
let kNN ( entries : Entry list, newEntry : string * int[] , k : int) = | |
entries |> List.map( fun x -> ( x.Label, distance (x.Values, snd (newEntry) | |
|>Array.toList ))) | |
|> List.sortBy ( fun x -> snd x) | |
|> Seq.ofList | |
|> Seq.take k | |
|> Seq.countBy (fun x -> fst x) | |
|> Seq.toList | |
//Draws the digit | |
let drawDigit (pixels:float[], label:string) = | |
let tile = 20 | |
let form = new Form(TopMost = true, | |
Visible = true, | |
Width = 29 * tile, | |
Height = 29 * tile) | |
let panel = new Panel(Dock = DockStyle.Fill) | |
panel.BackColor <- Color.Black | |
form.Controls.Add(panel) | |
let graphics = panel.CreateGraphics() | |
pixels | |
|> Array.iteri (fun i p -> | |
let col = i % 28 | |
let row = i / 28 | |
let color = Color.FromArgb(int p, int p, int p) | |
let brush = new SolidBrush(color) | |
graphics.FillRectangle(brush,col*tile,row*tile,tile,tile)) | |
let point = new PointF((float32)5, (float32)5) | |
let font = new Font(family = FontFamily.GenericSansSerif, emSize = (float32)30) | |
graphics.DrawString(label, font, new SolidBrush(Color.YellowGreen), point) | |
form.Show() | |
//Point to the "train.csv" on your disk | |
let loaded = loadValues @"C:\personal\train.csv" | |
//Here is the unknown entry and its pixel values for | |
//all 28 by 28 values. | |
//This entry depicts a "9" from the training dataset. | |
let newEntry = ("X",[|0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;25;123;245;243;211;45;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;71;227;252;166;47;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;20;197;249;223;47;2;68;232;98;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;48;202;252;155;35;0;15;225;252;80;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;190;252;155;7;0;0;110;252;208;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;123;253;109;0;0;0;68;245;216;18;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;253;224;14;0;0;15;211;252;153;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;54;253;71;0;0;9;237;252;224;7;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;106;253;111;37;91;204;253;252;126;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;18;253;252;235;252;208;253;252;82;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;62;106;106;35;0;255;204;9;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;80;253;89;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;158;253;63;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;43;239;225;21;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;64;252;167;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;169;253;45;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;8;197;252;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;22;252;244;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;22;252;173;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;13;217;121;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0|]) | |
let pixels = snd(newEntry) |> Array.map (fun t -> float t) | |
//Let's consider only 5 nearest neighbors | |
let k = 5 | |
//Getting back the labels for each of the nearest neighbours | |
let labels = kNN (loaded , newEntry, k) | |
//Locating the guess. The one with the maximum votes | |
let guess = fst( labels |> List.item 0) | |
//Answer will be 9 | |
drawDigit (pixels , "I think that it is a " + guess) | |
Console.ReadLine() |> ignore | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment