基于KNN算法的图像识别

你需要完成一个图像识别的任务,主要使用的模型是KNN算法。使用的数据集是cifar-10,是图像识别领域最为经典的数据及之一。具体的数据可以从以下的链接下载: https://www.cs.toronto.edu/~kriz/cifar.html, 下载之后把是数据集解压在当前的工程的根目录下。

你即将要完成的几个任务是:

  1. 读取图片文件、展示图片、并做部分采样。采样的原因主要是为了节省训练的时间,因为我们知道KNN的搜索复杂度为O(N),何况图片也属于高维的数据,这也会增加搜索效率。
  2. 使用KNN算法识别图片。在这里,我们需要使用K折交叉验证来最适合的超参数。
  3. 使用PCA技术先给图片做降维,然后在使用KNN来识别图片。 另外,使用PCA把图片降维到2维度空间里,这时候相当于我们得到了每一个图片在(x,y)轴上的坐标。之后把采样过的一些图片在二维空间里根据每个坐标点来展示一下。
  4. 给每一个图片抽取两个经典的特征,color historgramHOG, 抽取的过程已经给你写好。抽取完之后再通过KNN来训练。
  5. 基于第四步,换成神经网络模型去识别。虽然还没有讲到神经网络,但根据提前给定的参数设置并结合sklearn的文档,完全可以去实现一个神经网络模型。

1. 文件的读取、可视化、以及采样

在这部分我们需要读取图片文件,并展示部分图片便于观察,以及做少量的采样。

  1. 文件的读取: 读取部分的代码已经提供,你只需要调用一下即可以读取图片数据。
  2. 可视化: 选择其中的一些样本做可视化,也就是展示图片的内容以及它的标签。
  3. 采样:统计一下各类出现的个数以及采样部分样本作为后续的模型的训练。
# 文件的读取,直接通过给定的`load_CIFAR10`模块读取数据。 
from load_data import load_CIFAR10   
import numpy as np
import matplotlib.pyplot as plt

cifar10_dir = 'cifar-10-batches-py'  

# 清空变量,防止重复导入多次。 
try:
   del X_train, y_train
   del X_test, y_test
   print('清除之前导入过的变量...Done!')
except:
   pass

# 读取文件,并把数据保存到训练集和测试集合。  
X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)

# 先来查看一下每个变量的大小,确保没有任何错误!X_train和X_test的大小应该为 N*W*H*3
# N: 样本个数, W: 样本宽度 H: 样本高度, 3: RGB颜色。 y_train和y_test为图片的标签。
print ("训练数据和测试数据:", X_train.shape, y_train.shape, X_test.shape, y_test.shape)
print ("标签的种类: ", np.unique(y_train)) # 查看标签的个数以及标签种类,预计10个类别。

classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
num_classes = len(classes)  # 样本种类的个数
samples_per_class = 5       # 每一个类随机选择5个样本

# plt.subplot函数以及 plt.imshow函数用来展示图片

for y,cls in enumerate(classes):
    idxs = np.flatnonzero(y_train==y)
    idxs = np.random.choice(idxs,samples_per_class,replace=False)
    for i,idx in enumerate(idxs):
        plt_idx = i * num_classes + y + 1
        plt.subplot(samples_per_class,num_classes,plt_idx)
        plt.imshow(X_train[idx].astype('uint8'))
        plt.axis('off')
        if i == 0:
            plt.title(cls)
plt.show()

number_of_each_class=[]
for y,cls in enumerate(classes):
    idxs = np.flatnonzero(y_train==y)
    number_of_each_class.append(len(idxs))
plt.plot(classes,number_of_each_class)
plt.show()

# 随机采样训练样本5000个和测试样本500个。训练样本从训练集里采样,测试样本从测试集里采样。
num_training = 5000
num_test = 500

random_train = np.random.randint(50000,size=5000)
random_test = np.random.randint(10000,size=500)

X_train = X_train[random_train]
y_train = y_train[random_train]

X_test = X_test[random_test]
y_test = y_test[random_test]

print (X_train.shape, y_train.shape, X_test.shape, y_test.shape)

2. 使用KNN算法识别图片。

这部分主要的工作是通过K折交叉验证来训练KNN,以及选择最合适的K值和p值。具体KNN的描述请看官方文档: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html

KNN有几个关键的参数:

K: 指定选择多少个neighbors。 这个值越大,我们知道KNN的决策边界就会越平滑,而且越不容易过拟合, 但不保证准确率会很高。
p: 不同距离的指定,看以下的说明。

