一、概述

上一篇文章我们利用ML.NET的多元分类算法实现了一个手写数字识别的例子,这个例子存在一个问题,就是输入的数据是预处理过的,很不直观,这次我们要直接通过图片来进行学习和判断。思路很简单,就是写一个自定义的数据处理通道,输入为文件名,输出为float数字,里面保存的是像素信息。

样本包括6万张训练图片和1万张测试图片,图片为灰度图片,分辨率为20*20 。train_tags.tsv文件对每个图片的数值进行了标记,如下:

二、源码

全部代码:

namespace MulticlassClassification_Mnist
{
class Program
{
//Assets files download from:https://gitee.com/seabluescn/ML_Assets
static readonly string AssetsFolder = @"D:\StepByStep\Blogs\ML_Assets\MNIST";
static readonly string TrainTagsPath = Path.Combine(AssetsFolder, "train_tags.tsv");
static readonly string TrainDataFolder = Path.Combine(AssetsFolder, "train");
static readonly string ModelPath = Path.Combine(Environment.CurrentDirectory, "Data", "SDCA-Model.zip"); static void Main(string[] args)
{
MLContext mlContext = new MLContext(seed: ); TrainAndSaveModel(mlContext);
TestSomePredictions(mlContext); Console.WriteLine("Hit any key to finish the app");
Console.ReadKey();
} public static void TrainAndSaveModel(MLContext mlContext)
{
// STEP 1: 准备数据
var fulldata = mlContext.Data.LoadFromTextFile<InputData>(path: TrainTagsPath, separatorChar: '\t', hasHeader: false);
var trainTestData = mlContext.Data.TrainTestSplit(fulldata, testFraction: 0.1);
var trainData = trainTestData.TrainSet;
var testData = trainTestData.TestSet; // STEP 2: 配置数据处理管道
var dataProcessPipeline = mlContext.Transforms.CustomMapping(new LoadImageConversion().GetMapping(), contractName: "LoadImageConversionAction")
.Append(mlContext.Transforms.Conversion.MapValueToKey("Label", "Number", keyOrdinality: ValueToKeyMappingEstimator.KeyOrdinality.ByValue))
.Append(mlContext.Transforms.NormalizeMeanVariance( outputColumnName: "FeaturesNormalizedByMeanVar", inputColumnName: "ImagePixels")); // STEP 3: 配置训练算法 (using a maximum entropy classification model trained with the L-BFGS method)
var trainer = mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(labelColumnName: "Label", featureColumnName: "FeaturesNormalizedByMeanVar");
var trainingPipeline = dataProcessPipeline.Append(trainer)
.Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictNumber", "Label")); // STEP 4: 训练模型使其与数据集拟合
ITransformer trainedModel = trainingPipeline.Fit(trainData); // STEP 5:评估模型的准确性
var predictions = trainedModel.Transform(testData);
var metrics = mlContext.MulticlassClassification.Evaluate(data: predictions, labelColumnName: "Label", scoreColumnName: "Score");
PrintMultiClassClassificationMetrics(trainer.ToString(), metrics); // STEP 6:保存模型
mlContext.Model.Save(trainedModel, trainData.Schema, ModelPath);
} private static void TestSomePredictions(MLContext mlContext)
{
// Load Model
ITransformer trainedModel = mlContext.Model.Load(ModelPath, out var modelInputSchema); // Create prediction engine
var predEngine = mlContext.Model.CreatePredictionEngine<InputData, OutPutData>(trainedModel); DirectoryInfo TestFolder = new DirectoryInfo(Path.Combine(AssetsFolder, "test"));
foreach(var image in TestFolder.GetFiles())
{
count++; InputData img = new InputData()
{
FileName = image.Name
};
var result = predEngine.Predict(img); Console.WriteLine($"Current Source={img.FileName},PredictResult={result.GetPredictResult()}");
}
}
} class InputData
{
[LoadColumn()]
public string FileName; [LoadColumn()]
public string Number; [LoadColumn()]
public float Serial;
} class OutPutData : InputData
{
public float[] Score;
public int GetPredictResult()
{
float max = ;
int index = ;
for (int i = ; i < Score.Length; i++)
{
if (Score[i] > max)
{
max = Score[i];
index = i;
}
}
return index;
}
}
}

三、分析

整个处理流程和上一篇文章基本一致,这里解释两个不一样的地方。

1、自定义的图片读取处理通道

