测车牌号(机器学习)
一、案例简介
图像的智能处理一直是人工智能领域广受关注的一类技术,代表性的如人脸识别与 CT 肿瘤识别,在人工智能落地的进程中发挥着重要作用。其中车牌号识别作为一个早期应用场景,已经融入日常生活中,为我们提供了诸多便利,在各地的停车场和出入口都能看到它的身影。车牌号识别往往分为字符划分和字符识别两个子任务,本案例我们将关注字符识别的任务,尝试用 K-NN 的方法对分割好的字符图像进行自动识别和转化。
二、作业说明
基本要求
- 完成数据的读入和表示,将图片表示成向量并和 label 对应上;
- 构建 K-NN 模型(可调库)对测试集中的图片进行预测并计算准确率;
- 分析当 K 取不同值时测试准确率的变化。
扩展要求
- 分析不同距离度量方式对模型效果的影响;
- 对比平权和加权 K-NN 的效果;
- 分析训练集大小对测试结果的影响。
三、数据概览
本次我们使用已经分割好的车牌图片作为数据集,包括数字 0-9、字母 A-Z(不包含 O 和 I)以及省份简称共 65 个类,编号从 0 到 64。数据已经分成了训练集和测试集,里面的文件夹用 label 编号命名,一个文件夹下的所有图片都属于该文件夹对应的类,每个图片都是 20 * 20 的二值化灰度图。
下面演示一下如何借助 PIL 库将图片转化为向量:
from PIL import Image
img = Image.open('data/train/0/4-3.jpg') # 打开图片
img # 显示图片from PIL import Image
img = Image.open('data/train/0/4-3.jpg') # 打开图片
img # 显示图片
import numpy as np
pixels = np.array(img) # 转化为 numpy 矩阵
pixels.shape
(20, 20)
四、模型构建
import os
from tqdm import tqdm
#读取图片label函数
def readFileIndex(filepath):
lables = dict()
filetype = '.jpg'
for i in range(0,65):#因为有0-64个类别
str1 = str(i)
filepath1 = os.path.join(filepath,str1)#读取文件夹
for root,dirs,files in os.walk(filepath1):
for j in files:
if filetype+' 'in j+' ':
str1 = str(i)+'/'+j
lables[str1] = i
return lables
labels_train = readFileIndex(r'C:\Users\mzq\Desktop\基于knn的车牌号识别\基于knn的车牌号识别\data\train')
labels_test = readFileIndex(r'C:\Users\mzq\Desktop\基于knn的车牌号识别\基于knn的车牌号识别\data\test')
#图片文件读取函数,返回一个一维的数组
def readPict(path):
returanVec=np.zeros(400)
img = Image.open(path)
pixel = np.array(img)#此时为二维矩阵,将其转为一维,方便运行算法拟合
for i in range(20):
for j in range(20):
returanVec[i*20+j]=pixel[i][j]
return returanVec
#读取所有图片函数,返回两个列表
def readFileContent(lables,flag):
X = []
Y = []
for lableName in lables:#取出每一个路径进行读取
filename = lableName
if(flag == "train"):#判断是训练集文件夹还是测试集文件夹
pict = readPict("data/train/" + filename)
else:
pict = readPict("data/test/" + filename)
X.append(pict)
Y.append(lables[filename])
return X,Y
X_train,Y_train = readFileContent(labels_train,"train")
X_test,Y_test = readFileContent(labels_test,"test")
from sklearn.neighbors import KNeighborsClassifier
neigh = KNeighborsClassifier(weights = 'distance')
neigh.fit(X_train,Y_train)
print(neigh.score(X_test,Y_test))
0.7016077170418007
def test_knn_n(n):
neigh = KNeighborsClassifier(n_neighbors=n,weights = 'distance')
neigh.fit(X_train,Y_train)
return neigh.score(X_test,Y_test)
list_n_score = []
for i in tqdm(range(1,20)):
list_n_score.append(test_knn_n(i))
neighbors数量对模型精度的影响
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False # 步骤二(解决坐标轴负数的负号显示问题)
plt.style.use("ggplot")
plt.figure(figsize=(15,7))
plt.plot([i for i in range(1,20)],list_n_score)
plt.xlabel("$neighbors#34;,fontsize = 15)
plt.ylabel("精度",fontsize = 15)
plt.title("$neighbors$数量对模型精度的影响",fontsize = 20);
是否加权对模型精度的影响
def test_knn_n(n,distance):
neigh = KNeighborsClassifier(n_neighbors=n,weights = distance)
neigh.fit(X_train,Y_train)
return neigh.score(X_test,Y_test)
list_n_score_uniform = []
list_n_score_distance = []
for i in tqdm(range(1,20)):
list_n_score_uniform.append(test_knn_n(i,'uniform'))
list_n_score_distance.append(test_knn_n(i,'distance'))
plt.figure(figsize=(15,7))
plt.plot([i for i in range(1,20)],list_n_score_uniform)
plt.plot([i for i in range(1,20)],list_n_score_distance)
plt.xlabel("$neighbors#34;,fontsize = 15)
plt.ylabel("精度",fontsize = 15)
plt.title(" 是否加权对模型精度的影响",fontsize = 20);
plt.legend(['uniform', 'distance'])
- distance 表示距离加权投票
- uniform 表示不加权平均投票
距离度量方式对模型精度的影响
KNeighborsClassifier()默认使用的是=2的闵氏距离,公式如下:
- 绝对距离 当p=1时,得到绝对值距离,也叫曼哈顿距离(Manhattan distance)、出租汽车距离或街区距离(city block distance)。在二维空间中可以看出,这种距离是计算两点之间的直角边距离,相当于城市中出租汽车沿城市街道拐直角前进而不能走两点连接间的最短距离。绝对值距离的特点是各特征参数以等权参与进来,所以也称等混合距离。
- 欧氏距离 当p=2时,得到欧几里德距离(Euclidean distance)距离,就是两点之间的直线距离(以下简称欧氏距离)。欧氏距离中各特征参数是等权的。
- 切比雪夫距离 令−>∞,得到切比雪夫距离。
def test_knn_n(n,distance,p = 2):
neigh = KNeighborsClassifier(n_neighbors=n,weights = distance,p = p)
neigh.fit(X_train,Y_train)
return neigh.score(X_test,Y_test)
list_n_score_p1 = []
list_n_score_p2 = []
for i in tqdm(range(1,20)):
list_n_score_p1.append(test_knn_n(i,'distance',1))
list_n_score_p2.append(test_knn_n(i,'distance',2))
plt.figure(figsize=(15,7))
plt.plot([i for i in range(1,20)],list_n_score_uniform)
plt.plot([i for i in range(1,20)],list_n_score_distance)
plt.xlabel("$neighbors#34;,fontsize = 15)
plt.ylabel("精度",fontsize = 15)
plt.title("距离度量方式对模型精度的影响",fontsize = 20);
plt.legend(['距离加权投票$p = 1#39;, '距离加权投票$p = 2#39;])
训练集大小对测试结果的影响
X_train[0].shape
(400,)
import pandas as pd
X = pd.DataFrame(X_train)
X['target'] = Y_train
X[X.columns[:-1]]
sample_rate = np.linspace(0.1,1,9)
score_all = []
for i in tqdm(sample_rate):
sample = X.sample(frac=i)
x = sample[sample.columns[:-1]]
y = sample[sample.columns[-1]]
neigh = KNeighborsClassifier(n_neighbors=2,weights = 'distance',p = 2)
neigh.fit(x,y)
score_all.append(neigh.score(X_test,Y_test))
plt.figure(figsize=(15,7))
plt.plot(sample_rate,score_all)
plt.xlabel("采样比例",fontsize = 15)
plt.ylabel("精度",fontsize = 15)
plt.title("采样比例对模型精度的影响",fontsize = 20);