KNN依赖于两个样本的距离计算,这里简单介绍一下一个概念叫做Minkowski Distance,是一个种通用的距离计算方法。假如我们有两个点,分别由两个向量来表达x=(x1,x2,...,xd) 和 y=(y1,y2,...,yd) ,这时候根据Minkowski Distance的定义可以得到以下的结果:

dist(x,y) = (\sum_{i=1}^d|x_i-y_i|^p)^\frac{1}{p}

从上述的距离来看其实不难发现p=1 时其实就是绝对值的距离,p=2 时就是欧式距离。所以欧式距离其实是Minkowski Distance的一个特例而已。所以这里的p 值是可以调节的比如p=1,2,3,4,... 。

# 首先我们 Reshape一下图片。图片是的每一个图片变成一个向量的形式。也就是把原来大小为(32, 32, 3)的图片直接转换成一个长度为32*32*3=3072的向量。
# 这样就直接可以作为模型的输入。 X_train_1和y_train_1是用来解决第一个问题的处理后的数据。 
X_train1 = np.reshape(X_train, (X_train.shape[0], -1))
X_test1 = np.reshape(X_test, (X_test.shape[0], -1))
print(X_train1.shape, X_test1.shape) # 确保维度正确

# 训练数据: (X_train1, y_train), 测试数据:(X_test1, y_test)
params_k = [1,3,5,7]  # 可以选择的K值
params_p = [1,2] # 可以选择的P值

# 构建模型
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
param_grid = {"n_neighbors":params_k,"p":params_p}
model = GridSearchCV(estimator=KNeighborsClassifier(),param_grid=param_grid,cv=5)
model.fit(X_train1,y_train)
# 输出最好的K和p值 
print(model.best_params_)

# 输出在测试集上的准确率
predict_model = model.best_estimator_
y_pre = predict_model.predict(X_test1)
correct = np.count_nonzero((y_pre==y_test)==True)
print("Predict precision is: ",correct/len(y_test)) 

3. 抽取图片特征,再用KNN算法来识别图片

一种解决图像识别问题中各种环境不一致的方案是抽取对这些环境因素不敏感的特征,这就是所谓的特征工程。 在这里,我们即将会提取两种类型的特征,分别是color histogramHOG特征,并把它们拼接在一起作为最终的特征向量。 至于这些特征的概念请参考第三章的内容,或者网络上的一些解释。我们已经提供了抽取特征的工具,只需要调用就可以使用了。 所以你需要做的任务是:

  1. 调用特征提取工具给每一个图片提取特征。 如果想深入了解,可以查看其代码
  2. 使用K折交叉验证去学出最好的模型(同上)
from features import *

num_color_bins = 10 # 设定color histogram的 bin大小

# 分别设置接下来需要调用的两个特征抽取器,分别是hog_feature, color_histogram_hsv
feature_fns = [hog_feature, lambda img: color_histogram_hsv(img, nbin=num_color_bins)]

# 抽取特征,分别对特征数据和测试数据,把结果存放在X_train2和X_test2
X_train2 = extract_features(X_train, feature_fns, verbose=True)
#X_val_feats = extract_features(X_val, feature_fns)
X_test2 = extract_features(X_test, feature_fns)

# 打印转换后的数据大小。
print (X_train2.shape, X_test2.shape)

# 对于X_train2, X_test2做归一化

from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler

x_normalizer = StandardScaler()
X_train2 = x_normalizer.fit_transform(X_train2)
X_test2 = x_normalizer.transform(X_test2)

# 通过K折交叉验证构造最好的KNN模型,并输出最好的模型参数,以及测试集上的准确率。 
# 训练数据: (X_train2, y_train), 测试数据:(X_test2, y_test)
params_k = [1,3,5,7]  # 可以选择的K值
params_p = [1,2] # 可以选择的P值
param_grid = {"n_neighbors":params_k,"p":params_p}
# 构建模型
model = GridSearchCV(estimator=KNeighborsClassifier(),param_grid=param_grid,cv=5)           
model.fit(X_train2,y_train)
# 输出最好的K和p值 
print(model.best_params_)

# 输出在测试集上的准确率
predict_model = model.best_estimator_
y_pre = predict_model.predict(X_test2)
correct = np.count_nonzero((y_pre==y_test)==True)
print("Predict precision is: ",correct/len(y_test))

你应该会看到效果的提升,没错! 因为提取出来的特征会比原始的像素特征更加Robust。 除了上面所讲到的2种类型特征,也可以尝试其他图形识别的特征。

4. 使用PCA对图片做降维,并做可视化

