機器學習 (三) k-近鄰算法應用-約會網站匹配系統

前言

       目前一些姻緣網站專門給人介紹對象,也經常有人陷入介紹門中,怎麼樣來提高準確率降低網站帶來的風險呢?其實裏面有些推薦算法和匹配算法在裏面,今天我們簡單介紹其中一種。
       大家是否還記得在第一次註冊的會員的時候,它會讓你填一些年齡、性格、體貌特徵選項等等,其實這些都是有用的個人特徵數據,網站拿到這些數據之後會根據數據給你匹配最可能適合你的妹子,提高成功率,下面我們就來看一下它是如何給你匹配對象的,裏面可能用到了k-近鄰算法。

簡介

       上一篇博文已經介紹過它的原理背景以及實現方式,相信大家已經瞭解,現在假設把喜歡的妹子歸類爲三種人:不喜歡的人、魅力一般的人、極具魅力的人,我們都希望找到極具魅力的人,理想的白富美,分類目標是把網站推薦給我們的人自動分到這三類中,該如何分類呢?

場景

       當我們有了這樣一個業務場景或者想法之後,接下來需要考慮有沒有特徵項、有多少特徵項、有哪些特徵權重大對分類結果有幫助等,這些都是我們需要考慮的問題,俗話說“巧婦難爲無米之炊”就是這個道理,有米好下鍋。假設我們已經收集了三個重要特徵

1.每年獲得飛行常客里程數
2.玩視頻遊戲所耗時間百分比
3.每週消費的冰激凌公升數

數據如下:
這裏寫圖片描述
上圖中的第一列、二列、三列分別對應上面三個特徵,最右側爲類別標籤標示該樣本屬於哪一類,

分析步驟

收集數據

       收集數據有各種方法,如業務系統平時產生的日誌數據、物理設備收集的數據等,或者使用問卷調查或其它方式,只要收集的有用的數據都是可以的。

準備數據

       在有了數據以及確定了哪些特徵列之後就需要將保存在DB或者文件裏面的數據處理爲可以讓程序讀入的數據了,將數據處理爲程序可以理解的格式,這裏的程序可以是python也可以是其它處理程序,程序可以處理即可,一般我們會把數據處理抽象出來一個函數,如下代碼是激昂上面的數據文件轉爲python數據對象。


def file2matrix(filename):
    '''
    從文件加載數據 返回訓練集和類別標籤
    :param filename:數據文件
    :return:特徵訓練集、列表標籤
    '''
    # 打開數據文件
    fr = open(filename)
    # 獲取樣本個數
    arrayOLines = fr.readlines()
    # 獲取樣本個數
    numberOfLines = len(arrayOLines)
    # 初始化特徵矩陣
    returnMat = np.zeros((numberOfLines,3))
    # 初始化標籤數組
    classLabelVector = []
    # 樣本的索引、序號
    index = 0
    for line in arrayOLines:
        # 每行兩邊去空格
        line = line.strip()
        # 以製表符分割每項
        listFromLine = line.split('\t')
        # 前三列存入特徵矩陣中
        returnMat[index,:] = listFromLine[0:3]
        # 最後一列爲類別標籤存入列表數組中
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

分析數據

       分析數據是爲了確保收集的數據可用、有價值,即研究數據可用性、可靠性、準確性等爲目的的,而通常爲了直觀感受數據我們經常以圖形化的形式將數據展示出來,如散點圖、條形圖等下面我們先拿特徵數據的第二列和第三列“玩視頻遊戲所耗時間百分比”和“每週所消耗的冰淇淋公升數”來繪製一幅散點圖,先來看看效果如何

歸一化數值

       從數據集來看,第一列數據明顯比其它兩列值大很多,爲了減少值得差異性帶來的誤差,需要對大值數據做一下處理,此處
採用均值法,每項的值=(當前值-最小值)/ (最大值-最小值),這樣處理過的數據取值範圍都在0-1之間,符合我們的預期,自動處理歸一化函數如下:

def autoNorm(dataSet):
    '''
    歸一化數值
    :param dataSet:待處理數據集
    :return:處理後的結果
    '''
    # 第一列的最小值 
    minVals = dataSet.min(0)
    # 第一列的最大值
    maxVals = dataSet.max(0)
    # 取值範圍
    ranges = maxVals - minVals
    # 初始化全爲0的矩陣
    normDataSet = np.zeros(np.shape(dataSet))
    # 獲取行數
    m = dataSet.shape[0]
    # 將最小值重複m行 1列輸出,相減
    normDataSet = dataSet - np.tile(minVals,(m,1))
    # 將取值範圍重複m行1列輸出,相除
    normDataSet = normDataSet / np.tile(ranges,(m,1))
    # 返回處理後的矩陣、取值範圍、最小值
    return normDataSet,ranges,minVals


def createScatterGraph(datingDataMat,datingDataLabels):
    '''
    根據矩陣數據創建散點圖
    :param datingDataMat: 矩陣
    :param datingDataLabels: 類別標籤
    :return:
    '''
    # 創建一個區域顯示數據
    fig = plt.figure()
    ax = fig.add_subplot(111)
    # 加載2、3列數據
    ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
    plt.show()

這裏寫圖片描述

       從圖中很難以分辨出來樣本究竟屬於哪一類,這是由於沒有把類別標籤加入,爲了好看我們再把不同類別的樣本加上不同的顏色,圖下圖(圖片,加入顏色後)
