《机器学习(周志华)》学习笔记(十)

Q:什么是K临近学习?

K临近学习是一种监督学习算法。与以往的监督学习,如线性回归、决策树、支持向量机等不同,K临近学习虽然也需要训练数据,但不需要事先使用训练集做模型训练。

K临近学习的核心思想是“物以类聚,人以群分”。假设一个二分类任务,给定一个样本要求判定该样本是正类还是反类,则仅需以该样本为圆心,找出离该样本最近的k个训练样例,若这k个训练样例中正例多,则判定该样本为正例,否则判定为反例。整个判定只需要设置一个k值,即选择多少个邻居,不需要做其他事情,因此不需要事先使用训练集做模型训练

k临近学习

举一个可能不怎么恰当的例子:在下课时间内判断一个小学生教室里某个学生a是男是女(下课时段内人们可以自由走动聚集),则可以看看距离学生a最近的k个学生,是男生多还是女生多,如果男生多则判定学生a是男生,否则判定为女生。因为按照常理判断,小学生一般是男生和男生一起玩,女生和女生一起玩的,只有个别例外。

在找出样本附近k个邻居时(实现标记好的训练样例),需要计算样本间的距离。这个距离一般使用欧氏距离,也可以用其他合适的距离做计算。

"""
Simple K nearest neighbors classifier

:file: supervised.py
:author: Richy Zhu
:email: rickyzhu@foxmail.com
"""
import numpy as np

class KNNClissifier:

    def __init__(self):
        self.X = None
        self.y = None
        self.k = None
        self.func_dist = None

    def _top_k_nn(self, centroid, surroundings):
        '''
        return indices of the top k nearest neighbors of centroid 
        from surroundings
        '''
        distances = []
        for vec in surroundings:
            distances.append(self.func_dist(centroid - vec))
        distances = np.array(distances)
        top_k = np.argsort(distances)[:self.k]

        # print('neightbors and distances:', \
        #    [(v, d) for v, d in zip(surroundinds, distances)])
        return top_k

    def fit(self, X, y, k=1, func_dist=None):
        '''
        Train the K nearest neighbor classifier model

        Parameters
        ----------
        X: ndarray of shape (m, n)
            sample data where row represent sample and column represent feature
        y: ndarray of shape (m,)
            labels of sample data
        k: int
            number of neightbors selected to compare
        func_dist: function
            distance function, by default Euclidean distance

        Returns
        -------
        self
            trained model
        '''
        self.X = X
        self.y = y
        self.k = k
        if func_dist == None:
            self.func_dist = np.linalg.norm # Euclidean distance
        else:
            self.func_dist = func_dist

    
    def predict(self, X):
        '''
        Make prediction by the trained model.

        Parameters
        ----------
        X: ndarray of shape (m, n)
            data to be predicted, the same shape as trainning data

        Returns
        -------
        C: ndarray of shape (m,)
            Predicted class label per sample.
        '''
        if self.X is None:
            raise Exception("Model haven't been trained!")

        from collections import Counter
        C = []
        for x in X:
            top_k_indices = self._top_k_nn(x, self.X)
            top_k_labels = self.y[top_k_indices]

            # top_k = self.X[top_k_indices]
            # print('top k neightbors:', top_k)
            # print('class of top k neightbors:', top_k_labels)
            C.append(Counter(top_k_labels).most_common(1)[0][0])

        return np.array(C)

测试代码如下

from sklearn.datasets import load_boston, load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, accuracy_score

print('\nK Nearest Neighbors')
print('---------------------------------------------------------------------')
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target)

kclf = KNNClissifier()
kclf.fit(X_train, y_train, 2)
y_pred = kclf.predict(X_test)
print(y_pred, y_test)
print('My Accuracy:', accuracy_score(y_test, kclf.predict(X_test)))

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier()
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print('Sk Accuracy:', accuracy_score(y_test, clf.predict(X_test)))

测试结果如下

$ python supervised_example.py
K Nearest Neighbors
---------------------------------------------------------------------
My Accuracy: 0.9473684210526315
Sk Accuracy: 0.9736842105263158

