使用F#读取MNIST数据集

3

我可以做到这一点,但我不知道它为什么有效。我使用了MNIST数据库,该数据库是从http://yann.lecun.com/exdb/mnist/下载的,并按照该页面底部的指南编写了(尚未完成的)方法。

// TRAINING SET IMAGE FILE (train-images-idx3-ubyte):
// [offset] [type]          [value]          [description] 
// 0000     32 bit integer  0x00000803(2051) magic number 
// 0004     32 bit integer  60000            number of images 
// 0008     32 bit integer  28               number of rows 
// 0012     32 bit integer  28               number of columns 
// 0016     unsigned byte   ??               pixel 
// 0017     unsigned byte   ??               pixel 
// ........ 
// xxxx     unsigned byte   ??               pixel

// TEST SET IMAGE FILE (t10k-images-idx3-ubyte):
// [offset] [type]          [value]          [description] 
// 0000     32 bit integer  0x00000803(2051) magic number 
// 0004     32 bit integer  10000            number of images 
// 0008     32 bit integer  28               number of rows 
// 0012     32 bit integer  28               number of columns 
// 0016     unsigned byte   ??               pixel 
// 0017     unsigned byte   ??               pixel 
// ........ 
// xxxx     unsigned byte   ??               pixel
let loadMnistImage file =
    use stream = File.Open(file, FileMode.Open)
    use reader = new BinaryReader(stream)
    let magicNumber = readInt(reader)
    let nImages = readInt(reader)
    let nRows = readInt(reader)
    let nColumns = readInt(reader)
    (magicNumber, nImages, nRows, nColumns);;

那是容易的部分。困难的部分在于 readInt 函数的形式。我不能只使用 BitConverter.ToInt(); 我在这个页面上找到了答案:https://code.google.com/p/aguaviva-libs/source/browse/c%23/NeuronalNetwork/sets/HandWriting.cs?spec=svn9ffdf444c6317be049572cea59170602c8f28bea&r=9ffdf444c6317be049572cea59170602c8f28bea
翻译该方法。
int Read(BinaryReader b, int i)
{
   int res = 0;

   while (i-- > 0)
   {
      res <<= 8;
      res |= b.ReadByte()
   }
   return res;
}

将代码转换成F#语言会得到以下结果:
let readInt (b : BinaryReader) =
    [1..4] |> List.fold (fun res item -> (res <<< 8) ||| (int)(b.ReadByte())) 0

(假设 = 4)。这是可行的:在F#交互式环境中,这些行)
loadMnistImage @"Data\t10k-images.idx3-ubyte"
loadMnistImage @"Data\train-images.idx3-ubyte"

分别给出(2051, 10000, 28, 28)(2051, 60000, 28, 28)的结果,这与第一个代码片段中的注释中的值相符。

我不理解的是为什么它能够工作。所有这些位移和按位或运算符折叠是什么意思?为什么不能只使用BitConverter.ToInt()


2
你所使用的方法与计算机的字节序无关。但是,使用BitConverter时,结果会根据字节序而变化。 - John Palmer
谢谢约翰。那么现在还没有内置的方法来做到这一点吗? - Rob Lyndon
我认为没有任何内置的方法存在。 - John Palmer
1
内置的端序不可知转换器方法是 System.Net.IPAddress.NetworkToHostOrder - Gene Belitski
2个回答

3

标准库方法IPAddress.NetworkToHostOrder(Int32)在将int从网络顺序转换时考虑执行平台的字节序。后者按照标准约定是大端序。由于MNIST文件遵循这一约定并且是大端序,因此以下一对标准库方法将作为无关字节序的替代您的readInt函数:

let readInt (reader: System.IO.BinaryReader) =
    reader.ReadInt32() |> System.Net.IPAddress.NetworkToHostOrder

一个等价但更冗长的变体涉及 BitConverter 的使用,如下:

let readInt (reader: System.IO.BinaryReader) =
    (reader.ReadBytes(4),0)
    |> System.BitCoverter.ToInt32
    |> System.Net.IPAddress.NetworkToHostOrder

2

将我的评论发布为答案

按照现有的写法,该方法将在运行代码的机器的字节序无论是大端还是小端都能正常工作。

标准库方法将根据运行代码的机器的字节序返回结果。这可能会产生与您预期不同的结果(相对字节顺序被反转)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接