标题

KNN算法

  1. KNN算法简介
      KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。
      说明:KNN没有显示的训练过程,它是“懒惰学习”的代表,它在训练阶段只是把数据保存下来,训练时间开销为0,等收到测试样本后进行处理。
  2. 实验过程
    首先,创建两个不同的二维点集,每个点集有两类,用pickle模块保存,用不同的保存文件运行该脚本两次,得到4个二维数据集文件,每个分布都有两个文件:一个用作训练,一个用作测试。
    代码如下:
# -*- coding: utf-8 -*-
from numpy.random import randn
import pickle
from pylab import *

# create sample data of 2D points
n = 200
# two normal distributions两个正态分布的数据集
class_1 = 0.6 * randn(n,2)
class_2 = 1.2 * randn(n,2) + array([5,1])
labels = hstack((ones(n),-ones(n)))
# save with Pickle
#with open('points_normal.pkl', 'w') as f:
#用pickle模块保存
with open('points_normal_test.pkl', 'wb') as f:
    pickle.dump(class_1,f)
    pickle.dump(class_2,f)
    pickle.dump(labels,f)
# normal distribution and ring around it
print("save OK!")
#正态分布,并使数据成环绕状分布
class_1 = 0.6 * randn(n,2)
r = 0.8 * randn(n,1) + 5
angle = 2*pi * randn(n,1)
class_2 = hstack((r*cos(angle),r*sin(angle)))
labels = hstack((ones(n),-ones(n)))
# save with Pickle
#with open('points_ring.pkl', 'w') as f:
with open('points_ring_test.pkl', 'wb') as f:
    pickle.dump(class_1,f)
    pickle.dump(class_2,f)
    pickle.dump(labels,f)
    
print ("save OK!")

之后,用Pickle模块创建一个kNN分类器模型。载入上面所保存的一个数据集,并测试数据中的数据点。并将分类结果可视化。

# -*- coding: utf-8 -*-
import pickle
from pylab import *
from PCV.classifiers import knn
from PCV.tools import imtools

pklist=['points_normal.pkl','points_ring.pkl']

figure()

# load 2D points using Pickle 用Pickle载入二维数据点
for i, pklfile in enumerate(pklist):
    with open(pklfile, 'rb') as f:
        class_1 = pickle.load(f)
        class_2 = pickle.load(f)
        labels = pickle.load(f)
    # load test data using Pickle 用Pickle载入测试数据
    with open(pklfile[:-4]+'_test.pkl', 'rb') as f:
        class_1 = pickle.load(f)
        class_2 = pickle.load(f)
        labels = pickle.load(f)

    model = knn.KnnClassifier(labels,vstack((class_1,class_2)))
    # test on the first point
    print (model.classify(class_1[0]))

    #define function for plotting
    def classify(x,y,model=model):
        return array([model.classify([xx,yy]) for (xx,yy) in zip(x,y)])

    # lot the classification boundary
    subplot(1,2,i+1)
    imtools.plot_2D_boundary([-6,6,-6,6],[class_1,class_2],classify,[1,-1])
    titlename=pklfile[:-4]
    title(titlename)
show()
  1. 实现结果
    画出来的结果如图所示,用K临近分类器分类二维数据。每个示例中,不同颜色代表不同的类,正确的用星号“*”表示,错误的用原点“o”表示,曲线是分类器的决策边界。由此可以看出,kNN决策边界适用于没有任何明确模型的类分布。
    3.1 当参数k=5时,通过改变点的数量n:
     n=100时:

      n=200时:

      n=500时(跑的非常慢,电脑基本快要炸掉了):


    3.2 取n=200,改变k的值
    k=3:

    k=5:

    k=9:

    k=13:

    可以看出,随着k值不断增大,K临近分类器出现错误的点也不断增多,所以k的取值越小,分类的结果也就越好(但也不能太小,k取3~5时为佳)。

