8d9d7ee48c3d48fc79d6d4207faf1161.png

本文由瀚高基础软件社区作者赫庆贺创作贡献

概述

本文介绍Greenplum和MADlib在机器学习方面的基本概念、架构和原理。并结合MNIST数据集完成对0-9阿拉伯数字的图形识别的应用实例。旨在为数据库开发者和DBA提供简单机器学习的了解和入门的方式。

一、知识与软件准备

1.1 Greenplum 

Greenplum是全球首个开源MPP数据库,内核基于PostgreSQL。作为PB级分布式数据库,支持大规模并行数据计算,被广泛应用于企业的OLAP业务场景。Greenplum集群的节点类型主要有master和segment。master提供数据库统一访问入口并协调各计算节点运行,segment作为物理存储和数据计算节点。通过增加segment节点可以实现系统性能的线性扩展。 Greenplum的并行计算能力和线性扩展能力赋能于MADlib,使其有更好的并发度,用更全量的数据和更少的数据移动来做模型训练。

1.2 MADlib

MADlib 是Apache 软件基金会顶级开源项目。作为一个机器学习函数库,MADlib能以扩展插件的形式嵌入到Greenplum和PostgreSQL数据库中,提供SQL层的接口。数据库用户可以直接通过SQL语言调用MADlib的接口完成机器学习的各个过程。 MADlib的架构如下:

f459cd7bbe266c11f534624b7fbb2b4a.png

为了保证执行效率,MADlib底层算法主要由C++编写,上层接口由python编写。目前已支持超过50种常用算法,并且为开发人员提供了扩展算法的方式。

所有算法都支持在Greenplum各个segment节点上并行运算,并将各个segment上的运算结果汇聚到master节点形成最终结果,计算时的数据仅在数据库内部移动。为了实现这一点,MADlib在数据库内创建一系列的汇聚函数(UDA)和计算函数(UDF)。在MADlib安装时这些函数会被创建在数据库内部。

MADlib支持深度学习算法,集成Keras、Tensorflow、Scipy库,并集成CUDA、CUDNN以支持GPU加速计算。

1.3 机器学习

机器学习是一门多领域交叉学科,涉及概率论、统计学、算法复杂度理论等多门学科。机器学习是人工智能(AI)的核心子领域,机器学习的理论基础是能够让计算机可以自动学习的算法。算法按照学习策略、应用领域、学习形式等方式划分为不同类别。常见机器学习算法有:朴素贝叶斯、线性回归、神经网络、决策树、支持向量机等等。幸运的是,作为一个软件开发者可以不用去了解算法的实现细节,直接调用函数库接口就能使用这些算法。机器学习已在生物特征检测、图像识别、数据挖掘、语义分析等多个领域有广泛应用。

传统方式下,计算机对某一个事物分类的判别方式通常需要一组事先定义好的阈值区间和一系列严格的判断逻辑组合来完成。例如让计算机判断一个物体是不是苹果,要事先告诉计算机苹果的定义是什么。

假设我们定义苹果的属性取值区间为:颜色为红色或青色,直径范围是2cm-10cm,重量范围20g-500g,口感香甜。

计算机判断物体是不是苹果的流程通常如下:

3348f5bdf5098a0a4bd5a90a4abd276b.png

机器学习则提供了另一种思路来解决这类问题。我们不需要给计算机预先定义苹果是什么。我们只需要告诉计算机这种东西是苹果、这种东西是苹果、这种东西也是苹果......

所有的苹果样例组成了数据集。通过机器学习算法对数据集的处理(训练)可以产生判断苹果的模型。随着数据集的变化,模型可以通过重复训练来优化调整。有了模型便可以判断一个新的物体是不是苹果了。

2e1d6261bb16ee61f76530805a088e5f.png

以上示例,对事务的分类判别方式称之为分类算法,分类算法仅仅是众多机器学习算法中的一种。除了分类算法还有用于预测连续性输出值的回归类算法,用来挖掘事务之间的关联关系的关联规则类算法等等。

