MDS和ISOMAP降维
本作业使用MDS和ISOMAP两种降维算法,对耶鲁大学人脸数据集进行降维,然后使用作业1中的对率回归(逻辑回归)分类器进行训练。耶鲁大学人脸数据集下载自新浪微盘。
数据的读取
作业的分类目的是区分照片中的人是否有胡子,而原数据集中,有胡子的照片只有44张,远少于没有胡子的,所以我删去了55张没有胡子的人的照片。最终保留有胡子的照片44张,没有胡子的照片66张,共110张。代码中使用 PIL 的 Image 模块读取图片,并同时读取 Label 文件,Label 文件用 0 表示没有胡子,用 1 表示有胡子。
if __name__ == '__main__':
print('正在读取数据并降维')
data = np.empty([110, 10000], np.float32)
for idx in range(110):
image = Image.open('Data/s' + str(idx + 1) + '.bmp')
data[idx] = np.reshape(image, [10000])
file = open('Data/labels.txt')
label = np.array(file.readline().strip('\n').split(','), np.int32)
MDS降维函数
算法部分我就不说了,直接上图。内容来自周志华《机器学习》教材。
其中,
dist2ij
代表第
i
项数据到第
# 获取欧氏距离
# data: 要获取欧氏距离的矩阵,大小 m * n
# return:m * m 的矩阵,第 [i, j] 个元素代表 data 中元素 i 到元素 j 的欧氏距离
def get_distance(data):
data_count = len(data)
mat_distance = np.zeros([data_count, data_count], np.float32)
for idx in range(data_count):
for sub_idx in range(data_count):
mat_distance[idx][sub_idx] = np.linalg.norm(data[idx] - data[sub_idx])
return mat_distance
随后,按照算法要求完成MDS算法的编程。
# mds 算法的具体实现
# data:需要降维的矩阵
# target:目标维度
# return:降维后的矩阵
def mds(data, target):
data_count = len(data)
if target > data_count:
target = data_count
val_dist_i_j = 0.0
vec_dist_i_2 = np.zeros([data_count], np.float32)
vec_dist_j_2 = np.zeros([data_count], np.float32)
mat_b = np.zeros([data_count, data_count], np.float32)
mat_distance = get_distance(data)
for idx in range(data_count):
for sub_idx in range(data_count):
dist_ij_2 = np.square(mat_distance[idx][sub_idx])
val_dist_i_j += dist_ij_2
vec_dist_i_2[idx] += dist_ij_2
vec_dist_j_2[sub_idx] += dist_ij_2 / data_count
vec_dist_i_2[idx] /= data_count
val_dist_i_j /= np.square(data_count)
for idx in range(data_count):
for sub_idx in range(data_count):
dist_ij_2 = np.square(mat_distance[idx][sub_idx])
mat_b[idx][sub_idx] = -0.5 * (dist_ij_2 - vec_dist_i_2[idx] - vec_dist_j_2[sub_idx] + val_dist_i_j)
a, v = np.linalg.eig(mat_b)
list_idx = np.argpartition(a, target - 1)[-target:]
a = np.diag(np.maximum(a[list_idx], 0.0))
return np.matmul(v[:, list_idx], np.sqrt(a))
ISOMAP降维函数
ISO降维是另一种降维算法,但它也要用到MDS。书上对ISOMAP算法的描述如下:
因此,首先需要实现最短路径算法。在这里我选择的是 Dijkstra 算法。
# 使用 Dijkstra 算法获取最短路径,并更新距离矩阵
# data: 距离矩阵,大小 m * m
# src:最短路径的起始点,范围 0 到 m-1
def dijkstra(data, src):
inf = float('inf')
data_count = len(data)
col_u = data[src].copy()
dot_remain = data_count - 1
while dot_remain > 0:
dot_k = np.argpartition(col_u, 1)[1]
length = data[src][dot_k]
for idx in range(data_count):
if data[src][idx] > length + data[dot_k][idx]:
data[src][idx] = length + data[dot_k][idx]
data[idx][src] = data[src][idx]
dot_remain -= 1
col_u[dot_k] = inf
在ISOMAP算法中,首先获得每个数据点的
k
近邻最短路径,
# isomap 算法的具体实现
# data:需要降维的矩阵
# target:目标维度
# k:k 近邻算法中的超参数
# return:降维后的矩阵
def isomap(data, target, k):
inf = float('inf')
data_count = len(data)
if k >= data_count:
raise ValueError('K的值最大为数据个数 - 1')
mat_distance = get_distance(data)
knn_map = np.ones([data_count, data_count], np.float32) * inf
for idx in range(data_count):
top_k = np.argpartition(mat_distance[idx], k)[:k + 1]
knn_map[idx][top_k] = mat_distance[idx][top_k]
for idx in range(data_count):
dijkstra(knn_map, idx)
return mds(data, target)
搞定~接下来在 Main 中调用即可。在这里偷了个懒,由于Yale数据集中,每个人都有11张照片,所以我用10张训练,1张测试,总共有100张训练数据,10张测试数据,迭代500次。这里直接使用了 机器学习作业1 中提到的对率回归分类器。
# 对数据进行降维,从原先的 10000 维降低至 20 维,并保存到文件
# 然后使用作业一中的对率回归分类器进行训练,迭代次数 500 次
# 最后使用测试数据检验模型准确率,测试数据共 10 份
data_reduced = Reduction.isomap(data, 20, 15)
np.savetxt('data_reduced.txt', data_reduced, '%.7e', '\t')
sys.stdout.write('降维操作完成,低维度数据已保存到 data_reduced.txt\n')
classifier = Classifier.Classifier(20)
for repeat in range(500):
for idx in range(110):
if idx % 11 != 0:
classifier.fit(data_reduced[idx], label[idx])
sys.stdout.write('\r正在训练,已完成 %.1f%%' % (repeat * 100 / 500))
sys.stdout.write('\r训练完毕,下面开始测试\n')
correct_times = 0
for idx in range(10):
val = classifier.classify(data_reduced[idx * 11])
print('第 %2d 次预测值:%d,真实值:%d' % (idx + 1, val, label[idx * 11]))
if val == label[idx * 11]:
correct_times += 1
print('测试完毕,准确率:%.2f%%' % (correct_times * 100 / 10))
运行结果
在 Python 3.5 的环境中,运行结果如下:
正在读取数据并降维
降维操作完成,低维度数据已保存到 data_reduced.txt
训练完毕,下面开始测试
第 1 次预测值:0,真实值:0
第 2 次预测值:1,真实值:1
第 3 次预测值:0,真实值:0
第 4 次预测值:0,真实值:0
第 5 次预测值:0,真实值:0
第 6 次预测值:0,真实值:0
第 7 次预测值:1,真实值:1
第 8 次预测值:0,真实值:0
第 9 次预测值:1,真实值:1
第 10 次预测值:1,真实值:1
测试完毕,准确率:100.00%
同时,程序将降维后的数据保存到了 txt 文件中,取其中一份数据放上来:
2.7251480e+02 4.0315417e+02 -5.1114426e+02 -6.7107399e+01 2.8956531e+02 -3.9103143e+00 -9.1109747e+02 7.2691174e+02 3.5637573e+02 9.6454456e+02 -3.3063428e+02 -5.5546588e+02 -9.0832664e+01 3.0922861e+02 -7.5431165e+02 -1.1431976e+02 7.9602502e+02 5.0018698e+02 3.3077114e+03 -2.6078193e+03
详细代码和数据文件请访问:
https://coding.net/u/dapanbest/p/MLHomeworks/git/tree/master/DimensionReduction
完结撒花!