Compressing and enhancing hand-written notes

原文链接,作者:Matt Zucker


翻译者:马峰,禁止全文转载

我写了一个程序去清洁我扫描的手写的笔记,同时也是为了减少扫描文档的大小。

示例:原始效果和处理后的效果

左边是扫描的文档@300DPI, 7.2MB PNG, 790KB;右边是处理后的文档,300DPI,PNG,121KB

声明:这里所描述的处理流程或多或少Office Lens这个App也能够做到,也可能已经有别的工具也已经能够做到类似的效果。我并没有在这里声明这是一个新发明,而只是我自己实现的一个有用的工具。

如果你很想知道最后的处理的效果,你可以直接查看这个仓库里的代码,或者直接跳转到结果部分,那里可以直接看到可交互式的3D示意图。

缘由

我所教授的某些课并没有指定的教科书,对于这些课程,我喜欢每周都指定一些学生分享他所记录的课堂笔记给他的同学们,以便于通过这些学生手写的资料可以再次检查他们对于课程理解的程度。这些课程笔记全部以PDF格式被放在一个教学的网站上。

学校里有一个复印机可以方便的扫描笔记到PDF文档,但是文档的质量却并不好,比如下面的这类:

看起来复印机会随机选择是否二值化每一个数学符号,比如上图中的x,或者把他们转化为JPG,比如上图中的根号。不用说,我们可以做的更好。

预备

我们从一个学生的可爱的笔记开始,比如下面这个:

原初的300DPI扫描出来的PNG是7.2MB,如果我们吧这个PNG转化为JPG(85%质量),将会是790KB。因为扫描出来的PDF基本是一个JPG或者PNG的容器,所以我们并不期待,转为PDF后,文件大小会大大减少。但是800KB每页对加载时间来说实在是太大了,我觉得最好是100KB每页比较好。

虽然上图中的学生的笔记写的非常的整齐,但是上图中扫描出来的笔记还是把背面的文字透进来了,这会让阅读者失去兴趣,同时也对文件大小的压缩很不利。

这是通过我们的程序处理之后的样子:

这是一个非常小的PNG文件,大概121KB。这就是想要的部分,文件很小,而且看起来也更清爽。

处理和渲染图像的基础

这是产生一个压缩的、更清晰的图片的基本步骤:

  1. 确定原来的扫描的图片的背景的颜色
  2. 通过设定阈值于前景色与背景色的差值去分离前景色
  3. 通过从前景色中选择一部分“代表色”,将图片中转化为index色的PNG

在我们深入这些步骤之前,我们应该首先去理解color image是怎么被数字化存储的。因为人类有3个不同种类的对颜色敏感的细胞,我们可以通过联合各种各样的红、绿、蓝三色的密度去形成不同的颜色。最终的颜色等同于在RGB颜色空间里的3个不同的点。

虽然一个真正的矢量色彩空间将会允许连续的无限的像素密度,但是我们在数字化它们的时候,一般会离散化,通常我们会把红、绿、蓝三色分别用8个bit来存储。尽快这样,当我们把一个图片放在一个连续的色彩空间里分析仍旧是一个强大的工具。

找到背景颜色

因为我们扫描的笔记大部分是手写的文字和线段,我们可以期待纸的颜色应该是比较单一的,如果扫描仪每次都把没有写上文字的背景白纸当作一种固定的颜色话,我们可以很容易把这个背景色找出来。但是,现实不是这样。纸的颜色是随机变化的,由于扫描仪上镜头的灰尘,或者纸张本身的色差等。因此,在现实里,纸张本身的颜色的颜色可能在RGB色彩空间里千变万化的。

尽管实际的每张纸的相似度很低,但是每张纸的颜色的分布却是非常的类似。两个都是大部分是灰白色,同时可能零散夹杂一些红、绿、蓝色等。这里有一个比较同样的10000个像素,通过亮度(就是红绿蓝三色的总和)对他们进行排序:

我们可以看到底部的大概80%到90%的颜色是同样的,但是,进一步观察时又不一样。实际上,大部分上图中的颜色是在RGB(240,240, 242),但是我们实际观察到原图时,只有10000个像素点中226个符合,大概占3%不到。

