最近我也入坑机器学习了呢
推荐一本好书(机器学习实战),别问我问什么,那么好的内容,为什么不一起入坑呢。欢迎fold 代码哦,python 代码:
https://github.com/unnunique/MachineLearning/tree/master/Ch02
https://github.com/unnunique/unnunique.github.io/tree/master/ScalaDemo/src/main/scala/com/sydney/dream/classdefine
需要书籍: 请留言,留下邮箱:
转载请注明出处哦,
kNN 近邻算法
大体上机器学习分为分类和回归,有监督学习和无监督学习。kNN 属于监督学习和分类算法模块。
kNN 概念(分类算法)
在一大堆数据集合中,每一数据都存在自己的标签,即,每一数据都属于一个分类。当我们输入没有分类的新数据的时候,将新数据和数据集合中的每一数据进行比较,然后计算新数据离哪些数据最相近(把原始数据分布在坐标轴上,计算新数据和原始数据的距离)。距离计算好后,我们选取k个最相近的数据,这个就是kNN 中k 的来源。一般上k不大于20,。k个数据中出现最多的分类,即为kNN 算出来的新数据的分类。
先来看一个简单电影分类的例子。
我们以kiss 次数和打斗次数来区分是动作电影还是动作电影。
来接一波图和数据。
假如我们有一个位置的电影,问号脸?我们如何来区分他是爱情电影还是动作电影。
我们来计算他距离其他电影的距离。
例如:(18-98)**2 +(90-2)**2, 然后开方。
比如算的的距离最近的三个是Robo Slaye 3000, Kavin Longblode, Califonia Ma, 则起是动作片。(没有计算过哈哈。)
接下来我们用scala 以及python 来各自实现一波:
kNN 电影分类python 实现
写代码嘛,先来一段注释,然后请佛祖开光,这个还是很皮很开心的
''' reated on Mar 03, 2018 kNN: k Nearest Neighbors Input: inX: vector to compare to existing dataset (1xN) dataSet: size m data set of known vectors (NxM) labels: data set labels (1xM vector) k: number of neighbors to use for comparison (should be an odd number) Output: the most popular class label @author: lidiliang ''' from numpy import * import operator ## movie dataSets def createMovieDataSets(): # 函数定义 dataSets = array([[3, 104], [2, 100], [1, 81], [101, 10], [99, 5], [98, 2]]) # python 自带的array 产生一个一维数组 lables = ['A', 'A', 'A', 'B', 'B', 'B'] # 以为数组 return group, lables # 数据返回 def classify0(inX, dataSet, lables, k): # 函数定义 dataSetSize = dataSet.shape[0] # dataSet 行数 diffMatV0 = tile(inX, (dataSetSize, 1)) - dataSet # 两个M*N 的数组相减 diffMatV1 = diffMat**2 # 平方 distancesV0 = diffMatV1.sum(axis=1) # 平方后相加 distancesV1 = distancesV0**0.5 # 开方 sortedDistancesIndexes = distancesV1.argsort() # 距离排序后,返回其索引值 classCount={} # 字典 for i in range(k): voteLable = lables[sortedDistancesIndexes[i]] # 统计类别 classCount[voteLable] = classCount.get(voteLable, 0) + 1 sortedClassCount = sorted(classCount.iteritems(), key = operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
然后秀一波实验,感觉python 写起来完全像是在写脚本,然后,一个最简单的例子,就这么华丽丽的实现了。
[root@steve-lidiliang /user/MLiA_SourceCode/Ch02]# clear [root@steve-lidiliang /user/MLiA_SourceCode/Ch02]# python Python 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import kNNMovie >>> dataSets, labels = kNNMovie.createMovieDataSets() >>> dataSets array([[ 3, 104], [ 2, 100], [ 1, 81], [101, 10], [ 99, 5], [ 98, 2]]) >>> labels ['A', 'A', 'A', 'B', 'B', 'B'] >>> kNNMovie.classify0([0,1000], dataSets, labels, 3) 'A' >>>
kNN 电影分类Scala 实现:
直接上代码实现吧,具体流程和上面python 大致一样,写Scala 好烧脑,哎。
package com.sydney.dream.classdefine import util.control.Breaks._ // 首先顶一个Movie 类来实现数据的封装 class Movie(var name : String,var action : Int,var kiss : Int,var typeMovie : Int ) { def this(name : String, action : Int, kiss : Int) { this(name, action, kiss, 0) } override def toString = s"movieName: ${name}, actinNum: ${action}, kissNum: ${kiss}, typeMovie: ${typeMovie}" } // 数据模型好了,那当然先再来一个类似java main 的东东了,no bb ,just do it object Movie { def main(args : Array[String]): Unit = { // 准备数据 val movies = Array(new Movie("两小无猜", 1, 100, 0), new Movie("泰坦尼克号", 20, 100, 0), new Movie("初恋那件小事", 0, 500, 0), new Movie("功夫足球", 100, 3, 1), new Movie("神棍", 300, 1, 1), new Movie("逃学威龙", 200, 3, 1)) // 计算new Movie("test", 50, 50) 到各个已知movie 的距离 val distances = getDistances(new Movie("test", 50, 50), movies) println("排序前的数据") for((k, v) <- distances) println(k, v) // 对计算出来的距离进行排序 val sortedDistances = distances.toSeq.sortWith(_._2>_._2) println println("排序后的数据:") for((k, v) <- sortedDistances) println(k, v) // 返回分类标签 val lable = getClassLable(movies, sortedDistances, 3) // 展示最终这个新电影属于哪个分类 println showLable(lable) } def showLable(lable : Int) { if (lable == 1) { println("这个片子是动作片......") } else { println("这个片子是爱情片......") } } // 返回分类标签 def getClassLable(movies : Array[Movie], sortedDistances : Seq[(Int, Double)], k : Int) : Int = { var tmp : Map[Int, Int] = Map() var count = 0 for((key, v) <- sortedDistances) { if (count >= k) { } else { tmp += (movies(key).typeMovie -> (tmp.getOrElse(movies(key).typeMovie, 0) + 1)) } count = count + 1 } //for((key, v) <- tmp) {println(key, v)} val seqTmp = tmp.toSeq.sortWith(_._2 > _._2) println println("最相邻的K个数据的分类统计...") for((key, v) <- seqTmp) {println(key, v)} seqTmp(0)._1 } // 获取距离列表 def getDistances(movie : Movie, movies : Array[Movie]) : Map[Int, Double] = { var distances : Map[Int, Double] = Map() for (i <- 0 until movies.length) { val distance2 = Math.pow((movies(i).action - movie.action), 2) + Math.pow((movies(i).kiss - movie.kiss), 2) val distance = Math.sqrt(distance2) distances += (i -> distance) } distances } }
接下来看一个相对来说,稍微复杂一点点的例子,利用kNN 优化相亲网站
先来简单过一下需求背景。
某都市杭州大龄单身妹纸小百合在某百合相亲网站上进行相亲,寻求高富帅。小百合发现自己相过的对象可以分为三类:
1,十分喜欢的人
2,一般般喜欢的人
3,不喜欢的屌丝
这三类人,可以用下面的特征数据进行衡量:
1,每年飞灰机的里程数。
2,玩游戏的次数。
3,每周吃掉的冰淇淋的公升数。
这些数据保存在txt 文件中类似如下:(数据可以在附件中获取哦,亲,)
32404 3.513921 0.9918541
27268 4.398172 0.9750242
5477 4.276823 1.174874 3
从左到右分别表示:
飞行的里程数, 玩游戏次数 冰淇淋公升数 喜欢程度(1,非常喜欢, 2, 一般般, 3,不喜欢)
来,让我们借鉴电影的例子,来实现相关的程序。
kNN python 实现约会网站分类优化
然后把读取到的数据转化为矩阵。
函数如下:(包含kNN 近邻算法的实现)
from numpy import * import operator # kNN suan fa def classify0(inX, dataSet, lables, k): dataSetSize = dataSet.shape[0] diffMat = tile(inX, (dataSetSize, 1)) - dataSet sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis=1) distances = sqDistances**0.5 sortedDistIndicies = distances.argssort() calssCount = {} for i in range(k): vateIlable = lables[sortedDistIndicies[i]] calssCount[vateIlable] = classCount.get(vateIlable, 1) + 1 sortedClassCount = sorted(classCount.iteritems, key = operator.itemgetter(1), reverse = True) return sortedClassCount[0][0] # Read data from files def file2matrix(filename): fr = open(filename) arrayLines = fr.readlines() numberOfLines = len(arrayLines) returnMat = zeros((numberOfLines, 3)) classLableVector = [] index = 0 for line in arrayLines: line = line.strip() listFromLine = line.split('\t') returnMat[index, :] = listFromLine[0:3] classLableVector.append(int(listFromLine[-1])) index += 1 return returnMat, classLableVector
接下来考虑一个问题,如下是4组数据:
我们来计算第三组和第四组的距离:
(0-67)**2 + (20000-32000)**2 + (0.1-1.1)**2,然后开方。
Oh my good(夸张脸), 有没有发现什么。 每年飞行常客里程数对最终结果的影响远远大于其他两个特征。所以计算出来的结果,可能只与每年 飞行常客的里程数有关。
为了解决这个问题:
我们使用一些技巧,把这些数据都转换为0到1之间的数据。
专业上叫做归一化数据:
我们利用如下的公式,把数据转换到0到1之间:
newValue = (oldValue - min)/(max - min)
在上一步,我们已经把数据从文件中读取,并且赋给了矩阵datingDataMat。接下来,我们就用上面这个公式,把这个矩阵中的数据进行归一化(皈依佛门)
# Shu zhi gui yi hua def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet)) m = dataSet.shape[0] normDataSet = dataSet - tile(minVals, (m, 1)) normDataSet = normDataSet/tile(ranges, (m, 1)) return normDataSet, ranges, minVals
好了,到现在为止,我们改准备的都准备了,数据已有,数据已经皈依佛门,算法已有,接下来我们进行测试:
测试代码如下:
def datingClassTest(): testRate = 0.10 datingDataMat, datingLables = file2matrix('datingTestSet2.txt') normMat, ranges, minVals = autoNorm(datingDataMat) m = normMat.shape[0] numTest = int(m*testRate) errorCount = 0.0 for i in range(numTest): classifierResult = classify0(normMat[i, :], normMat[numTest:m, :], datingLables[numTest:m], 3) print "the classifier came back with: %d, the real answer is : %d" % (classifierResult, datingLables[i]) if (classifierResult != datingLables[i]): errorCount += 1.0 print "the total error rate is: %f" % (errorCount/float(numTest))
测试结果:
the classifier came back with: 3, the real answer is : 3
the classifier came back with: 2, the real answer is : 3
the classifier came back with: 1, the real answer is : 1
the classifier came back with: 2, the real answer is : 2
the classifier came back with: 1, the real answer is : 1
the classifier came back with: 3, the real answer is : 3
the classifier came back with: 3, the real answer is : 3
the classifier came back with: 2, the real answer is : 2
the classifier came back with: 1, the real answer is : 1
the classifier came back with: 3, the real answer is : 1
the total error rate is: 0.050000
错误率为百分之五,效果还行。
最后,来一段python 代码模拟约会网站优化:
交互式模拟相亲网站过滤过程:
def classifyPerson(): resultList = ['not at all', 'in small doses', 'in large doses'] gameTime = float(raw_input(\ "please input the time to playing video games: ")) flyMiles = float(raw_input(\ "please input the miles to flying: ")) icecream = float(raw_input(\ "please input the consumed icecream: ")) datingDataMat, datingLables = file2matrix('datingTestSet2.txt') normMat, ranges, minVals = autoNorm(datingDataMat) inArr = array([flyMiles, gameTime, icecream]) classifierResult = classify0((inArr-minVals)/ranges, normMat, datingLables, 3) print "You will probably like this person: ", resultList[classifierResult -1]
kNN 约会网站优化 Scala 实现
我比较倾向于直接进行上代码,所以nobb 了。
package com.sydney.dream.classdefine import scala.io.Source import scala.collection.mutable.ArrayBuffer // 用来封装约会数据 class DatingData(var fly : Float, var playGame : Float, var iceCream : Float, var typeLike : Int) { // 重载构造函数 def this(fly : Float, playGame : Float, iceCream : Float) { this(fly, playGame, iceCream, 0) } // 重写toString 方法 override def toString = s"fly: ${fly}, playGame: ${playGame}, iceCream: ${iceCream}, typeLike: ${typeLike} " } // dating 分类程序 /** * 1, 加载数据 * 2,计算一个未知person 和其他person 的距离 * 3,把数据从大到小排序 * 4, 按照传入的K, 统计并且返回标签 */ object Dating { def main(args : Array[String]) : Unit = { println("starting...") // 参数验证: if(args.length != 1) { println("请传入datingDataSet2.txt 的路径") } // 数据处理 // 文件路径 val datingDataSetFilePath = args(0) // 获取文件对象 val datingDataSetFile = Source.fromFile(datingDataSetFilePath) // 数据存放在ArrayBuffer 中 var datingDataArrBuf = new ArrayBuffer[DatingData]() // 封装DatingData, 并且添加到datingDataArrBuf 中 for(line <- datingDataSetFile.getLines) { val arr = line.split("\t") val datingData = new DatingData(arr(0).toFloat, arr(1).toFloat, arr(2).toFloat, arr(3).toInt) datingDataArrBuf += datingData } println("datingDataSet 的总行数是:" + datingDataArrBuf.length) // 计算距离 println("计算距离...") val datingData = new DatingData(12345.0f, 15.09f , 1.23f) val distances = getDistance(datingData, datingDataArrBuf) // 对距离从大到小排序 // 距离排序 println("距离排序...") val seqDistances = sortDistances(distances) println("获取最终分类标签...") val classLable = getClassLable(datingDataArrBuf, seqDistances, 3) println("最终的分类标签是: " + classLable) } // 计算距离 def getDistance(datingData : DatingData, datingDataArrBuf : ArrayBuffer[DatingData]) : Map[Int, Double] = { //用于结果封装返回 var distances : Map [Int, Double] = Map() for(i <- 0 until datingDataArrBuf.length) { // 求取欧式距离的平方 val distance2 = Math.pow((datingDataArrBuf(i).fly - datingData.fly), 2) + Math.pow((datingDataArrBuf(i).playGame - datingData.playGame), 2) + Math.pow((datingDataArrBuf(i).iceCream - datingData.iceCream), 2) // 求取欧氏距离 val distance = Math.sqrt(distance2) // 把距离添加到返回结果中 distances += (i -> distance) } distances } // 把计算出来的距离按照从大到小的顺序排序 def sortDistances(distances : Map[Int, Double]) : Seq[(Int, Double)] = { distances.toSeq.sortWith(_._2 > _._2) } // 按照K,统计排序后的距离,返回分类标签 def getClassLable(datingDataArrBuf : ArrayBuffer[DatingData], sortedDistances : Seq[(Int, Double)], k : Int) : Int = { var tmp : Map[Int, Int] = Map() var count = 0 for((key, value) <- sortedDistances) { if (count >= k) {} else { tmp += (datingDataArrBuf(key).typeLike -> (tmp.getOrElse(datingDataArrBuf(key).typeLike, 0) +1)) } count = count + 1 } val seqTmp = tmp.toSeq.sortWith(_._2 > _._2) seqTmp(0)._1 } }写得有些累,scala 和python 都是新学的语言。暂时更新到这里。后面会继续更新,不好意思了,各位大佬们。