namespace MulticlassClassification_Mnist
{
public class LoadImageConversionInput
{
public string FileName { get; set; }
} public class LoadImageConversionOutput
{
[VectorType()]
public float[] ImagePixels { get; set; }
public string ImagePath;
} [CustomMappingFactoryAttribute("LoadImageConversionAction")]
public class LoadImageConversion : CustomMappingFactory<LoadImageConversionInput, LoadImageConversionOutput>
{
static readonly string TrainDataFolder = @"D:\StepByStep\Blogs\ML_Assets\MNIST\train"; public void CustomAction(LoadImageConversionInput input, LoadImageConversionOutput output)
{
string ImagePath = Path.Combine(TrainDataFolder, input.FileName);
output.ImagePath = ImagePath; Bitmap bmp = Image.FromFile(ImagePath) as Bitmap; output.ImagePixels = new float[];
for (int x = ; x < ; x++)
for (int y = ; y < ; y++)
{
var pixel = bmp.GetPixel(x, y);
var gray = (pixel.R + pixel.G + pixel.B) / / ;
output.ImagePixels[x + y * ] = gray;
}
bmp.Dispose();
} public override Action<LoadImageConversionInput, LoadImageConversionOutput> GetMapping()
=> CustomAction;
}
}

这里可以看出,我们自定义的数据处理通道,输入为文件名称,输出是一个float数组,这里数组必须要指定宽度,由于图片分辨率为20*20,所以数组宽度指定为400,输出ImagePath为文件详细地址,用来调试使用,没有实际用途。处理思路非常简单,遍历每个Pixel,计算其灰度值,为了减少工作量我们把灰度值进行缩小,除以了16 ,由于后面数据会做归一化,所以这里影响不是太明显。

2、模型测试

            DirectoryInfo TestFolder = new DirectoryInfo(Path.Combine(AssetsFolder, "test"));
int count = ;
int success = ;
foreach(var image in TestFolder.GetFiles())
{
count++; InputData img = new InputData()
{
FileName = image.Name
};
var result = predEngine.Predict(img); if(int.Parse(image.Name.Substring(,))==result.GetPredictResult())
{
success++;
}
}

我们把测试目录里的全面图片读出遍历了一遍,将其测试结果和实际结果做了一次验证,实际上是把评估(Evaluate)的事情又重复做了一次,两次测试的成功率基本接近。

四、关于图片特征提取

我们是采用图片所有像素的灰度值来作为特征值的,但必须要强调的是:像素值矩阵不是图片的典型特征。虽然有时候对于较规则的图片,通过像素提取方式进行计算,也可以取得很好的效果,但在处理稍微复杂一点的图片的时候,就不管用了,原因很明显,我们人类在分析图片内容时看到的特征更多是线条等信息,绝对不是像素值,看下图:

我们人类很容易就判断出这两个图片表达的是同一件事情,但其像素值特征却相差甚远。

传统的图片特征提取方式很多,比如:SIFT、HOG、LBP、Haar等。 现在采用TensorFlow的模型进行特征提取效果非常好。下一篇文章介绍图片分类时再进行详细介绍。

五、资源获取

源码下载地址:https://github.com/seabluescn/Study_ML.NET

工程名称:MulticlassClassification_Mnist_Useful

MNIST资源获取:https://gitee.com/seabluescn/ML_Assets

点击查看机器学习框架ML.NET学习笔记系列文章目录