因为这种模式所占有的像素点是如此之低,我们应该有一个疑问,如何才能可依赖的描述一个图像的颜色分布。我们实际上是有一个很好的方案去确定上面的图片中的色彩分布,如果我们在寻找模式之前降低图片的像素的位数的话。这里是我们如果把8bit的像素将为4bit的像素后的效果图:

现在我们可以看到RGB(224, 224, 224)大概可以占据36%的比例。通过减少像素的位数,我们可以把临近相似的像素点合并为更大的点,这样我们就容易放在数据的波峰。

这里有一个精度和可依赖的权衡:更小的点可以让颜色更好的区分,但是更大的点可以让得出的结论更可依赖。最后我们在每个channel里使用6bit去寻找背景颜色,这个看起来是一个很好的平衡点。

分离前景色

一旦我们分离了背景色,我们可以用同样类似的方法去分离前景色。一个很自然的计算两个像素的相似度的方案就是去计算它们在RGB色彩空间的Euclidean Distance,但是这种简单的方法并不能分离下面的颜色:

这里是一个表关于前景色与背景色之间的Euclidean Distance:

Color Where found R G B Dist. from BG
white background 238 238 242
gray bleed-through from back 160 168 166 129.4
black ink on front of page 71 73 71 290.4
red ink on front of page 219 83 86 220.7
pink vertical line at left margin 243 179 182 84.3

你可以看到,我们这里的选择的背景色跟真正的白色之间差距比粉色还远。任何基于Euclidean Distance的threshold在将粉色作为前景色时,必须要考虑到背景色。

我们可以避免这个问题,如果我们从RGB的色彩空间移动到HSV色彩空间,它用一个圆柱体来表达:

HSV圆柱体把七彩的颜色分布圆周分布在它的外顶部,hue表达的是圆周的角度,圆柱体的底部是黑色,顶部是白色,中间是灰色的阴影。中间的saturation是0,外部是1.0。最后value表示的是亮度,从黑到白。

因此,如果我们使用HSV,会得到:

Color Value Saturation Value diff. from BG Sat. diff from BG
white 0.949 0.017
gray 0.659 0.048 0.290 0.031
black 0.286 0.027 0.663 0.011
red 0.859 0.621 0.090 0.604
pink 0.953 0.263 0.004 0.247

我们可以看到黑、白、灰在数值上差异很大,但是saturation都很低,远低于红、粉。通过HSV的信息,我们可以成功地标记一个像素是否属于前景色:

  • value的差值大于0.3或者
  • saturation大于0.2

第一个主要是分离黑色墨水的颜色,而第二个主要是分离红色墨水盒粉色线段。两个都可以把灰色成功地分离出去,不同的图片可能需要不同的saturation和value threshold。

选择一系列代表颜色

一旦我们分离了前景色,我们剩下的工作就是要把纸上的相关的笔迹分离出来。我们可以把这些集合可视化出来:但是这次,我们不把颜色作为一团像素,我们将把他们当作RGB色彩空间中3D的点来处理。最终的离散图看起来是成块的。

(等待更新)

通向机器学习之路的简单实用指南(一):背景知识、基础概念和工作流

原文链接,作者:Dan Kuster

翻译者:马峰,本文禁止全文转载

Hello,亲爱的人类!非常欢迎你们来到这一系列关于如何使用机器学习去解决问题的文章。通常我们会以基础的概念为起点,然后在后面会有一些列Python和
TensorFlow的代码来实现。然后我们将进一步展示如何合并利用和拓展这些基本的概念去解决更有趣的问题。

一种效率不高的学习方式

现在我们可以很容易在tensorfolow的官方网站或者其他地方找到教程,比如(1
2345)。
但是我们也发现许多很聪明的人,在学习tensorflow的时候,也会在第一次就深深跌入机器学习的代码实现中。这会导致你一个通用的效率不高的模式,你认为你
在学习的时候,停滞在实现的细节里,而实际上你只是停滞在思考的流程里:

  • 搜索一些tensorflow的教程,然后放进浏览器里
  • 然后开始循着一个简单的教程,很快学完后,觉得好棒,很容易就实现了教程里的功能,而且使用的是DNN(deep neural networks)
  • 然后尝试着使用一些非常复杂的模型去解决新问题
  • 嗯,出现了一些错误导致不能工作,而且看起来这个错误不容易被解决
  • 再次去网上搜索寻找答案,也许别人已经解决了我的这类问题

