sourcecode is half done
After Width: | Height: | Size: 28 KiB |
|
@ -1,149 +0,0 @@
|
||||||
\documentclass[a4paper,12pt,UTF8,titlepage]{ctexart}
|
|
||||||
|
|
||||||
%页边距
|
|
||||||
\usepackage{geometry}
|
|
||||||
\geometry{left=2.0cm,right=2.0cm,top=2.5cm,bottom=2.5cm}
|
|
||||||
|
|
||||||
%页眉
|
|
||||||
\usepackage{fancyhdr}
|
|
||||||
\pagestyle{fancy}
|
|
||||||
\lhead{李志星 15060025 }
|
|
||||||
\chead{Spark MLlib应用报告}
|
|
||||||
\rhead{\leftmark}
|
|
||||||
|
|
||||||
%文档信息/同时也用于生成报告封面
|
|
||||||
\author{李志星\\ 15060025}
|
|
||||||
\title{\Huge MLlib中Logistic Regression的应用}
|
|
||||||
|
|
||||||
|
|
||||||
\usepackage{graphicx}
|
|
||||||
\usepackage{subfigure}
|
|
||||||
\DeclareGraphicsExtensions{.eps,.ps,.jpg,.bmp,.gif,.png}
|
|
||||||
|
|
||||||
\usepackage{pythonhighlight}
|
|
||||||
|
|
||||||
|
|
||||||
\begin{document}
|
|
||||||
\maketitle
|
|
||||||
\section{前言}
|
|
||||||
|
|
||||||
\subsection{手写识别}
|
|
||||||
手写识别(Handwriting recognition)是计算机在纸、照片、触摸屏或其他设备中接收并识别人手写的文字等信息的技术,主要应用于光学字符识别(OCR)。手写识别系统能够用来识别汉字、英语、数字等字符。不过本报告的重点不在手写识别,而在于理解MLlib中的logistic回归,因此以识别数字为例。识别数字0~9是个十类别问题,logistic回归最常用的场景是二分类,如果要用logistic回归解决这个问题,要采用one-against-one和one-against-all等做法进行处理,虽然复杂些,但是基本原理是一样的。因此本报告把主要关注点放在对MLlib算法的研究,只利用0和1的样本从而解决二分类问题。
|
|
||||||
|
|
||||||
\subsection{Logistic Regression}
|
|
||||||
|
|
||||||
\subsection{MLlib算法主要机制}
|
|
||||||
在MLlib中有许多的概念,其中对理解其算法比较重要的有:\emph{DataFrame},\emph{Pipeline},\emph{Transfromer}和\emph{Estimator}。
|
|
||||||
|
|
||||||
\begin{itemize}
|
|
||||||
\item DataFrame:MLlib使用SaprkSQL中DataFrame来操作数据集,是最近才添加的API。在MLLib模块中有两个包都可以用来调用机器学习算法:mllib和ml。mllibRDD操作,而ml基于DataFrame,ml是官方推荐使用的。通过\emph{DataFrame}可以操作各种各样的数据:文本、图像和结构化数据等。\emph{DataFrame}以命名列的方式组织的分布式数据集 ,等同于关系型数据库中的一个表,和R/Python中的\emph{DataFrame}类似,不过进行了很多的优化。
|
|
||||||
\item Pipeline:在MLlib中有一个很关键的概念:\emph{Pipeline}。在利用解决机器学习问题时,经常要用对数据进行一系列的处理,MLlib用\emph{Pipeline}来表示这样的工作流,在\emph{Pipeline}中,包含一组以一定顺序执行的\emph{ PipelineStage}( \emph{Transformer}和\emph{Estimator})。
|
|
||||||
\item Transformer:Transformer是对特征转换和学习得到的模型的抽象,每一个\emph{Transfromer}都要实现transform()方法,它把一个\emph{DataFrame}处理后得到另一个\emph{DataFrame},一般来说新的DataFrame比原来的DataFrame要多一些列。
|
|
||||||
\item Estimator:Estimator是对一些机器学习算法或者其他的数据处理算法的抽象,每一个\emph{Estimator}都有一个方法fit(),它以\emph{DataFrame}为参数,返回一个模型,也就是\emph{Transformer},比如在MLlib中 LogisticRegression 就是一个\emph{Estimator},而LogisticRegressionModel就是一个\emph{Transformer}。
|
|
||||||
\end{itemize}
|
|
||||||
|
|
||||||
一个比较简单形象的例子就是文本文档的处理,如下图*所示,上面一行表示了一个包含3个stage的Pipeline,前两个Tokenizer和HashingTF是Transformer,第三个LogisticRegression是一个Estimer。下面一行是一个流经pipeline的数据流,圆柱体代表了DataFrame.当调用pipeline的fit()方法时, Tokenizer.transform() 把原始的Raw text切分成单词,然后把这些单词添加到原DataFrame中形成新的DataFrame从而让HashingTF.transfore()进行处理,他把单词列转化成特征矩阵又添加到新的DataFrame中,最后通过 LogisticRegression.fit()方法产生LogisticRegressionModel。生成的模型可以用于后续的测试等。
|
|
||||||
|
|
||||||
\begin{figure}[h!]
|
|
||||||
\centering
|
|
||||||
\includegraphics[width=16cm]{pipeline.jpg}
|
|
||||||
\caption{MLlib pipeline}
|
|
||||||
\label{pipeline}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\section{解决方案}
|
|
||||||
\subsection{数据集}
|
|
||||||
本报告用到的数据集是我从网上搜集到的,一个文本文件对应一个样本,里面包含一个32*32的0/1矩阵,矩阵中每一个点相当于手写图像处理后一个像素点的值(如下图*)。训练数据和测试数据中分别有300和76个样本。
|
|
||||||
|
|
||||||
\begin{figure}[h!]
|
|
||||||
\centering
|
|
||||||
\subfigure[数字0对应的一个样本]{
|
|
||||||
\label{0} %% label for first subfigure
|
|
||||||
\includegraphics[width=1.0in]{data_0.jpg}}
|
|
||||||
\hspace{0.2in}
|
|
||||||
\subfigure[数字1对应的一个样本]{
|
|
||||||
\label{1} %% label for second subfigure
|
|
||||||
\includegraphics[width=1.0in]{data_1.jpg}}
|
|
||||||
|
|
||||||
\caption{样本示例}
|
|
||||||
\label{0_1} %% label for entire figure
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\subsection{代码}
|
|
||||||
如前言中所述,MLlib中有两个用于机器学习的包mllib和ml,根据应用趋势和其官方网站的建议,我采用了ml。详细代码见文件***.py。代码解释如下:
|
|
||||||
|
|
||||||
\subsubsection{导入依赖}
|
|
||||||
此段代码导入需要用到的包,包括数据处理的Vectors、算法训练的 LogisticRegression、算法评估BinaryClassificationEvaluator以及其他的一些用于和Spark操作的包。
|
|
||||||
\begin{python}
|
|
||||||
from pyspark import SparkContext
|
|
||||||
from pyspark.sql import SQLContext
|
|
||||||
from pyspark.ml.classification import LogisticRegression
|
|
||||||
from pyspark.mllib.linalg import Vectors
|
|
||||||
from os import listdir
|
|
||||||
from pyspark.ml.evaluation import BinaryClassificationEvaluator
|
|
||||||
\end{python}
|
|
||||||
|
|
||||||
\subsubsection{初始化环境}
|
|
||||||
SparkContext是在写Spark程序时入口,用来连接Spark并进行后续的操作,一般还会结合SparkConf对象来设置对Saprk集群的配置。这里我们用默认的设置即可。
|
|
||||||
SQLContext用来创建DataFrame。
|
|
||||||
\begin{python}
|
|
||||||
sc = SparkContext(appName="PythonlogExample")
|
|
||||||
sqlContext = SQLContext(sc)
|
|
||||||
\end{python}
|
|
||||||
|
|
||||||
\subsubsection{加载数据}
|
|
||||||
用于训练的DataFrame中应该包含两列:特征向量和类别。其中类别是数字1或者0.特征向量就是把32*32的矩阵转换成一个1024维的向量即可。load\_data函数接受一个表示训练样本的所在的目录的参数,遍历该目录下所有的文件也就是样本,从样本的名字解析出它的类别是0还是1,从文件内容中读取特征向量。然后生成DataFrame数据并返回。
|
|
||||||
|
|
||||||
\begin{python}
|
|
||||||
def load_data(data_folder):
|
|
||||||
file_list=listdir(data_folder)
|
|
||||||
file_num=len(file_list)
|
|
||||||
datas = list()
|
|
||||||
file_num=len(file_list)
|
|
||||||
datas = list()
|
|
||||||
for i in range(file_num):
|
|
||||||
filename=file_list[i]
|
|
||||||
fr=open('%s/%s' %(data_folder,filename))
|
|
||||||
data_in_line = list()
|
|
||||||
for j in range(32):
|
|
||||||
\end{python}
|
|
||||||
\newpage
|
|
||||||
\begin{python}
|
|
||||||
line_str=fr.readline()
|
|
||||||
for k in range(32):
|
|
||||||
data_in_line.append(int(line_str[k]))
|
|
||||||
label = filename.split('.')[0].split("_")[0]
|
|
||||||
datas.append((float(label),Vectors.dense(data_in_line)))
|
|
||||||
return sqlContext.createDataFrame(datas,["label","features"])
|
|
||||||
\end{python}
|
|
||||||
|
|
||||||
\subsubsection{模型训练}
|
|
||||||
再加载完训练数据后,即可用LogisticRegression来对其进行训练。新建LogisticRegression对象时可以指定一些参数,我在这里制定了最大迭代数和正则化参数。调用LogisticRegression的fit函数即可生成相应的LogisticRegressionModel。
|
|
||||||
|
|
||||||
\begin{python}
|
|
||||||
train_df = load_data("train")
|
|
||||||
lr = LogisticRegression(maxIter=10, regParam=0.3)
|
|
||||||
lrModel = lr.fit(train_df)
|
|
||||||
\end{python}
|
|
||||||
|
|
||||||
\subsubsection{模型评估}
|
|
||||||
利用测试数据对训练得到的模型进行评估,BinaryClassificationEvaluator用于评估二分类结果,我最后利用它计算了一下该模型的正确率。
|
|
||||||
\begin{python}
|
|
||||||
test_df = load_data("test")
|
|
||||||
predictions = lrModel.transform(test_df)
|
|
||||||
evaluator = BinaryClassificationEvaluator(labelCol="label", rawPredictionCol="rawPrediction", metricName="areaUnderPR")
|
|
||||||
accuracy = evaluator.evaluate(predictions)
|
|
||||||
print("Test Error = %g " % (1.0 - accuracy))
|
|
||||||
\end{python}
|
|
||||||
|
|
||||||
\subsection{结果}
|
|
||||||
|
|
||||||
|
|
||||||
\subsection{算法分析}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\end{document}
|
|
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 32 KiB |
|
@ -47,7 +47,7 @@ SystemML & 66 & 289 & 109 \\
|
||||||
\caption{各个系统在github上的关注度}
|
\caption{各个系统在github上的关注度}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
从上表可以看出关注度最高的是Tensforflow,其次是MLlib,无论是Star数还是fork数都是遥遥领先于其他几个系统。如此高的关注度说明了这两个系统得到了开发人员的广泛认可和支持。在我看来这就形成了一个如下图所示的“开源软件开发良性循环”:一个项目质量高,那么用户和开发者就越喜欢,从而就会积极地去贡献自己的力量,也就会促使该项目的质量进一步提高,如此循环不断。当然TensorFlow本身质量高之外,和Google在业内的影响力也分不开。我最看好的是MLlib,因为现在它是Spark原生集成的库,对运行在Sark上有先天的优势,借着Sark的势头应该会得到更加迅猛的发展和应用。
|
从上表可以看出关注度最高的是Tensforflow,其次是MLlib,无论是Star数还是fork数都是遥遥领先于其他几个系统。如此高的关注度说明了这两个系统得到了开发人员的广泛认可和支持。在我看来这就形成了一个如下图所示的“开源软件开发良性循环”:一个项目质量高,那么用户和开发者就越喜欢,从而就会积极地去贡献自己的力量,也就会促使该项目的质量进一步提高,如此循环不断。当然TensorFlow本身质量高之外,和Google在业内的影响力也分不开。我最看好的是MLlib,因为现在它是Spark原生集成的库,对运行在Sark上有先天的优势,借着Sark的势头应该会得到更加迅猛的发展和应用【!!!再加一些Spar今年峰会概况】。
|
||||||
|
|
||||||
\begin{figure}[h!]
|
\begin{figure}[h!]
|
||||||
\centering
|
\centering
|
|
@ -0,0 +1,301 @@
|
||||||
|
\documentclass[a4paper,12pt,UTF8,titlepage]{ctexart}
|
||||||
|
|
||||||
|
%页边距
|
||||||
|
\usepackage{geometry}
|
||||||
|
\geometry{left=2.0cm,right=2.0cm,top=2.5cm,bottom=2.5cm}
|
||||||
|
|
||||||
|
%页眉
|
||||||
|
\usepackage{fancyhdr}
|
||||||
|
\pagestyle{fancy}
|
||||||
|
\lhead{李志星 15060025 }
|
||||||
|
\chead{Spark MLlib应用报告}
|
||||||
|
\rhead{\leftmark}
|
||||||
|
|
||||||
|
%文档信息/同时也用于生成报告封面
|
||||||
|
\author{李志星\\ 15060025}
|
||||||
|
\title{\Huge MLlib中Logistic Regression的应用}
|
||||||
|
|
||||||
|
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{subfigure}
|
||||||
|
\DeclareGraphicsExtensions{.eps,.ps,.jpg,.bmp,.gif,.png}
|
||||||
|
|
||||||
|
\usepackage{pythonhighlight}
|
||||||
|
% "define" Scala
|
||||||
|
\usepackage{listings}
|
||||||
|
\lstdefinelanguage{scala}{
|
||||||
|
morekeywords={abstract,case,catch,class,def,%
|
||||||
|
do,else,extends,false,final,finally,%
|
||||||
|
for,if,implicit,import,match,mixin,%
|
||||||
|
new,null,object,override,package,%
|
||||||
|
private,protected,requires,return,sealed,%
|
||||||
|
super,this,throw,trait,true,try,%
|
||||||
|
type,val,var,while,with,yield},
|
||||||
|
otherkeywords={=>,<-,<\%,<:,>:,\#,@},
|
||||||
|
sensitive=true,
|
||||||
|
morecomment=[l]{//},
|
||||||
|
morecomment=[n]{},
|
||||||
|
morestring=[b]",
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"""
|
||||||
|
}
|
||||||
|
\usepackage{color}
|
||||||
|
\definecolor{dkgreen}{rgb}{0,0.6,0}
|
||||||
|
\definecolor{gray}{rgb}{0.5,0.5,0.5}
|
||||||
|
\definecolor{mauve}{rgb}{0.58,0,0.82}
|
||||||
|
% Default settings for code listings
|
||||||
|
\lstset{frame=tb,
|
||||||
|
language=scala,
|
||||||
|
aboveskip=3mm,
|
||||||
|
belowskip=3mm,
|
||||||
|
showstringspaces=false,
|
||||||
|
columns=flexible,
|
||||||
|
basicstyle={\small\ttfamily},
|
||||||
|
numbers=none,
|
||||||
|
numberstyle=\tiny\color{gray},
|
||||||
|
keywordstyle=\color{blue},
|
||||||
|
commentstyle=\color{dkgreen},
|
||||||
|
stringstyle=\color{mauve},
|
||||||
|
frame=single,
|
||||||
|
breaklines=true,
|
||||||
|
breakatwhitespace=true
|
||||||
|
tabsize=3
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
\section{前言}
|
||||||
|
|
||||||
|
\subsection{手写识别}
|
||||||
|
手写识别(Handwriting recognition)是计算机在纸、照片、触摸屏或其他设备中接收并识别人手写的文字等信息的技术,主要应用于光学字符识别(OCR)。手写识别系统能够用来识别汉字、英语、数字等字符。不过本报告的重点不在手写识别,而在于理解MLlib中的机器学习算法,因此以识别数字为例。识别数字0~9是个十类别问题,分类算法中最常用的场景是二分类,如果要用二分类解决这个问题,要采用one-against-one和one-against-all等做法进行处理,虽然复杂些,但是基本原理是一样的。因此本报告把主要关注点放在对MLlib算法的研究,只利用0和1的样本从而解决二分类问题。
|
||||||
|
|
||||||
|
\subsection{Logistic Regression}
|
||||||
|
在Zaharia提出Spark的论文中,他使用了Logistic Regression来作为一个实例来展示介绍Spark,因此在本报告中我也利用Logistic Regression来解决手写识别问题。其实在本质上Logistic Regression是个线性回归,它只不过是在特征到结果的映射过程中多加了一个二值函数映射:即先把样本的特征线性求和,接着把该连续值映射到0和1上。其大致的思路如下图所示:
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=8cm]{LG_arch.png}
|
||||||
|
\caption{Logistic Regression示意图}
|
||||||
|
\label{LG_arch}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{MLlib算法主要机制}
|
||||||
|
在MLlib中有许多的概念,其中对理解其算法比较重要的有:\emph{DataFrame},\emph{Pipeline},\emph{Transfromer}和\emph{Estimator}。
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item DataFrame:MLlib使用SaprkSQL中DataFrame来操作数据集,是最近才添加的API。在MLLib模块中有两个包都可以用来调用机器学习算法:mllib和ml。mllibRDD操作,而ml基于DataFrame,ml是官方推荐使用的。通过\emph{DataFrame}可以操作各种各样的数据:文本、图像和结构化数据等。\emph{DataFrame}以命名列的方式组织的分布式数据集 ,等同于关系型数据库中的一个表,和R/Python中的\emph{DataFrame}类似,不过进行了很多的优化。
|
||||||
|
\item Pipeline:在MLlib中有一个很关键的概念:\emph{Pipeline}。在利用解决机器学习问题时,经常要用对数据进行一系列的处理,MLlib用\emph{Pipeline}来表示这样的工作流,在\emph{Pipeline}中,包含一组以一定顺序执行的\emph{ PipelineStage}( \emph{Transformer}和\emph{Estimator})。
|
||||||
|
\item Transformer:Transformer是对特征转换和学习得到的模型的抽象,每一个\emph{Transfromer}都要实现transform()方法,它把一个\emph{DataFrame}处理后得到另一个\emph{DataFrame},一般来说新的DataFrame比原来的DataFrame要多一些列。
|
||||||
|
\item Estimator:Estimator是对一些机器学习算法或者其他的数据处理算法的抽象,每一个\emph{Estimator}都有一个方法fit(),它以\emph{DataFrame}为参数,返回一个模型,也就是\emph{Transformer},比如在MLlib中 LogisticRegression 就是一个\emph{Estimator},而LogisticRegressionModel就是一个\emph{Transformer}。
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
一个比较简单形象的例子就是文本文档的处理,如下图*所示,上面一行表示了一个包含3个stage的Pipeline,前两个Tokenizer和HashingTF是Transformer,第三个LogisticRegression是一个Estimer。下面一行是一个流经pipeline的数据流,圆柱体代表了DataFrame.当调用pipeline的fit()方法时, Tokenizer.transform() 把原始的Raw text切分成单词,然后把这些单词添加到原DataFrame中形成新的DataFrame从而让HashingTF.transfore()进行处理,他把单词列转化成特征矩阵又添加到新的DataFrame中,最后通过 LogisticRegression.fit()方法产生LogisticRegressionModel。生成的模型可以用于后续的测试等。
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=16cm]{pipeline.jpg}
|
||||||
|
\caption{MLlib pipeline}
|
||||||
|
\label{pipeline}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\section{解决方案}
|
||||||
|
\subsection{数据集}
|
||||||
|
本报告用到的数据集是我从网上搜集到的,一个文本文件对应一个样本,里面包含一个32*32的0/1矩阵,矩阵中每一个点相当于手写图像处理后一个像素点的值(如下图*)。训练数据和测试数据中分别有300和76个样本。
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\subfigure[数字0对应的一个样本]{
|
||||||
|
\label{0} %% label for first subfigure
|
||||||
|
\includegraphics[width=1.0in]{data_0.jpg}}
|
||||||
|
\hspace{0.2in}
|
||||||
|
\subfigure[数字1对应的一个样本]{
|
||||||
|
\label{1} %% label for second subfigure
|
||||||
|
\includegraphics[width=1.0in]{data_1.jpg}}
|
||||||
|
|
||||||
|
\caption{样本示例}
|
||||||
|
\label{0_1} %% label for entire figure
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection{代码}
|
||||||
|
如前言中所述,MLlib中有两个用于机器学习的包mllib和ml,根据应用趋势和其官方网站的建议,我采用了ml。Spark本身使用Scala语言编写的,但是它同样为Java、Python和R提供了几乎一致的接口,在本次报告中我使用的python。详细代码见文件***.py。代码解释如下:
|
||||||
|
|
||||||
|
\subsubsection{导入依赖}
|
||||||
|
此段代码导入需要用到的包,包括数据处理的Vectors、算法训练的 LogisticRegression、算法评估BinaryClassificationEvaluator以及其他的一些用于和Spark操作的包。
|
||||||
|
\begin{python}
|
||||||
|
from pyspark import SparkContext
|
||||||
|
from pyspark.sql import SQLContext
|
||||||
|
from pyspark.ml.classification import LogisticRegression
|
||||||
|
from pyspark.mllib.linalg import Vectors
|
||||||
|
from os import listdir
|
||||||
|
from pyspark.ml.evaluation import BinaryClassificationEvaluator
|
||||||
|
\end{python}
|
||||||
|
|
||||||
|
\subsubsection{初始化环境}
|
||||||
|
SparkContext是在写Spark程序时入口,用来连接Spark并进行后续的操作,一般还会结合SparkConf对象来设置对Saprk集群的配置。这里我们用默认的设置即可。
|
||||||
|
SQLContext用来创建DataFrame。
|
||||||
|
\begin{python}
|
||||||
|
sc = SparkContext(appName="PythonlogExample")
|
||||||
|
sqlContext = SQLContext(sc)
|
||||||
|
\end{python}
|
||||||
|
|
||||||
|
\subsubsection{加载数据}
|
||||||
|
用于训练的DataFrame中应该包含两列:特征向量和类别。其中类别是数字1或者0.特征向量就是把32*32的矩阵转换成一个1024维的向量即可。load\_data函数接受一个表示训练样本的所在的目录的参数,遍历该目录下所有的文件也就是样本,从样本的名字解析出它的类别是0还是1,从文件内容中读取特征向量。然后生成DataFrame数据并返回。
|
||||||
|
|
||||||
|
\begin{python}
|
||||||
|
def load_data(data_folder):
|
||||||
|
file_list=listdir(data_folder)
|
||||||
|
file_num=len(file_list)
|
||||||
|
datas = list()
|
||||||
|
file_num=len(file_list)
|
||||||
|
datas = list()
|
||||||
|
for i in range(file_num):
|
||||||
|
filename=file_list[i]
|
||||||
|
fr=open('%s/%s' %(data_folder,filename))
|
||||||
|
data_in_line = list()
|
||||||
|
for j in range(32):
|
||||||
|
line_str=fr.readline()
|
||||||
|
for k in range(32):
|
||||||
|
data_in_line.append(int(line_str[k]))
|
||||||
|
label = filename.split('.')[0].split("_")[0]
|
||||||
|
datas.append((float(label),Vectors.dense(data_in_line)))
|
||||||
|
|
||||||
|
return sqlContext.createDataFrame(datas,["label","features"])
|
||||||
|
\end{python}
|
||||||
|
|
||||||
|
\subsubsection{模型训练}
|
||||||
|
再加载完训练数据后,即可用LogisticRegression来对其进行训练。新建LogisticRegression对象时可以指定一些参数,我在这里制定了最大迭代数和正则化参数。调用LogisticRegression的fit函数即可生成相应的LogisticRegressionModel。
|
||||||
|
|
||||||
|
\begin{python}
|
||||||
|
train_df = load_data("train")
|
||||||
|
lr = LogisticRegression(maxIter=10)
|
||||||
|
lrModel = lr.fit(train_df)
|
||||||
|
\end{python}
|
||||||
|
|
||||||
|
\subsubsection{模型评估}
|
||||||
|
利用测试数据对训练得到的模型进行评估,BinaryClassificationEvaluator用于评估二分类结果,我最后利用其提供的两个指标areaUnderROC和areaUnderPR中的areaUnderROC评价了一下该模型。areaUnderROC是ROC曲线右下角部分占正方形格子的面积比例,该值越大说明分类的效果越好。
|
||||||
|
\newpage
|
||||||
|
\begin{python}
|
||||||
|
test_df = load_data("test")
|
||||||
|
predictions = lrModel.transform(test_df)
|
||||||
|
evaluator = BinaryClassificationEvaluator(labelCol="label", rawPredictionCol="rawPrediction", metricName="areaUnderPR")
|
||||||
|
accuracy=evaluator.evaluate(predictions)
|
||||||
|
print("Test Error = %g " % (1.0 - accuracy))
|
||||||
|
\end{python}
|
||||||
|
|
||||||
|
\subsection{结果}
|
||||||
|
在本次试验中,我分别设置最大迭代次数为0,5,10,来观察算法的训练效果。利用训练得到模型在测试数据上预测得到DataFrame predictions,然后调用predictions的show()方法,该方法会打印出前20个结果,一共五列,分别是label,features,rawPrediction,probability和prediction。label和features这两个列是加载的测试数据的类标号和特征向量。rawPrediction,probability和prediction是利用模型对测试数据进行预测得到的,我查了一下相关文档,rawPrediction和probability都是一个向量,其维数就是类标号的个数,rawPrediction字面上的意思是原始的预测,直观的讲就是每个类的置信度(confidence),并且这个向量中的所有元素相加得到的和是0,probability是在给定rawPrediction的条件概率(该样本属于每个类的可能性),其计算方法根据所采用的分类算法而不同。在Logistic Regression中是这样计算的 1/(1+exp(-rawPrediction)。prediction是最后对样本的预测,它是对rawPrediction利用argmax()函数得到的,也就是取对应rawPrediction最大的那个类。
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item maxIter=0时,算法给每个样本都预测为0,这样就有一半的测试样本是错误的,最后的areaUnderROC为0.5。\newline
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\subfigure{
|
||||||
|
\label{0} %% label for first subfigure
|
||||||
|
\includegraphics[width=10cm]{show_0.jpg}}
|
||||||
|
\hspace{0.2in}
|
||||||
|
\subfigure{
|
||||||
|
\label{1} %% label for second subfigure
|
||||||
|
\includegraphics[width=8cm]{areaUnderROC_0.jpg}}
|
||||||
|
\caption{maxIter=0时预测值和areaUnderROC}
|
||||||
|
\label{0_1} %% label for entire figure
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
\item maxIter=5时,areaUnderROC为0.5。\newline
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\subfigure{
|
||||||
|
\label{2} %% label for first subfigure
|
||||||
|
\includegraphics[width=10cm]{show_1.jpg}}
|
||||||
|
\hspace{0.2in}
|
||||||
|
\subfigure{
|
||||||
|
\label{3} %% label for second subfigure
|
||||||
|
\includegraphics[width=8cm]{areaUnderROC_1.jpg}}
|
||||||
|
\caption{maxIter=5时预测值和areaUnderROC}
|
||||||
|
\label{0_1} %% label for entire figure
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
\item maxIter=10时,areaUnderROC为0.5。\newline
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\subfigure{
|
||||||
|
\label{4} %% label for first subfigure
|
||||||
|
\includegraphics[width=10cm]{show_2.jpg}}
|
||||||
|
\hspace{0.2in}
|
||||||
|
\subfigure{
|
||||||
|
\label{5} %% label for second subfigure
|
||||||
|
\includegraphics[width=8cm]{areaUnderROC_2.jpg}}
|
||||||
|
\caption{maxIter=10时预测值和areaUnderROC}
|
||||||
|
\label{0_1} %% label for entire figure
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
从实验结果可以看出,随着迭代次数的增加,算法的效果也是越来越好。以rawPrediction或者probability为例,样本取对的类标签的置信度或者概率随着迭代次数的增加不断增加。
|
||||||
|
\subsection{库源代码分析}
|
||||||
|
如前所述,Spark 使用scala编写的,其Java接口是直接调用的scala实现,而Python接口又是基于Java接口实现的,因此虽然我报告中使用的Python接口实现的,但是算法的具体实现细节是用scala实现的,由于本人未曾使用过scala,因此在这里根据对代码的直观理解简要的介绍一下其大致思想。
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item 参数设置,算法一开始都是一些基本的对参数的设置,比如迭代次数,规则化参数,是否对数据标准化等等。例如:
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=16cm]{setMaxIter.jpg}
|
||||||
|
\caption{setMaxIter}
|
||||||
|
\label{setMaxIter}
|
||||||
|
\end{figure}
|
||||||
|
\item train()方法进行训练。\newline
|
||||||
|
首先是对训练数据集进行统计便于后续的处理。\newline
|
||||||
|
\newpage
|
||||||
|
\begin{lstlisting}[escapeinside='']
|
||||||
|
val (summarizer, labelSummarizer) = {
|
||||||
|
//'此处用于生成相应的统计数据'
|
||||||
|
}
|
||||||
|
val histogram = labelSummarizer.histogram
|
||||||
|
val numInvalid = labelSummarizer.countInvalid
|
||||||
|
val numClasses = histogram.length
|
||||||
|
val numFeatures = summarizer.mean.size
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
其次是对训练数据集进行一些有效性和特殊性的判断。\newline
|
||||||
|
\begin{lstlisting}
|
||||||
|
if (numClasses > 2) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
由于在Spark MLlib中ml版本中LogisticRegression还未支持多分类,所以它会先判断样本是否是二分类,否则会先报异常。如果想用多分类的话可以用mllib版本。我看了下mlib版本的源码,它会进行判断,如果是二分类它会调用ml版本的实现,如果是多分类才会调用自己的实现。此外我发现Spark的帮助文档里有个小缺陷,在ml关于LogisticRegression的介绍中,他指出算法的详细实现细节可以看mllib的实现,但是mllib的实现中关于二分类并没有具体的算法细节,反而在ml的实现中是有的。
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
} else if ($(fitIntercept) && numClasses == 2 && histogram(0) == 0.0) {
|
||||||
|
...
|
||||||
|
} else if ($(fitIntercept) && numClasses == 1) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
上面两个else分支是判断训练数据中的样本是否都是属于正样本否者负样本,与此同时,如果模型函数中还需要截距的话,那么其实不需要进行训练了,只要把系数都设为0,然后把截距设为正无穷或者负无穷即可。
|
||||||
|
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
if (!$(fitIntercept) && numClasses == 2 && histogram(0) == 0.0) {
|
||||||
|
...
|
||||||
|
} else if (!$(fitIntercept) && numClasses == 1) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
上面两段代码也是判断训练数据中的样本是否都是属于正样本否者负样本,但是模型函数中如果不需要截距的话,那么本次训练可能就会出现问题,所以它会进行警告。
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\begin{lstlisting}
|
||||||
|
if (!$(fitIntercept) && (0 until numFeatures).exists { i =>
|
||||||
|
featuresStd(i) == 0.0 && featuresMean(i) != 0.0 }) {
|
||||||
|
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\end{document}
|