机器学习1

理解kNN

kNN的理论

kNN是可用于监督学习的最简单的分类算法之一,这个想法是在特征空间中搜索测试数据的最近邻。将用下面的图片来研究它。
在这里插入图片描述
在图像中,有两个族,蓝色正方形和红色三角形,称每一种为。它们的房屋显示在它们的城镇地图中,称之为特征空间(可以将要素空间视为投影所有数据的空间。例如,考虑一个2D坐标空间,每个数据都有两个要素,x和y坐标。可以在2D坐标空间中表示此数据,对吧?现在假设如果有三个要素,则需要3D空间;现在考虑N个要素,需要N维空间,对吗?这个N维空间就是其要素空间。在上面图像中,可以将其视为2D情况。有两个功能)。
现在有一个新成员进入城镇并创建了一个新房屋,显示为绿色圆圈;它应该被添加到这些蓝色/红色家族之一当中,称该过程为分类。可应用kNN算法。
一种方法是检查谁是它的最近邻,从图像中可以明显看出它是红色三角形家族;因此,它也被添加到了红色三角形中。此方法简称为最近邻,因为分类仅取决于最近邻。
但这是有问题的,红三角可能是最近的;但是,如果附近有很多蓝色方块怎么办?然后,蓝色方块在该地区的权重比红色三角更大;因此,仅检查最接近的一个是不够的。相反,检查一些k近邻的族。那么,无论谁占多数,新样本都属于那个族。在上面图像中,取k=3,即3个最近族;有两个红色和一个蓝色(有两个等距的蓝色,但是由于k = 3,只取其中一个),所以它应该加入红色家族。但是,如果取k=7怎么办?然后,有5个蓝色族和2个红色族,它应该加入蓝色族。因此,所有这些都随k的值而变化。更有趣的是,如果k=4怎么办?他有2个红色邻居和2个蓝色邻居,这是一个平滑;因此最好将k作为奇数。由于分类取决于k个最近的邻居,因此该方法称为k近邻
在kNN中,确实在考虑k个邻居,但对所有邻居都给予同等的重视,对吧?这公平吗?例如,以k=4的情况为例,说这是平局;但是注意,这两个红色族比其他两个蓝色族离它更近;因此,它更应该被添加到红色。那么如何用数学解释呢?可根据每个家庭到新来者的距离来给它们一些权重,对于那些靠近它的人,权重增加;而那些远离它的人,权重减轻。然后,分别添加每个族的总权重,谁得到的总权重最高,新样本归为那一族。这称为modified kNN
那么在这里看到的一些重要内容是什么?

  1. 需要了解镇上所有房屋的信息,对吗?因为,必须检查新样本到所有现有房屋的距离,以找到最近的邻居。如果有许多房屋和家庭,则需要大量的内存,并且需要更多的时间进行计算。
  2. 几乎没有时间进行任何形式的训练或准备。

OpenCV中的kNN

就像上面一样,将在这里做一个简单的例子,有两个族(类)。
因此在这里,将红色系列标记为Class-0(因此用0表示),将蓝色系列标记为Class-1(用1表示)。创建25个族或25个训练数据,并将它们标记为0类或1类。借助Numpy中的Random Number Generator来完成所有这些工作。
然后在Matplotlib的帮助下对其进行绘制。红色系列显示为红色三角形,蓝色系列显示为蓝色正方形。
之后会得到与上面那张图片相似的东西,由于使用的是随机数生成器,因此每次运行代码都将获得不同的数据。
接下来启动kNN算法,并传递trainData和响应以训练kNN(它会构建搜索树)。
然后,将在OpenCV中的kNN的帮助下将一个新样本带入一个族并将其分类。在进入kNN之前,需要了解测试数据(新样本数据)上的知识。数据应为浮点数组,其大小为 n u m b e r   o f   t e s t d a t a × n u m b e r   o f   f e a t u r e s number\ of\ testdata\times number\ of\ features number of testdata×number of features;然后找到新加入的最近邻。可以指定想要多少个邻居。它返回:

  1. 给新样本的标签取决于之前看到的kNN理论。如果要使用“最近邻居”算法,只需指定k=1即可,其中k是邻居数。
  2. k最近邻的标签。
  3. 衡量新加入到每个最近邻的相应距离。