Q:会不会出现训练样例分布太过稀疏,以至于选择的k个邻居其实都不是离该样本很近,导致邻居们不足以代表目标样本?

会,而且样本的维度越高(特征/属性越多),这种情况越容易出现。因为采集只有两个特征的样本数据很简单(西瓜的体积和重量),但采集有十个特征的样本数据就不容易了(西瓜的体积、重量、含水量、密度、甜度、表面积······),而采集五十个甚至一百个特征的样本数据就更难了,即便能做到,成本也会高的惊人。因此高维样本的数的分布,稀疏才是常态,这在机器学习中称作“维数灾难”。另外高维样本的距离计算起来也不容易。

Q:若出现上述情况,可以怎么解决?

一个重要的解决方法是降维。顾名思义,把维度降低,就是讲原始的高维空间中样本映射到低维空间中,同时保持一些对于学习任务来说很重要的信息不丢失。

举个例子,假如我们要训练自己的对于跑车外形的鉴别能力。我们当然不可能把车行里全部三维的样本(真正的实物跑车)弄回家处理,因此我们可以选择降维,把车辆信息从三维空间映射二维空间——给所有跑车拍照,把照片带回家处理。虽说拍照会损失很多车辆的信息,比如车内配置,但对于我们的学习任务:训练自己的对于跑车外形的鉴别能力来说,最重要的信息,车辆外形信息得到了很好的保持。这就是典型的降维。当然这个例子中降维的主要动机不是由于样本稀疏分布,而是由于我们没钱把完整的三维样本带回家而已。

Q:机器学习中对样本数据的降维方法有哪些?

  • 维度缩放(MDS):保持降维后各个样本间的距离不变。
  • 主成分分析(PCA):保持降维后各个样本间的线性关系。
  • 核化线性降维:保持高维空间中样本分布的部分结构信息。
  • 流型学习:借鉴了拓扑学流型概念(好吧,我也不知道什么东西)的降维方法。
  • 度量学习:直接学习出最优的降维后样本间距离。这个涉及的数学知识有点深,看不懂。

Q:什么是 维度缩放(MDS)?

MDS示例

想象左边的三维空间中,样本点全部落在一张弯曲的纸上,而右边的二维空间则是将左边的弯曲的纸拉直了,然后所有点都变成了落在一个平面上。在这个变化过程中,所有点之间的距离是保持不变的。如果左边有两个点:(0,0,0)和(1,1,1),距离为根号3,那么变换后这两个点之间的距离应该也为根号3,比如(0,0)和(1,)。

那么具体算法应该如何?假设D是原样本数据集,里面的每个元素是\vec{x_i},Z是降维后的样本数据集,里面每个元素是\vec{z_i}dist_{ij}是原样本集中x_ix_j的距离,也是降维后样本集中\vec{z_i}\vec{z_j}的距离。我们可以通过距离dist_{ij}这点,通过降维后矩阵的内积矩阵B来间接确定降维后的矩阵。

嗯,下面是一波数学公式。
这轮计算要求解降维后矩阵Z的内积矩阵B!
这轮计算要求解降维后矩阵Z的内积矩阵B!
这轮计算要求解降维后矩阵Z的内积矩阵B!

首先是dist的计算公式


dist的计算公式

将dist计算公式两边平方后,用完全平方公式展开,注意看b_{ij}是什么东西,这个b_{ij}是我们这轮计算要求解的量。

将dist计算公式两边平方后,用完全平方公式展开

为求解,需要先求解和,那么这两个又怎么求?

先做一些简化,比如数据中心化,也就是说每个样本都减去样本总体的平均值,这样处理后所有样本的加起来等于0。下面公式中矩阵的是指一个n×n矩阵A的主对角线(从左上方至右下方的对角线)上各个元素的总和。B是Z的内积矩阵,内积矩阵B的迹就是等于Z中各个列向量的范数的和,因此可以写成下面的形式。

联立上面三式可求解出b_ii和b_jj

然后为了方便最后计算b_{ij},定义三个常量。

上面三等式左边都是常量,不随i和j的变化而变化

