在集大宿舍路边拍摄自行车、电动车和摩托车照片,并对图像进行截取得到数据集。
截取图片后将每张图片大小设定为400*500,按类别存储于不同文件夹中。
本实验认为所有通道的所有像素均对预测结果有影响,所以读取时将每张图片的三通道展平为一维向量。由于摩托车数据较少因此不放入数据集中。同时此做法数据量较大,在读取图片后使用归一化减小计算量。
"""
读取数据集返回数据和标签
读取的图片大小为400*500
"""
def lode_data():
data=[]
labels=[]
for filename in os.listdir(r"../data/cycle"): #listdir的参数是文件夹的路径
filenames = '../data/cycle/'+filename
img = cv2.imread(filenames,1)
#归一化
img = (img - np.min(img)) / (np.max(img) - np.min(img))
data.append(img.flatten())
labels.append('cycle')
for filename in os.listdir(r"../data/electric_vehicle"): #listdir的参数是文件夹的路径
filenames = '../data/electric_vehicle/'+filename
img = cv2.imread(filenames,1)
#归一化
img = (img - np.min(img)) / (np.max(img) - np.min(img))
data.append(img.flatten())
labels.append('electric_vehicle')
data = np.array(data)
labels = np.array(labels)
# print(labels)
return data, labels
"""
分离训练集和测试集,默认比率为0.75
"""
def separate_dataset(dataset, labels, spearate_rate=0.75):
train_data = []
train_labels = []
test_data = []
test_labels = []
#随机打乱数据集
dataset_tmp = list(range(len(dataset)))
random.shuffle(dataset_tmp)
train_data_index = dataset_tmp[:int(len(dataset)*spearate_rate)]
test_data_index = dataset_tmp[int(len(dataset)*spearate_rate):]
for i in train_data_index:
train_data.append(dataset[i])
train_labels.append(labels[i])
for i in test_data_index:
test_data.append(dataset[i])
test_labels.append(labels[i])
train_data = np.array(train_data)
train_labels = np.array(train_labels)
test_data = np.array(test_data)
test_labels = np.array(test_labels)
return train_data, train_labels, test_data, test_labels
knn是k近邻算法,原理为对输入图片进行预测时,将其与训练集中所有图片计算距离,再选取距离最近的前k个图片
中对应标签最多的类别作为预测类别。本次实验距离公式采用2范式也就是欧式距离,将两张图片向量对应位置之差平方求和后取根号作为图片的距离。
d i s t e n c e = ∑ i = 0 n − 1 ( x i − y i ) 2 distence = \sqrt{\sum_{i = 0}^{n - 1}{\left ( x_i-y_i\right )}^2 } distence=i=0∑n−1(xi−yi)2
其中n为向量长度。
"""
knn分类器
img为预测图像
dataset为已有训练集
labels为已有标签
k为k近邻参数,默认为1
"""
def classify0(img, dataset, labels, k=1):
k = int(k)
assert k > 0, "k必须为正整数"
if len(img.shape) > 1:
# 将输入非一维图片转为一维
img = img.flatten()
diff_mat = np.tile(img, (dataset.shape[0], 1)) - dataset
sq_distences = (diff_mat ** 2).sum(axis=1)
distences = sq_distences ** 0.5
sorted_distences = distences.argsort()
classcount={}
for i in range(k):
vote_label = labels[sorted_distences[i]]
classcount[vote_label] = classcount.get(vote_label, 0) + 1
sorted_classcount = sorted(classcount.items(), key = operator.itemgetter(1), reverse = True)
return sorted_classcount[0][0]
当k=3时的预测结果
data, labels = lode_data()
train_data, train_labels, test_data, test_labels = separate_dataset(data, labels)
a = classify0(test_data[0], train_data, train_labels, 3)
print(a)
print(test_labels[0])
可以看出预测结果出错,但是想要看分类器效果还需要对所有测试集图片进行测试
定义函数
"""
统计预测正确的图片数量
"""
def test_all(train_data, train_labels, test_data, test_labels, k=1):
right = 0
for i in range(len(test_data)):
if classify0(test_data[i], train_data, train_labels, k) == test_labels[i]:
right+=1
return right
预测结果
data, labels = lode_data()
train_data, train_labels, test_data, test_labels = separate_dataset(data, labels)
right = test_all(train_data, train_labels, test_data, test_labels, 1)
accuracy = right/len(test_labels)
print(f"accuracy = {accuracy}")
编写代码
data, labels = lode_data()
train_data, train_labels, test_data, test_labels = separate_dataset(data, labels)
accuracy_all = []
for i in tqdm(range(len(train_data) - 1)):
right = test_all(train_data, train_labels, test_data, test_labels, i + 1)
accuracy = right/len(test_labels)
accuracy_all.append(accuracy)
plt.plot(range(len(train_data) - 1),accuracy_all)
plt.show()
对于k较小时由于图片背景相似又类别不同导致准确率不稳定,在k等于22时准确率达到最大值,此时图片数量增大,背景对投票结果的影响变小,因此准确率较高。在k>=30时,由于电动车本身站数据集比重较高,导致投票结果为电动车可能性更大,使得结果准确率趋于稳定。