一、关于spark ml pipeline与机器学习

一个典型的机器学习构建包含若干个过程
1、源数据ETL
2、数据预处理
3、特征选取
4、模型训练与验证
以上四个步骤可以抽象为一个包括多个步骤的流水线式工作,从数据收集开始至输出我们需要的最终结果。因此,对以上多个步骤、进行抽象建模,简化为流水线式工作流程则存在着可行性,对利用spark进行机器学习的用户来说,流水线式机器学习比单个步骤独立建模更加高效、易用。
受 scikit-learn 项目的启发,并且总结了MLlib在处理复杂机器学习问题的弊端(主要为工作繁杂,流程不清晰),旨在向用户提供基于DataFrame 之上的更加高层次的 API 库,以更加方便的构建复杂的机器学习工作流式应用。一个pipeline 在结构上会包含一个或多个Stage,每一个 Stage 都会完成一个任务,如数据集处理转化,模型训练,参数设置或数据预测等,这样的Stage 在 ML 里按照处理问题类型的不同都有相应的定义和实现。两个主要的stage为Transformer和Estimator。Transformer主要是用来操作一个DataFrame 数据并生成另外一个DataFrame 数据,比如svm模型、一个特征提取工具,都可以抽象为一个Transformer。Estimator 则主要是用来做模型拟合用的,用来生成一个Transformer。可能这样说比较难以理解,下面就以一个完整的机器学习案例来说明spark ml pipeline是怎么构建机器学习工作流的。

二、使用spark ml pipeline构建机器学习工作流

在此以Kaggle数据竞赛Display Advertising Challenge的数据集(该数据集为利用用户特征进行广告点击预测)开始,利用spark ml pipeline构建一个完整的机器学习工作流程。
Display Advertising Challenge的这份数据本身就不多做介绍了,主要包括3部分,numerical型特征集、Categorical类型特征集、类标签。
首先,读入样本集,并将样本集划分为训练集与测试集:
  1. //使用file标记文件路径,允许spark读取本地文件
  2. String fileReadPath = "file:\\D:\\dac_sample\\dac_sample.txt";
  3. //使用textFile读入数据
  4. SparkContext sc = Contexts.sparkContext;
  5. RDD<String> file = sc.textFile(fileReadPath,1);
  6. JavaRDD<String> sparkContent = file.toJavaRDD();
  7. JavaRDD<Row> sampleRow = sparkContent.map(new Function<String, Row>() {
  8. public Row call(String string) {
  9. String tempStr = string.replace("\t",",");
  10. String[] features = tempStr.split(",");
  11. int intLable= Integer.parseInt(features[0]);
  12. String intFeature1  = features[1];
  13. String intFeature2  = features[2];                String CatFeature1 = features[14];
  14. String CatFeature2 = features[15];
  15. return RowFactory.create(intLable, intFeature1, intFeature2, CatFeature1, CatFeature2);
  16. }
  17. });
  18. double[] weights = {0.8, 0.2};
  19. Long seed = 42L;
  20. JavaRDD<Row>[] sampleRows = sampleRow.randomSplit(weights,seed);


得到样本集后,构建出 DataFrame格式的数据供spark ml pipeline使用:

  1. List<StructField> fields = new ArrayList<StructField>();
  2. fields.add(DataTypes.createStructField("lable", DataTypes.IntegerType, false));
  3. fields.add(DataTypes.createStructField("intFeature1", DataTypes.StringType, true));
  4. fields.add(DataTypes.createStructField("intFeature2", DataTypes.StringType, true));
  5. fields.add(DataTypes.createStructField("CatFeature1", DataTypes.StringType, true));
  6. fields.add(DataTypes.createStructField("CatFeature2", DataTypes.StringType, true));
  7. //and so on
  8. StructType schema = DataTypes.createStructType(fields);
  9. DataFrame dfTrain = Contexts.hiveContext.createDataFrame(sampleRows[0], schema);//训练数据
  10. dfTrain.registerTempTable("tmpTable1");
  11. DataFrame dfTest = Contexts.hiveContext.createDataFrame(sampleRows[1], schema);//测试数据
  12. dfTest.registerTempTable("tmpTable2");

由于在dfTrain、dfTest中所有的特征目前都为string类型,而机器学习则要求其特征为numerical类型,在此需要对特征做转换,包括类型转换和缺失值的处理。

首先,将intFeature由string转为double,cast()方法将表中指定列string类型转换为double类型,并生成新列并命名为intFeature1Temp,
之后,需要删除原来的数据列 并将新列重命名为intFeature1,这样,就将string类型的特征转换得到double类型的特征了。
  1. //Cast integer features from String to Double
  2. dfTest = dfTest.withColumn("intFeature1Temp",dfTest.col("intFeature1").cast("double"));
  3. dfTest = dfTest.drop("intFeature1").withColumnRenamed("intFeature1Temp","intFeature1");

