一、机器学习概念:
机器学习是一门多领域交叉学科,涉及概率论、统计学、计算机科学等多门学科。它的核心概念是通过算法让计算机从数据中学习,改善自身性能。机器学习专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,并重新组织已有的知识结构来不断改善自身的性能。
机器学习就是数据通过算法构建出模型并对模型进行评估 ,评估的性能如果达到要求就拿这个模型来测试其他的数据,如果达不到要求就要调整算法来重新建立模型,再次进行评估如此循环往复,最终获得满意的经验来处理其他的数据。
机器学习:算法统称,其中包含很多算法,给算法划分类别,主要划分为2大类(3类或4类) 。
第一类、监督学习(Supervised Learning) 。从数据集来说,有y值的,有label和target 。数据集:特征features +标签label
标签监督学习算法中标签label值的类型,可以划分为两类:
标签label值类型:离散值,称为分类算法
- 比如鸢尾花数据集
- 离散值,个数是一定的,从几个值中获取的数据
标签label值类型:连续值,称为回归算法
- 比如天气预测
第二类、非监督学习(Unsupervised Learning) 。从数据集来说,没有y值,没有label或target 。数据集:特征features
可以细分为两类算法:
- 聚类算法:就是把相似的对象聚集在一起的过程。想象一下,你有一堆不同颜色、形状和大小的球,你想要将它们分成几组,使得每组内的球尽可能相似,而不同组之间的球则尽量不同。
- 降维算法:将特征值由高向低下降,比如将鸢尾花数据集中特征值features是4个维度(4列值)降低到三个维度(3列)
数据集:
在机器学习中,数据集的基本概念是指用于训练和评估机器学习模型的数据集合。这些数据集合通常是由真实世界中的数据收集而来,它们由一组样本组成,每个样本包含一个或多个特征和一个或多个标签(也称为目标变量)。
1.1机器学习核心:
机器学习核心:数据+算法
1.2机器学习库:
机器学习库:机器学习库是包含各种机器学习算法和工具的集合,其中包含分类算法、回归算法、聚类算法和降维算法、推荐算法。目前来说,使用比较广泛2个算法库(机器学习算法库:将算法实现出来,用户只需要调用API,带入数据,训练模型即可)
算法库一:scikit-learn
- 基于Python语言实现机器学习算法库,可以人工智能学习,必学库
算法库二:Spark MLlib
- 基于Spark之上实现机器学习算法库,目前在大数据领域中经常被使用
特点:将常用的算法实现出来,结合海量数据训练模型,RDD数据存储内容,迭代训练模型更加快速,实际项目中往往scikit-learn 和 Spark MLlib一起使用的,都使用Python语言
Spark模块中提供两个数据接口封装数据,导致SparkMLlib库中有两种实现API,两个数据接口分别是:
- RDD数据结构
- DataFrame数据结构
推荐使用基于DataFrame API库。
1.3机器学习三要素:
使用机器学习库时,三要素为:算法、模型、策略。
- 模型(model):模型在未进行训练前,其可能的参数是多个甚至无穷的,故可能的模型也是多个甚至无穷的,这些模型构成的集合就是假设空间。
- 策略(strategy):即从假设空间中挑选出参数最优的模型的准则。模型的分类或预测结果与实际情况的误差(损失函数)越小,模型就越好。那么策略就是误差最小。
- 算法(algorithm):即从假设空间中挑选模型的方法(等同于求解最佳的模型参数)。机器学习的参数求解通常都会转化为最优化问题,故学习算法通常是最优化算法,例如最速梯度下降法、牛顿法以及拟牛顿法等。
1.4机器学习术语
术语一:数据集
术语二:样本
数据集中一条数据。
术语三:特征features
每个样本中特征feature组合
使用向量Vector表示,认为向量就是数组Array
向量分为两种:
- 稠密向量(Dense Vector):向量中特征值为非0的占比大于50%,称为稠密向量
- 稀疏向量(Sparse Vector):向量中特征值为0的占比大于50%,称为稀疏向量
术语四:矩阵
将多个特征(向量)放在一起就是矩阵Matrix,由于向量分为稠密跟系数,所以矩阵分两种:
- 稠密矩阵:矩阵每行数据为稠密向量,那么此矩阵为稠密矩阵
- 稀疏矩阵:矩阵每行数据为稀疏向量,那么此矩阵为稠密矩阵
术语五:标签label
针对监督学习算法来说,就相当于 y = kx + b 中y的值
ok,讲完基本概念,接下来提供三个案例来讲解数据如何处理以及常用算法。
二、鸢尾花数据集:
鸢(yuan)尾花 lris Dataset 数据集是机器学习领域经典数据集,该数据集可以从加州大 学欧文分校(UCI)的机器学习库中得到。鸢尾花数据集包含了150条鸢尾花信息,每50 条取自三个鸢尾花中之一:Setosa、Versicolour和 Virginica,每个花的特征用下面5种属性 描述。 (1)萼片长度(厘米) (2)萼片宽度(厘米) (3)花瓣长度(厘米) (4)花瓣宽度(厘米) (5)类(Setosa、Versicolour、Virginica) 花的萼片是花的外部结构,保护花的更脆弱的部分(如花)。在许多花中,片是绿 的,只有花瓣是鲜艳多彩的,然而对与鸢尾花,片也是鲜艳多彩的。下图中的Virginica 鸢尾花的图片,鸢尾花的萼片比花瓣大并且下垂,而花向上。如下图:
在鸢尾花中花数据集中,包含 150个样本和4个特征,因此将其记作 150x4 维的矩阵。
接下来使用Scala语言来构建分类模型:
1.构建SparkSession实例对象
val spark: SparkSession = SparkSession.builder()
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[4]")
.config("spark.sql.shuffle.partitions", 4)
.getOrCreate()
import spark.implicits._
2.加载鸢尾花数据集iris.data,属于csv文件
val irisSchema: StructType = new StructType()
.add("sepal_length", DoubleType, nullable = true)
.add("sepal_width", DoubleType, nullable = true)
.add("petal_length", DoubleType, nullable = true)
.add("petal_width", DoubleType, nullable = true)
.add("class", StringType, nullable = true)
val rawIrisDF: DataFrame = spark.read
.option("sep", ",")
// 当CSV文件首行不是列名称时,自定义Schema
.option("header", "false")
.option("inferSchema", "false")
.schema(irisSchema)
.csv("datas/iris/iris.data")
//rawIrisDF.printSchema()
//rawIrisDF.show(10, truncate = false)
3.将 萼片长度、宽度及花瓣长度、宽度 封装值 特征features向量中
val assembler = new VectorAssembler()
.setInputCols(rawIrisDF.columns.dropRight(1))
.setOutputCol("features") // 添加一列,类型为向量
val df1: DataFrame = assembler.transform(rawIrisDF)
///df1.printSchema()
//df1.show(10, truncate = false)
4.转换类别字符串数据为数值数据
val indexer: StringIndexer = new StringIndexer()
.setInputCol("class") // 对哪列数据进行索引化
.setOutputCol("label") // 数据索引化后列名
val df2: DataFrame = indexer
.fit(df1) // fit方法表示调用函数,传递DataFrame,获取模型
.transform(df1)
/*
root
|-- sepal_length: double (nullable = true)
|-- sepal_width: double (nullable = true)
|-- petal_length: double (nullable = true)
|-- petal_width: double (nullable = true)
|-- class: string (nullable = true)
|-- features: vector (nullable = true) // 特征 x
|-- label: double (nullable = false) // 标签 y
算法:y = kx + b
*/
df2.printSchema()
df2.show(150, truncate = false)
5.将特征数据features进行标准化处理转换,使用StandardScaler
机器学习核心三要素: 数据(指的就是特征features) + 算法 = 模型(最佳)
调优中,最重要的就是特征数据features,如果特征数据比较好,处理恰当,可能得到较好模型
在实际开发中,特征数据features需要进行各个转换操作,比如正则化、归一化或标准化等等。
为什么需要对特征数据features进行归一化等操作呢???? 原因在于不同维度特征值,值的范围跨度不一样,导致模型异常
val scaler: StandardScaler = new StandardScaler()
.setInputCol("features")
.setOutputCol("scale_features")
.setWithStd(true) // 使用标准差缩放
.setWithMean(false) // 不适用平均值缩放
val irisDF: DataFrame = scaler
.fit(df2)
.transform(df2)
//irisDF.printSchema()
//irisDF.show(10, truncate = false)
6.选择一个分类算法,构建分类模型
分类算法属于最多算法,比如如下分类算法: 1. 决策树(DecisionTree)分类算法 2. 朴素贝叶斯(Naive Bayes)分类算法,适合构建文本数据特征分类,比如垃圾邮件,情感分析 3. 逻辑回归(Logistics Regression)分类算法 4. 线性支持向量机(Linear SVM)分类算法 5. 神经网络相关分类算法,比如多层感知机算法 -> 深度学习算法 6. 集成融合算法:随机森林(RF)分类算法、梯度提升树(GBT)算法
7.将数据应用到算法中,训练模型以及对模型进行评估
//训练模型
val lrModel: LogisticRegressionModel = lr.fit(irisDF)
//评估模型到底如何
val summary: LogisticRegressionSummary = lrModel.summary
// 准确度:accuracy: 0.9733333333333334
println(s"accuracy: ${summary.accuracy}")
// 精确度:precision: 1.0, 0.96, 0.96, 针对每个类别数据预测准确度
println(s"precision: ${summary.precisionByLabel.mkString(", ")}")
// 应用结束,关闭资源
spark.stop()
总结:
总的来说,数据集训练模型的过程可以分为四个步骤:
- 特征提取(提取鸢尾花数据集)
- 类别标签label索引化
- label标签值,如果是离散值,算法是分类算法;如果是连续值,算法是回归
- 无论是 label还是features数据必须是数值类型(Double类型 )
- 特征features标准化
- 选择一个分类算法,构建分类模型
三、波士顿房价预测
波士顿房价预测是一个经典的机器学习任务,类似于程序员世界的“Hello World”。波士顿的房价是由各方面影响而来的,所以要预测波士顿的房价必须从数据集属性来研究。接下来将使用线性回归算法对波士顿房价数据集构建回归模型并评估模型性能。
波士顿房价预测步骤:
1. 加载波士顿房价数据集 2. 获取特征features和标签label 3. 特征数据转换处理(归一化)等 4. 创建线性回归算法实例对象,设置相关参数,并且应用数据训练模型 5. 模型评估,查看模型如何 6. 模型预测
这里由于线性回归算法,默认情况下,对特征features数据进行标准化处理转换,所以此处不再进行处理,所以不需要进行步骤3,将特征数据转换处理。
其实会发现大部分模型训练的时间都会花在数据处理上面,大概会花费百分之八十的时间,而算法的使用与模型评估预测只占百分之二十,这也是由于本来就有机器学习库中封装的API.
代码如下:
import org.apache.spark.ml.linalg
import org.apache.spark.ml.linalg.Vectors
import org.apache.spark.ml.regression.{LinearRegression, LinearRegressionModel}
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
import org.apache.spark.storage.StorageLevel
/**
* 波士顿房价数据集,共506条数据,13个属性(特征值,features),1个房价(预测值,target)
*/
object LrBostonRegression {
def main(args: Array[String]): Unit = {
// 构建SparkSession实例对象,通过建造者模式创建
val spark: SparkSession = SparkSession
.builder()
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[3]")
.config("spark.sql.shuffle.partitions", "3")
.getOrCreate()
import spark.implicits._
val bostonPriceDF: Dataset[String] = spark.read
.textFile("datas/housing/housing.data")
.filter(line => null != line && line.trim.split("\\s+").length == 14)
val bostonDF: DataFrame = bostonPriceDF
.mapPartitions{iter =>
iter.map{line =>
val parts: Array[String] = line.trim.split("\\s+")
// 获取标签label
val label: Double = parts(parts.length - 1).toDouble
// 获取特征features
val values: Array[Double] = parts.dropRight(1).map(_.toDouble)
val features: linalg.Vector = Vectors.dense(values)
// 返回二元组
(features, label)
}
}
// 调用toDF函数,指定列名称
.toDF("features", "label")
//bostonDF.printSchema()
//bostonDF.show(20, truncate = false)
// TODO: 需要将数据集划分为训练数据集和测试数据集
val Array(trainingDF, testingDF) = bostonDF.randomSplit(Array(0.8, 0.2), seed = 123L)
trainingDF.persist(StorageLevel.MEMORY_AND_DISK).count() // 触发缓存
val lr: LinearRegression = new LinearRegression()
// 设置特征列和标签列名称
.setFeaturesCol("features")
.setLabelCol("label")
// 是否对特征数据进行标准化转换处理
.setStandardization(true)
// 设置算法底层求解方式,要么是最小二乘法(正规方程normal),要么是拟牛顿法(l-bfgs)
.setSolver("auto")
// 设置算法相关超参数的值
.setMaxIter(20)
.setRegParam(1).setElasticNetParam(0.4)
val lrModel: LinearRegressionModel = lr.fit(trainingDF)
// Coefficients:斜率,就是k Intercept:截距,就是b
println(s"Coefficients: ${lrModel.coefficients}, Intercept: ${lrModel.intercept}")
val trainingSummary = lrModel.summary
println(s"RMSE: ${trainingSummary.rootMeanSquaredError}")
lrModel.transform(testingDF).show(10, truncate = false)
// 应用结束,关闭资源
spark.stop()
}
}
运行结果如下:
四、泰坦尼克号生存预测
泰坦尼克号生存预测与第一个鸢尾花数据集一样,使用的算法是逻辑回归(Logistics Regression)分类算法
逻辑回归算法与线性回归算法类似:二分类
逻辑回归算法是分类算法的一种:
逻辑回归算法,充分利用sigmod函数和线性回归算法,综合起来,进行预测分类操作;如果要实现多分类,使用softmax函数(深度学习算法中函数)。
泰坦尼克号之中包含的变量有12个,其中要注意的是Age是有缺省值的。
缺省值的处理方法有很多,在这里我们选择对缺省值做填充:
将Age列的值挑选出来使用过滤器将不为空值过滤出来,计算平均值,然后使用Age平均值替代缺省的值。
模型预测步骤如下:
1. 加载数据、数据过滤与基本转换 2. 数据准备:特征工程(提取、转换与选择) 3. 使用算法和数据构建模型:算法参数 4. 模型评估
代码如下:
import org.apache.spark.ml.classification.{LogisticRegression, LogisticRegressionModel}
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer, VectorAssembler}
import org.apache.spark.sql.{DataFrame, SparkSession}
import org.apache.spark.sql.functions._
/**
* 基于泰塔尼克号数据集,使用逻辑回归构建分类模型,评估模型
*/
object TitanicLrClassification {
def main(args: Array[String]): Unit = {
// 构建SparkSession实例对象,通过建造者模式创建
val spark: SparkSession = {
SparkSession
.builder()
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[3]")
.config("spark.sql.shuffle.partitions", "3")
.getOrCreate()
}
// 导入隐式转换和函数库
import spark.implicits._
val rawTitanicDF: DataFrame = spark.read
.option("header", "true")
.option("inferSchema", "true")
.csv("datas/titanic/train.csv")
/*
root
|-- PassengerId: integer (nullable = true)
|-- Survived: integer (nullable = true)
|-- Pclass: integer (nullable = true)
|-- Name: string (nullable = true)
|-- Sex: string (nullable = true)
|-- Age: double (nullable = true)
|-- SibSp: integer (nullable = true)
|-- Parch: integer (nullable = true)
|-- Ticket: string (nullable = true)
|-- Fare: double (nullable = true)
|-- Cabin: string (nullable = true)
|-- Embarked: string (nullable = true)
*/
//rawTitanicDF.printSchema()
//rawTitanicDF.show(10, truncate = false)
// 2.1 Age年龄字段有缺省值,填充为年龄字段平均值
val avgAge: Double = rawTitanicDF
.select($"Age")
.filter($"Age".isNotNull)
.select(
round(avg($"Age"), 2).as("avgAge")
)
.first()
.getAs[Double](0)
//println(s"Avg Age = $avgAge")
val ageTitanicDF: DataFrame = rawTitanicDF
.select(
// 标签label
$"Survived".as("label"),
$"Pclass", $"Sex", $"SibSp", $"Parch", $"Fare", $"Age",
// 当年龄为null时,使用平均年龄代替
when($"Age".isNotNull, $"Age").otherwise(avgAge).as("defaultAge")
)
// 2.2 对Sex字段类别特征换换,使用StringIndexer和OneHotEncoder
// male ->0 ,female -> 1
val indexer: StringIndexer = new StringIndexer()
.setInputCol("Sex")
.setOutputCol("sexIndex")
val indexerTitanicDF = indexer.fit(ageTitanicDF).transform(ageTitanicDF)
// male -> [1.0, 0.0] female -> [0.0, 1.0]
val encoder: OneHotEncoder = new OneHotEncoder()
.setInputCol("sexIndex")
.setOutputCol("sexVector")
.setDropLast(false)
val sexTitanicDF: DataFrame = encoder.transform(indexerTitanicDF)
// 2.3 将特征值组合, 使用VectorAssembler
val assembler: VectorAssembler = new VectorAssembler()
.setInputCols(
Array("Pclass", "sexVector", "SibSp", "Parch", "Fare", "defaultAge")
)
.setOutputCol("features")
val titanicDF: DataFrame = assembler.transform(sexTitanicDF)
//titanicDF.printSchema()
//titanicDF.show(20, truncate = false)
// 2.4 划分数据集为训练集和测试集
val Array(trainingDF, testingDF) = titanicDF.randomSplit(Array(0.8, 0.2))
trainingDF.cache().count()
val logisticRegression: LogisticRegression = new LogisticRegression()
.setLabelCol("label")
.setFeaturesCol("features")
.setPredictionCol("prediction") // 使用模型预测时,预测值的列名称
// 二分类
.setFamily("binomial")
.setStandardization(true)
// 超参数
.setMaxIter(100)
.setRegParam(0.1)
.setElasticNetParam(0.8)
val lrModel: LogisticRegressionModel = logisticRegression.fit(trainingDF)
// y = θ0 + θ1x1+ θ2x2+ θ3x4+ θ4x4+ θ5x5+ θ6x6
println(s"coefficients: ${lrModel.coefficientMatrix}") // 斜率, θ1 ~ θ6
println(s"intercepts: ${lrModel.interceptVector}") // 截距, θ0
val predictionDF: DataFrame = lrModel.transform(testingDF)
//predictionDF.printSchema()
predictionDF
.select("label", "prediction", "probability", "features")
.show(40, truncate = false)
// 分类中的ACCU、Precision、Recall、F-measure、Accuracy
val accuracy = new MulticlassClassificationEvaluator()
.setLabelCol("label")
.setPredictionCol("prediction")
// 四个指标名称:"f1", "weightedPrecision", "weightedRecall", "accuracy"
.setMetricName("accuracy")
.evaluate(predictionDF)
println(s"accuracy = $accuracy")
// 应用结束,关闭资源
spark.stop()
}
}
1.获取特征值
2.特征值组合:
年龄有默认字段
性别先转换为索引,然后转换为向量
sexVector表示形式:(向量个数,索引下标,索引下标的值)
也就是说(2,[0],[1.0])这个数据表示为,这个向量里面有两个值,后面两个表示为0下标的值是1.0,其他为0,那么这个向量就是(1.0,0)。
准确率百分之83.
至此,三个案例都讲完啦。
总结:
使用数据集训练模型的步骤大差不差,都需要加载数据后对数据进行一系列处理(过滤与基本转换),然后构建特征工程,选择算法(有的算法要输入参数)得到模型,最后进行评估预测此模型是否可用(值得信任),可以的话就进行保存以便下次再用。
(以上部分资料来自黑马程序员,自用笔记,侵删。)