KNN

Intro

k近鄰法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一種基本分類與回歸方法。它的工作原理是:存在一個樣本數據集合,也稱作為訓練樣本集,并且樣本集中每個數據都存在標簽,即我們知道樣本集中每一個數據與所屬分類的對應關系。輸入沒有標簽的新數據后,將新的數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取樣本最相似數據(最近鄰)的分類標簽。一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法中k的出處,通常k是不大于20的整數。最后,選擇k個最相似數據中出現次數最多的分類,作為新數據的分類。

比較Metric

使用歐氏距離度量樣本之間特征的相似度。

算法步驟

  • 計算訓練數據集中的點與當前點之間的距離;
  • 按照距離遞增次序排序;
  • 選取與當前點距離最小的k個點;
  • 確定前k個點所在類別的出現頻率;
  • 返回前k個點所出現頻率最高的類別作為當前點的預測分類。

小問題

一位女士對男士的印象可以分為:不喜歡的人、魅力一般的人、極具魅力的人;主要根據三個特征來決定印象:每年獲得的飛行??屠锍虜?、玩視頻游戲所消耗時間百分比、每周消費的冰淇淋公升數;

代碼

數據預處理,符號化

# 讀取數據,并符號化
def file2matrix(filepath):
    with open(filepath, mode="r") as fr:
        arrayOLines = fr.readlines()
        # 得到文件行數
        numberOfLines = len(arrayOLines)
        # 返回的NumPy矩陣,解析完成的數據:numberOfLines行,3列
        returnMat = np.zeros((numberOfLines, 3))
        # 返回的分類標簽向量
        classLabelVector = []
        # 行的索引值
        index = 0
        for line in arrayOLines:
            line = line.strip()
            # 使用s.split(str="",num=string,cout(str))將字符串根據'\t'分隔符進行切片。
            listFromLine = line.split("\t")
            # 將數據前三列提取出來,存放到returnMat的NumPy矩陣中,也就是特征矩陣
            returnMat[index, :] = listFromLine[0:3]
            # 根據文本中標記的喜歡的程度進行分類,1代表不喜歡,2代表魅力一般,3代表極具魅力
            if listFromLine[-1] == "didntLike":
                classLabelVector.append(1)
            elif listFromLine[-1] == "smallDoses":
                classLabelVector.append(2)
            elif listFromLine[-1] == "largeDoses":
                classLabelVector.append(3)
            index += 1
        return returnMat, classLabelVector

數據標準化,為了提出樣本的不同特征之間值范圍的影響;

# 標準化是機器學習和深度學習中的常見操作
def auto_norm(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]
    # np.tile 鋪瓷磚函數
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(ranges, (m, 1))
    return normDataSet, ranges, minVals

測試

# 返回一個測試樣本的分類結果
def classify0(test_set, train_set, labels, k):
    # numpy函數shape[0]返回dataSet的行數
    dataSetSize = train_set.shape[0]
    # 在列向量方向上重復inX共1次(橫向),行向量方向上重復inX共dataSetSize次(縱向)
    # 這里如果test_set不是一條數據,就是一種粗粒度的比較
    diffMat = np.tile(test_set, (dataSetSize, 1)) - train_set
    # 二維特征相減后平方
    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
    # reverse降序排序字典
    sortedClassCount = sorted(
        classCount.items(), key=lambda item: item[1], reverse=True
    )
    # 返回次數最多的類別,即所要分類的類別
    return sortedClassCount[0][0]

# 給定一個男人的三個特征,判斷其所屬類別
def classifyPerson():
    # 輸出結果
    resultList = ["討厭", "有些喜歡", "非常喜歡"]
    # 三維特征用戶輸入
    precentTats = float(input("玩視頻游戲所耗時間百分比:"))
    ffMiles = float(input("每年獲得的飛行??屠锍虜?"))
    iceCream = float(input("每周消費的冰激淋公升數:"))
    # 打開的文件名
    filename = "../dataset/datingTestSet.txt"
    # 打開并處理數據
    datingDataMat, datingLabels = file2matrix(filename)
    # 訓練集歸一化
    normMat, ranges, minVals = auto_norm(datingDataMat)
    # 生成NumPy數組,測試集
    inArr = np.array([ffMiles, precentTats, iceCream])
    # 測試集歸一化
    norminArr = (inArr - minVals) / ranges
    # 返回分類結果
    classifierResult = classify0(norminArr, normMat, datingLabels, 3)
    # 打印結果
    print("你可能%s這個人" % (resultList[classifierResult - 1]))


if __name__ == "__main__":
    classifyPerson()

參考

參考 內涵數據集下載地址。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容