這裏寫圖片描述

       如上圖所示,假如類別標籤的數據已經很明顯看出來是屬於哪一類別,三種顏色十分明顯,現在效果明顯了還存在排列混亂的問題,因爲我們的最終目的是爲了分類不是展示數據,需要將三種分類明確的分出來,讓我們來試試第一列和第二列展示數據。

    ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * np.array(datingDataLabels),
               15.0 * np.array(datingDataLabels))

這裏寫圖片描述

測試算法

       當我們設計的一個學習分類器完成的時候,一個重要事情就是評估算法的好壞,這也決定了該算法能否上線的依據,在java開發中大家都知道有白盒測試、黑盒測試、冒煙測試等,那麼在機器學習中是否一樣有哪些測試方法呢?最常用的是交叉驗證測試法,交叉驗證又可以細分爲很多種,常用的有三種,這裏我們使用第一種簡單方法即隨機抽取90%數據作爲訓練集數據 10%作爲測試集合數據,代碼如下:

def datingClassTest():
    '''
    測試分類器的效果 錯誤率大小
    :return:
    '''
    # 調節測試樣本集大小參數
    hoRatio = 0.10
    # 加載樣本數據
    datingDataMat,datingDataLabels = file2matrix('datingTestSet2.txt')
    # 數據歸一化處理
    normMat,ranges,minVals = autoNorm(datingDataMat)
    # 獲取樣本個數
    m = normMat.shape[0]
    # 測試集樣本個數
    numTestVecs = int(m*hoRatio)
    # 保存錯誤的次數
    errorCount = 0.0
    # 循環錯誤集
    for i in range(numTestVecs):
        # 獲取分類結果 參數爲:待測樣本向量、已經樣本集、已知分類標籤、k值
        classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingDataLabels[numTestVecs:m],3)
        print "the classifier came back with: %d,the real answer is: %d" % (classifierResult,datingDataLabels[i])
        # 統計錯誤次數
        if (classifierResult != datingDataLabels[i]):
            errorCount += 1.0
    # 計算錯誤率 錯誤次數/測試樣本個數
    print "the total error rate is: %f" % (errorCount/float(numTestVecs))

使用算法

上面我們的算法已經測試完畢,接下來即可進入使用環節,這個環節我覺得可以提供出來一些頁面或者網頁供用戶使用,用戶輸入某個人的信息然後返回給用戶他是否會喜歡對方,這樣對於用戶體驗、用戶效果要好一些,爲了方便這裏就不繪製界面了,只是來使用下效果如何。

def classifyPerson():
    '''
    檢驗約會網站對象預測結果
    :return:
    '''
    # 結果
    resultList = ['不喜歡','有點喜歡','很喜歡']
    # 輸入特徵
    percentTats = float(raw_input("玩網遊所消耗的時間比"))
    ffMiles = float(raw_input("每年獲得的飛行常客里程數"))
    iceCream = float(raw_input("每年消費的冰淇淋公升數"))
    # 加載數據
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
    # 歸一化處理
    normMat,ranges,minVals = autoNorm(datingDataMat)
    # 生成特徵向量
    inArr = np.array([ffMiles,percentTats,iceCream])
    # 預測
    classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
    print "you will probably like this person:",resultList[classifierResult-1]

預測我的結果如下:
這裏寫圖片描述

根據結果來看我還是喜歡旅遊多一點、玩網遊少一點的妹子
       

概念解析

  • 錯誤率
    通常我們拿錯誤的次數除以總測試次數,作爲錯誤率
  • 交叉驗證(cross validation)
    1.k-folder cross-validation
    中文名叫做10-折交叉驗證,意思是隨機選取10份數據,取9份作爲訓練數據,10份作爲測試數據,循環進行10次,每次都計算出來錯誤率,取平均值,此方法可以儘可能的避免由於數據不均勻分佈對驗證結果產生的影響,也是最常用的方法。
    2.K * 2 folder cross-validation
    此法是10-折的變體,分完10份或k份後,再二分爲s0 s1 ,分別爲測試數據和訓練數據,此方法保證了測試集合訓練數據都足夠多
    3.least-one-out cross-validation(loocv)
    指每次n-1個樣本訓練,1個樣本測試,循環進行。

總結

       knn優缺點上篇已經總結,這裏說說對算法或機器學習的理解,各種機器學習算法層出不窮如何才能學好各種算法呢,這是值得思考的一個問題,我我覺得首先第一步還是要深刻理解這一領域涉及到的各種概念,知道原因了纔能有深入的理解,其次是工欲善其事,必先利其器,一些工具函數統計學、數學基礎要掌握,知道靈活運用。比如爲什麼機器學習中都使用矩陣來處理數據?知道原因才能想到如果自己寫個算法怎麼處理數據,下一篇計劃總結一下貝葉斯算法,待續…………

題外思考

井蓋爲什麼是園的而不是方的談認知觀?

       生活中其實處處都能體現出來人們的思想和想法,只要我們細心觀察有一顆發現問題的心,牛頓爲什麼發現蘋果?自然界或者人類社會中其實沒有什麼事情本來就是那樣的,不同的認知觀會發現不同的問題,如果當你看到井蓋時是否會想到爲什麼是圓的,還是覺得它本來就是那樣,兩種不同的認知觀會有不同的結果。
       有了認知觀後,其次需要思考園的和方的區別在哪裏,從易於使用、製造等多方面思考,就會發現哪種會好一些,生活中也沒有標準答案一說,以前的教學給我們灌輸的是標準答案,其實在不同條件、不同場景下,我們追求的結果可能是不同的,這就引出來因果學說。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章