机器学习作业2

来源:互联网 发布:抗抑郁药 知乎 编辑:程序博客网 时间:2024/06/03 14:31

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

原创粉丝点击