更清晰的思考到写代码的过程

如果这里有像一个专家一样使用机器学习的秘密,我想这个秘密应该是这样:机器学习的核心不是去写代码,而是一种科学思考的流程:首先把你遇到的唯一的问题翻译成机器学习的任务(Task),这个任务然后通过数据、机器学习的思想、工具(比如tensorflow)、互联网上的信息等被解决。我们现在就开始吧!

范围(scope)

这篇文章引入了一些列通用的关于机器学习的基础概念,这些概念可以帮助理解在机器学习中的大部分的解决问题的方案。我们在这里并不会展示大量通用的机器学习的理论,只是一系列可以帮助你大量提高机器学习能力的范式。这些范式是许多模型的基础,包括从最简单的线性回归到深度神经网络。

现在我们开始介绍一些简单的、基础的在机器学习中实用的概念,我们将会展示在机器学习的思考流程中与我们常见的科学的思考流程是多么的相似。在本系列文章的第二部分,我们还将把这些思考的流程放进一个非常的简单的机器学习的模型中,去解决一些光学识别(OCR)的任务。后面的文章,我们还将基于这些已经学习的概念,实现一个基本的神经网络去解释在1990到2000时代的对于机器的改进,导致可以训练深度神经网络成为可能,深度神经网络的成功也是机器学习在今天变得流行的重要原因。

为什么我应该阅读

  • 你已经拥有了良好的python编程能力,你喜欢去学习一些教程并且亲自去验证教程里的每一个步骤。你已经理解了机器学习的基本的知识–通过训练一个机器去做事情,通过数据而不是程序逻辑使机器可以自主工作。这就是在你学习本系列教程之前唯一需要知道的东西。如果你不懂python编程,不要欺骗你自己,从来没有机器可以在没有学会工具的前提下可以变得可以使用而且功能强大,如果你真的有兴趣去学习机器学习,应该用一个正确的方法。

  • “请帮忙!我已经搜索了几篇TensorFlow Tutorials并且发现了很多页的结果,应该从哪一个开始学起来?”学习的最好的方式是去实现你学到的东西,通过实验和练习去满足你的好奇心。不幸的是,大部分的教程都有作者自己学习过程的边际效应,并没有基于你当前的知识储备进行优化。我们正在写的这一系列教程,就是为了让读者可以首先理解机器学习的概念,并且可以通过tensorflow如何简单地实现这些概念。

  • “我已经看到google正通过sampled softmax和bucketing在使用双向的LSTM sequence to sequence神经翻译模型在他们的seq2seq模型中。听起来让人印象非常深刻,我应该从这里开始学习!”哈哈,这也是我开始学习tensorflow的起点。你比我更聪明。即时已经拥有了良好的机器学习的知识,能够快速通过同事或者社区找到帮助,并且通晓各种训练集,但是从更简单的开始通常是一个更好的主意。

  • 你已经阅读了大量的教程,但是你依然挣扎于把这些概念应用到实际的场景中去解决问题。这依然是你因为开始就阅读比较复杂的实例带来的毛病,解决的方法就是从最基础的概念开始。

哪些人不需要阅读(或者你阅读后会觉得不是你想要的)

  • 博士生。如果你已经是一个机器学习的研究者,并且拥有了良好的机器学习的基础知识和概念,你已经非常熟悉Torch和Thenao,你可以直接阅读Alec和Nathan的仓库:

  • 机器学习黑客。希望你已经知道了大部分这里的知识。但是有时去温习一下也是不错,温故而知新嘛。

  • 统计学家。如果你期待某种程度上的统计学上的严谨可以在这里讨论,我将会说对不起,这不会发生。没有人可以理解:为什么某些机器学习方法比如神经网络可以工作的这么好。就如也没有人知道统计学和数学为什么可以在一起工作的很好一样。

  • 真正的初学者。如果你想学一些更多的关于机器学习的方法和概念,而不是想学习一些快速的、实用的这些教程,你可以尝试Vincent Vanhoucke的相关课程。那个课程很棒,老师是tensorflow的一个技术领导。

