1. 程式人生 > 其它 >基於kNN的人臉識別演算法

基於kNN的人臉識別演算法

  • 摘要:

    本次實驗嘗試通過將人臉的影象轉化為特徵向量,然後訓練資料集,通過計算尤拉距離找到與待測人臉最接近的k個人臉,這樣對人臉進行歸類識別實現一個基於KNN 的人臉識別演算法,來達到人臉識別的入門級學習。

  • 演算法簡介:

    KNN演算法假設給定一個訓練資料集,其中的例項類別已定。分類時,對新的例項,根據其 k 個最近鄰的訓練例項的類別,通過多數表決等方式進行預測。因此,KNN演算法不具有顯式的學習過程。
    KNN演算法實際上利用訓練資料集對特徵向量空間進行劃分,並作為其分類的“模型”。 k值的選擇、距離度量以及分類決策規則是k近鄰演算法的三個基本要素。

  • 演算法流程:

    1.假設有一個帶有標籤的樣本資料集(訓練樣本集),其中包含每條資料與所屬分類的對應關係。遍歷訓練資料集,計算預測樣本與其他每一個樣本點的距離,按照由近到遠排序。完成訓練得到訓練後的資料集After training Data Set

    2.定義一個KNN引數k 值(1<=k<=20),表示納入投票決策的樣本數

    3.輸入沒有標籤的新資料後,將新資料的每個特徵與樣本集中資料對應的特徵進行比較(進行測試集的測試)。
    4.取前 k個樣本資料對應的分類標籤。求 k 個數據中出現次數最多的分類標籤作為新資料的分類。

  • 實驗環境:

    python版本為:python-3.8.1,所需執行庫有matplotlib、numpy及pillow

  • 原始碼

    import matplotlib.pyplot as plt
    from PIL import Image, ImageFilter
    import numpy
    import heapq
    
    dataBase="data_base"
    persons=[]
    faces=[]
    for i in range(1, 41):
        persons.append("s" + str(i))
    for i in range(1, 11):
        faces.append(str(i) + ".pgm")
    
    def parseImageToVector(path):
        """
        功能:將影象轉換為特徵向量
        輸入:影象的路徑
        返回值:特徵向量(numpy一維陣列)
        """
        img = numpy.array(Image.open(path)) 
        return img.flatten()
    
    def eularDistance(vec1, vec2):
        """
        功能:計算兩個特徵向量的eular距離
    輸入:特徵向量1,2
    返回值:尤拉距離
        """
        return numpy.sum(numpy.square(vec1-vec2))
    
    def trainSetInitialization(faces):
        """
        功能:初始化訓練資料集
     輸入:用作訓練集的人臉ID列表
     返回值:初始化的資料集,(資料, 標籤, 影象)元組列表
        """
        trainSet = []
        for person in persons:
            for face in faces:
                imgPath = dataBase + "/" + person + "/" + str(face) + ".pgm"
                imgVec = parseImageToVector(imgPath)
                trainSet.append((imgVec, person, str(face)))
        return trainSet
    
    def faceRecognition(face, trainSet, k):
        """
        功能:識別人臉,計算訓練資料集中的哪張臉與此臉相同(KNN實現)
        輸入:face陣列中的測試臉,trainDataSet訓練後的資料,kNN引數
    返回:資料集中最相同的面孔的標籤
        """
        heap = [] #小根堆,儲存(距離,標號,人臉)元組
        neighbors = [] # 儲存前k個點的資訊
        result = {} # { key : val } 表示一組k近鄰點中 { 標籤 : 標籤數量(1<=n<=k) }
        # 計算前k個最近的點,壓入小根堆heap
        for trainData in trainSet:
            # trainData[1]對應person標籤, trainData[0]對應該標籤下的某個特徵向量
            heapq.heappush(heap, (eularDistance(face, trainData[0]), trainData[1], trainData[2]) ) 
    
        # 找到前k個最近的點中數量最多的標籤,並加入結果result
        for i in range(k):
            first = heapq.heappop(heap)
            top = first[1] # 標籤
            topImg = first[2] # 影象
            neighbors.append((top, topImg))
            if top in result:
                result[top] = result[top] + 1
            else:
                result[top] = 1
        maximum = (None, 0)
        for label in result:
            if result[label] > maximum[1]:
                maximum = (label, result[label])
        # 顯示資訊
        print("測試所屬標籤:" + maximum[0])
        print("各標籤對應的數量" + str(result))
        print("與目標k近鄰的人臉資訊:")
        for neighbor in neighbors:
            path = dataBase + "/" + neighbor[0] + "/" + neighbor[1] + ".pgm"
            print(path)
        print("-------------分界線--------------")
        return maximum[0]
    
    def main():
        fault = 0
        total = 0
        kList = []
        misclassificationRateList = []
        for k in range(1, 21):
            for testIndex in range(1, 11):
                # 初始化訓練集
                trainImages = []
                for trainImage in range(1,11):
                    trainImages.append(trainImage)
                trainImages.remove(testIndex)
                trainSet = trainSetInitialization(trainImages)
    
                # 測試
                for person in persons:
                    path = dataBase + "/" + person + "/" + str(testIndex) + ".pgm"
                    faceVec = parseImageToVector(path)
                    print("測試人臉的路徑:" + path)
                    result = faceRecognition(faceVec, trainSet, k)
                    if person != result:
                        fault = fault + 1
                    total = total + 1
            kList.append(k)
            misclassificationRateList.append(fault / total)
            print("misclassification rate:", fault / total)
        # 顯示影象   
        plt.plot(kList, misclassificationRateList, alpha=0.7)
        plt.xticks(kList, kList)
        plt.ylabel("Misclassification Rate")
        plt.show()
    
    if __name__ == "__main__":
        main()
    
  • 實驗結果及分析:

    報告pdf