作者 | 邹欣
编辑 | 姗姗
【人工智能头条导读】又是一个很有热度的周末,除了炎热的天气,还有火热的世界杯。今天人工智能头条为大家准备的技术干货,让大家可以在空调下,吃瓜看球两不耽误就可以轻松完成AI应用实践入门。多少次,在我们查找很多资源、技术指导后,实操时还是会被一个报错而终止了前进的道路。小编也曾经历过这样的心路历程,所以一份好的指南对于刚开始实践操作的同学来说简直太有爱了,不仅节约了很多时间,操作和思路也都是清晰的。如果你是刚入门的AI小白,想通过一些简单的应用实践对AI应用有更深入的了解,现在就开始和我们一起开始吧!
作者简介:邹欣,现任微软亚洲研究院研发经理,负责必应搜索客户端、必应输入法、必应词典、微软小娜等产品。曾出版《移山之道》、《编辑之美》(合作)、《构建之法》三部书籍。
下面为大家介绍邹欣老师的AI应用开发实践系列教程之手写识别应用入门,本次教程学习主要分三步:
从零开始搭建你的开发环境,无论是Windows 还是 MAC OS 环境
简单的 MNIST 模型训练
入门手写体识别应用实践
这波操作大家放心可以轻松完成,教程中不仅介绍了每步需要做什么,还为你准备了有参考价值的命令行、代码、各安装软件版本号的明确说明、需要下载或者访问的链接以及截图和教程视频,可以说非常全面、到位了。
(1)准备工作:一台能联网的电脑,使用 win10 64 位操作系统,请确保鼠标、键盘、显示器都是好的
(2)安装步骤:
安装 Git
安装 VS,等待时间看网络状况,没准可以看半场球反正不用担心不用着急。
安装 python
安装 CUDA 和 cuDNN:这是教程中最繁琐的一步,这一步直接拉高教程的平均难度。
配置机器学习环境:这是教程中最简单的一步,为了方便用户配置环境,微软提供了一键安装工具!简直业界良心!
安装 VS Tools For AI插件
(3)离线模型的训练:注意在此过程中使用最新的 mnist.py 文件
文章链接:
https://blog.csdn.net/softwareteacher/article/details/80770079
(1)准备工作:一台能联网的电脑,使用 MAC OS 操作系统,请确保鼠标、键盘、显示器都是好的
(2)安装步骤:
安装 VS,同样时间长的小伙伴们不妨考虑去看半场球啊!
安装 python
安装 Tools for AI 插件
安装 Git
下载机器学习示例库
(3)模型训练:也是小编的第一个模型!在教程中孩子特别补充讲解了如何在远程 Linux 上进行训练。
文章链接:
https://blog.csdn.net/softwareteacher/article/details/80770239
敲重点!!!环境搭建和模型训练都搞定后,接下来终于可以上手应用实践了,以下为手写识别应用教程全文,enjoy!
手写体识别的应用已经非常流行了,如输入法,图片中的文字识别等。但对于大多数开发人员来说,如何实现这样的一个应用,还是会感觉无从下手。本文从简单的 MNIST 训练出来的模型开始,和大家一起入门手写体识别。
在本教程结束后,会得到一个能用的 AI应用,也许是你的第一个AI应用。虽然离实际使用还有较大的距离(具体差距在文章后面会分析),但会让你对AI应用有一个初步的认识,有能力逐步搭建出能够实际应用的模型。
原文链接:
https://blog.csdn.net/softwareteacher/article/details/80770347
使用 win10 64 位操作系统的计算机
参考上一篇博客 AI应用开发实战 - 从零开始配置环境。在电脑上训练并导出 MNIST 模型。
通过上一篇文章搭建环境的介绍后,就能得到一个能识别单个手写数字的模型了,并且识别的准确度会在 98%,甚至 99% 以上了。那么我们要怎么使用这个模型来搭建应用呢?
大致的步骤如下:
实现简单的界面,将用户用鼠标或者触屏的输入变成图片。
将生成的模型包装起来,成为有公开数据接口的类。
将输入的图片进行规范化,成为数据接口能够使用的格式。
最后通过模型来推理 (inference) 出图片应该是哪个数字,并显示出来。
是不是很简单?
步骤一:获取手写的数字
提问:那我们要怎么获取手写的数字呢?
回答:我们可以写一个简单的 WinForm 画图程序,让我们可以用鼠标手写数字,然后把图片保存下来。
首先,我们打开 Visual Studio,选择文件->新建->项目。
在弹出的窗口里选择 Visual C#->Windows窗体应用,项目名称不妨叫做 DrawDigit,解决方案名称不妨叫做 MnistForm,点击确定。
此时,Visual Studio 也自动弹出了一个窗口的设计图。
在 DrawDigit 项目上点击右键,选择属性,在生成一栏将平台目标从 Any CPU 改为 x 64。
否则,DrawDigit(首选32位)与它引用的 MnistForm(64位)的编译平台不一致会引发 System.BadImageFormatException 的异常。
然后我们对这个窗口做一些简单的修改:
首先我们打开 VS 窗口左侧的工具箱,这个窗口程序需要以下三种组件:
1. PictureBox:用来手写数字,并且把数字保存成图片
2. Label:用来显示模型的识别结果
3. Button:用来清理PictureBox的手写结果
那经过一些简单的选择与拖动还有调整大小,这个窗口现在是这样的:
一些注意事项
这些组件都可以通过右键->查看属性,在属性里修改它们的设置
为了方便把 PictureBox 里的图片转化成 Mnist 能识别的格式,PictureBox 的需要是正方形
可以给这些控件起上有意义的名称。
可以调整一下 label 控件大小、字体等,让它更美观。
经过一些简单的调整,这个窗口现在是这样的:
现在来让我们愉快地给这些组件添加事件!
还是在属性窗口,我们选择某个组件,右键->查看属性,点击闪电符号,给组件绑定对应的事件。每次绑定后,会跳到代码部分,生成一个空函数。点回设计视图继续操作即可。
组件类型 |
事件 |
pictureBox1 |
在Mouse下双击MouseDown、MouseUp、MouseMove来生成对应的响应事件函数。 |
button1 |
如上,在Action下双击Click。 |
Form1 |
如上,在Behavior下双击Load。 |
然后我们开始补全对应的函数体内容。
注意,如果在上面改变了控件的名称,下面的代码需要做对应的更改。
废话少说上代码!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;//用于优化绘制的结果
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MnistModel;
namespace DrawDigit
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Bitmap digitImage;//用来保存手写数字
private Point startPoint;//用于绘制线段,作为线段的初始端点坐标
private Mnist model;//用于识别手写数字
private const int MnistImageSize = 28;//Mnist模型所需的输入图片大小
private void Form1_Load(object sender, EventArgs e)
{
//当窗口加载时,绘制一个白色方框
model = new Mnist();
digitImage = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics g = Graphics.FromImage(digitImage);
g.Clear(Color.White);
pictureBox1.Image = digitImage;
}
private void clean_click(object sender, EventArgs e)
{
//当点击清除时,重新绘制一个白色方框,同时清除label1显示的文本
digitImage = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics g = Graphics.FromImage(digitImage);
g.Clear(Color.White);
pictureBox1.Image = digitImage;
label1.Text = "";
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
//当鼠标左键被按下时,记录下需要绘制的线段的起始坐标
startPoint = (e.Button == MouseButtons.Left) ? e.Location : startPoint;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
//当鼠标在移动,且当前处于绘制状态时,根据鼠标的实时位置与记录的起始坐标绘制线段,同时更新需要绘制的线段的起始坐标
if (e.Button == MouseButtons.Left)
{
Graphics g = Graphics.FromImage(digitImage);
Pen myPen = new Pen(Color.Black, 40);
myPen.StartCap = LineCap.Round;
myPen.EndCap = LineCap.Round;
g.DrawLine(myPen,startPoint, e.Location);
pictureBox1.Image = digitImage;
g.Dispose();
startPoint = e.Location;
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//当鼠标左键释放时
//同时开始处理图片进行推理
//暂时不处理这里的代码
}
}
}
步骤二:把模型包装成一个类
将模型包装成一个 C# 是整个过程中比较麻烦的一步。所幸的是,Tools for AI 对此提供了很好的支持。进一步了解,可以看这里(https://github.com/Microsoft/vs-tools-for-ai/blob/master/docs/zh-hans/docs/model-inference.md)。
首先,我们在解决方案 MnistForm 下点击鼠标右键,选择添加->新建项目,在弹出的窗口里选择 AI Tools->Inference->模型推理类库,名称不妨叫做 MnistModel,点击确定,于是我们又多了一个项目。
然后自己配置好这个项目的名称、位置,点击确定。
然后弹出一个模型推理类库创建向导,这个时候就需要我们选择自己之前训练好的模型了~
首先在模型路径里选择保存的模型文件的路径。这里我们使用在AI应用开发实战 - 从零开始配置环境博客中训练并导出的模型
note:模型可在 /samples-for-ai/examples/tensorflow/MNIST 目录下找到,其中 output 文件夹保存了检查点文件,export 文件夹保存了模型文件。
对于TensorFlow,我们可以选择检查点的 .meta 文件,或者是保存的模型的 .pb 文件
这里我们选择在AI应用开发实战 - 从零开始配置环境这篇博客最后生成的 export 目录下的检查点的 SavedModel.pb 文件,这时程序将自动配置好配置推理接口,见下图:
类名可以自己定义,因为我们用的是 MNIST,那么类名就叫 Mnist 好了,然后点击确定。
这样,在解决方案资源管理器里,在解决方案 MnistForm 下,就多了一个 MnistModel:
双击 Mnist.cs,我们可以看到项目自动把模型进行了封装,生成了一个公开的 infer 函数。
然后我们在 MnistModel 上右击,再选择生成,等待一会,这个项目就可以使用了~
步骤三:连接两个部分
这一步差不多就是这么个感觉:
I have an apple , I have a pen. AH~ , Applepen
首先,我们来给 DrawDigit 添加引用,让它能使用 MnistModel。在 DrawDigit 项目的引用上点击鼠标右键,点击添加引用,在弹出的窗口中选择 MnistModel,点击确定。
然后,由于 MNIST 的模型的输入是一个 28×28 的白字黑底的灰度图,因此我们首先要对图片进行一些处理。
首先将图片转为 28×28 的大小。
然后将 RGB 图片转化为灰阶图,将灰阶标准化到 [-0.5,0.5] 区间内,转换为黑底白字。
最后将图片用 mnist 模型要求的格式包装起来,并传送给它进行推理。
于是,我们在 pictureBox1_MouseUp 中添加上这些代码,并且在文件最初添加上 using MnistModel;:
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//当鼠标左键释放时
//开始处理图片进行推理
if (e.Button == MouseButtons.Left)
{
Bitmap digitTmp = (Bitmap)digitImage.Clone();//复制digitImage
//调整图片大小为Mnist模型可接收的大小:28×28
using (Graphics g = Graphics.FromImage(digitTmp))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(digitTmp, 0, 0, MnistImageSize, MnistImageSize);
}
//将图片转为灰阶图,并将图片的像素信息保存在list中
var image = new List<float>(MnistImageSize * MnistImageSize);
for (var x = 0; x < MnistImageSize; x++)
{
for (var y = 0; y < MnistImageSize; y++)
{
var color = digitTmp.GetPixel(y, x);
var a = (float)(0.5 - (color.R + color.G + color.B) / (3.0 * 255));
image.Add(a);
}
}
//将图片信息包装为mnist模型规定的输入格式
var batch = new List<IEnumerable<float>>();
batch.Add(image);
//将图片传送给mnist模型进行推理
var result = model.Infer(batch);
//将推理结果输出
label1.Text = result.First().First().ToString();
}
}
最后让我们尝试一下运行~
现在我们就有了一个简单的小程序,可以识别手写的数字了。
赶紧试试效果怎么样~
1.路径中不能有中文字符,否则可能找不到模型。
那么,如果要识别多个连写的数字,或支持字母该怎么做呢?大家多用用也会发现,如果数字写得很小,或者没写到正中,识别起来正确率也会不高。要解决这些问题,做成真正的产品,就不止这一个模型了。比如在多个数字识别中,可能要根据经验来切分图,或者训练另一个模型来检测并分割数字。要支持字母,则需要重新训练一个包含手写字母的模型,并准备更多的字母的数据。要解决字太小的问题,还要检测一下字的大小,做合适的放大等等。
我们可以看到,一个训练出来的模型本身到一个实际的应用之间还有不少的功能要实现。希望我们这一系列的介绍,能够帮助大家将机器学习的概念带入到传统的编程领域中,做出更聪明的产品。
原文链接:
https://blog.csdn.net/softwareteacher/article/details/80770347
在线公开课NLP专场
◆
精彩继续
◆
时间:6月28日 20:00-21:00
扫描海报二维码,免费报名
添加微信csdnai,备注:公开课,加入课程交流群