稠密SIFT特征向量

  1. 稠密SIFT特征向量
    该算法首先将表达目标的矩形区域分成相同大小的矩形块,计算每一个小块的SIFT特征,再对各个小块的稠密SIFT特征在中心位置进行采样,建模目标的表达.然后度量两个图像区域的不相似性,先计算两个区域对应小块的距离,再对各距离加权求和作为两个区域间的距离.因为目标所在区域靠近边缘的部分可能受到背景像素的影响,而区域的内部则更一致,所以越靠近区域中心权函数的值越大。
  2. 实验过程
    利用下面的代码可以计算稠密SIFT描述子,并可视化他们的位置:
    输入图像,计算出dsift描述子,
# -*- coding: utf-8 -*-
from PCV.localdescriptors import sift, dsift
from pylab import  *
from PIL import Image

dsift.process_image_dsift('gesture/empire.jpg','empire.dsift',90,40,True)
l,d = sift.read_features_from_file('empire.dsift')
im = array(Image.open('gesture/empire.jpg'))
sift.plot_features(im,l,True)
title('dense SIFT')
show()
  1. 实验结果

    尚大楼:

图像分类:手势分类

  1. 图像集:本次试验用稠密SIFT描述子来表示这些手势图像,并建立一个简单的手势识别系统。本次试验的数据库为自己拍摄的静态手势:
    1.1 集美大学手势“JMU”:

    1.2 “ok”:

    1.3 非常6+1:

    1.4 数字“4”:

先将图片进行批处理,像素改为50×50:

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_样本集


生成图片dsift描述子并可视化:

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_Pickle_02


将4类手势各18张(共72张)图片进行训练,其余图片进行测试,测试结果:

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_Pickle_03


正确率为88.37%

其中,手势“4”、手势“6+1”、手势“ok”测试结果全部正确,只有手势“jmu”只有两张正确,是因为训练集和测试集中的图片背景颜色不同,由于训练集中手势“jmu”的图片中黑色背景较多,导致测试集中只有“jmu”是黑色背景图片的被判定为是正确的,其余背景被判定为其他手势。

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_Pickle_04

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_利用cnn和rnn视频分类的解决方法_05

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_样本集_06

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_Pickle_07


将图片换为相同背景:

利用cnn和rnn视频分类的解决方法 基于cnn的图像分类算法_Pickle_08


正确率达到97.56%,只有一张判断错误。

因此,训练集和测试集的照片背景应尽可能拍摄同一色调,才不会因为背景导致识别出错。

附代码:

# -*- coding: utf-8 -*-
from PCV.localdescriptors import dsift
import os
from PCV.localdescriptors import sift
from pylab import *
from PCV.classifiers import knn

def get_imagelist(path):
    """    Returns a list of filenames for
        all jpg images in a directory. """

    return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]

def read_gesture_features_labels(path):
    # create list of all files ending in .dsift
    featlist = [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.dsift')]
    # read the features
    features = []
    for featfile in featlist:
        l,d = sift.read_features_from_file(featfile)
        features.append(d.flatten())
    features = array(features)
    # create labels
    labels = [featfile.split('/')[-1][0] for featfile in featlist]
    return features,array(labels)

def print_confusion(res,labels,classnames):
    n = len(classnames)
    # confusion matrix
    class_ind = dict([(classnames[i],i) for i in range(n)])
    confuse = zeros((n,n))
    for i in range(len(test_labels)):
        confuse[class_ind[res[i]],class_ind[test_labels[i]]] += 1
    print ('Confusion matrix for')
    print (classnames)
    print (confuse)

filelist_train = get_imagelist('gesture/mytrain/')
filelist_test = get_imagelist('gesture/mytest/')
imlist=filelist_train+filelist_test

# process images at fixed size (50,50)
for filename in imlist:
    featfile = filename[:-3]+'dsift'
    dsift.process_image_dsift(filename,featfile,10,5,resize=(50,50))



features,labels = read_gesture_features_labels('gesture/mytrain/')
test_features,test_labels = read_gesture_features_labels('gesture/mytest/')
classnames = unique(labels)

# test kNN
k = 1
knn_classifier = knn.KnnClassifier(labels,features)
res = array([knn_classifier.classify(test_features[i],k) for i in
range(len(test_labels))])
# accuracy
acc = sum(1.0*(res==test_labels)) / len(test_labels)
print ('Accuracy:', acc)

print_confusion(res,test_labels,classnames)