目录
KNN算法简介
算法原理
基本流程
KNN算法的三要素
距离度量
K值选择-交叉验证
KNN算法的优缺点以及改进方法
KNN改进算法介绍
KD树
Ball树
Annoy
HNSW
KNN算法手动实现并完成鸢尾花分类
主体部分
交叉验证选择最适K值
导入数据和预处理
完整代码
利用Sklearn实现KNN完成鸢尾花分类
利用Sklearn的KNN完成手写数字识别
导入数据并查看数字图像
数据预处理
选择最佳K值
训练模型,测试模型得分
应用手写数字识别
图片预处理
将彩色图片以灰度图片导入
黑白颠倒以及二值化
使用模型识别手写数字
完整代码
对模型的总结和评价
k近邻法(k-nearest neighbor,k-NN)属于监督学习中的分类算法,是机器学习中最简单的算法之一,同时也是最常用的分类算法之一。这篇文章将会基于numpy手动实现KNN,在此之后,将会利用sklearn实现的KNN完成数字识别。
KNN需要数据标准化。
KNN不同于其他机器学习算法,它不需要训练过程,当使用该算法预测一个新的值时,它会根据距离最近的K个点的类型来判断自己的类型:K个点中哪个类型多就属于哪个类型。
例:
当K等于3时,根据上述规则可得预测为红类;
当K等于5时,根据上述规则可得预测为蓝类。
计算新数据与每一个已知样本的距离
按距离从小到大排序
选出距离最小的K个点
通过这K个点的分类决策新数据所属类别
根据上面的例子,可以看出不同K的选择对预测结果有较大影响。实验表明,K值越小,越容易发生过拟合;K值越大,越容易发生欠拟合。除K值的选择外,影响KNN算法的还有距离度量、分类决策规则。
分类决策规则一般采用多数表决,即最近的K个样本中哪个种类多就属于哪一类。
设有同维度特征空间向量 ,则的距离为:
切比雪夫距离:
一般采用欧式距离作为距离度量。
交叉验证简介:将原始数据集可以分为训练集、验证集、测试集。利用训练集和验证集测试模型好坏的方法叫做交叉验证。注:交叉验证是没有用到测试集的!如果用测试集进行交叉验证,会导致预测结果乐观化。
通过交叉验证方法可以得到最合适的K值。
优点:
原理简单,便于实现。
KNN是惰性模型,不需要训练。
预测效果好。
对异常值不敏感。
缺点:通过KNN算法原理可以得知,如果要预测一个新数据,需要将它和每一个样本都计算一次距离,然后排序,选出最小的K个样本进行决策。那么预测一次的时间复杂度为O(n),时间复杂度较高。并且需要将所有样本都储存在内存,空间复杂度较高,即对内存要求较高。所以KNN算法的缺点主要是:
效率比较低,运行时间可能比较长。
对内存要求较高。
KNN适用于数据集较小的分类。对于数据集较大的可使用神经网络进行分类。
这里不会说明各算法的原理与实现,只会简单介绍,参考文章:盘点高效的KNN实现算法-技术圈 (proginn.com)
上述提到的均是KNN的线性扫描实现方法,即暴力法。通过分析,可知线性扫描不适用于数据规模较大的数据集。
实际上,KNN的实现方法高大10种,下面将介绍几种改进方法。
KD树是一种树形结构存储算法。
适用范围:
KD树在特征维度小于20时效率最高,一般适用于训练样本数远大于空间维数时的k近邻搜索。当空间维数接近训练实例数时,效率会迅速下降,几乎接近线形扫描。
ball树是KD树的一种改良算法。
Annoy,全称“Approximate Nearest Neighbors Oh Yeah”,是一种适合实际应用的快速相似查找算法。
Annoy 同样通过建立一个二叉树,使得每个点查找时间复杂度是O(log n)。和KD树不同的是,Annoy没有对k维特征进行切分。
HNSW(Hierarchcal Navigable Small World graphs)是基于图存储的数据结构。
上述四种算法中,Annoy和HNSW是可以在实际业务中落地的算法。
详细解释都写代码里了,因为主体部分实在太短了没必要分开写,就直接写在一个模块了:
import numpy as np
import operator
# 使用KNN预测数据类别
def run_KNN(X,X_train,y_train,K):
'''
:param X: 需要预测的数据集
:param X_train: X训练集
:param y_train: Y训练集
:param K: K个最近
:return: 预测集
'''
dataSize = X_train.shape[0]
y_predict = []
for x in X:
# 计算未知数据到每一个样本的欧氏距离
diff = np.tile(x,(dataSize,1)) - X_train # 把X扩大然后矩阵相减
squaredDist = np.sum(diff**2,axis=1) # axis = 1计算每一行的和
distance = squaredDist ** 0.5
# 对距离递增排序获取最前面K个样本的种类并统计各种类出现次数
nearIds = distance.argsort() # 按值排序,得到对应下标数组
classesCount = {}
for i in range(K):
y = y_train[nearIds[i]] # 得到对应的种类
classesCount[y] = classesCount.get(y,0)+1 # 0为设置默认值
# print(classesCount)
# 对字典按值进行递减排序
sortClassesCount = sorted(classesCount.items(),key=operator.itemgetter(1),reverse=True)
# 获取对象第二个元素 逆序
y_predict.append(sortClassesCount[0][0]) # 预测种类为出现次数最多的那一类
return y_predict
到这里,就已经手动实现了KNN算法,可以拿来预测类别了,确实很简单。
但我们还需要利用交叉验证集选择最好的K,本来想借助sklearn的交叉验证,但是对我们自己手写的KNN好像不太好用,所以还是自己写一个简易的吧。
不过像拆分训练集和计算Score这种事就交给sklearn吧。
注意,交叉验证集不能涉及到测试集!
这里用多次交叉验证计算score取均值:
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
# 交叉验证集
def chooseK(X_train_old,y_train_old,K = 10,C = 5):
'''
:param X_train_old: 原训练特征集
:param y_train_old: 原训练目标集
:param K: 最大K
:param C: 计算次数
:return: 最好的K值
'''
# K从1取到maxK,分别计算Score
scores = [0]
for k in range(1,K+1):
score = 0
for j in range(1,C+1):
# 拆分训练集为训练集和验证集
X_train, X_check, y_train, y_check = train_test_split(X_train_old, y_train_old, train_size=0.6)
y_predict = run_KNN(X_check,X_train,y_train,k)
# 计算Score
score = score + f1_score(y_check, y_predict, average='weighted')
score = score/(C)
# print(f"k = {k},score = {score}")
scores.append(score)
scores = np.array(scores)
bestK = scores.argmax()
plt_KScore(scores,K,bestK)
return bestK
# 绘制K对应的Score图像
def plt_KScore(scores,K,bestK):
k_range = range(1,K+1)
plt.plot(k_range,scores[1:])
plt.scatter(bestK,scores[bestK],marker='o',c='red')
plt.xlabel('Value of K in KNN')
plt.ylabel('Score')
plt.show()'Score')
plt.show()
下面将测试交叉验证集选择K值。
这里使用sklearn自带的鸢尾花数据集,数据标准化之前的文章已经手动实现过,这里直接用sklearn进行数据标准化。
import time
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
if __name__ == '__main__':
# 导入鸢尾花数据集
iris = datasets.load_iris()
dataSet = iris.data
target = iris.target
# 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(dataSet,target, train_size=0.7, random_state=10)
# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 标准化训练集X
X_test = scaler.transform(X_test)
现在开始测试交叉验证集选择K:
# 接主函数
start = time.time()
K = chooseK(X_train,y_train,50)
print(f"bestK is {K}")
end = time.time()
print(f"run time is {end-start}")
结果:
bestK is 7
run time is 2.0105364322662354
实际上会发现每次计算出来的“Best K”都不一样,不过都在那几个值跳动,这是因为在分割测试集和验证集时没有设置随机种子的原因。
现在,让我们用最好的K值来测试分类效果吧!
# 接主函数
start = time.time()
best_K = chooseK(X_train,y_train,50)
print(f"bestK is {best_K}")
y_predict = run_KNN(X_test,X_train,y_train,best_K)
score = f1_score(y_test, y_predict, average='weighted')
print(f"predict score is {score}")
end = time.time()
print(f"run time is {end-start}")
结果:
bestK is 4
predict score is 0.9778242192035296
run time is 0.400089502334594704
可以看出bestK和上一次算的不一样,不过最后分类的结果还是非常好的。
import time
import numpy as np
import operator
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
# 使用KNN预测数据类别
def run_KNN(X,X_train,y_train,K):
'''
:param X: 需要预测的数据集
:param X_train: X训练集
:param y_train: Y训练集
:param K: K个最近
:return: 预测集
'''
dataSize = X_train.shape[0]
y_predict = []
for x in X:
# 计算未知数据到每一个样本的欧氏距离
diff = np.tile(x,(dataSize,1)) - X_train # 把X扩大然后矩阵相减
squaredDist = np.sum(diff**2,axis=1) # axis = 1计算每一行的和
distance = squaredDist ** 0.5
# 对距离递增排序获取最前面K个样本的种类并统计各种类出现次数
nearIds = distance.argsort() # 按值排序,得到对应下标数组
classesCount = {}
for i in range(K):
y = y_train[nearIds[i]] # 得到对应的种类
classesCount[y] = classesCount.get(y,0)+1 # 0为设置默认值
# print(classesCount)
# 对字典按值进行递减排序
sortClassesCount = sorted(classesCount.items(),key=operator.itemgetter(1),reverse=True)
# 获取对象第二个元素 逆序
y_predict.append(sortClassesCount[0][0]) # 预测种类为出现次数最多的那一类
return y_predict
# 交叉验证集
def chooseK(X_train_old,y_train_old,K = 10,C = 5):
'''
:param X_train_old: 原训练特征集
:param y_train_old: 原训练目标集
:param K: 最大K
:param C: 计算次数
:return: 最好的K值
'''
# K从1取到maxK,分别计算Score
scores = [0]
for k in range(1,K+1):
score = 0
for j in range(1,C+1):
# 拆分训练集为训练集和验证集
X_train, X_check, y_train, y_check = train_test_split(X_train_old, y_train_old, train_size=0.6)
y_predict = run_KNN(X_check,X_train,y_train,k)
# 计算Score
score = score + f1_score(y_check, y_predict, average='weighted')
score = score/(C)
# print(f"k = {k},score = {score}")
scores.append(score)
scores = np.array(scores)
bestK = scores.argmax()
# plt_KScore(scores,K,bestK)
return bestK
# 绘制K对应的Score图像
def plt_KScore(scores,K,bestK):
k_range = range(1,K+1)
plt.plot(k_range,scores[1:])
plt.scatter(bestK,scores[bestK],marker='o',c='red')
plt.xlabel('Value of K in KNN')
plt.ylabel('Score')
plt.show()
if __name__ == '__main__':
# 导入鸢尾花数据集
iris = datasets.load_iris()
dataSet = iris.data
target = iris.target
# 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(dataSet,target, train_size=0.7, random_state=10)
# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 标准化训练集X
X_test = scaler.transform(X_test)
start = time.time()
best_K = chooseK(X_train,y_train,50)
print(f"bestK is {best_K}")
y_predict = run_KNN(X_test,X_train,y_train,best_K)
score = f1_score(y_test, y_predict, average='weighted')
print(f"predict score is {score}")
end = time.time()
print(f"run time is {end-start}")
关于sklearn封装的KNN的参数说明详细请参考文章: KNN算法详解及实现__dingzhen的博客-CSDN博客_knn
读懂每一个需要的参数很很很重要!!一定要去看参数说明。
sklearn的KNN实现方式包括了之前提到的线性扫描、KDTree和ballTree,没有规定特定参数时它会自己选则实现方法。详细说明都在链接里了。
这次我们用Sklearn封装的KNN来完成上面提到的例子,即鸢尾花分类。同样也用了交叉验证寻找最佳K值。
详见代码:
import time
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import f1_score
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
# 交叉验证找最好K
def findBestK(X_train,y_train,max_k = 50):
k_scores = [0]
for k in range(1,max_k+1):
Knn = KNeighborsClassifier(n_neighbors=k)
# 交叉验证,scoring='f1_weighted' 使用的与之前手写的交叉验证使用同一个F1-score规则
scores = cross_val_score(Knn,X_train,y_train,cv=5,scoring='f1_weighted')
k_scores.append(scores.mean())
k_scores = np.array(k_scores)
bestK = k_scores.argmax()
plt_KScore(k_scores,max_k,bestK)
return bestK
# 绘制K对应的Score图像
def plt_KScore(scores,K,bestK):
k_range = range(1,K+1)
plt.plot(k_range,scores[1:])
plt.scatter(bestK,scores[bestK],marker='o',c='red')
plt.xlabel('Value of K in KNN')
plt.ylabel('Score')
plt.show()
if __name__ == '__main__':
# 导入鸢尾花数据集
iris = datasets.load_iris()
dataSet = iris.data
target = iris.target
# 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(dataSet,target, train_size=0.7, random_state=10)
# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 标准化训练集X
X_test = scaler.transform(X_test)
# 寻找最好K值
start = time.time()
best_K = findBestK(X_train,y_train)
print(f"best_k is {best_K}")
model = KNeighborsClassifier(n_neighbors=best_K)
model.fit(X_train,y_train)
y_predict = model.predict(X_test)
score = f1_score(y_test, y_predict, average='weighted')
print(f"predict score is {score}")
end = time.time()
print(f"run time is {end - start}")
交叉验证集选择K:
最终结果:
best_k is 11
predict score is 0.9778242192035296
run time is 0.7521629333496094
通过自己实现的KNN与sklearn实现的KNN对比,发现:
自己实现的KNN寻找到的最佳K值不固定,而sklearn每次寻找的最佳K值都一样。导致自己实现的KNN最佳K值不固定的原因是拆分数据集和验证集时每次的随机种子不固定。偶尔有几次自己算出来的结果和sklearn算出来的结果完全一致。
使用同种实现方式(即线性扫描实现KNN)时,sklearn用的时间比自己实现的要长那么一点点,暂时不清楚原因。
到此为止,你已经学会了如何自己实现KNN算法,并且取得了不错的分类效果。现在我们用sklrean封装好的KNN来完成一些有趣的事吧!
手写数据识别是计算机视觉方面的入门级图像识别,一般都是用神经网络实现的,不过我们可以使用KNN来完成数据规模比较小的手写数字识别。
在此之前,请确保你已经安装tensorflow和keras,我们的数据集将来自keras。
当然也可以自己找数据。
需要的包:
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from sklearn.neighbors import KNeighborsClassifier
if __name__ == '__main__':
# 加载数据
(X_train,y_train),(X_test,y_test) = mnist.load_data()
# 绘制训练集的部分数字图像
for i in range(6):
plt.figure()
plt.imshow(X_train[i],cmap='gray')
plt.show()
查看X_train训练集的形状:
print(X_train.shape)
输出:
(60000, 28, 28)
看得出是个三维的,大概意思是60000张图片,像素,且是灰色(因为如果是彩色,应该是)
要用KNN算法,需要先将三维转化为二维矩阵 ,每一行代表一张图片,共60000张。
# 将训练集和测试集转化为二维数组
X_train = X_train.reshape([-1,28*28])
X_test = X_test.reshape([-1,28*28])
# 归一化
X_train /= 255
X_test /= 255
# 交叉验证法求最好K值
def findBestK(X_train,y_train,max_k = 50):
k_scores = [0]
for k in range(1,max_k+1):
print(f"{k} time")
Knn = KNeighborsClassifier(n_neighbors=k)
# 交叉验证,scoring='f1_weighted' 使用的与之前手写的交叉验证使用同一个F1-score规则
scores = cross_val_score(Knn,X_train,y_train,cv=5,scoring='f1_weighted')
k_scores.append(scores.mean())
k_scores = np.array(k_scores)
bestK = k_scores.argmax()
# plt_KScore(k_scores,max_k,bestK)
return bestK
# 绘制K对应的Score图像
def plt_KScore(scores,K,bestK):
k_range = range(1,K+1)
plt.plot(k_range,scores[1:])
plt.scatter(bestK,scores[bestK],marker='o',c='red')
plt.xlabel('Value of K in KNN')
plt.ylabel('Score')
plt.show()
# 接主函数内
start = time.time()
# 选择最佳K值,这一步如果觉得耗时太长,其实可以去掉
# 为了缩短时间,我们只取10000个数据用于选择最佳K值
best_K = findBestK(X_train[:10000], y_train[:10000])
print(f"best_k is {best_K}")
end = time.time()
print(f"run time is {end - start}")
结果:
best_k is 4
run time is 54.95526051521301
这一步太慢了!!整整耗时55秒。强烈建议不要进行,如果需要最佳K值,上面已经算出来了。
start = time.time()
# 选择最佳K值,这一步如果觉得耗时太长,其实可以去掉
# 为了缩短时间,我们只取10000个数据用于选择最佳K值
# best_K = findBestK(X_train[:10000], y_train[:10000])
# print(f"best_k is {best_K}")
# 训练模型
model = KNeighborsClassifier(n_neighbors=4)
model.fit(X_train,y_train)
# 计算得分
y_predict = model.predict(X_test)
score = f1_score(y_test, y_predict, average='weighted')
print(f"predict score is {score}")
end = time.time()
print(f"run time is {end - start}")
结果:
predict score is 0.9681286795453637
run time is 7.25964617729187
可以看出模型很不错!到此为止,使用KNN进行手写数字识别可以说完成了。
但是,作为一个合格的玩具,它还没完。
接下来是可选内容:把它变成可以玩的玩具!
现在假设你拿到了这样一张图片:
很显然,它是彩色的,但是我们之前训练用的图片是灰色的,而且,中间是白色的,像这样:
另外,两张像素可能不一样,前者像素不确定,后者为28*28所以我们需要先把图片做一些预处理:
将彩色图片变为灰色图片(即将三图层变为单图层)
将图片像素压缩为28*28
可以将图片二值化(即0和255),可能识别效果会更好。
接下来会用到opencv-python包,但是注意高版本的tensorflow和opencv-python可能出现不兼容的问题。最开始我用的tensorflow2.9.0,对应的numpy为1.23。但是安装opencv后会自动把numpy降为1.16,导致tensorflow用不了。所以这里可能需要切换一下环境。
我用的环境:tensorflow2.6.0+opencv4.5.5+numpy1.19.5
tensorflow版本降低后,数据集的导入可能会发生变化。
改变导入形式:
import cv2
import time
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist
from sklearn.metrics import f1_score
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
查看原图片:
path = 'number1.png' # path为图片储存位置
# 原图像
img1 = plt.imread(path)
plt.imshow(img1)
plt.show()
以灰度图片导入并查看:
imgOriginal = cv2.imread(path,0)
img = np.asarray(imgOriginal)
print(f"before resize shape is:{img.shape}")
img = cv2.resize(img,(28,28))
print(f"after resize shape is:{img.shape}")
plt.imshow(img,cmap='Greys_r') # cmap='Greys_r' 使plt显示灰度图像
plt.show()
如果不加cmap='Greys_r',matplotlib会把这张灰色图片由低维(1维)映射到高维(3维),然后以彩色图片的形式展出,这显然不符合我们这里的需求。
输出:
before resize shape is:(188, 186)
after resize shape is:(28, 28)
我们的样本数据集为黑底白字,所以需要将这张图片也要变为黑底白字。同时我们可以将其二值化,变为黑白图像:
这里的二值化只是简单实现了下,还有更好的二值化方法,以后再学了。
# 二值化
def Binarization(img,Reverse = False):
'''
:param img:
:param Reverse: 是否黑白颠倒
:return:
'''
if Reverse == False:
img = np.where(img > 160,255,0)
else:
img = np.where(img > 160,0,255)
return img
现在将图片二值化:
img = Binarization(img,Reverse = True)
plt.imshow(img, cmap='Greys_r') # cmap='Greys_r' 使plt显示灰度图像
plt.show()
最后不要忘记了归一化和转化矩阵:
X = img/255.0
X = X.reshape(-1,28*28)
predict = model.predict(X)
print(f"手写数字识别为:{predict[0]}")
结果:
手写数字识别为:2
import cv2
import time
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist
from sklearn.metrics import f1_score
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
# 交叉验证法求最好K值
def findBestK(X_train,y_train,max_k = 50):
k_scores = [0]
for k in range(1,max_k+1):
print(f"{k} time")
Knn = KNeighborsClassifier(n_neighbors=k)
# 交叉验证,scoring='f1_weighted' 使用的与之前手写的交叉验证使用同一个F1-score规则
scores = cross_val_score(Knn,X_train,y_train,cv=5,scoring='f1_weighted')
k_scores.append(scores.mean())
k_scores = np.array(k_scores)
bestK = k_scores.argmax()
# plt_KScore(k_scores,max_k,bestK)
return bestK
# 绘制K对应的Score图像
def plt_KScore(scores,K,bestK):
k_range = range(1,K+1)
plt.plot(k_range,scores[1:])
plt.scatter(bestK,scores[bestK],marker='o',c='red')
plt.xlabel('Value of K in KNN')
plt.ylabel('Score')
plt.show()
# 计算模型得分
def cal_score(model,X_test,y_test):
y_predict = model.predict(X_test)
score = f1_score(y_test, y_predict, average='weighted')
print(f"predict score is {score}")
# 二值化
def Binarization(img,Reverse):
'''
:param img:
:param Reverse: 是否黑白颠倒
:return:
'''
if Reverse == False:
img = np.where(img > 160,255,0)
else:
img = np.where(img > 160,0,255)
return img
# 识别数字
def identification_number(path,model = None,Reverse = False):
# 原图像
# img1 = plt.imread(path)
# plt.imshow(img1)
# plt.show()
imgOriginal = cv2.imread(path,0)
img = np.asarray(imgOriginal)
img = cv2.resize(img,(28,28))
# plt.imshow(img,cmap='Greys_r') # cmap='Greys_r' 使plt显示灰度图像
# plt.show()
img = Binarization(img,Reverse)
# plt.imshow(img, cmap='Greys_r') # cmap='Greys_r' 使plt显示灰度图像
# plt.show()
X = img/255.0
X = X.reshape(-1,28*28)
predict = model.predict(X)
print(f"手写数字识别为:{predict[0]}")
if __name__ == '__main__':
# 加载数据
(X_train,y_train),(X_test,y_test) = mnist.load_data()
# 归一化
X_train = X_train /255.0
X_test = X_test /255.0
# 将训练集和测试集转化为二维数组
X_train = X_train.reshape([-1,28*28])
X_test = X_test.reshape([-1,28*28])
# start = time.time()
# 选择最佳K值,这一步如果觉得耗时太长,其实可以去掉
# 为了缩短时间,我们只取10000个数据用于选择最佳K值
# best_K = findBestK(X_train[:10000], y_train[:10000])
# print(f"best_k is {best_K}")
# 构建模型,喂数据
model = KNeighborsClassifier(n_neighbors=4)
model.fit(X_train,y_train)
#
# # 计算得分
# cal_score(model,X_test,y_test)
# 识别图片
img_path = 'number1.png'
identification_number(img_path,model,Reverse=True) # 在这里修改是否需要颠倒图像
# end = time.time()
实际上因为没有用神经网络来做这个手写数字识别模型,加上二值化写的比较草率,它对于真实手写数字识别效果并没有预料的好。待会儿会举两个识别自己手写数字的例子。
在二值化时阈值的选择影响比较大,最开始阈值设置的200,结果自己手写的数字二值化后变成了一片白或黑色。后来调整到160-170左右,感觉比较合适。
咱也是第一次做手写数字识别玩具,整个过程还是很有意思的。
自己写的数字:
二值化和预测结果:
手写数字识别为:5
识别失败:
手写数字识别为:1