最近邻分类器(Nearest Neighbor Classifier)
先从简单的方法开始说,先提一提最近邻分类器/Nearest Neighbor Classifier,不过事先申明,它和深度学习中的卷积神经网/Convolutional Neural Networks其实一点关系都没有,我们只是从基础到前沿一点一点推进,最近邻是图像识别一个相对简单和基础的实现方式。
1.1 CIFAR-10
CIFAR-10是一个非常常用的图像分类数据集。数据集包含60000张32*32像素的小图片,每张图片都有一个类别标注(总共有10类),分成了50000张的训练集和10000张的测试集。如下是一些图片示例:
上图中左边是十个类别和对应的一些示例图片,右边是给定一张图片后,根据像素距离计算出来的,最近的10张图片。
1.2 基于最近邻的简单图像类别判定
假如现在用CIFAR-10数据集做训练集,判断一张未知的图片属于CIFAR-10中的哪一类,应该怎么做呢。一个很直观的想法就是,既然我们现在有每个像素点的值,那我们就根据输入图片的这些值,计算和训练集中的图片距离,找最近的图片的类别,作为它的类别,不就行了吗。
恩,想法很直接,这就是『最近邻』的思想。偷偷说一句,这种直接的做法在图像识别中,其实效果并不是特别好。比如上图是按照这个思想找的最近邻,其实只有3个图片的最近邻是正确的类目。
即使这样,作为最基础的方法,还是得掌握,我们来简单实现一下吧。我们需要一个图像距离评定准则,比如最简单的方式就是,比对两个图像像素向量之间的l1距离(也叫曼哈顿距离/cityblock距离),公式如下:
其实就是计算了所有像素点之间的差值,然后做了加法,直观的理解如下图:
我们先把数据集读进内存:
#! /usr/bin/env python
#coding=utf-8
import os
import sys
import numpy as np
def load_CIFAR_batch(filename):
"""
cifar-10数据集是分batch存储的,这是载入单个batch
@参数 filename: cifar文件名
@r返回值: X, Y: cifar batch中的 data 和 labels
"""
with open(filename, 'r') as f:
datadict=pickle.load(f)
X=datadict['data']
Y=datadict['labels']
X=X.reshape(10000, 3, 32, 32).transpose(0,2,3,1).astype("float")
Y=np.array(Y)
return X, Y
def load_CIFAR10(ROOT):
"""
读取载入整个 CIFAR-10 数据集
@参数 ROOT: 根目录名
@return: X_train, Y_train: 训练集 data 和 labels
X_test, Y_test: 测试集 data 和 labels
"""
xs=[]
ys=[]
for b in range(1,6):
f=os.path.join(ROOT, "data_batch_%d" % (b, ))
X, Y=load_CIFAR_batch(f)
xs.append(X)
ys.append(Y)
X_train=np.concatenate(xs)
Y_train=np.concatenate(ys)
del X, Y
X_test, Y_test=load_CIFAR_batch(os.path.join(ROOT, "test_batch"))
return X_train, Y_train, X_test, Y_test
# 载入训练和测试数据集
X_train, Y_train, X_test, Y_test = load_CIFAR10('data/cifar10/')
# 把32*32*3的多维数组展平
Xtr_rows = X_train.reshape(X_train.shape[0], 32 * 32 * 3) # Xtr_rows : 50000 x 3072
Xte_rows = X_test.reshape(X_test.shape[0], 32 * 32 * 3) # Xte_rows : 10000 x 3072
下面我们实现最近邻的思路:
class NearestNeighbor:
def __init__(self):
pass
def train(self, X, y):
"""
这个地方的训练其实就是把所有的已有图片读取进来 -_-||
"""
# the nearest neighbor classifier simply remembers all the training data
self.Xtr = X
self.ytr = y
def predict(self, X):
"""
所谓的预测过程其实就是扫描所有训练集中的图片,计算距离,取最小的距离对应图片的类目
"""
num_test = X.shape[0]
# 要保证维度一致哦
Ypred = np.zeros(num_test, dtype = self.ytr.dtype)
# 把训练集扫一遍 -_-||
for i in xrange(num_test):
# 计算l1距离,并找到最近的图片
distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
min_index = np.argmin(distances) # 取最近图片的下标
Ypred[i] = self.ytr[min_index] # 记录下label
return Ypred
nn = NearestNeighbor() # 初始化一个最近邻对象
nn.train(Xtr_rows, Y_train) # 训练...其实就是读取训练集
Yte_predict = nn.predict(Xte_rows) # 预测
# 比对标准答案,计算准确率
print 'accuracy: %f' % ( np.mean(Yte_predict == Y_test) )
最近邻的思想在CIFAR上得到的准确度为38.6%,我们知道10各类别,我们随机猜测的话准确率差不多是1/10=10%,所以说还是有识别效果的,但是显然这距离人的识别准确率(94%)实在是低太多了,不那么实用。
1.3 关于最近邻的距离准则
我们这里用的距离准则是l1距离,实际上除掉l1距离,我们还有很多其他的距离准则。
- 比如说l2距离(也就是大家熟知的欧氏距离)的计算准则如下:
- 比如余弦距离计算准则如下:
更多的距离准则可以参见scipy相关计算页面.
2. K最近邻分类器(K Nearest Neighbor Classifier)
这是对最近邻的思想的一个调整。其实我们在使用最近邻分类器分类,扫描CIFAR训练集的时候,会发现,有时候不一定距离最近的和当前图片是同类,但是最近的一些里有很多和当前图片是同类。所以我们自然而然想到,把最近邻扩展为最近的N个临近点,然后统计一下这些点的类目分布,取最多的那个类目作为自己的类别。
恩,这就是KNN的思想。
KNN其实是一种特别常用的分类算法。但是有个问题,我们的K值应该取多少呢。换句话说,我们找多少邻居来投票,比较靠谱呢?
3.1 交叉验证与参数选择
在现在的场景下,假如我们确定使用KNN来完成图片类别识别问题。我们发现有一些参数是肯定会影响最后的识别结果的,比如:
- 距离的选择(l1,l2,cos等等)
- 近邻个数K的取值。
每组参数下其实都能产生一个新的model,所以这可以视为一个模型选择/model selection问题。而对于模型选择问题,最常用的办法就是在交叉验证集上实验。
数据总量就那么多,如果我们在test data上做模型参数选择,又用它做效果评估,显然不是那么合理(因为我们的模型参数很有可能是在test data上过拟合的,不能很公正地评估结果)。所以我们通常会把训练数据分为两个部分,一大部分作为训练用,另外一部分就是所谓的cross validation数据集,用来进行模型参数选择的。比如说我们有50000训练图片,我们可以把它分为49000的训练集和1000的交叉验证集。
# 假定已经有Xtr_rows, Ytr, Xte_rows, Yte了,其中Xtr_rows为50000*3072 矩阵
Xval_rows = Xtr_rows[:1000, :] # 构建1000的交叉验证集
Yval = Ytr[:1000]
Xtr_rows = Xtr_rows[1000:, :] # 保留49000的训练集
Ytr = Ytr[1000:]
# 设置一些k值,用于试验
validation_accuracies = []
for k in [1, 3, 5, 7, 10, 20, 50, 100]:
# 初始化对象
nn = NearestNeighbor()
nn.train(Xtr_rows, Ytr)
# 修改一下predict函数,接受 k 作为参数
Yval_predict = nn.predict(Xval_rows, k = k)
acc = np.mean(Yval_predict == Yval)
print 'accuracy: %f' % (acc,)
# 输出结果
validation_accuracies.append((k, acc))
这里提一个在很多地方会看到的概念,叫做k-fold cross-validation,意思其实就是把原始数据分成k份,轮流使用其中k-1份作为训练数据,而剩余的1份作为交叉验证数据(因此其实对于k-fold cross-validation我们会得到k个accuracy)。以下是5-fold cross-validation的一个示例:
以下是我们使用5-fold cross-validation,取不同的k值时,得到的accuracy曲线(补充一下,因为是5-fold cross-validation,所以在每个k值上有5个取值,我们取其均值作为此时的准确度)
可以看出大概在k=7左右有最佳的准确度。
3.2 最近邻方法的优缺点
K最近邻的优点大家都看出来了,思路非常简单清晰,而且完全不需要训练…不过也正因为如此,最后的predict过程非常耗时,因为要和全部训练集中的图片比对一遍。
实际应用中,我们其实更加关心实施predict所消耗的时间,如果有一个图像识别app返回结果要半小时一小时,你一定第一时间把它卸了。我们反倒不那么在乎训练时长,训练时间稍微长一点没关系,只要最后应用的时候识别速度快效果好,就很赞。后面会提到的深度神经网络就是这样,深度神经网络解决图像问题时训练是一个相对耗时间的过程,但是识别的过程非常快。
另外,不得不多说一句的是,优化计算K最近邻时间问题,实际上依旧到现在都是一个非常热门的问题。Approximate Nearest Neighbor (ANN)算法是牺牲掉一小部分的准确度,而提高很大程度的速度,能比较快地找到近似的K最近邻,现在已经有很多这样的库,比如说FLANN.
最后,我们用一张图来说明一下,用图片像素级别的距离来实现图像类别识别,有其不足之处,我们用一个叫做t-SNE的技术把CIFAR-10的所有图片按两个维度平铺出来,靠得越近的图片表示其像素级别的距离越接近。然而我们瞄一眼,发现,其实靠得最近的并不一定是同类别的。
其实观察一下,你就会发现,像素级别接近的图片,在整张图的颜色分布上,有很大的共性,然而在图像内容上,有时候也只能无奈地呵呵嗒,毕竟颜色分布相同的不同物体也是非常多的。
最近邻分类器(Nearest Neighbor Classifier)相关推荐
- [论文阅读] Nearest Neighbor Classifier Embedded Network for Active Learning
论文地址:https://www.aaai.org/AAAI21Papers/AAAI-39.WanF.pdf 代码:https://github.com/WanFang13/NCE-Net 发表于: ...
- 数字图像处理笔记二 - 图片缩放(最近邻插值(Nearest Neighbor interpolation))
图片缩放的两种常见算法: 最近邻域内插法(Nearest Neighbor interpolation) 双向性内插法(bilinear interpolation) 本文主要讲述最近邻插值(Near ...
- 最近邻搜索|Nearest neighbor search
维基百科:https://en.wikipedia.org/wiki/Nearest_neighbor_search 觉得整理的挺好,翻译 最近邻搜索( NNS ) 作为**邻近搜索(proximit ...
- 论文阅读“Nearest Neighbor Matching for Deep Clustering”(CVPR2021)
论文标题 Nearest Neighbor Matching for Deep Clustering 论文作者.链接 作者:Dang, Zhiyuan and Deng, Cheng and Yang ...
- 最近邻插值(nearest neighbor)-----python
图像内插是在诸如放大.收缩.旋转和几何校正等任务中广泛应用的基本工具.从根本上来看,内插是用已知数据来估计未知位置的数值的处理. 最近邻内插法(nearest neighbor):假设一幅大小为500 ...
- 机器学习之深入理解K最近邻分类算法(K Nearest Neighbor)
[机器学习]<机器学习实战>读书笔记及代码:第2章 - k-近邻算法 1.初识 K最近邻分类算法(K Nearest Neighbor)是著名的模式识别统计学方法,在机器学习分类算法中占有 ...
- Simple and Effective Few-Shot Named Entity Recognition with Structured Nearest Neighbor Learning
1. abstract 我们提出了一个基于最近邻学习和结构化推理的简单few-shot命名实体识别(NER)系统.我们的系统使用在源域上训练的监督NER模型作为特征提取器.在多个测试域中,我们发现在这 ...
- EMNLP20 - Simple and effective few-shot named entity recognition with structured nearest neighbor le
文章目录 Abstract Introduction Problem Statement and Setup Model Nearest neighbor classification for few ...
- 文献记录(part90)--A novel density-based clustering algorithm using nearest neighbor graph
学习笔记,仅供参考,有错必纠 关键词:基于密度的聚类:最近邻图:DBSCAN 文章目录 A novel density-based clustering algorithm using nearest ...
最新文章
- 滴滴CTO张博:我人生重要的四次选择
- Java的Kafka:构建安全,可扩展的消息传递应用程序
- 【存储过程】MySQL存储过程/存储过程与自定义函数的区别
- reg型变量怎么赋值_UiPath变量介绍和使用
- OpenCV学习笔记(十六):直方图均衡化:equalizeHist()
- Applications Manager应用性能透视能力--Java Web事务监控
- 砸入近 30 亿美元后,马斯克拒绝加入 Twitter 董事会
- php使用memcached缓存总结
- 林子雨-2.3 面向对象编程基础
- awgn信道matlab,AWGN信道下数字通信系统的蒙特卡洛仿真(基于matlab).doc
- Sketch for mac|矢量绘图设计
- 在Windows中安装配置JDK+Eclipse+Maven
- b5纸尺寸_标准a2纸尺寸是多少厘米,设计宣传手册尺寸有哪些
- 树莓派Raspberry Pi Pico开发板踩坑:重置后设备管理器各种不识别,未知设备/unknown device
- 网络安全防护之主机病毒查杀
- deepin firewall
- mac mysql mysqldb_在 Mac 中安装 MySQLdb (Python mysql )
- Druid数据库密码加密 包含单数据源密码加密,多数据源密码加密详细配置
- OpenFlow协议初探——OpenFLow中的流和流表
- 计算机专业硕士北欧,北欧哪个国家计算机专业比较好
热门文章
- Libgdx粒子效果介绍与使用心得
- pb rows changed between retrieve and update的问题
- java经纬度凸包graham_凸包算法(Graham扫描法)详解
- 阿里云推出“通达云OA”办公系统 基于钉钉的移动OA应用
- Android——扩大ImageButton的点击区域
- win10常用dos命令
- opencv-python 读取视频时如何判断读取到最后一张
- u盘一插上计算机就不响应,u盘插上没反应解决方法
- 家庭教育机构如何线上线下灵活结合?
- ccd坏点测试软件,如何检测CCDLCD坏点