转载:http://blog.jobbole.com/109702/
注意,该数据读取器将数值从英制单位转换为公制单位。这对 OLS 的应用没有什么大影响,不过我们还是采用更为常用的公制单位。
这样操作之后我们得到一个数组 Array[Array[Double]],该数组包含了数据点和 Array[Double] 值,该值代表男性或女性。这种格式既有利于将数据绘图,也有利于将数据导入机器学习算法中。
我们首先看看数据是什么样的。为此,用下列代码将数据绘成图。
object LinearRegressionExample extends SimpleSwingApplication { def top = new MainFrame { title = "Linear Regression Example" val basePath = "/Users/.../OLS_Regression_Example_3.csv" val testData = getDataFromCSV(new File(basePath)) val plotData = (testData._1 zip testData._2).map(x => Array(x._1(1) ,x._2)) val maleFemaleLabels = testData._1.map( x=> x(0).toInt) val plot = ScatterPlot.plot( plotData, maleFemaleLabels, '@', Array(Color.blue, Color.green) ) plot.setTitle("Weight and heights for male and females") plot.setAxisLabel(0,"Heights") plot.setAxisLabel(1,"Weights") peer.setContentPane(plot) size = new Dimension(400, 400) }
如果你执行上面这段代码,就会弹出一个窗口显示以下右边那幅图像。注意当代码运行时,你可以滚动鼠标来放大和缩小图像。
在这幅图像中,绿色代表女性,蓝色代表男性,可以看到,男女的身高和体重有很大部分是重叠的。因此,如果我们忽略男女性别,数据看上去依旧是呈线性的(如左图所示)。然而,若不考虑男女性别差异,模型就不够精确。
在本例中,找出这种区别(将数据依性别分组)是小事一桩,然而,你可能会碰到一些其中的数据区分不那么明显的数据集。意识到这种可能性对数据分组是有帮助的,从而有助于改善机器学习应用程序的性能。
既然我们已经考察过数据,也知道我们确实可以建立一条回归线来拟合数据,现在就该训练模型了。Smile 库提供了普通最小二乘算法,我们可以用如下代码轻松调用:
val olsModel = new OLS(testData._1,testData._2)
有了这个 OLS 模型,我们现在可以根据某人的身高和性别预测其体重了:
println("Prediction for Male of 1.7M: " +olsModel.predict(Array(0.0,170.0))) println("Prediction for Female of 1.7M:" + olsModel.predict(Array(1.0,170.0))) println("Model Error:" + olsModel.error())
结果如下:
Prediction for Male of 1.7M: 79.14538559840447 Prediction for Female of 1.7M:70.35580395758966 Model Error:4.5423150758157185
回顾前文的分类算法,它有一个能够反映模型性能的先验值。回归分析是一种更强大的统计方法,它可以给出一个实际误差。这个值反映了偏离拟合回归线的平均程度,因此可以说,在这个模型中,一个身高1.70米的男性的预测体重是 79.15kg ± 4.54kg,4.54 为误差值。注意,如果不考虑数据的男女差异,这一误差会增加到 5.5428。换言之,考虑了数据的男女差异后,模型在预测时,精确度提高了 ±1kg
最后一点,Smile 库也提供了一些关于模型的统计信息。R平方值是模型的均方根误差(RMSE)与平均函数的 RMSE 之比。这个值介于 0 与 1 之间。假如你的模型能够准确的预测每一个数据点,R平方值就是 1,如果模型的预测效果比平均函数差,则该值为 0。在机器学习领域中,通常将该值乘以 100,代表模型的精确度。它是一个归一化值,所以可以用来比较不同模型的性能。
本部分总结了线性回归分析的过程,如果你还想了解如何将回归分析应用于非线性数据,请随时学习下一个实例“应用文本回归尝试畅销书排行预测”。
应用文本回归尝试预测最畅销书排行
在实例“根据身高预测体重”中,我们介绍了线性回归的概念。然而,有时候需要将回归分析应用到像文本这类的非数字数据中去。
在本例中,我们将通过尝试预测最畅销的 100 本 O’Reilly 公司出版的图书,说明如何应用文本回归。此外,我们还介绍在本例的特殊情况下应用文本回归无法解决问题。原因仅仅是这些数据中不含有可以被我们的测试数据利用的信号。即使如此,本例也并非一无是处,因为在实践中,数据可能会含有实际信号,该信号可以被这里要介绍的文本回归检测到。
本例使用到的数文件可以在这里下载。除了 Smile 库,本例也会使用 Scala-csv 库,因为 csv 中包含带逗号的字符串。我们从获取需要的数据开始:
object TextRegression { def main(args: Array[String]): Unit = { //Get the example data //获取案例数据 val basePath = "/users/.../TextRegression_Example_4.csv" val testData = getDataFromCSV(new File(basePath)) } def getDataFromCSV(file: File) : List[(String,Int,String)]= { val reader = CSVReader.open(file) val data = reader.all() val documents = data.drop(1).map(x => (x(1),x(3)toInt,x(4))) return documents } }
现在我们得到了 O’Reilly 出版社最畅销100部图书的书名、排序和详细说明。然而,当涉及某种回归分析时,我们需要数字数据。这就是问什么我们要建立一个文档词汇矩阵 (DTM)。注意这个 DTM 与我们在垃圾邮件分类实例中建立的词汇文档矩阵 (TDM) 是类似的。区别在于,DTM 存储的是文档记录,包含文档中的词汇,相反,TDM 存储的是词汇记录,包含这些词汇所在的一系列文档。
我们自己用如下代码生成 DTM:
import java.io.File import scala.collection.mutable class DTM { var records: List[DTMRecord] = List[DTMRecord]() var wordList: List[String] = List[String]() def addDocumentToRecords(documentName: String, rank: Int, documentContent: String) = { //Find a record for the document //找出一条文档记录 val record = records.find(x => x.document == documentName) if (record.nonEmpty) { throw new Exception("Document already exists in the records") } var wordRecords = mutable.HashMap[String, Int]() val individualWords = documentContent.toLowerCase.split(" ") individualWords.foreach { x => val wordRecord = wordRecords.find(y => y._1 == x) if (wordRecord.nonEmpty) { wordRecords += x -> (wordRecord.get._2 + 1) } else { wordRecords += x -> 1 wordList = x :: wordList } } records = new DTMRecord(documentName, rank, wordRecords) :: records } def getStopWords(): List[String] = { val source = scala.io.Source.fromFile(new File("/Users/.../stopwords.txt"))("latin1") val lines = source.mkString.split("n") source.close() return lines.toList } def getNumericRepresentationForRecords(): (Array[Array[Double]], Array[Double]) = { //First filter out all stop words: //首先过滤出所有停用词 val StopWords = getStopWords() wordList = wordList.filter(x => !StopWords.contains(x)) var dtmNumeric = Array[Array[Double]]() var ranks = Array[Double]() records.foreach { x => //Add the rank to the array of ranks //将评级添加到排序数组中 ranks = ranks :+ x.rank.toDouble //And create an array representing all words and their occurrences //for this document: //为该文档创建一个数组,表示所有单词及其出现率 var dtmNumericRecord: Array[Double] = Array() wordList.foreach { y => val termRecord = x.occurrences.find(z => z._1 == y) if (termRecord.nonEmpty) { dtmNumericRecord = dtmNumericRecord :+ termRecord.get._2.toDouble } else { dtmNumericRecord = dtmNumericRecord :+ 0.0 } } dtmNumeric = dtmNumeric :+ dtmNumericRecord } return (dtmNumeric, ranks) } } class DTMRecord(val document : String, val rank : Int, var occurrences : mutable.HashMap[String,Int] )
观察这段代码,注意到这里面有一个方法 def getNumericRepresentationForRecords(): (Array[Array[Double]], Array[Double])。这一方法返回一个元组,该元组以一个矩阵作为第一个参数,该矩阵中每一行代表一个文档,每一列代表来自 DTM 文档的完备词汇集中的词汇。注意第一个列表中的浮点数表示词汇出现的次数。
第二个参数是一个数组,包含第一个列表中所有记录的排序值。
现在我们可以按如下方式扩展主程序,这样就可以得到所有文档的数值表示:
val documentTermMatrix = new DTM() testData.foreach(x => documentTermMatrix.addDocumentToRecords(x._1,x._2,x._3))
有了这个从文本到数值的转换,现在我们可以利用回归分析工具箱了。我们在“基于身高预测体重”的实例中应用了普通最小二乘法 (OLS),不过这次我们要应用“最小绝对收缩与选择算子”(Lasso) 回归。这是因为我们可以给这种回归方法提供某个 λ 值,它代表一个惩罚值。该惩罚值可以帮助 LASSO 算法选择相关的特征(单词)而丢弃其他一些特征(单词)。
LASSO 执行的这一特征选择功能非常有用,因为在本例中,文档说明包含了大量的单词。LASSO 会设法找出那些单词的一个合适的子集作为特征,而要是应用 OLS,则所有单词都会被使用,那么运行时间将会变得极其漫长。此外,OLS 算法实现会检测非满秩。这是维数灾难的一种情形。
无论如何,我们需要找出一个最佳的 λ 值,因此,我们应该用交叉验证法尝试几个 λ 值,操作过程如下:
for (i <- 0 until cv.k) { //Split off the training datapoints and classifiers from the dataset //从数据集中将用于训练的数据点与分类器分离出来 val dpForTraining = numericDTM ._1 .zipWithIndex .filter(x => cv .test(i) .toList .contains(x._2) ) .map(y => y._1) val classifiersForTraining = numericDTM ._2 .zipWithIndex .filter(x => cv .test(i) .toList .contains(x._2) ) .map(y => y._1) //And the corresponding subset of data points and their classifiers for testing //以及对应的用于测试的数据点子集及其分类器 val dpForTesting = numericDTM ._1 .zipWithIndex .filter(x => !cv .test(i) .contains(x._2) ) .map(y => y._1) val classifiersForTesting = numericDTM ._2 .zipWithIndex .filter(x => !cv .test(i) .contains(x._2) ) .map(y => y._1) //These are the lambda values we will verify against //这些是我们将要验证的λ值 val lambdas: Array[Double] = Array(0.1, 0.25, 0.5, 1.0, 2.0, 5.0) lambdas.foreach { x => //Define a new model based on the training data and one of the lambda's //定义一个基于训练数据和其中一个λ值的新模型 val model = new LASSO(dpForTraining, classifiersForTraining, x) //Compute the RMSE for this model with this lambda //计算该模型的RMSE值 val results = dpForTesting.map(y => model.predict(y)) zip classifiersForTesting val RMSE = Math .sqrt(results .map(x => Math.pow(x._1 - x._2, 2)).sum / results.length ) println("Lambda: " + x + " RMSE: " + RMSE) } }
多次运行这段代码会给出一个在 36 和 51 之间变化的 RMSE 值。这表示我们排序的预测值会偏离至少 36 位。鉴于我们要尝试预测最高的 100 位,结果表明这个模型的效果非常差。在本例中,λ 值变化对模型的影响并不明显。然而,在实践中应用这种算法时,要小心地选取 λ 值: λ 值选得越大,算法选取的特征数就越少。 所以,交叉验证法对分析不同 λ 值对算法的影响很重要。
引述 John Tukey 的一句话来总结这个实例:
“数据中未必隐含答案。某些数据和对答案的迫切渴求的结合,无法保证人们从一堆给定数据中提取出一个合理的答案。”
应用无监督学习合并特征(PCA)
主成分分析 (PCA) 的基本思路是减少一个问题的维数。这是一个很好的方法,它可以避免维灾难,也可以帮助合并数据,避开无关数据的干扰,使其中的趋势更明显。
在本例中,我们打算应用 PCA 把 2002-2012 年这段时间内 24 只股票的股价合并为一只股票的股价。这个随时间变化的值就代表一个基于这 24 只股票数据的股票市场指数。把这24种股票价格合并为一种,明显地减少了处理过程中的数据量,并减少了数据维数,对于之后应用其他机器学习算法作预测,如回归分析来说,有很大的好处。为了看出特征数从 24 减少为 1 之后的效果,我们会将结果与同一时期的道琼斯指数 (DJI) 作比较。
随着工程的开始,下一步要做的是加载数据。为此,我们提供了两个文件:Data file 1 和 Data file 2.
object PCA extends SimpleSwingApplication{ def top = new MainFrame { title = "PCA Example" //Get the example data //获取案例数据 val basePath = "/users/.../Example Data/" val exampleDataPath = basePath + "PCA_Example_1.csv" val trainData = getStockDataFromCSV(new File(exampleDataPath)) } def getStockDataFromCSV(file: File): (Array[Date],Array[Array[Double]]) = { val source = scala.io.Source.fromFile(file) //Get all the records (minus the header) //获取所有记录(减去标头) val data = source .getLines() .drop(1) .map(x => getStockDataFromString(x)) .toArray source.close() //group all records by date, and sort the groups on date ascending //按日期将所有记录分组,并按日期将组升序排列 val groupedByDate = data.groupBy(x => x._1).toArray.sortBy(x => x._1) //extract the values from the 3-tuple and turn them into // an array of tuples: Array[(Date, Array[Double)] //抽取这些3元组的值并将它们转换为一个元组数组:Array[(Date,Array[Double])] val dateArrayTuples = groupedByDate .map(x => (x._1, x ._2 .sortBy(x => x._2) .map(y => y._3) ) ) //turn the tuples into two separate arrays for easier use later on //将这些元组分隔为两个数组以方便之后使用 val dateArray = dateArrayTuples.map(x => x._1).toArray val doubleArray = dateArrayTuples.map(x => x._2).toArray (dateArray,doubleArray) } def getStockDataFromString(dataString: String): (Date,String,Double) = { //Split the comma separated value string into an array of strings //把用逗号分隔的数值字符串分解为一个字符串数组 val dataArray: Array[String] = dataString.split(',') val format = new SimpleDateFormat("yyyy-MM-dd") //Extract the values from the strings //从字符串中抽取数值 val date = format.parse(dataArray(0)) val stock: String = dataArray(1) val close: Double = dataArray(2).toDouble //And return the result in a format that can later //easily be used to feed to Smile //并以一定格式返回结果,使得该结果之后容易输入到Smile中处理 (date,stock,close) } }
有了训练数据,并且我们已经知道要将24个特征合并为一个单独的特征,现在我们可以进行主成分分析,并按如下方式为数据点检索数据。
//Add to `def top` //添加到‘def top’中 val pca = new PCA(trainData._2) pca.setProjection(1) val points = pca.project(trainData._2) val plotData = points .zipWithIndex .map(x => Array(x._2.toDouble, -x._1(0) )) val canvas: PlotCanvas = LinePlot.plot("Merged Features Index", plotData, Line.Style.DASH, Color.RED); peer.setContentPane(canvas) size = new Dimension(400, 400)
这段代码不仅执行了 PCA,还将结果绘成图像,y 轴表示特征值,x 轴表示每日。
为了能看出 PCA 合并的效果,我们现在通过如下方式调整代码将道琼斯指数加入到图像中:
首先把下列代码添加到 def top 方法中:
//Verification against DJI //用道琼斯指数验证 val verificationDataPath = basePath + "PCA_Example_2.csv" val verificationData = getDJIFromFile(new File(verificationDataPath)) val DJIIndex = getDJIFromFile(new File(verificationDataPath)) canvas.line("Dow Jones Index", DJIIndex._2, Line.Style.DOT_DASH, Color.BLUE)
然后我们需要引入下列两个方法:
def getDJIRecordFromString(dataString: String): (Date,Double) = { //Split the comma separated value string into an array of strings //把用逗号分隔的数值字符串分解为一个字符串数组 val dataArray: Array[String] = dataString.split(',') val format = new SimpleDateFormat("yyyy-MM-dd") //Extract the values from the strings //从字符串中抽取数值 val date = format.parse(dataArray(0)) val close: Double = dataArray(4).toDouble //And return the result in a format that can later //easily be used to feed to Smile //并以一定格式返回结果,使得该结果之后容易输入到Smile中处理 (date,close) } def getDJIFromFile(file: File): (Array[Date],Array[Double]) = { val source = scala.io.Source.fromFile(file) //Get all the records (minus the header) //获取所有记录(减去标头) val data = source .getLines() .drop(1) .map(x => getDJIRecordFromString(x)).toArray source.close() //turn the tuples into two separate arrays for easier use later on //将这些元组分隔为两个数组以方便之后使用 val sortedData = data.sortBy(x => x._1) val dates = sortedData.map(x => x._1) val doubles = sortedData.map(x => x._2 ) (dates, doubles) }
这段代码加载了 DJI 数据,并把它绘成图线添加到我们自己的股票指数图中。然而,当我们执行这段代码时,效果图有点无用。
如你所见,DJI 的取值范围与我们的计算特征的取值范围偏离很远。因此,现在我们要将数据标准化。办法就是根据数据的取值范围将数据进行缩放,这样,两个数据集就会落在同样的比例中。
用下列代码替换 getDJIFromFile 方法:
def getDJIFromFile(file: File): (Array[Date],Array[Double]) = { val source = scala.io.Source.fromFile(file) //Get all the records (minus the header) //获取所有记录(减去标头) val data = source .getLines() .drop(1) .map(x => getDJIRecordFromString(x)) .toArray source.close() //turn the tuples into two separate arrays for easier use later on //将这些元组分隔为两个数组以方便之后使用 val sortedData = data.sortBy(x => x._1) val dates = sortedData.map(x => x._1) val maxDouble = sortedData.maxBy(x => x._2)._2 val minDouble = sortedData.minBy(x => x._2)._2 val rangeValue = maxDouble - minDouble val doubles = sortedData.map(x => x._2 / rangeValue ) (dates, doubles) }
用下列代码替换 def top 方法中 plotData 的定义:
val maxDataValue = points.maxBy(x => x(0)) val minDataValue = points.minBy(x => x(0)) val rangeValue = maxDataValue(0) - minDataValue(0) val plotData = points .zipWithIndex .map(x => Array(x._2.toDouble, -x._1(0) / rangeValue))
现在我们看到,虽然 DJI 的取值范围落在 0.8 与 1.8 之间,而我们的新特征的取值范围落在 -0.5 与 0.5 之间,但两条曲线的趋势符合得很好。学完这个实例,加上段落中对 PCA 的说明,现在你应该学会了 PCA 并能把它应用到你自己的数据中。
应用支持向量机(SVM)
在我们实际开始应用支持向量机 (SVM) 之前,我会稍微介绍一下 SVM。基本的 SVM 是一个二元分类器,它通过挑选出一个代表数据点之间最大距离的超平面,将数据集分为两部分。一个 SVM 就带有一个所谓的“校正率”值。如果不存在理想分割,则该校正率提供了一个误差范围,允许人们在该范围内找出一个仍尽可能合理分割的超平面。因此,即使仍存在一些令人不快的点,在校正率规定的误差范围内,超平面也是合适的。这意味着,我们无法为每种情形提出一个“标准的”校正率。不过,如果数据中没有重叠部分,则较低的校正率要优于较高的校正率。
我刚刚说明了作为一个二元分类器的基本 SVM,但是这些原理也适用于具有更多类别的情形。然而,现在我们要继续完成具有 2 种类别的实例,因为仅说明这种情况已经足够了。
在本例中,我们将完成几个小案例,其中,支持向量机 (SVM) 的表现都胜过其他分离算法如 KNN。这种方法与前几例中的不同,但它能帮你更容易学会怎么使用以及何时使用 SVM。
对于每个小案例,我们会提供代码、图像、不同参数时的 SVM 运行测试以及对测试结果的分析。这应该使你对输入 SVM 算法的参数有所了解。
在第一个小案例中,我们将应用高斯核函数,不过在 Smile 库中还有其他核函数。其他核函数可以在这里找到。紧接着高斯核函数,我们将讲述多项式核函数,因为这个核函数与前者有很大的不同。
我们会在每个小案例中用到下列的基本代码,其中只有构造函数 filePaths 和 svm 随每个小案例而改变。
object SupportVectorMachine extends SimpleSwingApplication { def top = new MainFrame { title = "SVM Examples" //File path (this changes per example) //文件路径(随案例而改变) val trainingPath = "/users/.../Example Data/SVM_Example_1.csv" val testingPath = "/users/.../Example Data/SVM_Example_1.csv" //Loading of the test data and plot generation stays the same //加载测试数据,绘图生成代码保持相同 val trainingData = getDataFromCSV(new File(path)) val testingData = getDataFromCSV(new File(path)) val plot = ScatterPlot.plot( trainingData._1, trainingData._2, '@', Array(Color.blue, Color.green) ) peer.setContentPane(plot) //Here we do our SVM fine tuning with possibly different kernels //此处,我们用可能的不同核函数对SVM进行微调 val svm = new SVM[Array[Double]](new GaussianKernel(0.01), 1.0,2) svm.learn(trainingData._1, trainingData._2) svm.finish() //Calculate how well the SVM predicts on the training set //计算SVM对测试集的预测效果 val predictions = testingData ._1 .map(x => svm.predict(x)) .zip(testingData._2) val falsePredictions = predictions .map(x => if (x._1 == x._2) 0 else 1 ) println(falsePredictions.sum.toDouble / predictions.length * 100 + " % false predicted") size = new Dimension(400, 400) } def getDataFromCSV(file: File): (Array[Array[Double]], Array[Int]) = { val source = scala.io.Source.fromFile(file) val data = source .getLines() .drop(1) .map(x => getDataFromString(x)) .toArray source.close() val dataPoints = data.map(x => x._1) val classifierArray = data.map(x => x._2) return (dataPoints, classifierArray) } def getDataFromString(dataString: String): (Array[Double], Int) = { //Split the comma separated value string into an array of strings //把用逗号分隔的数值字符串分解为一个字符串数组 val dataArray: Array[String] = dataString.split(',') //Extract the values from the strings //从字符串中抽取数值 val coordinates = Array( dataArray(0).toDouble, dataArray(1).toDouble) val classifier: Int = dataArray(2).toInt //And return the result in a format that can later //easily be used to feed to Smile //并以一定格式返回结果,使得该结果之后容易输入到Smile中处理 return (coordinates, classifier) }
案例1(高斯核函数)
在本案例中,我们介绍了最常用的 SVM 核函数,即高斯核函数。我们的想法是帮助读者寻找该核函数的最佳输入参数。本例中用到的数据可以在这里下载。
从该图中可以清楚看出,线性回归线在这里起不了作用。我们要使用一个 SVM 来作预测。在给出的第一段代码中,高斯核函数的 sigma 值为 0.01,边距惩罚系数为 1.0,类别总数为 2,并将其传递给了 SVM。那么,这些都代表什么意思呢?
我们从高斯核函数说起。这个核函数反映了 SVM 如何计算系统中成对数据的相似度。对于高斯核函数,用到了欧氏距离中的方差。我们特意挑选高斯核函数的原因是,数据中并不含有明显的结构如线性函数、多项式函数或者双曲线函数。相反地,数据聚集成了3组。
我们传递到高斯核中构造函数的参数是 sigma。这个 sigma 值反映了核函数的平滑程度。我们会演示改变这一取值如何影响预测效果。我们将边距惩罚系数取 1。这一参数定义了系统中向量的边距,因此,这一值越小,约束向量就越多。我们会执行一组运行测试,通过结果向读者说明这个参数在实践中的作用。注意其中 s: 代表 sigma,c: 代表校正惩罚系数。百分数表示预测效果的误差率, 它只不过是训练之后,对相同数据集的错误预测的百分数。
不幸的是,并不存在为每个数据集寻找正确 sigma 的黄金法则。不过,可能最好的方法就是计算数据的 sigma 值,即 √(variance),然后在这个值附近取值看看哪一个 sigma 值效果最好。因为本例数据的方差在 0.2 与 0.5 之间,我们把这区间作为中心并在中心的两边都选取一些值,以比较我们的案例中使用高斯核的 SVM 的表现。
看看表格中的结果和错误预测的百分比,它表明产生最佳效果的参数组合是一个非常低的 sigma (0.001) 和一个 1.0 及以上的校正率。不过,如果把这个模型应用到实际中的新数据上,可能会产生过拟合。因此,在用模型本身的训练数据测试模型时,你应该保持谨慎。一个更好的方法是使用交叉验证,或用新数据验证。
案例2(多项式核函数)
高斯核并不总是最佳选择,尽管在应用 SVM 时,它是最常用的核函数。因此,在本例中,我们将演示一个多项式核函数胜过高斯核函数的案例。注意,虽然本案例中的示例数据是构建好的,但在本领域内相似的数据(带有一点噪声)是可以找到的。本案例中的训练数据可以在这里下载,测试数据在这里下载。
对于本例数据,我们用一个三次多项式创建了两个类别,并生成了一个测试数据文件和一个训练数据文件。训练数据包含x轴上的前500个点,而测试数据则包含x轴上500到1000这些点。为了分析多项式核函数的工作原理,我们将数据汇成图。左图是训练数据的,右图是测试数据的。
考虑到本实例开头给出的基本代码,我们作如下的替换:
val trainingPath = "/users/.../Example Data/SVM_Example_2.csv" val testingPath = "/users/.../Example Data/SVM_Example_2_Test_data.csv"
然后,如果我们使用高斯核并且运行代码,就可以得到如下结果:
可以看到,即使是最佳情况,仍然有 27.4% 的测试数据被错误分类。这很有趣,因为当我们观察图像时,可以看到两个类别之间有一个很明显的区分。我们可以对 sigma 和校正率进行微调,但是当预测点很远时(例如 x 是 100000),sigma 和校正率就会一直太高而使模型表现不佳(时间方面与预测效果方面)。
因此,我们将高斯核替换为多项式核,代码如下:
val svm = new SVM[Array[Double]](new PolynomialKernel(2), 1.0,2)
注意我们给多项式核的构造函数传递 2 的方式。这个 2 代表它要拟合的函数的次数。如果我们不单考虑次数为 2 的情况,我们还考虑次数为2、3、4、5的情况,并且让校正率再一次在 0.001 到 100 之间变化,则得到如下结果:
从中我们可以看到,次数为 3 和 5 的情况得到了100%的准确率,这两种情况中测试数据与训练数据之间没有一个点是重叠的。与高斯核的最佳情况 27.4% 的错误率相比,这种表现令人惊喜。确实要注意本例这些数据是构建好的,因此没有什么噪声数据。所以才能出现所有的“校正率”都为 0% 错误率。如果添加了噪声,则需要对校正率进行微调。
以上就是对支持向量机这一部分的总结。
结论
在了解了机器学习的整体思想之后,你应该可以辨别出哪些情况分别属于分类问题、回归问题或是维数约化问题。此外,你应该理解机器学习的基本概念,什么是模型,并且知道机器学习中的一些常见陷阱。
在学完本文中的实例之后,你应该学会应用 K-NN、朴素贝叶斯算法以及线性回归分析了。此外,你也能够应用文本回归、使用 PCA 合并特征以及应用支持向量机。还有非常重要的一点,就是能够建立你自己的推荐系统。
如果你有疑问或关于本文的反馈,请随时通过 Github、LinkedIn 或 Twitter 联系我。
相关推荐
机器学习作为一门研究计算机如何利用经验自动提高性能的学科,在近年来取得了突飞猛进的发展,并且在多个领域都取得了显著的应用成果。从信用卡欺诈检测的数据挖掘程序到个性化推荐系统,再到自动驾驶汽车,机器学习...
通过阅读《DELPHI5开发人员指南》并结合配套代码,开发者不仅可以学习到DELPHI5的基础知识,还能深入探讨高级主题,如并发编程、网络编程、图形和多媒体处理等。这些知识点对于任何希望在DELPHI平台上进行专业开发的...
总的来说,这个面向初学者的Python机器学习和深度学习教程会引导你了解基本概念,熟悉常用工具,掌握模型构建和优化技巧,并通过实际项目体验机器学习的全过程。随着你深入学习,你将能够解决各种实际问题,从简单的...
《Delphi 6 开发人员指南》是一本针对初学者和有一定经验的Delphi程序员的宝贵资源,旨在帮助读者深入理解和掌握Delphi 6编程环境。这本书详细介绍了Delphi 6的各种特性和功能,使读者能够高效地构建高质量的Windows...
《Delphi7开发指南》是一本专为程序员和软件开发者设计的教程,旨在深入解析Delphi7这一经典编程环境的使用技巧与开发流程。Delphi是Borland公司(现为Embarcadero Technologies)推出的集成开发环境(IDE),以其...
总之,《机器学习实践:测试驱动的开发方法》是一本将TDD原则应用于机器学习的实用指南,旨在帮助读者构建更稳定、高效的机器学习系统。通过书中丰富的示例和详细的步骤,读者将深化对TDD的理解,提升在机器学习项目...
总的来说,OpenMLDB是一个针对机器学习应用的创新性数据库解决方案,它通过优化数据处理和模型训练流程,实现了在线与离线环境的一致性,提升了复杂查询的性能,并提供了友好的开发体验。对于那些致力于构建高性能、...
TensorFlow是由Google Brain团队开发的开源库,用于各种机器学习任务,包括但不限于深度学习、神经网络和数据流图的构建。它支持高性能的数值计算,可在多种硬件平台上运行,包括CPU、GPU以及TPU(张量处理单元)。 ...
这本书全面覆盖了Tensorflow的核心概念、基本操作以及在机器学习领域的实际应用,旨在帮助读者理解和掌握这一强大的深度学习框架。通过阅读本书,你可以系统地学习到以下关键知识点: 1. **Tensorflow简介**:...
根据提供的文件信息,标题为《Python3程序开发指南(第二版)》,描述为该书是用于基础学习且仅供学习交流,标签为"Python3"。尽管部分内容看似重复的网址链接,但可以推测这可能是出版信息或者版权信息的占位符。为了...
《Delphi 7 开发指南》是一本专为Delphi初学者和有经验的开发者设计的教程,旨在帮助读者深入理解和掌握使用Delphi 7进行软件开发的技术与技巧。Delphi,作为Borland公司(现Embarcadero Technologies)推出的一种...
阿里云机器学习PAI-DSW是一款专为开发者设计的云端深度学习开发环境,它旨在简化机器学习和深度学习的工作流程,提供一站式的解决方案。该平台整合了多种工具和服务,帮助用户快速构建、训练和部署模型,无需关注...
这份《C++ Builder 5 开发人员指南》全面覆盖了从入门到高级的各个层面,是学习和掌握C++ Builder 5的理想资料。 **第一部分:基础知识** 这部分通常涵盖C++语言的基础知识,包括语法、变量、数据类型、运算符、...
这个名为"Python-AI机器学习深度学习相关教程分类大列表"的压缩包文件,很显然是一个资源集合,包含了大量用于学习和研究这些主题的教程和指南。下面将详细阐述这些知识点: 1. **Python编程基础**:Python是AI的...
对于实际应用,本书可能还会介绍一些Python在Web开发、数据分析、机器学习等领域中的应用,比如使用Django或Flask框架搭建Web应用,或者使用Pandas和NumPy进行数据处理,以及用Scikit-learn进行机器学习模型训练。...
Apache PredictionIO是面向开发人员,数据科学家和最终用户的开源机器学习框架。 它支持事件收集,算法部署,评估,通过REST API查询预测结果。 它基于可扩展的开源服务,例如Hadoop,HBase(和其他数据库),...
在IT领域,机器学习是人工智能的一个重要分支,它允许计算机通过经验学习和改进,而无需显式编程。这个压缩包“机器学习算法代码”显然包含了用于实现不同机器学习算法的源代码,主要以Java语言编写。Java作为一种...
《Python 入门到机器学习》是一本专为初学者设计的人工智能实战指南,它旨在带领读者逐步踏入编程世界,特别是聚焦于Python语言,并最终掌握机器学习的基础知识。本书内容丰富,涵盖从基础语法到高级应用,再到实际...