看过《非诚勿扰》节目的朋友们都知道,节目开始有个选择心动区和观察区的环节。假设你是负责设计预测嘉宾偏好的程序员,我们将做个预测程序,根据嘉宾前面几次选择的女嘉宾,大致可以估算下下面出来的女嘉宾,他到底会不会喜欢。让我们开始吧。 首先简单介绍下算法, KNN的全称是k- Nearest Neighbors. 在数据中找到最接近的数据,然后根据剩下的k个数据做选择。 怎么测量A数据和B数据是否接近方面,我们用到了测量数据距离的公式,Euclidian:
d=(xA0−xB0)2+(xA1−xB1)d = \sqrt{(xA_0 -xB_0)^2 + (xA_1 - xB_1)} d=(xA0−xB0)2+(xA1−xB1)
例如计算点(0,0) 和点(1,2)的距离就是:
d=(0−1)2+(0−2)2d=\sqrt{(0-1)^2 + (0-2)^2}d=(0−1)2+(0−2)2
我们将出来的女嘉宾大致根据年龄,升高,体重,相貌做些基础的划分。
但因为相貌这个东西很难做量化。所以,我们把相貌替换成收入做参考比较。下面就是要实现的代码:
def classify0(inX, dataSet, labels, k):
# getting the rows of the dataset.
dataSetSize = dataSet.shape[0]
# tile the second parameter means tile shape
diffMat = tile(inX, (dataSetSize, 1)) - dataSet
sqDiffMat = diffMat ** 2
# This axis = 1 mean sum the row of data
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances ** 0.5
# example: x=np.array([1,4,3,-1,6,9])
# y=array([3,0,2,1,4,5])
sortedDistIndicies = distances.argsort()
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
# P
# dict = {'Name': 'Zara', 'Age': 27}
# print "Value : %s" % dict.get('Age')
# print "Value : %s" % dict.get('Sex', "Never")
# Value : 27 Value : Never
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
这片段代码摘自Machin Learning In Action.
它是怎么完成对新上场的女嘉宾,判断男嘉宾会划分到观察区还是心动区。我们采集到女嘉宾的基本信息如,身高,体重,长相…。 根据这些基本信息来判断她是否是他喜欢的类型。
def classify0(inX, dataSet, labels, k):
def 是definition的简称。表示对函数的定义,对于这个词。看过《巨齿鲨》的朋友一定对小女孩不陌生。Jonas说自己不是疯子,只是看了别人没看到东西。她就对Jonas说:That’s the definnition of crazy. 就是这里的definition.
有四个参数:
第一个inX,一组样本用例。就是新上场的女嘉宾基本数据信息【身高,体重,收入】,如【170cm, 60kg, 1000~2000收入】
第二个dataSet, 采集到所有女嘉宾的身高,体重,收入等数据信息。
第三个Labels,这标记样本数据,就是前面男嘉宾之前几次选择标记为观察区,还是心动区的结果。我们将0代表观察区,1表示心动区
第四个参数K, 就是比对K个人的相关信息,参考最为接近的K个女孩他是把她划入观察区还是心动区。
dataSetSize = dataSet.shape[0]
这个是获得样本的条数,这里就是获得男嘉宾已经对女嘉宾选好了的数据条数,当然,在非诚勿扰中,一次只能选择12个女嘉宾,但对于程序来说,如果能有更多的女嘉宾选择的话,对于数据的准确度,会有所提高。
diffMat = tile(inX, (dataSetSize, 1)) - dataSet
这行就是计算其中一条样例与其他对象的差值。其中需要强调的是tile. 在英语中tile指的是瓷砖,做动词就是平铺。
《老友记》第8季,第七集中,女佣很喜欢Manica的清洁剂就说到:Mrs Bing, this tile cleaner is in credible. 这就是其中的tile.
回到代码,假设现在男嘉宾已经选择好了9女嘉宾到底是在观察区还是心动区,现在新上来第10为女嘉宾。让我们做预测,我就用tile把这第10位女嘉宾复制9份,然后减掉前面9位女嘉宾的dataSet数据。得到diffMat这种差值数据集合。
sqDiffMat = diffMat ** 2
这行就是对这差值求平方。为什么这么做,我们会想新上场的女孩,可能跟前面一两个个女孩的身高有些接近,但不管是比前面高一点,还是矮一点都无所谓,我们只要接近的,所以用平方去除掉可能存在的负数。
sqDistances = sqDiffMat.sum(axis=1)
这个是对矩阵中的行进行相加,axis 是最关键的参数,表示维度。我们这里就是把所以对应的差值全部加起来,得到新上场的女嘉宾跟所有其他已经被选择好的女嘉宾之间差距的得分。
distances = sqDistances ** 0.5
这个在对值求平方根,是公式的一部分,不做重点强调,强调的是**的意思就是求数值的指数幂。
sortedDistIndicies = distances.argsort()
这行运行代码时的重点,也是难点,就是它的还回值到底是什么呢?
这里distances. 已经找的了第10个女孩和前面9个人的差距值,那我们要排序找到差距最小到最大的值。得到sortedDistIndicies这个值对应的序列号,如距离值为[5,2,3,1,0] 那差距最小的应该是0,0是在第4位,所以sortedDistIndicies的第一个值是4,差距第二小的是1,他是在第3位,所以,第二个值是3…得到[4,3,1,2,0]
classCount = {}
这里创建一个字典,存储跟新上台女嘉宾最为接近的K个人的划分结果。
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
# P
# dict = {'Name': 'Zara', 'Age': 27}
# print "Value : %s" % dict.get('Age')
# print "Value : %s" % dict.get('Sex', "Never")
# Value : 27 Value : Never
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
这片段中的get对我来说非常重要。总结下get用法:
1.《老友记》第八季第七集中,女佣看见Manica清洁剂很好用,后面补充到:“How did you get it?” 这里get就是,得到,买到意思。
2. 《泰坦尼克号》电影中,Rose在甲板上问Jone:“You do get around for a poor guy?” 这里的get就是“动身”,身体发出的动作。
3. 《穿普拉达的女王》电影中,女主角气愤的对男朋友说:“But I can’t let Miranda get to me” 我不想让Miranda影响到我,这里的get,是收到影响。
我们这代码中get是什么意思呢?当然就是第一种情况,在字典{}中得到它的值,如果没值的话,默认为0.
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0]
[0]
后文待续。。。。。