目录
1.k-近邻算法概述
KNN(K-Nearest Neighbor)算法是机器学习算法中最基础、最简单的算法之一。它既能用于分类,也能用于回归。KNN通过测量不同特征值之间的距离来进行分类。KNN算法的核心思想是:在已知训练集数据及其标签的情况下,对于一个新输入的测试数据,算法会在训练集中找到与这个测试数据最相似的K个邻居,然后根据这K个邻居的类别来决定测试数据的类别。
具体来说,算法的步骤包括:
- 计算距离:计算测试数据与训练集中每个数据点之间的距离。
- 选择邻居:按照距离大小对训练数据进行排序,选择距离最小的K个数据点作为邻居。
- 投票决策:统计这K个邻居中各个类别的出现频率,频率最高的类别即为测试数据的预测分类。
此过程中我们计算距离需要运用到欧式距离公式,计算两个向量点xA和xB之间的距离:
亦或者,也可以采用曼哈顿距离:
而后,我们通过选定的k值。将输入的数据通过计算与其它点之间的距离按从小到大排序,选定k个值,计算这k个值里面各类别出现的频率,将频率最高的类别复制给输入的数据作为其标签。
该算法的优点为:
(1)简单易用;
(2)模型训练时间快(惰性模型,不需要对数据作出任何的假设,完全根据数据决定);
(3)预测效果好;
(4)对异常值不敏感。
该算法的缺点为:
(1)计算量较大,预测阶段可能较慢;
(2)对内存要求高,该算法存储了所有训练数据;
(3)对不相关的功能和数据规模敏感。
1.1准备:使用Python导入数据
首先我们需要去寻找数据集,并将数据集下载到本地。常见的免费数据集下载地址有:https://www.kaggle.com/datasets
本次实验我是从kaggle中下载了猫狗的数据集,其数据集中准备了测试集、训练集、验证集的图片文件夹,以及文本(文本中存放了图片的数据和标签)。
在进行导入数据前,我们需要导入一些必要的库,以用来后面进行数据的处理。(这仅是部分,若有需要可以按自己的代码补充相对应的库)
import os
import cv2
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
from PIL import Image
import matplotlib.pyplot as plt
导入数据我们一般可以采用open、readlines函数来实现,亦或者可以采用pandas库中的pd.read_csv来实现(若数据集为csv文件)
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
ps:filename是自己的数据集路径
1.2从文本文件中解析数据
首先我们需要从txt文本中,将数据进行导入和分割,因为该文本中的数据是分为两列属性。我们可以采用img,classLabelVector来分别存储。
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
classLabelVector = []#用于存放标签
img = []#用于存放图片
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')#将数据集每一行都分割来 分割后为'cats/cat.4987.jpg 0'
for item in listFromLine:
img_path, classLabel = item.split(' ')
img_data = image_to_feature_matrix(img_path)
img.append(img_data)
classLabelVector.append(int(classLabel))
index += 1
return classLabelVector, img
(1)代码思路:我们首先通过Open函数打开存放有我们数据和标签的文件,通过readlines遍历文本当中的每一行。img,classLabelVector分别用来存放图片数据和标签。此后通过一次for循环,将文本数据中的每一行通过换行符进行分割,而后分割后的每一行数据在进行for循环,通过空格符进行分割,并通过append函数,存储在相对应的数组中。
如果你想要实现图像识别分类,你需要将图片信息转化为灰度图,而后将灰度图转换为特征向量来进行存储,否则很容易会造成输入的规模与分类器的规模不一致的问题。(因为我下载的数据集txt文本中图片不是完整路径,因此这里采用了拼接路径)
def image_to_feature_matrix(image_path, size=(64, 64)):
# 读取图片文件
img = cv2.imread('C:\\AI\\Lib\\cats_dogs_dataset\\'+image_path)
# 缩放图片
img = cv2.resize(img, size)
# 将图片转换为灰度图
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 将灰度图转换为特征向量
feature_vector = gray_img.flatten()
return feature_vector
2.实例:使用K-近邻算法实现猫狗分类
2.1准备数据:从文本文件中解析数据
我们通过for循环和split分割属性,将其分别加入到对应的数组中,我们需要对图像进行处理(为了保证输入规模与KNN分类器的规模一致)。我们需要将图片转化为灰度图,并将其转化为特征向量存储。
def image_to_feature_matrix(image_path, size=(64, 64)):
# 读取图片文件
img = cv2.imread('C:\\AI\\Lib\\cats_dogs_dataset\\'+image_path)
# 缩放图片
img = cv2.resize(img, size)
# 将图片转换为灰度图
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 将灰度图转换为特征向量
feature_vector = gray_img.flatten()
return feature_vector
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
classLabelVector = []#用于存放标签
img = []#用于存放图片
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')#将数据集每一行都分割来 分割后为'cats/cat.4987.jpg 0'
for item in listFromLine:
img_path, classLabel = item.split(' ')
img_data = image_to_feature_matrix(img_path)
img.append(img_data)
classLabelVector.append(int(classLabel))
index += 1
return classLabelVector, img
而后,我们采用KNN算法,定义一个classify函数,其中inX为输入的向量,X_train为训练集,labels为标签,k为选定的值。我们可以通过采用欧式距离公式来进行计算,而后通过最近距离的k个点频率最高的类别来确定输入数据的标签。
def classify(inX, X_train, labels, k):#knn算法
dataSetSize = len(X_train)
diffMat = np.tile(inX, (dataSetSize, 1)) - X_train
sqDiffMat = diffMat**2
sqDistance = sqDiffMat.sum(axis=1)
distances = sqDistance**0.5
sortedDisIndicies = distances.argsort()
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDisIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(classCount.items(), key=lambda x: x[1], reverse=True)
return sortedClassCount[0][0]
而后我们可以通过train_test_split函数来划分训练集和测试集,在该示例中,我们将测试集设置为百分之二十。然后将img,classLabelVector分别作为训练集和测试集的参数传入。
if __name__ == "__main__":
filename = 'C:\\AI\\Lib\\cats_dogs_dataset\\test.txt'
classLabelVector, img = file2matrix(filename)
print(img)
print(classLabelVector)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(img, classLabelVector, test_size=0.2, random_state=42)
进一步地,我们可以通过KNeighborsClassifier函数创建KNN分类器,也可以自主设置img_path,并通过我们定义的classify函数来实现knn算法及预测。
# 创建KNN分类器
knn = KNeighborsClassifier(n_neighbors=3)
#通过输入预测
img_path = 'cats/cat.4358.jpg'
inX = image_to_feature_matrix(img_path)
knn1 = classify(inX, X_train, y_train, k=3)
print(knn1)
接下来就是训练模型以及计算该模型的准确率。我们可以通过fit、predict、accuracy_score函数来实现该功能。
# 训练模型
knn.fit(X_train, y_train)
# 预测
y_pred = knn.predict(X_test)
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print("准确率:", accuracy)
2.2分析数据:使用Matplotlib创建散点图
创建散点图之前,我们需要导入plt库(用来制图)。我们可以利用plt库当中的xlabel、ylabel来设置横纵坐标,scatter则可以用来绘制点的分散,我们可以设置点的颜色,以及某个颜色所代表的意义。最后我们通过show函数来展示散点图的分布。
#散点图分布
plt.scatter(range(len(y_pred)), y_pred, c='r', label='Predicted')
plt.scatter(range(len(y_test)), y_test, c='b', label='True')
plt.xlabel('Index')
plt.ylabel('Class')
plt.legend()
plt.show()
2.3效果图展示
该结果展示的是特征矩阵,因为该示例的数据集为猫狗图片,因此特征矩阵存放的是像素的值。
该结果展示的是各图片所对应的标签,其中0表示猫,1表示狗。
该图片展示的是散点图的分布,因为该示例为二分类任务。从该图上看来,该模型的性能还是较好的。
3.常见错误及解决思路
常见错误:(1)我们在对图像数据进行存储的时候,规模和分类器的规模会不一致。
(2)在进行数据分割的时候,很容易造成分割不完全,以至于后续无法找到正确路径
(3)找不到txt文件中的图片文件出现FileNotFound的Error
解决思路:(1)我们利用cv2进行图像的转化,将其像素的值存储到img数组中,当中可以利用flatten来将其拉伸为一维数组
(2)我们在分割的时候需要注意我们每一步代码,分割后的结果是什么,像该示例我们第一次按换行符进行分割,分割的结果是文本内的每一行数据。需要在进行一次分割,将图像数据与标签分别进行存储
(3)因为txt文件当中的图像数据并不是完整的路径,因此我们需要通过拼接路径,将完整的图片路径拼出来。