如果intFeature特征是年龄或者特征等类型,则需要进行分箱操作,将一个特征按照指定范围进行划分:

  1. /*特征转换,部分特征需要进行分箱,比如年龄,进行分段成成年未成年等 */
  2. double[] splitV = {0.0,16.0,Double.MAX_VALUE};
  3. Bucketizer bucketizer = new Bucketizer().setInputCol("").setOutputCol("").setSplits(splitV);

再次,需要将categorical 类型的特征转换为numerical类型。主要包括两个步骤,缺失值处理和编码转换。

缺失值处理方面,可以使用全局的NA来统一标记缺失值:
  1. /*将categoricalb类型的变量的缺失值使用NA值填充*/
  2. String[] strCols = {"CatFeature1","CatFeature2"};
  3. dfTrain = dfTrain.na().fill("NA",strCols);
  4. dfTest = dfTest.na().fill("NA",strCols);

缺失值处理完成之后,就可以正式的对categorical类型的特征进行numerical转换了。在spark ml中,可以借助StringIndexer和oneHotEncoder完成

这一任务:
  1. // StringIndexer  oneHotEncoder 将 categorical变量转换为 numerical 变量
  2. // 如某列特征为星期几、天气等等特征,则转换为七个0-1特征
  3. StringIndexer cat1Index = new StringIndexer().setInputCol("CatFeature1").setOutputCol("indexedCat1").setHandleInvalid("skip");
  4. OneHotEncoder cat1Encoder = new OneHotEncoder().setInputCol(cat1Index.getOutputCol()).setOutputCol("CatVector1");
  5. StringIndexer cat2Index = new StringIndexer().setInputCol("CatFeature2").setOutputCol("indexedCat2");
  6. OneHotEncoder cat2Encoder = new OneHotEncoder().setInputCol(cat2Index.getOutputCol()).setOutputCol("CatVector2");

至此,特征预处理步骤基本完成了。由于上述特征都是处于单独的列并且列名独立,为方便后续模型进行特征输入,需要将其转换为特征向量,并统一命名,

可以使用VectorAssembler类完成这一任务:
  1. /*转换为特征向量*/
  2. String[] vectorAsCols = {"intFeature1","intFeature2","CatVector1","CatVector2"};
  3. VectorAssembler vectorAssembler = new VectorAssembler().setInputCols(vectorAsCols).setOutputCol("vectorFeature");

通常,预处理之后获得的特征有成千上万维,出于去除冗余特征、消除维数灾难、提高模型质量的考虑,需要进行选择。在此,使用卡方检验方法,

利用特征与类标签之间的相关性,进行特征选取:
  1. /*特征较多时,使用卡方检验进行特征选择,主要是考察特征与类标签的相关性*/
  2. ChiSqSelector chiSqSelector = new ChiSqSelector().setFeaturesCol("vectorFeature").setLabelCol("label").setNumTopFeatures(10)
  3. .setOutputCol("selectedFeature");

在特征预处理和特征选取完成之后,就可以定义模型及其参数了。简单期间,在此使用LogisticRegression模型,并设定最大迭代次数、正则化项:

  1. /* 设置最大迭代次数和正则化参数 setElasticNetParam=0.0 为L2正则化 setElasticNetParam=1.0为L1正则化*/
  2. /*设置特征向量的列名,标签的列名*/
  3. LogisticRegression logModel = new LogisticRegression().setMaxIter(100).setRegParam(0.1).setElasticNetParam(0.0)
  4. .setFeaturesCol("selectedFeature").setLabelCol("lable");

在上述准备步骤完成之后,就可以开始定义pipeline并进行模型的学习了:

  1. /*将特征转换,特征聚合,模型等组成一个管道,并调用它的fit方法拟合出模型*/
  2. PipelineStage[] pipelineStage = {cat1Index,cat2Index,cat1Encoder,cat2Encoder,vectorAssembler,logModel};
  3. Pipeline pipline = new Pipeline().setStages(pipelineStage);
  4. PipelineModel pModle = pipline.fit(dfTrain);

上面pipeline的fit方法得到的是一个Transformer,我们可以使它作用于训练集得到模型在训练集上的预测结果:

  1. //拟合得到模型的transform方法进行预测
  2. DataFrame output = pModle.transform(dfTest).select("selectedFeature", "label", "prediction", "rawPrediction", "probability");
  3. DataFrame prediction = output.select("label", "prediction");
  4. prediction.show();

分析计算,得到模型在训练集上的准确率,看看模型的效果怎么样:

  1. /*测试集合上的准确率*/
  2. long correct = prediction.filter(prediction.col("label").equalTo(prediction.col("'prediction"))).count();
  3. long total = prediction.count();
  4. double accuracy = correct / (double)total;
  5. System.out.println(accuracy);

最后,可以将模型保存下来,下次直接使用就可以了:

  1. String pModlePath = ""file:\\D:\\dac_sample\\";
  2. pModle.save(pModlePath);

三,梳理和总结:

上述,借助代码实现了基于spark ml pipeline的机器学习,包括数据转换、特征生成、特征选取、模型定义及模型学习等多个stage,得到的pipeline
模型后,就可以在新的数据集上进行预测,总结为两部分并用流程图表示如下:
训练阶段:
预测阶段:

借助于Pepeline,在spark上进行机器学习的数据流向更加清晰,同时每一stage的任务也更加明了,因此,无论是在模型的预测使用上、还是

模型后续的改进优化上,都变得更加容易。

spark ml 的例子的更多相关文章

  1. Spark ML 几种 归一化(规范化)方法总结

    规范化,有关之前都是用 python写的,  偶然要用scala 进行写, 看到这位大神写的, 那个网页也不错,那个连接图做的还蛮不错的,那天也将自己的博客弄一下那个插件. 本文来源 原文地址:htt ...

  2. Spark ML Pipeline简介

    Spark ML Pipeline基于DataFrame构建了一套High-level API,我们可以使用MLPipeline构建机器学习应用,它能够将一个机器学习应用的多个处理过程组织起来,通过在 ...

  3. Spark小课堂Week7 从Spark中一个例子看面向对象设计

    Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...

  4. Extending sparklyr to Compute Cost for K-means on YARN Cluster with Spark ML Library

    Machine and statistical learning wizards are becoming more eager to perform analysis with Spark MLli ...

  5. Spark ML下实现的多分类adaboost+naivebayes算法在文本分类上的应用

    1. Naive Bayes算法 朴素贝叶斯算法算是生成模型中一个最经典的分类算法之一了,常用的有Bernoulli和Multinomial两种.在文本分类上经常会用到这两种方法.在词袋模型中,对于一 ...

  6. Spark ML源码分析之一 设计框架解读

    本博客为作者原创,如需转载请注明参考           在深入理解Spark ML中的各类算法之前,先理一下整个库的设计框架,是非常有必要的,优秀的框架是对复杂问题的抽象和解剖,对这种抽象的学习本身 ...

  7. Spark ML源码分析之二 从单机到分布式

            前一节从宏观角度给大家介绍了Spark ML的设计框架(链接:http://www.cnblogs.com/jicanghai/p/8570805.html),本节我们将介绍,Spar ...

  8. Spark ML源码分析之四 树

            之前我们讲过,在Spark ML中所有的机器学习模型都是以参数作为划分的,树相关的参数定义在treeParams.scala这个文件中,这里构建一个关于树的体系结构.首先,以Decis ...

  9. 使用spark ml pipeline进行机器学习

    一.关于spark ml pipeline与机器学习 一个典型的机器学习构建包含若干个过程 1.源数据ETL 2.数据预处理 3.特征选取 4.模型训练与验证 以上四个步骤可以抽象为一个包括多个步骤的 ...

随机推荐

  1. js 获取字符串的 像素 宽度 ----字符串格式化输出

    function getLenPx(str, font_size) { var str_leng = str.replace(/[^\x00-\xff]/gi, 'aa').length; retur ...

  2. 吴裕雄 实战PYTHON编程(9)

    import cv2 cv2.namedWindow("ShowImage1")cv2.namedWindow("ShowImage2")image1 = cv ...

  3. 关于sql 增删改

    1.更改数据库的名称 --更改数据库的名称,逗号前面是之前的,后面是改成的名 sp_renamedb student,xuesheng 2.表中有数据的情况下再添加列.删除列 --后来添加列,只能默认 ...

  4. Honeycomb

    Honeycomb http://codeforces.com/gym/102028/problem/F time limit per test 4.0 s memory limit per test ...

  5. 使用UltraISO制作光盘镜像

    为什么使用光盘镜像文件: 1. 有些光盘中的内容必须在光盘运行环境中运行: 有些光盘的内容要在光盘运行的时候才能运行,即使你安装到电脑上都不行!例如某些游戏光盘等,这样就得每次使用时都要用光盘,对光驱 ...

  6. springmvc中Controller方法的返回值

    1.1 返回ModelAndView controller方法中定义ModelAndView对象并返回,对象中可添加model数据.指定view. 1.2 返回void 在controller方法形参 ...

  7. PAT 1077 互评成绩计算(20)(代码+思路)

    1077 互评成绩计算(20 分) 在浙大的计算机专业课中,经常有互评分组报告这个环节.一个组上台介绍自己的工作,其他组在台下为其表现评分.最后这个组的互评成绩是这样计算的:所有其他组的评分中,去掉一 ...

  8. TYVJ 1940 创世纪

    Description: 上帝手中有着 N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界.每 种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世 ...

  9. noi 1997 最优乘车

    H城是一个旅游胜地,每年都有成千上万的人前来观光.为方便游客,巴士公司在各个旅游景点及宾馆,饭店等地都设置了巴士站并开通了一些单程巴上线路.每条单程巴士线路从某个巴士站出发,依次途经若干个巴士站,最终 ...

  10. 开发中常遇到的Python陷阱和注意点-乾颐堂

    最近使用Python的过程中遇到了一些坑,例如用datetime.datetime.now()这个可变对象作为函数的默认参数,模块循环依赖等等. 在此记录一下,方便以后查询和补充. 避免可变对象作为默 ...