机器学习作业2 - MDS和ISOMAP降维

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降维函数

算法部分我就不说了,直接上图。内容来自周志华《机器学习》教材。

dist求解过程
MDS算法

其中, dist2ij 代表第 i 项数据到第 j 项数据的欧氏距离,因此,我们首先实现欧氏距离求解函数。

# 获取欧氏距离
# 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算法的描述如下:

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 近邻最短路径,k 之外的点的距离一律设为无限大。然后调用最短路径算法,更新距离矩阵,最后使用更新过的距离矩阵作为MDS算法的输入,求得结果。

# 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
完结撒花!

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值