代码和报告均为本人自己实现(实验满分),只展示任务实验结果,如果需要报告或者代码可以私聊博主
有任何疑问或者问题,也欢迎私信博主,大家可以相互讨论交流哟~~求点赞+关注
后续持续更新机器学习专栏
一、案例简介
图像的智能处理一直是人工智能领域广受关注的一类技术,代表性的如人脸识别与 CT 肿瘤识别,在人工智能落地的进程中发挥着重要作用。其中车牌号识别作为一个早期应用场景,已经融入日常生活中,为我们提供了诸多便利,在各地的停车场和出入口都能看到它的身影。车牌号识别往往分为字符划分和字符识别两个子任务,本案例我们将关注字符识别的任务,尝试用 K-NN 的方法对分割好的字符图像进行自动识别和转化。
二、作业说明
基本要求
- 完成数据的读入和表示,将图片表示成向量并和 label 对应上;
- 构建 K-NN 模型(可调库)对测试集中的图片进行预测并计算准确率;
- 分析当 K 取不同值时测试准确率的变化。
扩展要求
- 分析不同距离度量方式对模型效果的影响;
- 对比平权和加权 K-NN 的效果;
- 分析训练集大小对测试结果的影响。
实验结果
三、数据概览
本次我们使用已经分割好的车牌图片作为数据集,包括数字 0-9、字母 A-Z(不包含 O 和 I)以及省份简称共 65 个类,编号从 0 到 64。数据已经分成了训练集和测试集,里面的文件夹用 label 编号命名,一个文件夹下的所有图片都属于该文件夹对应的类,每个图片都是 20 * 20 的二值化灰度图。
下面演示一下如何借助 PIL 库将图片转化为向量:
四、模型构建
读取数据
import os
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
def load_data(data_path):
images = []
labels = []
for label_dir in os.listdir(data_path):
if label_dir.startswith('.'):
continue
label = int(label_dir)
label_path = os.path.join(data_path, label_dir)
for image_file in os.listdir(label_path):
image_path = os.path.join(label_path, image_file)
image = Image.open(image_path)
image_array = np.array(image).flatten()
images.append(image_array)
labels.append(label)
return np.array(images), np.array(labels)
train_images, train_labels = load_data(data_path='./data/train')
test_images, test_labels = load_data(data_path='./data/test')
构建模型并且预测图像计算准确率
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
# 构建K-近邻分类器
knn = KNeighborsClassifier(n_neighbors=1)
# 训练模型
knn.fit(train_images, train_labels)
# 预测测试集
y_pred = knn.predict(test_images)
# 计算准确率
accuracy = accuracy_score(test_labels, y_pred)
print('Accuracy: {:.2f}%'.format(accuracy * 100))
Accuracy: 71.68%
通过对k的不同取值发现,当k=1的时候正确率最高,结果最优
k_values = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for k in k_values:
knn_model = KNeighborsClassifier(n_neighbors=k)
knn_model.fit(train_images, train_labels)
pred_labels = knn_model.predict(test_images)
accuracy = accuracy_score(test_labels, pred_labels)
print("K =", k, "时的准确率:", accuracy)
不同的距离度量方式可以对K-NN模型的效果产生影响。在K-NN算法中,常用的距离度量方式包括欧氏距离(Euclidean distance)、曼哈顿距离(Manhattan distance)、闵可夫斯基距离(Minkowski distance)等,通过运行发现欧式距离和闵可夫斯基距离的结果是一样的。
关于我这里为什么取值k=5,因为我发现k=1的时候,结果差距真的很小,不容易看到影响。
# 定义不同的距离度量方式
distance_metrics = ['euclidean', 'manhattan', 'minkowski']
# 初始化结果列表
accuracy_results = []
# 遍历距离度量方式
for metric in distance_metrics:
# 创建K-NN模型
knn = KNeighborsClassifier(n_neighbors=5, metric=metric)
# 使用训练集拟合模型
knn.fit(train_images, train_labels)
# 使用测试集进行预测
y_pred = knn.predict(test_images)
# 计算准确率并添加到结果列表
accuracy = accuracy_score(test_labels, y_pred)
accuracy_results.append(accuracy)
# 输出不同距离度量方式的准确率结果
for metric, accuracy in zip(distance_metrics, accuracy_results):
print(f"{metric}: {accuracy}")
对比平权和加权knn的效果发现,加权后效果更好一点。
# 对比平权和加权K-NN的效果
knn_model_uniform = KNeighborsClassifier(n_neighbors=5, weights='uniform') # 平权K-NN
knn_model_uniform.fit(train_images, train_labels)
pred_labels_uniform = knn_model_uniform.predict(test_images)
accuracy_uniform = accuracy_score(test_labels, pred_labels_uniform)
print("平权K-NN的准确率:", accuracy_uniform)
knn_model_distance = KNeighborsClassifier(n_neighbors=5, weights='distance') # 加权K-NN
knn_model_distance.fit(train_images, train_labels)
pred_labels_distance = knn_model_distance.predict(test_images)
accuracy_distance = accuracy_score(test_labels, pred_labels_distance)
print("加权K-NN的准确率:", accuracy_distance)
平权K-NN的准确率: 0.6928188638799572
加权K-NN的准确率: 0.7016077170418007
通过不同训练集大小训练模型时,我决定使用欧式距离和加权的方式进行,通过运行发现并不是训练集越大越好,反而是训练集为原来训练集0.6的情况下最优,再增加正确率就会递减。
# 训练集大小比例
train_sizes = [i / 10 for i in range(1, 10)]
# 保存每个训练集大小比例下的准确率
accuracy_results = []
for size in train_sizes:
# 按照指定的比例划分训练集
X_train, _, y_train, _ = train_test_split(train_images, train_labels, train_size=size,random_state=42)
# 创建K-NN模型
knn = KNeighborsClassifier(n_neighbors=1,weights='distance', metric='euclidean')
# 拟合训练数据
knn.fit(X_train, y_train)
# 预测测试数据
predictions = knn.predict(test_images)
# 计算准确率
accuracy = accuracy_score(test_labels, predictions)
# 将准确率添加到结果列表
accuracy_results.append(accuracy)
plt.figure(figsize=(8, 6))
plt.plot(train_sizes, accuracy_results,marker='o')
plt.title('Training Set Size vs Accuracy')
plt.xlabel('Training Set Size Ratio')
plt.ylabel('Accuracy')
plt.show()