为什么没有设立一个仓库

因为我们程序员总是很懒,当我们被强迫去探索和有点挣扎的时候,学习才会真正的沉淀下来。低效率对学习是有好处的。所有在这里的代码都是免费的和可见的,但是你必须开始学会思考并形成你自己的习惯。如果我们放一个repo,你将会直接clone它,因此使你的学习过程变得短路。除此之外,已经有了一个很好的repo,后面会提及。

关于机器学习的参考书

你当然不能错过Goodfellow, Bengio, Couville的书,在线版本也是免费的。


Setup

  1. 阅读tensorflow官方文档,如果你看到一个陌生的概念,需要查看相关的文档。

  2. (可选的)如果你想要GPU计算,可以下载安装CUDA。

  3. 使用官方文档安装tensorflow。linux推荐,macos也是可以。

  4. 运行最简单的测试,如果你的测试没有通过,则表示你安装的tensorflow是不可以用的。如果你不能安装GPU的版本,你可以直接安装CPU的版本。

什么是TensorFlow?它有什么特点?

Tensorflow是google开发的第二代的关于机器学习的系统。这里有很多原因人们需要对tensorflow欢呼。作为实用问题的解决者,我们唯一关注tensorflow的理由是,它通常使我们的实验更快。Tensorflow的下列特征使它实验时更快:

  • 符号的差异化能力使我们在写python代码时可以跟数学文献上的符号比较一致,然后tensorflow可以编译成可执行的代码。研究人员实用数学符号是因为,数学符号可以帮助他们更容易思考,但是把这些数学公式转化为真实的可以执行的代码却是困难的,只有很少的数学家可以写出来具体的代码的实现。完全是不同的技能集,因此,能够同时高效地写数学公式和具体的实现对数学社区是一个巨大的时间的节省。虽然一些工具比如Theano也可以高效的实现数学公式和符号,但是tensorflow并不仅仅提供这唯一的可以使用的特点。但是正是tensorflow的流行和神经网络,导致了大量的用户真正第一次接触到数学到实现的便捷。

  • 编译到CPU或者GPU。训练机器学习模型对计算资源要求是非常高的,对于tensorflow这样的工具,我们可以借助GPU的强大的并发运算能力,可以达到最大30倍的提高。如果要使用GPU,你可能需要大量的编程技巧去调用GPU的并发机制,但是tensorflow可以快速的编译到GPU后者CPU,只需要很少的代码,就可以在两个平台自由切换。因此,tensorflow拥有了一个非常强大的多GPU支持的架构。

  • 限制。tensorflow实现了许多限制和抽象去帮助我们思考一个问题,并且准备数据去解决问题。这可以帮助我们在解决问题的时候借用并发、模块化的概念。限制激发你的创造力,不应该被低估。

  • 模型生命周期管理。模型的生命周期管理在机器学习中是一个痛。什么时候,我们需要从一个保存的检查点中重新加载模型并重新开始训练?如何部署一个模型到远程的服务器?如何可视化一个模型以便我们可以确信我们实现的模型正是我们所需?如何加载和卸载数据流?tensorflow有一个专门的feature去处理这些问题。

  • Python总是有一个强大的社区,那里的人们很快就接受了tensorflow。如果你没有有经验的同事,你可以直接去github后者stackoverflow上寻找答案。如果你在一个问题停留超过30分钟,你应该求助你的朋友,或者互联网。

机器学习流程的基础部分

通过例子学习

机器学习的最重要的一点就是:把人从实现和发现规则(rule)中解放出来。就像人类一样,我们通过实例学习。我们通过观察实例,形成了一个关于机器如何工作的模型(或者前提),然后我们观察这个模型的效果。当某个实例出现的结果与我们的模型不一致的时候,我们更新模型去接受,以便观察到的效果与我们的模型的预测结果更加一致。我们可以记忆之前的实例,通过更加通用的模型,用来预测我们从来没有看到实例。

跟人类的对外部的传感输入相比,计算机程序仅仅具有非常有限的方式去观测事物,但是现代化的模型或者工作流是非常类似的。机器学习也是通过实例学习,但是机器通过输入的数据流去学习知识,更新它内部的状态以便与外部的输入预期一致。一个机器模型可以记忆之前的输入,从而可以预测它之前从来没有见过的实例。

