MNISTの読み込み(F#)
機械学習のデータとして、手書き数字の画像がまとめられた下記のサイトを利用することがあります。
MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges
訓練データとして60000画像、テストデータとして10000画像用意されています。
今回はこのデータをF#で利用できるようパースしてみたいと思います。(フォーマットは上記サイトに記載されています。)
読み込み関数
データはbig endianで保存されています。
let readInt32BigEndian (reader : BinaryReader) : int = BitConverter.ToInt32(reader.ReadBytes(4) |> Array.rev, 0) let loadLabels (path : string) : byte[] = use reader = new BinaryReader(File.OpenRead(path)) assert (readInt32BigEndian reader = 2049) let count = readInt32BigEndian reader reader.ReadBytes(count) let loadImages (path : string) : byte[][] = use reader = new BinaryReader(File.OpenRead(path)) assert (readInt32BigEndian reader = 2051) let count = readInt32BigEndian reader let height = readInt32BigEndian reader let width = readInt32BigEndian reader [| 1..count |] |> Array.map (fun _ -> reader.ReadBytes(width * height))
動作確認
実際にpngで保存してみて中身を確認してみます。
let savePng8 (width : int) (height : int) (pixels : byte[]) (path : string) : unit = use stream = new FileStream(path, FileMode.Create) let encoder = PngBitmapEncoder() let bmp = BitmapSource.Create(width, height, 96.0, 96.0, PixelFormats.Gray8, null, pixels, width) encoder.Frames.Add(BitmapFrame.Create(bmp)) encoder.Save(stream) let main argv = let images = loadImages "train-images.idx3-ubyte" let labels = loadLabels "train-labels.idx1-ubyte" Array.iteri2 (fun i image label -> let path = sprintf "image%d(%d).png" i label File1.savePng8 28 28 image path ) (images |> Array.take 3) (labels |> Array.take 3)
5 | 0 | 4 |
学習用に変形
実際にデータを利用する際には、数学ライブラリのデータで取得したほうが便利です。
今回はMath.NETのVector形式に変換してみます。
また、画像データを255で割って正規化し、ラベルデータを10要素のVectorに変換します。(例:3 → [0, 0, 0, 1, 0, 0, 0, 0, 0, 0])
open MathNet.Numerics.LinearAlgebra let loadLabelVectors (path : string) : Vector<double>[] = loadLabels path |> Array.map (fun label -> [| 0uy..9uy |] |> Array.map (fun x -> if x = label then 1.0 else 0.0) |> Vector.Build.Dense ) let loadImageVectors (path : string) : Vector<double>[] = loadImages path |> Array.map (Array.map (fun x -> (double x) / 255.0) >> Vector.Build.Dense)