共计 9478 个字符,预计需要花费 24 分钟才能阅读完成。
提醒:本文最后更新于 2024-08-28 15:03,文中所关联的信息可能已发生改变,请知悉!
双十一憋在寝室炼丹
在这次作业中,我们将尝试提取基本的图像特征并使用提取的特征进行图像分类。
你需要在 TODO 模块的 """ 你的代码 """ 中填写相应的代码。
你也可以添加任意数量的 cell 来辅助你完成实验。
import random
import numpy as np
import matplotlib.pyplot as plt
from past.builtins import xrange
%matplotlib inline
plt.rcParams['figure.figsize'] = (15., 12.) # 设置默认大小
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
%load_ext autoreload
%autoreload 2
The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
数据加载
理解该数据集的类型以及访问方式
# 读取提供的 cifar10-mini 数据集,data = np.load('cifar10-mini.npz')
X_train= data['X_train']
X_val= data['X_val']
X_test= data['X_test']
y_train= data['y_train']
y_val= data['y_val']
y_test= data['y_test']
# 打印数据 shape
print(X_train.shape)
print(X_val.shape)
print(X_test.shape)
(5000, 32, 32, 3)
(500, 32, 32, 3)
(500, 32, 32, 3)
提取图像特征
方向梯度直方图 HOG (Histogram of Oriented Gridients)特征检测算法,最早是由法国研究员 Dalal 等在 CVPR-2005 上提出来的,一种解决人体目标检测的图像描述子,是一种用于表征图像局部梯度方向和梯度强度分布特性的描述符。其主要思想是:在边缘具体位置未知的情况下,边缘方向的分布也可以很好的表示图像中物体的外形轮廓,但会忽略掉颜色信息。特征维度是 144 维
颜色直方图 (color histogram)特征则是提取图像的颜色信息并忽略掉纹理信息。因此同时使用这两种特征的分类效果会好于仅使用单一特征,【加分项】你可以尝试进行对比实验验证这一假设
hog_feature 和 color_histogram_hsv 两个函数都是接收一张图像然后返回这张图像的特征向量。你可以使用这两个函数中的一个提取所有图像的特征并将其存入 X_train_feats, X_val_feats, X_test_feats 这三个变量中(他们分别代表训练集、验证集和测试集的特征)。
如果你遇到了错误 ImportError: No module named past.builtins,可以在终端中执行 pip install future
from features import *
################################################################################
# TODO: #
# 你需要使用 hog_feature, color_histogram_hsv 两个函数完成特征的提取 #
# 你可以在 features.py 中查看这两个函数的代码 #
################################################################################
""" 你的代码 """
feature_fns = [hog_feature, lambda img: hog_feature(img)]
# feature_fns = [hog_feature, lambda img: color_histogram_hsv(img)]
X_train_feats = extract_features(X_train, feature_fns, verbose=True)
X_val_feats = extract_features(X_val, feature_fns)
X_test_feats = extract_features(X_test, feature_fns)
################################################################################
# END OF YOUR CODE #
################################################################################
# 预处理: 减去均值
mean_feat = np.mean(X_train_feats, axis=0, keepdims=True)
X_train_feats -= mean_feat
mean_feat = np.mean(X_val_feats, axis=0, keepdims=True)
X_val_feats -= mean_feat
mean_feat = np.mean(X_test_feats, axis=0, keepdims=True)
X_test_feats -= mean_feat
# 预处理: 除以标准差,这能保证所有的值在 0~1 之间
std_feat = np.std(X_train_feats, axis=0, keepdims=True)
X_train_feats /= std_feat
std_feat = np.std(X_val_feats, axis=0, keepdims=True)
X_val_feats /= std_feat
std_feat = np.std(X_test_feats, axis=0, keepdims=True)
X_test_feats /= std_feat
# 预处理: 增加一个偏置值,在 K-NN 中,该步操作并无必要,但增加偏置值对其他分类器如 SVM 等有帮助。X_train_feats = np.hstack([X_train_feats, np.ones((X_train_feats.shape[0], 1))])
X_val_feats = np.hstack([X_val_feats, np.ones((X_val_feats.shape[0], 1))])
X_test_feats = np.hstack([X_test_feats, np.ones((X_test_feats.shape[0], 1))])
Done extracting features for 1000 / 5000 images
Done extracting features for 2000 / 5000 images
Done extracting features for 3000 / 5000 images
Done extracting features for 4000 / 5000 images
使用 k-NN 算法对图像进行分类
使用上面提取的特征执行 k-NN 算法对图像分类,在这里,【加分项】你也可以实验对比使用 HOG 特征、颜色直方图特征与使用图像原始特征(像素)哪个好。或者尝试特征拼接。
这里你需要补全 KNearestNeighbor 类中的部分关键函数的代码,包括compute_distances_one_loop,compute_distances_two_loop,compute_distances_no_loop
你可能需要认真阅读玩下面两篇文章后才能完成这部分作业:
class KNearestNeighbor(object):
""" 使用 L2 距离的 kNN 分类器 """
def __init__(self):
pass
def train(self, X, y):
"""
训练分类器
输入:- X: 一个形状为 (num_train, D) 的 numpy 数组,包含训练数据。- y: 一个形状为 (num_train,) 的 numpy 数组,包含训练标签,其中 y[i]是 X[i]的标签。"""
self.X_train = X
self.y_train = y
def predict(self, X, k=1, num_loops=0):
"""
使用该分类器预测测试数据的标签。输入:- X: 一个形状为 (num_test, D) 的 numpy 数组,包含测试数据。- k: 为预测标签投票的近邻的数量。- num_loops: 决定使用哪种方法来计算训练点和测试点之间的距离。训练点和测试点之间的距离。输出:- y: 一个形状为 (num_test,) 的 numpy 数组,包含测试数据的预测标签。测试数据的预测标签,其中 y[i]是测试点 X[i]的预测标签。"""
if num_loops == 0:
dists = self.compute_distances_no_loops(X)
elif num_loops == 1:
dists = self.compute_distances_one_loop(X)
elif num_loops == 2:
dists = self.compute_distances_two_loops(X)
else:
raise ValueError('Invalid value %d for num_loops' % num_loops)
return self.predict_labels(dists, k=k)
def compute_distances_two_loops(self, X):
"""
计算 X 中每个测试点和 self.X_train 中每个训练点之间的距离。需要使用一个嵌套循环。输入:
- X:一个形状为 (num_test, D) 的 numpy 数组,包含测试数据。输出:
- dists: 一个形状为 (num_test, num_train) 的 numpy 数组,其中 dists[i, j] 是第 i 个测试点和第 j 个训练点之间的欧几里得距离。"""
num_test = X.shape[0]
num_train = self.X_train.shape[0]
dists = np.zeros((num_test, num_train))
for i in xrange(num_test):
for j in xrange(num_train):
#####################################################################
# TODO: #
# 计算第 i 个测试点和第 j 个训练点之间的 l2 距离,并将结果存入 dists[i, j]中
#####################################################################
""" 你的代码 """
dists[i, j] = np.linalg.norm(X[i] - self.X_train[j])
#####################################################################
# END OF YOUR CODE #
#####################################################################
return dists
def compute_distances_one_loop(self, X):
"""
计算 X 中每个测试点和 self.X_train 中每个训练点之间的距离。只需在测试数据上进行一次循环。输入 / 输出。与 compute_distances_two_loops 相同
"""
num_test = X.shape[0]
num_train = self.X_train.shape[0]
dists = np.zeros((num_test, num_train))
for i in xrange(num_test):
#######################################################################
# TODO: #
# 计算第 i 个测试点和所有训练点之间的 l2 距离,并将结果存入 dists[i, :]中。#
#######################################################################
""" 你的代码 """
dists[i] = np.linalg.norm(X[i]-self.X_train, axis=1)
#######################################################################
# END OF YOUR CODE #
#######################################################################
return dists
def compute_distances_no_loops(self, X):
"""
计算 X 中每个测试点和 self.X_train 中每个训练点之间的距离,不使用显式循环。输入 / 输出。与 compute_distances_two_loops 相同
"""
num_test = X.shape[0]
num_train = self.X_train.shape[0]
dists = np.zeros((num_test, num_train))
##########################################################################
# TODO: #
# 计算所有测试点和所有训练点之间的 l2 距离,不使用任何显式循环 #
# 并将结果存储在 dists 中 #
##########################################################################
""" 你的代码 """
x2 = np.sum(X**2, axis=1).reshape((num_test, 1))
y2 = np.sum(self.X_train**2, axis=1).reshape((1, num_train))
xy = -2 * np.matmul(X, self.X_train.T)
dists = np.sqrt(x2 + xy + y2)
##########################################################################
# END OF YOUR CODE #
##########################################################################
return dists
def predict_labels(self, dists, k=1):
"""
给出一个测试点和训练点之间距离的矩阵。为每个测试点预测一个标签。输入:
- dists: 一个形状为 (num_test, num_train) 的 numpy 数组,其中 dists[i, j] ... 表示第 i 个测试点和第 j 个训练点之间的距离。输出:
- y: 一个形状为 (num_test,) 的 numpy 数组,包含测试数据的预测标签。其中 y[i]是测试点 X[i]的预测标签。"""
num_test = dists.shape[0]
y_pred = np.zeros(num_test)
for i in xrange(num_test):
# 一个长度为 k 的列表,存储第 i 个测试点的 k 个最近的邻居的标签。closest_y = []
# 使用距离矩阵找到第 1 个测试点的 k 个最近的邻居,并使用 self.y_train 来找到这些邻居的标签。#
# 将这些标签存储在 closest_y 中。#
k_nearest_idxs = np.argsort(dists[i, :])[:k]
closest_y = self.y_train[k_nearest_idxs]
# 找到标签列表 closest_y 中最常见的标签,将这个标签存入 y_pred[i]。#
y_pred[i] = np.argmax(np.bincount(closest_y))
return y_pred
# 实例化
classifier = KNearestNeighbor()
# 训练 KNN 分类器
classifier.train(X_train_feats, y_train)
# 使用验证集调整 k 的值
k_choices = [1, 3, 5, 8, 10, 12, 15, 20, 50, 100]
k_to_accuracies = {} # 用来存储结果
##########################################################################################
# TODO: #
# 你需要从 k_choice 中找出最好的 k #
# 如果 accuracy <= 0.1,说明结果不对,随机猜的准确率是 0.1,需要修改 k_nearest_neighbor.py #
##########################################################################################
#遍历所有的 k,在验证集上进行结果测试分类的准确性
for k in k_choices:
""" 你的代码 """
k_to_accuracies[k] = []
num_folds = 5
X_train_folds = np.array_split(X_train_feats, num_folds)
y_train_folds = np.array_split(y_train, num_folds) # 不均等分割
accuracies = []
for i in range(num_folds):
X_train_cv = np.vstack(X_train_folds[0:i] + X_train_folds[i+1:])
y_train_cv = np.hstack(y_train_folds[0:i] + y_train_folds[i+1:])
X_valid_cv = X_train_folds[i]
y_valid_cv = y_train_folds[i]
classifier.train(X_train_cv, y_train_cv)
dists = classifier.compute_distances_no_loops(X_valid_cv)
y_valid_pred = classifier.predict_labels(dists, k)
num_correct = np.sum(y_valid_pred == y_valid_cv)
accuracy = float(num_correct) / y_valid_cv.shape[0]
accuracies.append(accuracy)
k_to_accuracies[k] = accuracies
for k in k_choices:
accuracies = k_to_accuracies[k]
print('k = %d, average accuracy = %f' % (k, np.average(accuracies)))
plt.scatter([k] * len(accuracies), accuracies)
accuracies_mean = np.array([np.mean(v) for k,v in sorted(k_to_accuracies.items())])
accuracies_std = np.array([np.std(v) for k,v in sorted(k_to_accuracies.items())])
plt.errorbar(k_choices, accuracies_mean, yerr=accuracies_std)
plt.title('accuracy-k')
plt.xlabel('k')
plt.ylabel('accuracy')
plt.show()
##########################################################################################
# END OF YOUR CODE #
##########################################################################################
print(k_to_accuracies)
k = 1, average accuracy = 0.304400
k = 3, average accuracy = 0.297800
k = 5, average accuracy = 0.311400
k = 8, average accuracy = 0.317200
k = 10, average accuracy = 0.326200
k = 12, average accuracy = 0.321400
k = 15, average accuracy = 0.314200
k = 20, average accuracy = 0.316600
k = 50, average accuracy = 0.289600
k = 100, average accuracy = 0.265600

{1: [0.292, 0.29, 0.287, 0.325, 0.328], 3: [0.289, 0.296, 0.296, 0.29, 0.318], 5: [0.315, 0.297, 0.314, 0.3, 0.331], 8: [0.32, 0.308, 0.327, 0.303, 0.328], 10: [0.328, 0.309, 0.334, 0.319, 0.341], 12: [0.312, 0.315, 0.321, 0.332, 0.327], 15: [0.315, 0.304, 0.319, 0.32, 0.313], 20: [0.334, 0.312, 0.3, 0.326, 0.311], 50: [0.295, 0.282, 0.292, 0.298, 0.281], 100: [0.285, 0.276, 0.251, 0.27, 0.246]}
# 评估你的算法
##########################################################################################
# TODO: #
# 根据验证集的结果,选择合适的 K 值,并在测试集上测试结果 #
""" 你的代码 """
best_k = 10 # 填你上面选出的 K 值
##########################################################################################
# END OF YOUR CODE #
##########################################################################################
# 计算测试集上准确率
y_test_pred = classifier.predict(X_test_feats, k=best_k)
test_accuracy = np.mean(y_test == y_test_pred)
print(test_accuracy)
0.302