机器学习的技术优势

计算机拥有一个不可比拟的优势:速度、精度、可重复性、分享完全一样的拷贝、不会疲劳、存储、所需电力。因此,当我们尝试去优化一个学习的流程时,我们应该充分考虑这些机器所具备的优势。

比如,我们可以可以更加显式的强化评估的步骤。假如输入一个实例,模型然后会输出一个预测的结果。然后我们可以评估这个预测,然后基于这个评估进一步去更新我们的模型。

损失值是对学习的反馈

计算机很擅长于遍历一些列学习数据,但是我们怎么确信下一个遍历的更新与当前的更新是一致的。一个简单的方法是,写一个评估函数去返回这个损失值。该函数(通常被叫做损失函数或者成本函数)比较已知的答案与预测的答案,然后生成一个正态化的分数。现在我们可以应用同一个函数到每一个输入的数据,并且,我们可以把它的值正态化以便提高学习流程的动态性,通过我们这个正态化使用的是log变形。

损失函数的概念和损失值对于机器学习是非常重要的。损失值是机器学习的关键的反馈,它确立了你的机器学习的模型的优化目标。没有这个损失值,你的机器学习模型将不会知道哪个参数应该优化。这里有许多的关于损失值的计算函数,你必须基于你所面临的问题,而去选择一个最合适的。

机器学习的调优

最后一块关于机器学习的步骤是通过成本函数(cost function)去对机器学习的模型进行调优。过去的几十年,大量的聪明家伙花费大量的努力去解决优化问题:“给你一些列的损失值,什么是最佳的方法对模型进行调优?一个最好的调优器应该是获取一个输入的数据,然后可以完美地预测最好的调优参数。但是在现实世界里,调优器通常收敛参数以便保证更好的损失值–如果多轮之后,并没有很大的提高,调优器将会收敛并且终止。某些场景下,你可能希望调优器快速收敛到合适并且正确率较高的参数。但是某些场景下,你可能想要不计成本的获取最好的调优参数。调优是非常深奥和技术性的领域,我们将会在未来的文章里更深入地学习。

幸运的是,我们已经重构出去了损失值(loss value),因此对于损失值的优化是在一个模块的基础上的。尤其对于像Gradient descent这类的一级的遍历优化者,基于模块化的优化器是当前的利用后向繁殖(back propagation)训练神经网络的最优选择。因此,从实用的角度,从一个优化器切换到另外一个优化器,跟函数名的切换一样简单。

总的来说,一个非常模块化的系统

机器学习的流程是非常类似于科学的思考的流程,人类通过传感输入的数据去观察输出的结果,而机器学习则必须被喂养数据。通过对机器学习流程的调优,我们可以利用计算机的优势去获得更加有力的机器学习的流程。首先,我们重构出去了损失函数用来评估和更新模型,这让我们在定义机器学习的反馈机制时更加的系统化,它也给了我们统一的界面去尝试各种不同的损失计算的函数。最后,我们抽象化出来优化的流程,给一个评估到的损失值,然后通过损失值去更新模型的参数,以便产生更好的预测。这就是机器学习的基础部分。

自我检查

我们来复习一下,请快速回答问题。

  1. 什么是机器学习和人类的科学学习流程的不同?

  2. 我们要使用tensorflow的原因有哪些?

  3. 什么是最好的优化器?

机器学习所涉及的名词
  • task
  • model
  • encoding
  • feature engineering
  • inference
  • decoding
  • loss function

Cross Platform daemonize approach: py_daemoniker

Why daemonization?

Python on Windows ships with pythonw.exe, which provides a “GUI app with no GUI” to run Python in the background. But using it precludes ever using the terminal to exchange information with the process, even to start it. Developers wanting to create Windows background processes are forced to choose between writing their own system-tray-minimizable GUI application using pythonw.exe (or a freezing packager like pyinstaller), or to define and install their code as a service in the Win32 API. There is a pretty clear gap for a dead-simple way to “daemonize” running code on Windows, exactly as you would on a Unix system.

more: https://github.com/Muterra/py_daemoniker