联立上面各式,求得$b_{ij},也就是内积矩阵B的每个元素。


最后求解内积矩阵B

求得了内积矩阵B,如何反推出矩阵Z?这一部分我数学不好,不理解,书上直接给出:


由内积矩阵B,反推出矩阵Z

由此,求得降维后的矩阵Z。整个过程就是用原矩阵D,求出各个样本x_ix_j的距离dist_{ij},然后利用不变的dist_{ij},以及Z的内积矩阵B,求解出降维后矩阵Z。也就是

MDS算法

Q:什么是主成分分析(PCA)?

PCA是一种利用了线性变换的降维方式。那么PCA使用的线性变换有什么要求呢,或者说要满足什么条件呢?

想象这种线性变换是把一个高维空间中的样本点投影到一个超平面上(相对低维空间,二维平面的高维推广),按照常识,我们当然希望投影后的点能够尽量分开而不要很密集地堆在一起。翻译成数学语言,就是希望投影后的样本点总体方差最大。这就是我们的优化目标,也就是PCA的线性变换所要满足的条件。

希望投影后的样本点总体方差最大

假设原始数据集为X,里面每个元素都是d维向量x_i,变换后的数据集为Z,每个元素都是d’维向量z_i,使用矩阵W来对X做线性变换:

PCA使用的线性变换

按照总体方差最大化的优化目标,可以写出下面式子(假设样本经过了中心化,使得均值为0):
max \sum_i{(\vec{z_i} - \vec{\mu} )^2 } \\ = max \sum_i{(\vec{z_i} - \vec{0} )^2 } \\ = max \sum_i{(\textbf{W}^T\vec{x_i})^2 }\\ = max \sum_i{(\textbf{W}^T\vec{x_i})(\textbf{W}^T\vec{x_i})}\\ = max \sum_i{\textbf{W}^T\vec{x_i}\vec{x_i}^T\textbf{W}}

优化目标,求解W

如何求解W?可以使用拉格朗日乗子法

用拉格朗日乘子法得到了这个式子

只要把看成一个整体矩阵A(其实就是X的协方差矩阵),就不难看出这是一个求矩阵A的特征值和特征向量的问题,也就是说把A的特征值和特征向量全部求出来去最大的d'个特征值对应的特征向量,就可以构成W了。
PCA算法

得到W(变换矩阵)后,就可以对原样本X做线性变换降维了:


降维

得到的Z就是降维后的样本集(矩阵)。

以上基于“最大可分性”的推导较为简单,容易理解;PCA还可以根据“最近重构性”推导,嫌麻烦,不看了。

"""
Principle Component Analysis

:file: pca.py
:author: Richy Zhu
:email: rickyzhu@foxmail.com
"""
import numpy as np


class MyPCA:

    def __init__(self):
        self.projector = None

    def _covM(self, _X):
        '''compute covariance matrix'''
        M = np.zeros([_X.shape[1], _X.shape[1]])
        for i in range(len(_X)):
            col_vec = _X[i].reshape([-1,1]) # the i-th column vector
            M += col_vec.T.dot(col_vec)
        return M / len(_X)

    def fit_transform(self, X, k):
        '''
        PCA for X using Karhunen-Loeve Transform. Return the top k components.

        Parameters
        ----------
        X: ndarray of shape (m, n)
            sample data where row represent sample and column represent feature
        k: int 
            how many components to return 

        Returns
        -------
        X_transformed
            top k component of original data
        '''
        mean_vector = np.mean(X, axis=0)
        Xp = X - mean_vector  # non-zero vectors
        X_cov = self._covM(Xp)
        E, V = np.linalg.eig(X_cov) # eigenvectors and eigenvalues
        self.projector = np.array([V.T[np.argsort(-E)[i]] for i in range(k)])
        X_transformed = self.projector.dot(Xp.T).reshape(-1, k)
        return X_transformed

测试代码如下

import numpy as np
from sklearn.decomposition import PCA
X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])

mypca = MyPCA()
print('My k components:\n', mypca.fit_transform(X, 1))

