这篇文章并介绍了一个相对简单 C# 程序,向您介绍的 MNIST 数据集,这反过来你接触到红外的概念。
第四,你就会找到红外有趣的在其自己的权利。
演示了作为一位映射的图像显示当前所选的图像的能力 (左边的图 1),和作为一个十六进制形式 (右侧) 中的像素值的矩阵。
图 1 显示 MNIST 图像
您可以运行该演示程序之前,您需要下载并保存两个 MNIST 的数据文件,如我在下一节中,解释和编辑演示的源代码以指向那些两个文件的位置。
演示代码如此演示的代码重构为向非.NET 语言如 JavaScript 将是一个困难的任务使 Microsoft.NET 框架的广泛使用。
虽然这些条款有不同的含义,他们是有时互换使用,这可以使有点困难的相关信息在互联网上寻找。
MNIST 数据集
其基本思想是如果你有你想要测试红外的算法或软件的系统,可以运行您的算法或系统针对 MNIST 的数据集和比较您的结果与其他系统以前发布成果。
对应于每个图像的实际数字是显然对人,但确定数字是非常困难的挑战的计算机。
图 2 首八 MNIST 训练图像
每个的四个文件还包含标头信息,和所有的四个文件都存储在已经使用 gzip 格式压缩的二进制格式。
将文件解压缩,我用的免费的开源 7-Zip 实用程序。
创建 MNIST 查看器
演示有没有重大的.NET 版本依赖关系,因此,任何版本的 Visual Studio 应该工作。
而且,最后,我添加了一个列表框控件 (listBox1) 的日志记录消息。
后放置 UI 控件拖到 Windows 窗体,添加三个类范围字段:
public partial class Form1 : Form
{
private string pixelFile =
@"C:\MnistViewer\train-images.idx3-ubyte";
private string labelFile =
@"C:\MnistViewer\train-labels.idx1-ubyte";
private DigitImage[] trainImages = null;
...
第三个字段是一个程序定义 DigitImage 对象的数组。
我编辑窗体的构造函数略成 textBox1 和 textBox2 地点的文件路径,并给予放大倍数初始值 6:
public Form1()
{
InitializeComponent();
textBox1.Text = pixelFile;
textBox2.Text = labelFile;
comboBox1.SelectedItem = "6";
this.ActiveControl = button1;
}
我用的 ActiveControl 属性来设置初始焦点到 button1 控件,只是为了方便。
创建一个类来保存 MNIST 映像
我名为 DigitImage 的类,但您可能想要将它重命名为更具体,比如,MnistImage 的东西。
图 3 DigitImage 类定义
public class DigitImage
{
public int width; // 28
public int height; // 28
public byte[][] pixels; // 0(white) - 255(black)
public byte label; // '0' - '9'
public DigitImage(int width, int height,
byte[][] pixels, byte label)
{
this.width = width; this.height = height;
this.pixels = new byte[height][];
for (int i = 0; i < this.pixels.Length; ++i)
this.pixels[i] = new byte[width];
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
this.pixels[i][j] = pixels[i][j];
this.label = label;
}
}
字段标签还声明为类型字节,但可能已经类型 int 或 char 或字符串。
可以抄了像素值通过引用而不是通过值,但如果更改了源像素值,可能导致不必要的副作用。
MNIST 数据加载
该事件处理程序的农场大部分对 LoadData 方法的工作:
private void button1_Click(object sender, EventArgs e)
{
this.pixelFile = textBox1.Text;
this.labelFile = textBox2.Text;
this.trainImages = LoadData(pixelFile, labelFile);
listBox1.Items.Add("MNIST images loaded into memory");
}
方便的.NET BinaryReader 类专为读取二进制文件。
图 4 LoadData 方法
public static DigitImage[] LoadData(string pixelFile, string labelFile)
{
int numImages = 60000;
DigitImage[] result = new DigitImage[numImages];
byte[][] pixels = new byte[28][];
for (int i = 0; i < pixels.Length; ++i)
pixels[i] = new byte[28];
FileStream ifsPixels = new FileStream(pixelFile, FileMode.Open);
FileStream ifsLabels = new FileStream(labelFile, FileMode.Open);
BinaryReader brImages = new BinaryReader(ifsPixels);
BinaryReader brLabels = new BinaryReader(ifsLabels);
int magic1 = brImages.ReadInt32(); // stored as big endian
magic1 = ReverseBytes(magic1); // convert to Intel format
int imageCount = brImages.ReadInt32();
imageCount = ReverseBytes(imageCount);
int numRows = brImages.ReadInt32();
numRows = ReverseBytes(numRows);
int numCols = brImages.ReadInt32();
numCols = ReverseBytes(numCols);
int magic2 = brLabels.ReadInt32();
magic2 = ReverseBytes(magic2);
int numLabels = brLabels.ReadInt32();
numLabels = ReverseBytes(numLabels);
for (int di = 0; di < numImages; ++di)
{
for (int i = 0; i < 28; ++i) // get 28x28 pixel values
{
for (int j = 0; j < 28; ++j) {
byte b = brImages.ReadByte();
pixels[i][j] = b;
}
}
byte lbl = brLabels.ReadByte(); // get the label
DigitImage dImage = new DigitImage(28, 28, pixels, lbl);
result[di] = dImage;
} // Each image
ifsPixels.Close(); brImages.Close();
ifsLabels.Close(); brLabels.Close();
return result;
}
例如,映像的数量是由读取:
int imageCount = brImages.ReadInt32(); imageCount = ReverseBytes(imageCount);
例如,幻数 2051年大字节序形式是:
00000011 00001000 00000000 00000000
小字节序形式存储在相同的值是:
00000000 00000000 00001000 00000011
我用了一种高级别的方法,利用了.NET BitConverter 类,而不是使用一种低级、 位操作方法:
public static int ReverseBytes(int v)
{
byte[] intAsBytes = BitConverter.GetBytes(v);
Array.Reverse(intAsBytes);
return BitConverter.ToInt32(intAsBytes, 0);
}
每个图像具有隐式索引 ID,它是序列中的图像的图像的从零开始位置。
显示图像
要显示的图像的代码所示图 5。
图 5 显示 MNIST 图像
private void button2_Click(object sender, EventArgs e)
{
// Display 'next' image
int nextIndex = int.Parse(textBox4.Text);
DigitImage currImage = trainImages[nextIndex];
int mag = int.Parse(comboBox1.SelectedItem.ToString());
Bitmap bitMap = MakeBitmap(currImage, mag);
pictureBox1.Image = bitMap;
string pixelVals = PixelValues(currImage);
textBox5.Text = pixelVals;
textBox3.Text = textBox4.Text; // Update curr idx
textBox4.Text = (nextIndex + 1).ToString(); // ++next index
listBox1.Items.Add("Curr image index = " +
textBox3.Text + " label = " + currImage.label);
}
这通过帮助器方法 MakeBitmap 中列出的图 6。
图 6 MakeBitmap 方法
public static Bitmap MakeBitmap(DigitImage dImage, int mag)
{
int width = dImage.width * mag;
int height = dImage.height * mag;
Bitmap result = new Bitmap(width, height);
Graphics gr = Graphics.FromImage(result);
for (int i = 0; i < dImage.height; ++i)
{
for (int j = 0; j < dImage.width; ++j)
{
int pixelColor = 255 - dImage.pixels[i][j]; // black digits
Color c = Color.FromArgb(pixelColor, pixelColor, pixelColor);
SolidBrush sb = new SolidBrush(c);
gr.FillRectangle(sb, j * mag, i * mag, mag, mag);
}
}
return result;
}
如果放大倍数值为 3,则位图图像将 (28 * 3) 由 (28 * 3) = 84 由 84 像素大小的位图中的每个 3 由 3 广场将代表原始图像的一个像素。
替代方法是将像素值传递到要获得一个彩色的图像 (深浅的红色、 绿色或蓝色),而不是一种灰度图像的 RGB 参数之一。
FillRectangle 的第四和第五个参数是要绘制的矩形区域的高度与宽度从第二和第三个参数指定的角开始。
FillRectangle 方法将开始绘画在 x = (5 * 3) = 15 列和 y = (2 * 3) = 行 6 的位图和油漆颜色 (55,55,55) 3 由 3 像素的矩形 = 暗灰色。
显示图像的像素值
该方法是短和简单:
public static string PixelValues(DigitImage dImage)
{
string s = "";
for (int i = 0; i < dImage.height; ++i) {
for (int j = 0; j < dImage.width; ++j) {
s += dImage.pixels[i][j].ToString("X2") + " ";
}
s += Environment.NewLine;
}
return s;
}
尽管十六进制值可能会有点更难以解释比基础值 10,十六进制值格式更好。
从这里去哪儿?
这些技术包括使用卷积神经网络和生成额外的培训图像使用弹性变形。
jammc@microsoft.com。
衷心感谢以下技术专家对本文的审阅:狼 Kienzle (Microsoft 研究)