機器學習(七)-基於KNN分類的約會網站配對改進算法

1 項目介紹

某APP用戶一直使用在線約會軟件尋找適合本身的約會對象。儘管約會網站會推薦不一樣的人選,但她並非喜歡每個人。通過一番總結,她發現曾交往過三種類型的人:python

  • 不喜歡的人(3)
  • 魅力通常的人(2)
  • 極具魅力的人(1)

某APP用戶但願分類軟件能夠更好地幫助她將匹配對象劃分到確切的分類中。此外還能夠收集了約會軟件不曾記錄的數據信息,她認爲這些數據更有助於匹配對象的歸類。收集的部分信息以下圖所示:
數據集下載
在這裏插入圖片描述
樣本主要包含如下3種特徵:git

  • 每一年得到的飛行常客里程數
  • 玩視頻遊戲所耗時間百分比
  • 每週消費的冰淇淋公升數

2 準備數據:從文本文件中解析數據

在將上述特徵數據輸入到分類器以前,必須將待處理數據的格式改變爲分類器能夠接受的格式。github

import numpy as np
def file2matrix(filename):
    """

    :param filename: APP用戶收集的約會數據的文件名
    :return:
        returnMat: 用戶提供的每行數據信息,三列,
                    分別是每一年得到的飛行常客里程數,
                    玩視頻遊戲所耗時間百分比,
                    每週消費的冰淇淋公升數
        classLabelVetor:
                    用戶的評價信息, 通常分爲3類(1,2,3)

    """
    fr = open(filename)
    arrayOfLines = fr.readlines()
    # print(arrayOfLines)
    # 得到文件行數;
    numerOfLines = len(arrayOfLines)
    # 建立要返回的Numpy矩陣;
    returnMat = np.zeros((numerOfLines, 3))
    # 解析文件數據到矩陣中;
    classLabelVetor = []
    index = 0
    for line in arrayOfLines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        classLabelVetor.append(listFromLine[-1])
        index += 1
    return returnMat, classLabelVetor

print(file2matrix('data/datingTestSet2'))

返回的值顯示: 算法

在這裏插入圖片描述

3 分析數據:使用 Matplotlib 建立散點圖

使用Matplotlib庫圖形化清晰地標識了三個不一樣的樣本分類區域,具備不一樣愛好的人其類別區域也不一樣。app

def draw_pic(datingDataMat, datingLabels):
    """
    每一年獲取的飛行常客里程數與每週所消費的冰淇淋公升數」構成的散點圖。
    :param datingDataMat:
    :param datingLabels:
    :return:
    """
    #  中文顯示亂碼問題;
    myfont = font_manager.FontProperties(fname="/usr/share/fonts/cjkuni-uming/uming.ttc", size=12)
    # 建立畫布
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(datingDataMat[:, 0], datingDataMat[:, 2],
               15 * datingLabels, datingLabels)

    plt.xlabel("每一年的飛行里程數", fontproperties=myfont)
    plt.ylabel("每週消費的冰淇淋公升數", fontproperties=myfont)
    plt.grid(alpha=0.5)
    plt.show()
  • 效果展現

在這裏插入圖片描述

4 準備數據:歸一化數值

  • 計算樣本3和樣本4之間的距離:

在這裏插入圖片描述

    • 問題:
      飛行常客里程數對於計算結果的影響將遠遠大於其餘兩個特徵的影響
    • 解決方式:
      處理不一樣取值範圍的特徵值時,一般採用的方法是將數值歸一化,如將取值範圍處理爲0到1或者-1到1之間。
    • 歸一化公式: newValue = oldValue / max
    def autoNorm(dataSet):
        """
         歸一化數值,
        :param dataSet:用戶提供的每行數據信息,三列;
        :return:
            normDataSet: 歸一化的特徵信息;
            maxVals:每一個特徵數據的最大值;
        """
        # 獲取每一個特徵數據的最大值;
        maxVals = dataSet.max(0)
        # 獲取樣本個數;
        m = dataSet.shape[0]
        # 根據公式生成歸一化的特徵信息;
        normDataSet = dataSet / np.tile(maxVals, (m, 1))
        return normDataSet, maxVals

    4 實施 kNN 算法

    對未知類別屬性的數據集中的每一個點依次執行如下操做, 與上一個案例代碼相同:
    (1) 計算已知類別數據集中的點與當前點之間的距離;
    (2) 按照距離遞增次序排序;
    (3) 選取與當前點距離最小的k個點;
    (4) 肯定前k個點所在類別的出現頻率;
    (5) 返回前k個點出現頻率最高的類別做爲當前點的預測分類。機器學習

    def classify(inX, dataSet, labels, k):
        """
        :param inX: 要預測的數據
        :param dataSet: 咱們要傳入的已知數據集
        :param labels:  咱們要傳入的標籤
        :param k: KNN裏的k, 也就是說咱們要選幾個近鄰
        :return: 排序的結果
        """
        dataSetSize = dataSet.shape[0]  # (6,2) 6
        # tile會重複inX, 把他重複成(datasetsize, 1)型的矩陣
        # print(inX)
        # (x1 - y1), (x2- y2)
        diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
        # 平方
        sqDiffMat = diffMat ** 2
        # 相加, axis=1 行相加
        sqDistance = sqDiffMat.sum(axis=1)
        # 開根號
        distances = sqDistance ** 0.5
        # print(distances)
        # 排序 輸出的是序列號index,並非值
        sortedDistIndicies = distances.argsort()
        # print(sortedDistIndicies)
    
        classCount = {}
        for i in range(k):
            voteLabel = labels[sortedDistIndicies[i]]
            classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
            # print(classCount)
        sortedClassCount = sorted(classCount.items(), key=lambda d: float(d[1]), reverse=True)
        return sortedClassCount[0]

    5 測試算法:做爲完整程序驗證分類器

    若是分類器的正確率知足要求,就能夠使用這個軟件來處理約會網站提供的約會名單了。機器學習算法一個很重要的工做就是評估算法的正確率,一般咱們只提供已有數據的90%做爲訓練樣原本訓練分類器,而使用其他的10%數據去測試分類器,檢測分類器的正確率。學習

    def datingClassTest():
        """
        分類器針對約會網站的測試代碼, 獲取錯誤率;
        :return:
        """
        hoRatio = 0.10
        datingDataMat, datingLabels = file2matrix('data/datingTestSet2')
        normDataSet, maxVals = autoNorm(datingDataMat)
        # 樣本個數
        m = normDataSet.shape[0]
        # 測試集個數;
        numTestVecs = int(m*hoRatio)
        errorCount = 0.0
    
        for i in range(numTestVecs):
            classiferResult = classify(normDataSet[i, :],
                                       normDataSet[numTestVecs:m, :],
                                       datingLabels[numTestVecs:m], 3)
            # print(classiferResult)
            if classiferResult != datingLabels[i]:
                errorCount += 1
                print("正確結果:", datingLabels[i])
                print("預測結果:", classiferResult)
    
        # print("錯誤個數:", errorCount)
        return  errorCount
    • 執行效果展現:

    在這裏插入圖片描述

    6 使用算法:構建完整可用的預測系統

    def classifyPerson(Person):
        """
        使用這個分類器爲某APP用戶來對人們分類。
        :param Person:
        :return:
        """
        datingDataMat, datingLabels = file2matrix('data/datingTestSet2')
        normDataSet, maxVals = autoNorm(datingDataMat)
        classiferResult = classify(Person / maxVals, normDataSet, datingLabels, 3)
        if classiferResult  == '1':
            print("不喜歡")
        elif classiferResult == '2':
            print("有一點喜歡")
        else:
            print("很是喜歡")

    完整代碼

    # encoding:utf-8
    
    
    """
    KNN實現,基於KNN分類的約會網站配對改進算法
    """
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib import font_manager
    
    
    def file2matrix(filename):
        """
    
        :param filename: APP用戶收集的約會數據的文件名
        :return:
            returnMat: 用戶提供的每行數據信息,三列,
                        分別是每一年得到的飛行常客里程數,
                        玩視頻遊戲所耗時間百分比,
                        每週消費的冰淇淋公升數
            classLabelVetor:
                        用戶的評價信息, 通常分爲3類(1,2,3)
    
        """
        fr = open(filename)
        arrayOfLines = fr.readlines()
        # print(arrayOfLines)
        # 得到文件行數;
        numerOfLines = len(arrayOfLines)
        # 建立要返回的Numpy矩陣;
        returnMat = np.zeros((numerOfLines, 3))
        # 解析文件數據到矩陣中;
        classLabelVetor = []
        index = 0
        for line in arrayOfLines:
            line = line.strip()
            listFromLine = line.split('\t')
            returnMat[index, :] = listFromLine[0:3]
            classLabelVetor.append(listFromLine[-1])
            index += 1
        return returnMat, classLabelVetor
    
    
    def autoNorm(dataSet):
        """
        歸一化數值,
        計算樣本3和樣本4之間的距離: [(0-67)**2 + (20000 - 32 000)**2 + (1.1 - 0.1)**2]**0.5
        問題:
                飛行常客里程數對於計算結果的影響將遠遠大於其餘兩個特徵的影響
        解決方式:
                處理不一樣取值範圍的特徵值時,
                一般採用的方法是將數值歸一化,如將取值範圍處理爲0到1或者-1到1之間。
                歸一化公式: newValue = oldValue / max
        :param dataSet:用戶提供的每行數據信息,三列;
        :return:
            normDataSet: 歸一化的特徵信息;
            maxVals:每一個特徵數據的最大值;
        """
        # 獲取每一個特徵數據的最大值;
        maxVals = dataSet.max(0)
        # 獲取樣本個數;
        m = dataSet.shape[0]
        # 根據公式生成歸一化的特徵信息;
        normDataSet = dataSet / np.tile(maxVals, (m, 1))
        return normDataSet, maxVals
    
    
    def draw_pic(datingDataMat, datingLabels):
        """
        每一年獲取的飛行常客里程數與每週所消費的冰淇淋公升數」構成的散點圖。
        :param datingDataMat:
        :param datingLabels:
        :return:
        """
        #  中文顯示亂碼問題;
        myfont = font_manager.FontProperties(fname="/usr/share/fonts/cjkuni-uming/uming.ttc", size=12)
        # 建立畫布
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.scatter(datingDataMat[:, 0], datingDataMat[:, 2],
                   15 * datingLabels, datingLabels)
    
        plt.xlabel("每一年的飛行里程數", fontproperties=myfont)
        plt.ylabel("每週消費的冰淇淋公升數", fontproperties=myfont)
        plt.grid(alpha=0.5)
        plt.show()
    
    
    def classify(inX, dataSet, labels, k):
        """
        :param inX: 要預測的數據
        :param dataSet: 咱們要傳入的已知數據集
        :param labels:  咱們要傳入的標籤
        :param k: KNN裏的k, 也就是說咱們要選幾個近鄰
        :return: 排序的結果
        """
        dataSetSize = dataSet.shape[0]  # (6,2) 6
        # tile會重複inX, 把他重複成(datasetsize, 1)型的矩陣
        # print(inX)
        # (x1 - y1), (x2- y2)
        diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
        # 平方
        sqDiffMat = diffMat ** 2
        # 相加, axis=1 行相加
        sqDistance = sqDiffMat.sum(axis=1)
        # 開根號
        distances = sqDistance ** 0.5
        # print(distances)
        # 排序 輸出的是序列號index,並非值
        sortedDistIndicies = distances.argsort()
        # print(sortedDistIndicies)
    
        classCount = {}
        for i in range(k):
            voteLabel = labels[sortedDistIndicies[i]]
            classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
            # print(classCount)
        sortedClassCount = sorted(classCount.items(), key=lambda d: float(d[1]), reverse=True)
        return sortedClassCount[0][0]
    
    def datingClassTest():
        """
        分類器針對約會網站的測試代碼, 獲取錯誤率;
        :return:
        """
        hoRatio = 0.10
        datingDataMat, datingLabels = file2matrix('data/datingTestSet2')
        normDataSet, maxVals = autoNorm(datingDataMat)
        # 樣本個數
        m = normDataSet.shape[0]
        # 測試集個數;
        numTestVecs = int(m*hoRatio)
        errorCount = 0.0
    
        for i in range(numTestVecs):
            classiferResult = classify(normDataSet[i, :],
                                       normDataSet[numTestVecs:m, :],
                                       datingLabels[numTestVecs:m], 3)
            # print(classiferResult)
            if classiferResult != datingLabels[i]:
                errorCount += 1
                print("正確結果:", datingLabels[i])
                print("預測結果:", classiferResult)
    
        # print("錯誤個數:", errorCount)
        return  errorCount
    
    
    
    
    
    
    def classifyPerson(Person):
        """
        使用這個分類器爲某APP用戶來對人們分類。
        :param Person:
        :return:
        """
        datingDataMat, datingLabels = file2matrix('data/datingTestSet2')
        normDataSet, maxVals = autoNorm(datingDataMat)
        classiferResult = classify(Person / maxVals, normDataSet, datingLabels, 3)
        if classiferResult  == '1':
            print("不喜歡")
        elif classiferResult == '2':
            print("有一點喜歡")
        else:
            print("很是喜歡")
    
    
    if __name__ == '__main__':
        # personData = [30000, 10, 1.3]
        personData = [40920, 8.326976, 0.953952]
        classifyPerson(personData)
    • 執行結果

    在這裏插入圖片描述

    相關文章
    相關標籤/搜索