pca = PCA(n_components=1)
print('Sk k components:\n', pca.fit_transform(X))

print('My total varience ', np.var(mypca.fit_transform(X, 1)))
print('Sk total varience ', np.var(pca.fit_transform(X)))

测试结果如下

My k components:
 [[-1.41421356]
 [-2.12132034]
 [-3.53553391]
 [ 1.41421356]
 [ 2.12132034]
 [ 3.53553391]]
Sk k components:
 [[ 1.38340578]
 [ 2.22189802]
 [ 3.6053038 ]
 [-1.38340578]
 [-2.22189802]
 [-3.6053038 ]]
My total varience  6.333333333333333
Sk total varience  6.616285933932036

Q:什么是核化线性降维?

假设我们对现实世界采样,得到了以下的样本总体


样本分布情况具有一定空间结构特点

如果我们使用线性降维方式,比如最常用的PCA,降维后的数据集如下


丢失了原样本的空间分布信息

从降维结果来看,线性降维丢失了原本样本的空间分布信息,有时候这没关系,但有时候我们希望保留一定的原样本的空间分布信息,那就不能简单的使用线性降维,而需要使用非线性降维,比如最常用的KPCA,核主成分分析,即引入核函数。

假设原始数据集为X,里面每个元素都是d维向量x_i。既然X里的样本不适合做线性变换,我们可以先用一个非线性映射,将X里的样本映射成适合做线性变换的样本Z,每个元素都是向量z_i,然后······然后当然是使用PCA来降维后者干些别的事啦。

那么我们要怎么计算KPCA的变换矩阵W呢?

先来一波公式。
这波计算是的目标是计算变换矩阵W。
这波计算是的目标是计算变换矩阵W。
这波计算是的目标是计算变换矩阵W。

下面公式是把X非线性映射成Z后,计算PCA使用的变换矩阵W。


非线性映射后,求解W

从上面(10.19)式可以得到变换矩阵W的表达式


至于使用哪种非线性变换,可以先用Φ来表示。


X使用的非线性变换

那么(10.19)和(10.20)式就可以写成下面的形式



引入核函数


这是核函数

联立上面三式可以得到

Paste_Image.png

由此我们可以用特征值与特征向量的处理方法解出矩阵A。

至此为止我们还没有算出变换矩阵W!
至此为止我们还没有算出变换矩阵W!
至此为止我们还没有算出变换矩阵W!

但我们算出了矩阵A,而A与W有着莫大的联系



因此可以利用矩阵A代替变换矩阵W的作用,求解出降维后的样本:

降维后的样本z_i的计算公式

必须说一句,我个人觉得这里的符号使用不太合理,之前把x_i非线性映射后,用了z_j来表示,后面求解x_i降维后的样本,又用z_i来表示,这很容易造成混淆。

Q:什么是流型学习?

流行学习借鉴了拓扑学流型概念,可以用于保持一定的样本分布空间结构信息。

对于上面的例子,我们能否直接用MDS对原始样本集降维?不合适。因为MDS用到距离计算一般是两点间的直线距离(无论是欧式距离还是闵可夫斯基距离)。但是我们可以专门为其分布情况具有一定拓扑结构的样本,比如流型等,定制一种距离。流型中使用的距离就是测地线,以及两点间不穿过曲面的最短路程,可以想象一只蚂蚁从一点爬到另一点的路程。

使用测地线距离配合MDS算法

测地线怎么算?还是要用到化曲为直的思想,从起点开始往终点的方向,基于欧氏距离挑选出临近的点,然后挑选出临近点的临近点,以此类推,知道把终点囊括在内,构成一个包含起点和终点的无向图,然后使用图的最短路径算法求出起点到终点的最短路径,以此近似作为两点间的测地线。

算出测地线后以此作为空间中两点间的距离,然后就可以使用MDS算法了。这套思路总结起来称为 Isomap算法。

Isomap算法

LLE算法不作说明。


更多代码请参考https://github.com/qige96/programming-practice/tree/master/machine-learning


本作品首发于简书博客园平台,采用知识共享署名 4.0 国际许可协议进行许可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容