diff --git a/.gitignore b/.gitignore index bbe6263..08158fa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ *.log *.aux *.pdf -*.gz \ No newline at end of file +*.gz* \ No newline at end of file diff --git a/Logistic_regression.tex b/Logistic_regression.tex index 28bed07..e3b38e1 100644 --- a/Logistic_regression.tex +++ b/Logistic_regression.tex @@ -8,7 +8,7 @@ \usepackage{fancyhdr} \pagestyle{fancy} \lhead{李志星 15060025 } -\chead{比较分析报告} +\chead{Spark MLlib应用报告} \rhead{\leftmark} %文档信息/同时也用于生成报告封面 @@ -17,15 +17,26 @@ \usepackage{graphicx} +\usepackage{subfigure} \DeclareGraphicsExtensions{.eps,.ps,.jpg,.bmp,.gif,.png} +\usepackage{pythonhighlight} + + \begin{document} \maketitle -\section{MLlib相关概念} +\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来操作数据集。通过\emph{DataFrame}可以操作各种各样的数据:文本、图像和结构化数据等。\emph{DataFrame}以命名列的方式组织的分布式数据集 ,等同于关系型数据库中的一个表,和R/Python中的\emph{DataFrame}类似,不过进行了很多的优化。 +\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}。 @@ -41,10 +52,98 @@ \end{figure} -\section{问题求解} -手写识别(Handwriting recognition)是计算机在纸、照片、触摸屏或其他设备中接收并识别人手写的文字等信息的技术,主要应用于光学字符识别(OCR)。手写识别系统能够用来识别汉字、英语、数字等字符。不过本报告的重点不在手写识别,而在于理解MLlib中的logistic回归,因此以识别数字为例。识别数字0~9是个十类别问题,logistic回归最常用的场景是二分类,如果要用logistic回归解决这个问题,要采用one-against-one和one-against-all等做法进行处理,虽然复杂些,但是基本原理是一样的。因此本报告把主要关注点放在对MLlib算法的研究,只利用0和1的样本从而解决二分类问题。 +\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{算法分析} + + + -sdfsdf \end{document} diff --git a/data_0.jpg b/data_0.jpg new file mode 100644 index 0000000..59d24b8 Binary files /dev/null and b/data_0.jpg differ diff --git a/data_1.jpg b/data_1.jpg new file mode 100644 index 0000000..383fd78 Binary files /dev/null and b/data_1.jpg differ