监督学习——使用OpenCV实现k-NN
使用OpenCV,通过cv2.ml.KNearest_create
函数创建k-NN模型,分以下步骤进行:
- 生成一些训练数据
- 指定k值,创建一个k-NN对象
- 找到想要分类的新数据点的k个最近邻的点
- 使用多数投票来分配新数据点的类标签
- 画出结果图
引入所须的模块:
使用k-NN算法的OpenCV、处理数据的NumPy、用于绘图的Matplotlib。
本次使用的Jupyter Notebook,需调用%matplotlib inline
命令。
import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot') #画图风格选择
若出现No module named 'cv2'
错误,则说明未安装该模块,需在命令提示符终端输入下方代码
pip install opencv-python
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
下载过程中若报错,则输入下方代码更新pip版本
python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
生成训练数据
定义生成数据的函数,输入是要生成的数据点个数(num_samples
)和每个数据点的特征数(num_features
),所创建的数据矩阵有num_samples行、num_features列,其中每一个元素都为[0,100]范围内的一个随机整数,并创建一个所有样本在[0,2]单位内的随机整数标签值(0或1)。
def generate_data(num_samples, num_features=2):
"""Randomly generates a number of data points"""
data_size = (num_samples, num_features)
train_data = np.random.randint(0, 100, size=data_size)
labels_size = (num_samples, 1)
labels = np.random.randint(0, 2, size=labels_size)
return train_data.astype(np.float32), labels
举例生成11个数据点,并随机选择它们的坐标
train_data, labels = generate_data(11)
train_data #显示生成的数据
定义显示所有训练数据集的函数,输入为一个所有蓝色正方形的数据点列表(all_blue
)和一个所有红色三角形的数据点列表(all_red
):
def plot_data(all_blue, all_red):
plt.figure(figsize=(10, 6))
plt.scatter(all_blue[:, 0], all_blue[:, 1], c='b', marker='s', s=180)
plt.scatter(all_red[:, 0], all_red[:, 1], c='r', marker='^', s=180)
plt.xlabel('x coordinate (feature 1)')
plt.ylabel('y coordinate (feature 2)')
将所有数据点分成红色数据集和蓝色数据集,使用下面命令可快速选择之前创建的labels
数组中所有等于0的元素:
labels.ravel() == 0 #ravel平面化数组
将所创建train_data
中对应标签为0的设置为所有蓝色数据点,标签为1的设为所有红色数据点,画出数据点:
blue = train_data[labels.ravel() == 0]
red = train_data[labels.ravel() == 1]
plot_data(blue, red)
训练分类器
k-NN分类器为OpenCV中ml
模块的一部分,使用以下命令来创建一个新的分类器:
knn = cv2.ml.KNearest_create()
把训练数据传入到train方法中:
knn.train(train_data, cv2.ml.ROW_SAMPLE, labels)
预测新数据点的类别
knn提供的findNearest
方法很有用,可根据最近邻数据点的标签来预测新数据点的标签。
用generate_data
函数来生成一个新的数据点,其也会返回一个随机类别,可通过下划线 (_
)让python忽略输出值。
newcomer, _ = generate_data(1)
newcomer #array([[20., 36.]], dtype=float32)
将新的数据点用绿色的圆圈表示(待确定标签),加入图中显示:
plot_data(blue, red)
plt.plot(newcomer[0, 0], newcomer[0, 1], 'go', markersize=14)
调用findNearest
方法设置k=1,显示预测类别、最近邻点类别及距离:
ret, results, neighbor, dist = knn.findNearest(newcomer, 1)
print("Predicted label:\t", results) #[[0.]]
print("Neighbor's label:\t", neighbor) #[[0.]]
print("Distance to neighbor:\t", dist) #[[290.]]
同理,设置k=6。而当k为偶数时,会有一定概率出现类别平局的现象,此时,OpenCV的k-NN将会选择数据点整体距离更近的邻居。
ret, results, neighbors, dist = knn.findNearest(newcomer, 6)
print("Predicted label:\t", results) #[[1.]]
print("Neighbors' labels:\t", neighbors) #[[0. 1. 1. 1. 0. 1.]]
print("Distance to neighbors:\t", dist) #[[ 290. 1061. 1066. 2132. 2218. 2873.]]
扩大搜索范围,设置k=7,也可用以下代码预测类别:
knn.setDefaultK(7)
knn.predict(newcomer) #(1.0, array([[1.]], dtype=float32))
而随着k值增大,其预测类别可能会发生变化。对此最简单的解决方法是尝试一组k值,并观察哪个的结果最好,具体问题具体分析。
参考书籍:《机器学习:使用OpenCV和Python进行智能图像处理》