因此,看看它是如何工作的,新样本被标记为绿色。
knn.py

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 包含(x, y)值的25个已知/训练数据的特征集
trainData = np.random.randint(0, 100, (25, 2)).astype(np.float32)
# 用数字0和1分别标记红色或蓝色
responses = np.random.randint(0, 2, (25, 1)).astype(np.float32)
# 取红色族并绘图
red = trainData[responses.ravel() == 0]
plt.scatter(red[:, 0], red[:, 1], 80, 'r', '^')
# 取蓝色族并绘图
blue = trainData[responses.ravel() == 1]
plt.scatter(blue[:, 0], blue[:, 1], 80, 'b', 's')
newcomer = np.random.randint(0, 100, (1, 2)).astype(np.float32)
plt.scatter(newcomer[:, 0], newcomer[:, 1], 80, 'g', 'o')
knn = cv.ml.KNearest.create()
knn.train(trainData, cv.ml.ROW_SAMPLE, responses)
ret, results, neighbours, dist = knn.findNearest(newcomer, 3)
print('result: {}\n'.format(results))
print('neighbours: {}\n'.format(neighbours))
print('distance: {}\n'.format(dist))
plt.show()
result: [[1.]]

neighbours: [[1. 1. 0.]]

distance: [[ 45.  97. 106.]]

在这里插入图片描述
从输出和结果图可以看出,新样本有3个近邻,2个来自Blue家族,1个来自red家族;因此,它被标记为blue家族。

使用OCR手写数据集运行kNN

手写数字的OCR

构建一个可以读取手写数字的应用程序。为此,需要一些train_data和test_data。OpenCV带有一个图片digits.png(在文件夹opencv/samples/data/中),其中包含5000个手写数字(每个数字500个),每个数字都是20x20的图像。因此,第一步是将图像分割成5000个不同的数字;对于每个数字,将其展平为400像素的一行;那就是训练集,即所有像素的强度值。这是可以创建的最简单的特征集,将每个数字的前250个样本用作train_data,然后将250个样本用作test_data。
基本OCR应用程序已准备就绪,这个特定的例子的准确性是91.76%。一种提高准确性的选择是添加更多数据进行训练,尤其是错误的数据。因此,与其每次启动应用程序时都找不到该训练数据,不如将其保存,以便下次直接从文件中读取此数据并开始分类;可以借助一些Numpy函数(例如np.savetxt,np.savez,np.load等)来完成此操作。
它需要3.82 MB的内存。由于使用强度值(uint8数据)作为特征,因此最好先将数据转换为np.uint8,然后再将其保存;在这种情况下,仅占用0.96 MB;然后在加载时,可以转换回float32。
digit_ocr.py

import cv2 as cv
import numpy as np