1.4 数据集

机器学习中用来训练算法模型和测试算法正确性的数据。针对自身业务的机器学习模型训练最好使用自身应用场景的数据。也有许多不同应用场景下的公开数据集供学习研究使用。本文使用MNIST数据集作为训练和测试用数据。

MNIST来自美国国家标准与技术研究所(NIST),是被经常使用的典型数据集。MNIST由多人手写0-9的阿拉伯数字组成,可以把每个手写阿拉伯数字看做是一个28x28像素的图片,将图片转换成像素值的矩阵再以字节序的形式存储到文件中。

MNIST数据集的下载地址为:http://yann.lecun.com/exdb/mnist/

下载文件列表如图:

6c0dbf584170feaa7fc5fb89df6498a5.png

下载后可以用gunzip工具将每个压缩文件解压。数据集包含60000个训练数据样本,10000个测试数据样本。文件分别以格式存储数据。

b85e8a73c05faa10099fd625e9b135f0.png

1.5 数据处理代码

MNIST数据集需要将数据文件转换并导入Greenplum后才能被MADlib使用。为了方便数据集的处理,参照1.4节的数据文件格式,编写了代码文件mnist_transform.py:
import os  import struct  from PIL import Image  import numpy as npdef load_mnist(path, kind='train'):      """Load MNIST data from files"""      labels_path = os.path.join(path,'%s-labels-idx1-ubyte' % kind)      images_path = os.path.join(path,'%s-images-idx3-ubyte' % kind)      with open(labels_path, 'rb') as lbpath:          magic, n = struct.unpack('>II', lbpath.read(8))          labels = np.fromfile(lbpath, dtype=np.uint8)    with open(images_path, 'rb') as imgpath:          magic, num, rows, cols = struct.unpack('>IIII', imgpath.read(16))          images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)      return images, labels, numdef format_csv_line(seq, num, array):      linestr="%d,%d,\"{" % (seq, num)      for i in range(len(array)):          linestr += "%d," % array[i]      linestr = linestr[:-1]      linestr += "}\"\n"      return linestrdef array2csv(imgs, lbs, num, tofile):      """covert np.array to CSV format file"""      with open(tofile, "w") as f:          for seq in range(num):              f.write(format_csv_line(seq, lbs[seq], imgs[seq]))def array2imgfile(idx, imgs, filename):      """extract mnist data to png file by index"""      imgarray=imgs[idx].reshape(28, 28)      im = Image.fromarray(imgarray)      im.save("/home/gpadmin/madlib/mnist/images/%s_idx_%d.png" % (filename, idx), format="png")if __name__ == "__main__":      imgs, lbs, count = load_mnist("/home/gpadmin/madlib/mnist", "train")      array2csv(imgs, lbs, count, "/home/gpadmin/madlib/mnist/trans_data.csv")      imgs, lbs, count = load_mnist("/home/gpadmin/madlib/mnist", "t10k")      array2csv(imgs, lbs, count, "/home/gpadmin/madlib/mnist/test_data.csv")      #array2imgfile(0,imgs,"test")      print("transform completed.")

 load_mnist函数读取解压后的MNIST数据文件,并将数据放入numpy.array数组中。

array2csv函数将numpy.array数组中的数据装换成CSV格式文件,CSV文件可以通过copy命令导入数据库表,其格式与数据库表的格式要一致。请参考2.1节数据库表的创建语句。

array2imgfile 函数可以将数组中的一个样本转换成图片文件,以便通过图片查看器对数据样本进行可视查看。

二、应用实例

有了前面的各项准备工作,接下来我们要做的是以实现计算机对手写阿拉伯数字0-9的识别为例子,进一步探索Greenplum+MADlib机器学习方式。

具体过程是先将MNIST的数据集导入到Greenplum数据库中,使用MADlib提供接口,完成朴素贝叶斯算法模型的训练,并且使用训练的模型对测试数据进行识别。最后通过对比测试数据样本和识别结果来检验MADlib中朴素贝叶斯算法是否能对MNIST测试数据进行识别。