机器学习框架ML.NET学习笔记【5】多元分类之手写数字识别(续)的更多相关文章

  1. 机器学习框架ML.NET学习笔记【4】多元分类之手写数字识别

    一.问题与解决方案 通过多元分类算法进行手写数字识别,手写数字的图片分辨率为8*8的灰度图片.已经预先进行过处理,读取了各像素点的灰度值,并进行了标记. 其中第0列是序号(不参与运算).1-64列是像 ...

  2. 机器学习框架ML.NET学习笔记【6】TensorFlow图片分类

    一.概述 通过之前两篇文章的学习,我们应该已经了解了多元分类的工作原理,图片的分类其流程和之前完全一致,其中最核心的问题就是特征的提取,只要完成特征提取,分类算法就很好处理了,具体流程如下: 之前介绍 ...

  3. 机器学习框架ML.NET学习笔记【7】人物图片颜值判断

    一.概述 这次要解决的问题是输入一张照片,输出人物的颜值数据. 学习样本来源于华南理工大学发布的SCUT-FBP5500数据集,数据集包括 5500 人,每人按颜值魅力打分,分值在 1 到 5 分之间 ...

  4. 机器学习框架ML.NET学习笔记【3】文本特征分析

    一.要解决的问题 问题:常常一些单位或组织召开会议时需要录入会议记录,我们需要通过机器学习对用户输入的文本内容进行自动评判,合格或不合格.(同样的问题还类似垃圾短信检测.工作日志质量分析等.) 处理思 ...

  5. 机器学习框架ML.NET学习笔记【2】入门之二元分类

    一.准备样本 接上一篇文章提到的问题:根据一个人的身高.体重来判断一个人的身材是否很好.但我手上没有样本数据,只能伪造一批数据了,伪造的数据比较标准,用来学习还是蛮合适的. 下面是我用来伪造数据的代码 ...

  6. 机器学习框架ML.NET学习笔记【1】基本概念与系列文章目录

    一.序言 微软的机器学习框架于2018年5月出了0.1版本,2019年5月发布1.0版本.期间各版本之间差异(包括命名空间.方法等)还是比较大的,随着1.0版发布,应该是趋于稳定了.之前在园子里也看到 ...

  7. 机器学习框架ML.NET学习笔记【8】目标检测(采用YOLO2模型)

    一.概述 本篇文章介绍通过YOLO模型进行目标识别的应用,原始代码来源于:https://github.com/dotnet/machinelearning-samples 实现的功能是输入一张图片, ...

  8. 机器学习框架ML.NET学习笔记【9】自动学习

    一.概述 本篇我们首先通过回归算法实现一个葡萄酒品质预测的程序,然后通过AutoML的方法再重新实现,通过对比两种实现方式来学习AutoML的应用. 首先数据集来自于竞赛网站kaggle.com的UC ...

  9. 深度学习面试题12:LeNet(手写数字识别)

    目录 神经网络的卷积.池化.拉伸 LeNet网络结构 LeNet在MNIST数据集上应用 参考资料 LeNet是卷积神经网络的祖师爷LeCun在1998年提出,用于解决手写数字识别的视觉任务.自那时起 ...

随机推荐

  1. Mac替换sublime图标

    下载.icns格式一个图标:http://www.easyicon.net/language.en/iconsearch/sublime/ 终端执行:open /Applications/Sublim ...

  2. CF Round #459

    好菜啊 第一场cf就菜成这样...mdzz 可能是我把题看的太简单了吧... T1AC T2AC T3WA T4看错题 T5不会写 T3想的是栈+暴力 正解:  对于一个pretty串的任意一个位置, ...

  3. CodeForces - 1005E2:Median on Segments (General Case Edition) (函数的思想)

    You are given an integer sequence a1,a2,…,ana1,a2,…,an. Find the number of pairs of indices (l,r)(l, ...

  4. 1053 Path of Equal Weight (30)(30 分)

    Given a non-empty tree with root R, and with weight W~i~ assigned to each tree node T~i~. The weight ...

  5. ACM学习历程—NPU1086 随机数 2015年陕西省程序设计竞赛网络预赛(正式赛)C题 (计数排序 || set容器)

    Description 开学了,ACM队的边老板想在学校中请一些妹子一起做一项问卷调查,调查妹子们对ACM的了解情况,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100), ...

  6. vue之axios+php+mysql

    博主原创,未经许可请勿转载 哦 1.axios配置请看上篇 2.mysql数据库编写,表名为area_list 3.json.php文件在notebeans中编写 <?php header('C ...

  7. Vue cli项目开启Gzip

    目录 安装 compression-webpack-plugin 更改配置文件 服务器开启gzip功能 安装 compression-webpack-plugin 建议安装v1.1.11版本,最新版本 ...

  8. poj1149PIGS——网络最大流

    题目:http://poj.org/problem?id=1149 不把猪圈当做点,而把顾客当作点,把猪当作边权(流量): 因为猪圈中的猪可流动,所以共用一个猪圈的人互相连边: 注意应该连成链的形式, ...

  9. Python中正则匹配使用findall时的注意事项

    在使用正则搜索内容时遇到一个小坑,百度搜了一下,遇到这个坑的还不少,特此记录一下. 比如说有一个字符串  "123@qq.comaaa@163.combbb@126.comasdf111@a ...

  10. java 获取 HttpServletRequest 值 demo

    private void getHttpServletRequestInfo(HttpServletRequest request){ try { StringBuilder stringBuilde ...