PCA是一种常用的降维工具,可以把高维度的特征映射到任意低维的空间,所以这个方法也经常用来做数据的可视化。具体PCA相关的教程可以参考sklearn官方文档,https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html, 他有一个主要的参数需要设计,就是n_components, 指的是降维之后的维度。比如设置为2,就代表降维到2维的空间。现阶段,能看懂官方文档,以及指导如何使用就可以了。 需要完成以下任务:

  1. 通过PCA把数据降维,然后再通过KNN来分类
  2. 把降维之后的数据可视化
params_components = [10, 20, 50, 100]
params_k = [1,3,5,7]  # 可以选择的K值
params_p = [1,2] # 可以选择的P值
param_grid = {"n_neighbors":params_k,"p":params_p}

from sklearn.decomposition import PCA

#首先使用PCA对数据做降维,之后再用KNN做交叉验证。 每一个PCA的维度都需要做一次KNN的交叉验证过程。 
#       输入为原始的像素特征。 训练数据:X_train,  y_train  测试数据: X_test, y_test。 
data = []
for component in params_components:
    pca = PCA(n_components=component)
    X_train3 = pca.fit_transform(X_train2)#用原始像素特征会不会太久?这里换用X_train2
    X_test3 = pca.transform(X_test2)
    model = GridSearchCV(estimator=KNeighborsClassifier(),param_grid=param_grid,cv=5)
    model.fit(X_train3,y_train)
    
    predict_model = model.best_estimator_
    y_pre = predict_model.predict(X_test3)
    correct = np.count_nonzero((y_pre==y_test)==True)

    data.append([correct/len(y_test),component,model.best_params_['n_neighbors'],model.best_params_['p']])

data = np.array(data)
D = data[np.argmax(data[:,0])][1]
K = data[np.argmax(data[:,0])][2]
p = data[np.argmax(data[:,0])][3]
Precision = data[np.argmax(data[:,0])][0]

# 输出最好的 维度、K和p值 
print("Best dimension:%.1f Best K:%.1f Best p:%.1f"%(D,K,p))

# 输出在测试集上的准确率
print("Best precision:%6.3f"%Precision)

   
把数据映射到2维的空间,然后展示。 从X_train中随机选择50个图片做展示, 请使用subplots. 具体来讲的话:

  1. 首先把随机提取训练数据中的50个样本。
  2. 对这50个样本做数据的降维,使用PCA降维到2维的空间,这时候每一个图片变成了2维的向量
  3. 这2维的向量我们可以看作是图片的坐标

根据图片的坐标,把每个图片展示在相应的位置,需要使用imshow来展示图片。

并观察以下是否能看出想个相邻图片之间有一些共性?

random_50 = np.random.randint(5000,size=50)
X_train4 = X_train2[random_50]
second_pca= PCA(n_components=2)
second_pca.fit_transform(X_train4)
plt.subplot(111)
#plt.plot(X_train4[0],X_train4[1])
plt.imshow(X_train4)
plt.show()

5 使用神经网络识别图片

在这里,我们将尝试使用神经网络来识别图片。我们将会搭建包含一层隐含层的神经网络(多层就意味着是深度神经网络)。这里使用的数据为特征提取之后的数据(HOG, color histogram)。

创建神经网络模型(叫做multilayer perceptron), 并制定模型的参数。 具体神经网络的使用参考: https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html#sklearn.neural_network.MLPClassifier 在这里我们需要调整的参数为:

  1. hidden_layer_sizes: 隐含层神经元的个数,这个值越大模型越容易过拟合,但这个值越小模型的准确率就不高
  2. activation: 'logistic': 这是激活函数,可以让模型拥有非线性。有几个常见的选项个如'logistic', 'relu'等。我们在这里使用'logistic'为激活函数
    # 利用sklearn来实现神经网络模型,并使用交叉验证来选出最好的参数hidden_layer_size
    #  训练数据:(X_train2, y_train)  测试数据: (X_test2, y_test), 直接使用上面已经构建好的特征。 
    from sklearn.neural_network import MLPClassifier
    import warnings
    
    warnings.filterwarnings("ignore")
    params_hidden_layer_sizes = [10,20,30,40,50]
    activation = 'logistic'
    params_grid={"hidden_layer_sizes":params_hidden_layer_sizes}
    model = GridSearchCV(estimator=MLPClassifier(activation = activation),param_grid=params_grid,cv=5)
    model.fit(X_train2,y_train)
    
    # 输出最好的K和p值 
    print(model.best_params_)
    
    # 输出在测试集上的准确率
    
    predict_model = model.best_estimator_
    y_pre = predict_model.predict(X_test2)
    correct = np.count_nonzero((y_pre==y_test)==True)
    print("Predict precision is: ",correct/len(y_test))

     

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值