Skip to the content.

《Machine Learning In Action》 中knn部分的代码精简优化


Contact me:

Blog -> https://cugtyt.github.io/blog/index
Email -> cugtyt@qq.com
GitHub -> Cugtyt@GitHub


本系列博客主页及相关见此处
此处粘贴的代码来源为https://github.com/Jack-Cherish/Machine-Learning


1. classify0函数,通过计算距离判断类别,原代码过于繁杂

原代码:

def classify0(inX, dataSet, labels, k):
    #numpy函数shape[0]返回dataSet的行数
    dataSetSize = dataSet.shape[0]
    #在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    #二维特征相减后平方
    sqDiffMat = diffMat**2
    #sum()所有元素相加,sum(0)列相加,sum(1)行相加
    sqDistances = sqDiffMat.sum(axis=1)
    #开方,计算出距离
    distances = sqDistances**0.5
    #返回distances中元素从小到大排序后的索引值
    sortedDistIndices = distances.argsort()
    #定一个记录类别次数的字典
    classCount = {}
    for i in range(k):
        #取出前k个元素的类别
        voteIlabel = labels[sortedDistIndices[i]]
        #dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
        #计算类别次数
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    #python3中用items()替换python2中的iteritems()
    #key=operator.itemgetter(1)根据字典的值进行排序
    #key=operator.itemgetter(0)根据字典的键进行排序
    #reverse降序排序字典
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    #返回次数最多的类别,即所要分类的类别
    return sortedClassCount[0][0]

问题:

没有利用numpy的broadcasting便利,tile导致无用的扩充,而且类别的提取也有些复杂,使用Counter可以极大简化。

参考修改:

def classify0(inx, dataset, labels, k):
    # Compute distance
    dist = np.sum((inx - dataset)**2, axis=1)**0.5
    # A list that contains k nearest neighbor labels
    k_labels = [labels[index] for index in dist.argsort()[0 : k]]
    # Use Counter to compute most common label
    label = Counter(k_labels).most_common(1)[0][0]
    return label

2. autoNorm函数,同样写法复杂

原代码:

def autoNorm(dataSet):
    #获得数据的最小值
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    #最大值和最小值的范围
    ranges = maxVals - minVals
    #shape(dataSet)返回dataSet的矩阵行列数
    normDataSet = np.zeros(np.shape(dataSet))
    #返回dataSet的行数
    m = dataSet.shape[0]
    #原始值减去最小值
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    #除以最大和最小值的差,得到归一化数据
    normDataSet = normDataSet / np.tile(ranges, (m, 1))
    #返回归一化数据结果,数据范围,最小值
    return normDataSet, ranges, minVals

问题:

没有利用numpy的broadcasting便利,tile导致无用的扩充,简单的逻辑实现繁杂

参考修改:

def auto_norm(dataset):
    minvalue = np.min(dataset, axis=0)
    maxvalue = np.max(dataset, axis=0)
    ranges = maxvalue - minvalue
    norm_dataset = (dataset - minvalue) / ranges
    return norm_dataset, ranges, minvalue

多加利用语言和功能包的特性,能极大简化代码,逻辑也更为清晰,代码更加可读