img = cv.imread('./OpenCV/data/digits.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 将图像分割为5000个单元格,每个单元格为20 * 20
cells = [np.hsplit(row, 100) for row in np.vsplit(gray, 50)]
# 使其成为一个numpy数组,它的大小将是(50, 100, 20, 20)
x = np.array(cells)
# 准备train_data和test_data
train = x[:, :50].reshape(-1, 400).astype(np.float32)   #size = (2500, 400)
test = x[:, 50:100].reshape(-1, 400).astype(np.float32)   #size = (2500, 400)
# 为训练和测试数据创建标签
k = np.arange(10)
train_labels = np.repeat(k, 250)[:, np.newaxis]
test_labels = train_labels.copy()
# 初始化kNN,训练数据,然后使用k = 1的测试数据对其进行测试
knn = cv.ml.KNearest.create()
knn.train(train, cv.ml.ROW_SAMPLE, train_labels)
ret, result, neighbours, dist = knn.findNearest(test, k = 5)
# 检查分类的准确性。为此,将结果与test_labels进行比较,并检查哪个错误
matches = result == test_labels
correct = np.count_nonzero(matches)
accuracy = correct * 100.0 / result.size
print('accuracy: {}'.format(accuracy))
# 保存数据
np.savez('./OpenCV/data/knn_data.npz', train = train, train_labels = train_labels)
train_ui8 = train.astype(np.uint8)
train_labels_ui8 = train_labels.astype(np.uint8)
np.savez('./OpenCV/data/knn_data_ui8.npz', train = train_ui8, train_labels = train_labels_ui8)
# 加载数据
with np.load('./OpenCV/data/knn_data_ui8.npz') as data:
    print(data.files)
    train = data['train'].astype(np.float32)
    train_labels = data['train_labels'].astype(np.float32)
img2 = cv.imread('./OpenCV/data/8.png')
gray2 = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)
# gray2 = cv.resize(gray2, (20, 20), interpolation=cv.INTER_AREA)
test2 = np.array(gray2)
test2 = test2[:].reshape(-1, 400).astype(np.float32)
ret, result, neighbours, dist = knn.findNearest(test2, k = 30)
print('result: {}'.format(result))
print('neighbours: {}'.format(neighbours))
print('distance: {}'.format(dist))
accuracy: 91.76
['train', 'train_labels']
result: [[8.]]
neighbours: [[3. 8. 3. 8. 9. 8. 8. 7. 7. 8. 9. 9. 9. 2. 7. 2. 8. 5. 8. 9. 8. 9. 2. 7.     
  4. 8. 8. 8. 2. 0.]]
distance: [[1915472. 1999569. 2076609. 2085674. 2255547. 2294900. 2321767. 2323825.       
  2346767. 2366127. 2461850. 2487083. 2496366. 2513264. 2528853. 2531979.
  2539941. 2545010. 2550629. 2563323. 2590417. 2603583. 2608946. 2611357.
  2619456. 2625735. 2632547. 2641665. 2648350. 2658311.]]

8.png
在这里插入图片描述

英文字母的OCR

对英语字母执行相同的操作,但是数据和特征集会稍有变化。在这里,OpenCV代替了图像,而在opencv/samples/cpp/文件夹中附带了一个数据文件letter-recognitiontion.data。如果打开它,将看到20000行,乍一看可能看起来像垃圾;实际上,在每一行中,第一列是一个字母,这是标签;接下来的16个数字是它的不同特征。这些特征是从UCI机器学习存储库获得的,可以在此页面中找到这些特征的详细信息。 现有20000个样本,可以将前10000个数据作为训练样本,其余10000个作为测试样本。应该将字母更改为ASCII字符,因为不能直接使用字母。
letter_ocr.py

import cv2 as cv
import numpy as np

# 加载数据,转换器将字母转换为数字
data = np.loadtxt('./OpenCV/data/letter-recognition.data', dtype='float32',
                    delimiter=',', converters={0: lambda ch: ord(ch) - ord('A')})
# 将数据分为两部分,每部分10000个进行训练和测试
train, test = np.vsplit(data, 2)
# 将训练数据和测试数据拆分为特征和响应
response, trainData = np.hsplit(train, [1])
labels, testData = np.hsplit(test, [1])
# 初始化kNN,分类,测量准确性
knn = cv.ml.KNearest.create()
knn.train(trainData, cv.ml.ROW_SAMPLE, response)
ret, result, neighbours, dist = knn.findNearest(testData, k=5)
correct = np.count_nonzero(result == labels)
accuracy = correct * 100.0 / result.size
print('accuracy: {}'.format(accuracy))
accuracy: 93.06

准确性为93.06%。同样,如果要提高准确性,则可以迭代地在每个级别中添加错误数据。
学习来源:OpenCV-Python中文文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值