在进行第二章节的实例之前,需要先将Greenplum系统搭建好。Greenplum的安装方式可参考官方网站(https://cn.greenplum.org/)。

MADlib安装文档请参考:

https://cwiki.apache.org/confluence/display/MADLIB/Installation+Guide

安装时需要确保Greenplum已经启动,从源码编译安装到Greenplum时需要将编译好的函数库分发到各segment上。

本实例使用软件版本:

PostgreSQL 9.4.24 (Greenplum Database 6.0.0-beta.1 build dev)

MADlib version: 1.17.0

2.1 导入数据

以下步骤需要用客户端软件(如psql)连接到Greenplum数据库进行操作,使用PostgreSQL数据库时请酌情调整SQL。 创建训练数据表: CREATE TABLE mnist_example ( id int, label smallint, attributes smallint[]) DISTRIBUTED BY(id);   id 字段为样本编号。 label字段表示样本代表的阿拉伯数字。 attributes 字段是由784个元素构成的Array,用以保存样本图片的数据。 创建测试数据表: CREATE TABLE mnist_topredict ( id int, label smallint, attributes smallint[]) DISTRIBUTED BY(id);   将MNIST的训练数据和测试数据转换成CSV格式: 运行mnist_transform.py文件的转换函数(python3):
imgs, lbs, count = load_mnist("/home/gpadmin/madlib/mnist", "train")  array2csv(imgs, lbs, count, "/home/gpadmin/madlib/mnist/trans_data.csv")  imgs, lbs, count = load_mnist("/home/gpadmin/madlib/mnist", "t10k")  array2csv(imgs, lbs, count, "/home/gpadmin/madlib/mnist/test_data.csv")

得到CSV文件:test_data.csv  trans_data.csv

导入训练数据到表mnist_example:

copy  mnist_example from '/home/gpadmin/madlib/mnist/trans_data.csv' CSV;   

导入测试数据到表mnist_topredict:

copy mnist_topredict from '/home/gpadmin/madlib/mnist/test_data.csv' CSV;   2.2 训练模型 调用MADlib的API进行模型训练。 SELECT * FROM madlib.create_nb_prepared_data_tables('mnist_example','label','attributes',784,'mnist_feature_probs','mnist_priors');   参数 'mnist_example' 为2.1步骤创建的训练数据表名。 参数 'label'  指定以mnist_example表中的lable字段作为分类字段。 参数 'attributes' 指定以mnist_example表的attributes字段作为特征字段。 参数 784  表示数据特征的个数,在本例中图片的每个像素都作为一个特征,图片是28*28=784像素的。 参数'mnist_feature_probs' 指定特征概率输出表的名称 参数'mnist_priors' 指定分类输出表的名称,此表统计训练数据中各个分类出现的次数。

2.3 样本识别结果

创建可用来查询识别结果的视图 mnist_classified。

SELECT madlib.create_nb_probs_view('mnist_feature_probs','mnist_priors','mnist_topredict','id','attributes',784,'mnist_classified'); 

参数'mnist_feature_probs'  'mnist_priors'同2.2节

参数'mnist_topredict' 为待识别数据表,'id' 'attributes' 784参数指定该表的键和属性字段以及特征数。

参数'mnist_classified'为创建的视图名称。

创建好的视图结构如下,

e68ca564d291d9a0ec0c8d19aa5c9702.png

key 字段对应输入数据表(mnist_topredict)的id字段。 class 字段表示可能的阿拉伯数字 nb_prob 字段表示识别为对应class字段的概率,最大值为1。 对比查看对编号(key/id)为0的测试数据的识别结果以及待识别数据原型。 查看识别结果:

5445a1644920069eed1ac1d696f3cc7b.png

可以看到识别为数字 7的概率有1(100%).识别为其他是数字的概率都远低于数字7. 查看对应编号为0(id)的数据样本:

53fcafaf3128555ddb0babe34a50597a.png

编号为0的数据样本对应的阿拉伯数字为7。

通过mnist_transform.py文件里的array2imgfile函数将编号为0的测试数据转化为图片文件。

imgs, lbs, count = load_mnist("/home/gpadmin/madlib/mnist", "t10k")  array2imgfile(0,imgs,"test")
得到图片文件test_idx_0.png。 用图片查看器打开图片文件,验证为手写的数字 7。

deec023fe68274cc6d3b8a21a4767ded.png

对编号为0的测试数据样本识别达到期望目标。

再来看一个识别失败的例子。编号为1234的样本机器识别结果为:

e10d0ee94e753910305f4285485a0d44.png

机器识别为极大概率是数字5,其次为数字3,较小概率为数字8.为其他数字的概率远低于这三个。

对应的数据样本为:

b9c39ddd43fe1bac8e8d2cf028e1d2da.png

转换后的图片为:

f9d4733bb16effe39bb784d2449280af.png

显然对这张图片的识别未达到预期结果。

2.3 识别的正确率    

我们将nb_prob值最大的一项对应的class值作为最终识别数字,然后统计一下对10000个测试数据的识别正确率。

在数据库中创建PL/pgSQL函数mnist_stat():

CREATE OR REPLACE FUNCTION mnist_stat() RETURNS integer AS $$    DECLARE      topredict   RECORD;      classified  RECORD;      total       integer := 0;      sucnum      integer := 0;      failnum     integer := 0;  BEGIN      RAISE NOTICE 'Begin statstics';      FOR topredict IN SELECT id,label FROM mnist_topredict  LOOP          EXECUTE  'SELECT key,class FROM mnist_classified WHERE key=$1 ORDER BY nb_prob DESC LIMIT 1 ' INTO STRICT classified USING topredict.id;          IF topredict.label = classified.class THEN              sucnum := sucnum + 1;          ELSE              failnum := failnum +1;              RAISE NOTICE 'id % failed: %  --- % ',topredict.id,topredict.label,classified.class;          END IF;          total := total + 1;    END LOOP;      RAISE NOTICE 'total: % sucnum: % failnum:%',total,sucnum,failnum;    RETURN sucnum ;  END;  $$ LANGUAGE plpgsql;

执行统计过程:

testDB=# SELECT mnist_stat();
输出统计结果:

c1ba8236b1e82bdabffc3dea083b1312.png

识别样本总数10000,识别正确数为8377,识别错误数为1623.正确率为83.77%。 在没有任何针对性优化的情况下,83.77%正确率可以说明Greenplum +MADlib进行图像识别的可行性,并不能说明这种方式的优劣。因为机器学习算法识别的正确率与很多因素相关,如训练数据数量、测试样本、使用的机器学习算法、算法使用的参数等等。对于如何优化和提升还需进一步深入学习研究。

参考资料:

http://docs.greenplum.org/6-4/analytics/madlib.html#topic9

http://madlib.apache.org/documentation.html

http://yann.lecun.com/exdb/mnist/

13ccb71792b80293817a5eab1226dfbf.png

想学习机器学习算法?Greenplum原厂团队亲自操刀,和腾讯云大学合作打造了《基于 Greenplum 的机器学习算法与实践》系列课程。

十个章节,囊括了机器学习的前世今生、各大经典算法、深度学习、时间序列算法、图算法、数据分析扩展语言等丰富的内容,算法理论配合Greenplum应用实践。

bbef6b06cb84af3ad176ba08145117f1.png

b24f126c67e24c3237264bded622776d.png


67046e34-dd51-eb11-8da9-e4434bdf6706.svg

近期活动

6d046e34-dd51-eb11-8da9-e4434bdf6706.svg

54ffcf75075c8a079c473e6efeab11a0.png

点击文末“ ”,获取课程链接

1d7ed97c9d9d5c4d441f6a2879f97ad9.gif

cf2f20de98616eb7d567058335178364.png来一波 “在看”、“分享”和 “赞” 吧!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