├── LICENSE ├── README.md ├── docs ├── .ipynb_checkpoints │ └── UMAP-checkpoint.ipynb ├── ISOMAP.md ├── KPCA.md ├── LDA.md ├── LLE.md ├── MDS.md ├── PCA.md ├── QDA.md └── UMAP.ipynb ├── resources ├── KPCA │ ├── C_eigen.jpg │ ├── KPCA.jpg │ ├── RBF.png │ ├── covariance.jpg │ ├── polynomial.png │ ├── sigmoid_kernel.png │ └── uniform_C_eigen.jpg ├── LDA │ ├── generalized_rayleigh_quotient.png │ ├── lda_between.png │ ├── lda_between_2.png │ ├── lda_within.png │ ├── lda_within_2.png │ ├── optimizing.png │ └── prob │ │ ├── bayes.png │ │ ├── likelihood.png │ │ ├── linear_classifier.png │ │ └── linear_discrimilant_function.png ├── LLE │ ├── LLE.png │ ├── LLE_3.png │ ├── LLE_4.png │ ├── LLE_algorithm.png │ └── lle_2.jpg ├── MDS │ ├── D2B.jpg │ └── D2B_1.png ├── PCA │ ├── max_variance.png │ └── min_distance.png └── QDA │ └── discrimilant_function.png ├── results ├── Isomap │ ├── isomap.png │ ├── isomap_k_5.png │ └── origin_data.png ├── KPCA │ ├── gaussian_kenel.png │ ├── gaussian_kenel_2.png │ └── polynomial.png ├── LDA │ ├── moon.png │ └── regression.png ├── LLE │ ├── LLE_k_30.png │ ├── LLE_k_30_without0.png │ └── origin_data.png ├── MDS │ ├── to1dim.png │ └── to2dim.png └── PCA │ ├── PCA.png │ └── PCA_2.png └── src ├── ISLR ├── eleven.R └── ten.R ├── ISOMAP.py ├── KPCA.py ├── LDA.py ├── LLE.py ├── MDS.py ├── PCA.py └── QDA.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 hongyu wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dimension-reduction-algorithms 2 | ## 一、介绍 3 | 在高维情形下会出现数据样本稀疏,距离计算困难等问题,是所有机器学习方法面临的严峻考验,称为“维数灾难”(curse of dimensionality)。缓解维数灾难一个重要途径是降维, 4 | 即通过某种数学变换将数据映射到一个低维空间,在这个低维空间里,数据的密度大大地提高,距离计算更加容易。 5 | 6 | ## 二、分类 7 | 降维算法可以按照是否有监督,变换是否是线性的分成四类: 8 | 1. 无监督的线性降维算法,比如[PCA](docs/PCA.md) 9 | 2. 无监督的非线性降维算法,比如[KPCA](docs/KPCA.md)、[MDS](docs/MDS.md)、[ISOMAP](docs/ISOMAP.md)、[LLE](docs/LLE.md) 10 | 3. 有监督的线性降维算法,比如[LDA](docs/LDA.md) 11 | 4. 有监督的非线性降维算法(Embedding技术,比如NLP中的词嵌入技术word2vec,推荐中的图嵌入技术等等) 12 | 13 | **注意**:这里线性指的是降维变换f:高维空间 -> 低维空间是线性的。MDS、Isomap是将一个非线性降维变换的求解问题转化成了一个线性代数问题,它们并不是线性降维算法。 14 | 15 | ## 三、总结 16 | 在大部分实际应用情况下,数据降维是作为后续任务的一个预处理步骤,需要通过比较降维后学习器的效果来对一个具体的任务使用某种降维算法。 17 | 18 | 流形学习中的ISOMAP、LLE等算法非常依赖建图的质量,依赖样本的采样密度,而在高维空间是几乎不可能**密采样**的。Isomap在低维空间中效果非常酷炫, 19 | 但图上最短路径算法Floyd算法太慢,很难在高维空间得到应用。除算法层面外,高维空间还有curse of dimensionality问题,高维空间中任意两个样本点的欧式距离差别不大[2]。这直接导致依赖度量的流形学习算法在高维空间中无法建图,还有比如KNN、谱聚类等基于度量的算法同样在高维空间中失效。 20 | 21 | PCA、MDS和LDA都是比较简单、实用的降维算法,也较常用。KPCA是带核技巧的PCA,在特征空间(再生核希尔伯特空间)上进行PCA降维,然而核函数的选择方式依然没有有效的指导。 22 | 23 | ## 四、参考资料 24 | [1] 周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 25 | 26 | [2] [知乎-高维数据欧式距离失效](https://www.zhihu.com/question/323639342) 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/.ipynb_checkpoints/UMAP-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 统一流形逼近投影(UMAP)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## 一、介绍\n", 15 | "UMAP全称uniform manifold approximation and projection,统一流形逼近与投影,是继ISOMAP,LLE之后,流形学习在数据降维算法领域的又一力作。\n", 16 | "\n", 17 | "UMAP和t-SNE降维算法除了用在数据预处理之外,还用在**可视化高维数据**上,UMAP有非常快的计算速度(比t-SNE更快),可以实现大数据集的降维。" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "## 二、算法细节\n", 25 | "\n", 26 | "UMAP对数据有三点假设:\n", 27 | "\n", 28 | "* 数据均匀地分布在一个黎曼流形上。\n", 29 | "* 黎曼度量是局部不变的。\n", 30 | "* 黎曼流形是局部连接的。\n", 31 | "\n", 32 | "通过这些假设,UMAP使用模糊拓扑结构表示高维数据。给定数据的一些低维表示,可以使用类似的过程来构造等价的拓扑表示。\n", 33 | "UMAP在低维空间中优化数据坐标,以**最小化两个拓扑表示之间的交叉熵**,从而使得低维空间的表示可以同时保证局部和全局的结构。\n", 34 | "\n", 35 | "### 2.1 构造带权无向图\n", 36 | "UMAP首先将高维数据集$X = \\{x_i\\}_i$表示成一个带权无向图,具体来说,首先构造数据点的$k$-邻居图$\\hat{G} = (V, E, W)$(带权有向图),边的权重可以直观地理解为是该有向边存在的概率。\n", 37 | "\n", 38 | "$$P_{i|j} = exp(-\\frac{d(x_i, x_j)-\\rho_i}{\\sigma_i})$$\n", 39 | "\n", 40 | "其中$\\rho_i$表示第$i$个数据点与其最近邻之间的距离,$\\sigma_i$是一个归一化系数。接着进行对称化操作,把权重变为如下形式,此时边变成无向边,权重可以直观地理解为至少有一边存在的概率。\n", 41 | "\n", 42 | "$$P_{ij}=P_{i|j} + P_{j|i} - P_{i|j}P_{j|i}$$\n", 43 | "\n", 44 | "### 2.2 构造低维表示并优化\n", 45 | "\n", 46 | "构造了数据集$X$的带权无向图表示之后,再给出数据集低维空间(降维空间)图权重的定义方法,UMAP采取了类似$T$-分布的形式\n", 47 | "\n", 48 | "$$q_{ij} = (1 +a(y_i - y_j)^{2b})^{-1}$$\n", 49 | "\n", 50 | "UMAP使用二元交叉熵(CE)作为损失函数,去最小化高维空间中的带权无向图和低维空间中带权无向图对应边之间权重的差异。\n", 51 | "\n", 52 | "$$CE(P, Q) = \\sum_{i}\\sum_{j}[P_{ij}log(\\frac{P_{ij}}{Q_{ij}}) - (1 - P_{ij})log(\\frac{1 - P_{ij}}{1 - Q_{ij}})]$$\n", 53 | "\n", 54 | "UMAP使用SGD优化算法来优化二元交叉熵。" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "### 2.3 UMAP中的超参数\n", 62 | "UMAP中有三个超参数\n", 63 | "* 降维空间维度$d$。\n", 64 | "* 构造$k$-邻居图的$k$。\n", 65 | "* 最小允许距离$d_{min}$,用来防止降维空间中数据之间距离过近,从而导致点云稠密,降低可视化效果。" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "## 三、总结\n", 73 | "\n", 74 | "ISOMAP也是流形学习在降维算法上的应用,ISOMAP也需要先将流形表示成一个$k$-邻居图,然后在该图上计算节点之间的图上最短路径,作为流形上测地距离的近似,构造距离矩阵,然后用MDS算法进行降维。ISOMAP是一种**保测地距离的降维算法**。ISOMAP算法较慢,计算图上任意两点之间距离需要花费较多时间。\n", 75 | "\n", 76 | "相比之下,UMAP要高明地多,它放弃了保持任意两点之间测地距离这一全局结构,而是仅保持$k-$邻居图权重这一局部结构,但对降维可视化来说这已经足够了。" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "## 四、参考资料\n", 84 | "\n", 85 | "[1] UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction[J]. The Journal of Open Source Software, 2018, 3(29):861." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [] 94 | } 95 | ], 96 | "metadata": { 97 | "kernelspec": { 98 | "display_name": "Python 3", 99 | "language": "python", 100 | "name": "python3" 101 | }, 102 | "language_info": { 103 | "codemirror_mode": { 104 | "name": "ipython", 105 | "version": 3 106 | }, 107 | "file_extension": ".py", 108 | "mimetype": "text/x-python", 109 | "name": "python", 110 | "nbconvert_exporter": "python", 111 | "pygments_lexer": "ipython3", 112 | "version": "3.7.6" 113 | } 114 | }, 115 | "nbformat": 4, 116 | "nbformat_minor": 4 117 | } 118 | -------------------------------------------------------------------------------- /docs/ISOMAP.md: -------------------------------------------------------------------------------- 1 | # 等度量映射(Isomap) 2 | ## 一、介绍 3 | 等度量映射(Isometric Mapping, Isomap)属于流形学习(manifold learning)的范畴,流形学习假设处理的数据处在一个高维空间的低维流形上。设Y⊂Rd是一个低维流形, 4 | 映射f:Y-->RD是一个光滑嵌入(embedding),且D>d。数据集{yi}是随机生成的,且经过嵌入映射f映射维观测空间的数据{xi=f(yi)}。基于流形学习的降维任务就是要在给定高维空间观测样本{xi}的情况下, 5 | 发现隐含的嵌入(embedding)映射f,并重构低维流形下的数据{yi}。 6 | 7 | 流形最重要的性质(也是定义)就是流形局部拓扑同胚(homeomorphism)于一个欧式空间。比如三维空间中的二维曲面,在二维曲面任意一点的邻域内,都可以认为和R2空间在拓扑意义上“像的不能再像”(存在一个连续的双射)。 8 | 9 | 等度量映射的出发点在于使用流形的内蕴距离测地距(geodesic)代替嵌入空间的度量。接着构造关于测地距的距离矩阵,然后使用[MDS](../docs/MDS.md)进行降维。 10 | 11 | ## 二、原理 12 | ### 2.1 算法流程 13 | ``` 14 | 输入:样本集、近邻参数k、降维维度d' 15 | 1. 确定xi的k近邻点,k近邻之间的距离为欧式距离,与其它点的距离为∞,构造有 16 | 向带权图邻接矩阵W,权重为距离。 17 | 2. 调用最短路径算法计算任意两个样本之间距离dist(xi, xj),构造距离矩阵D。 18 | 3. 将D作为MDS的输入进行降维。 19 | 20 | ``` 21 | 可以看到,算法流程中最重要的一步就是如何计算数据关于测地距的距离矩阵。利用流形在局部上与欧式空间拓扑同胚的性质,对每个点关于其嵌入空间的欧式距离找出近邻点, 22 | 然后就能建立一个近邻连接图,于是计算两点之间测地线距离的问题,就转变为计算**近邻连接图上两点间最短路径的问题**。 23 | 24 | 25 | ### 2.2 最短路径问题 26 | 最短路径问题是图论中的一个经典问题,即求一张有向带权图任意两点之间的最短路径(路径权值和最小),对Isomap来说,求出最短路径相当于求出了两个点之间的测地距离。 27 | 迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd)算法是求解最短路径问题的经典算法。 28 | 29 | #### 2.2.1 Dijkstra算法 30 | Dijkstra算法是一种求单源最短路径的算法,即给定一个点A(源),算法输出A到图中各个顶点最短路径,算法复杂度O(N2)。Dijkstra算法基于贪婪算法思想(但并非求近似解,输出为最优解),算法流程如下: 31 | ``` 32 | 输入:图邻接矩阵,源节点i,求i到图中所有节点的最短路径 33 | 1. 构建一个musk向量M,记录节点是否访问(访问为T,否则为F),初始化均为F 34 | 2. 构建一个dist向量D,记录i到该节点最短距离,初始化为inf。 35 | 3. 考虑节点i,令M[i] = T,考虑该节点所有未标记出度,计算更新距离向量D。 36 | 4. 从所有标签为F的节点中,找出距离最小的节点记为j(贪婪算法的体现),令M[j]=T。 37 | 5. 令新节点j为i重复3,4 38 | ``` 39 | 如果除了输出节点i到图上任意节点之间最短距离之外,还要输出最短路径,则需要引入父亲向量P,记录当前节点的父节点。 40 | 41 | #### 2.2.2 Floyd算法 42 | Floyd算法是一种基于动态规划思想的算法,算法输出图中任意两个点之间的最短路径,时间复杂度O(N3),与将每个图中每个点作为Dijkstra算法的输入相比,Floyd算法更简便。 43 | 44 | ``` 45 | 输入:有向带权图的邻接矩阵W,求最短距离矩阵D 46 | 过程: 47 | 令D = W 48 | 1. for v = 1:N 49 | 2. # v为任意两个节点路径之间的中间节点 50 | 3. for i = 1:N 51 | 4. for j=1:N 52 | 5. if D[i, j] > D[i, v] + D[v, j]: 53 | 6. D[i, j] = D[i, v] + D[v, j] 54 | 55 | ``` 56 | Floyd算法被形象地称为“三层循环算法”,若除了输出两点之间最短距离之外还要输出最短路径,则需要引入路径矩阵P,记录任意两个节点之间的最佳中间节点即可。 57 | 58 | ## 2.3 构建有向带权图 59 | 通过数据点构建有向带权图时,可以使用knn或者ε-neighboured建图,这两种方法都能用Kd-tree进行加速。 60 | 61 | ε-neighboured建图确立了一个密度下界,只有数据密度超过这一下界才能形成一条边,当ε取的较大,则距离很远的点会误认为近邻,出现”短路“问题;若范围很小,则会导致图变得不连通,出现”断路“问题,短路和断路都会给最短路径计算造成困扰。 62 | 另外,ε-neighboured建图构建的是无向图,邻接矩阵对称。 63 | 64 | knn建图即求节点i的k个最近邻节点,然后生成k个从i出发的有向边,这种方法对数据不同的采样密度有较高的鲁棒性,图的邻接矩阵是非对称的。 65 | 66 | ## 三、效果 67 | ### 1. 原始数据(瑞士卷 1500点) 68 | 69 | ![origin-data](../results/Isomap/origin_data.png) 70 | 71 | ### 2. Isomap, k=30 72 | 73 | ![origin-data](../results/Isomap/isomap.png) 74 | 75 | ### 3. Isomap, k=5 76 | 77 | ![origin-data](../results/Isomap/isomap_k_5.png) 78 | 79 | 可以看瑞士卷作为三维空间中的二维流形,在用Isomap降维算法后被”展开“了,这是和PCA、KPCA、普通度量下MDS等降维算法非常不一样的地方。 80 | 81 | ## 四、总结 82 | 证明高维数据是一个高维空间中的低维流形最有说服力的证据就是GAN,生成器就是一个嵌入函数(embedding function),把低维随机噪声(低维流形)嵌入到高维空间中, 83 | 通过调整噪声的参数可以看到图像连续的变化。 84 | 85 | 在Isomap降维效果影响因素: 86 | 1. 如果数据是分类任务,则数据一般不会是一个高维空间的嵌入流形,而是分离的团簇,Isomap降维效果较差。不过也可以用建图之后互不相连的连接成分(connected component)进行降维。 87 | 2. Isomap非常依赖于建图的质量,而建图质量非常依赖于数据的**采样密度**,在瑞士卷的例子中,在三维空间中采样了1500个点,Isomap才有较好的效果。如果对图像几万维的数据, 88 | 需要非常多的样本才能覆盖住数据所处的流形。这是高维情形下面临的重大障碍,因此流形学习方法在实践中的降维往往没有预期的好。 89 | 3. 建图质量还和参数的选择(比如用knn建图中k的选取)有较大的关系。 90 | 91 | 流形学习的想法对机器学习的其它分支也产生了重要影响,比如流形假设、流形正则化等。个人认为,**流形**和**概率**是赋予数据最自然的性质,目前也有学者将两者结合进行研究,比如最优传输理论。 92 | 93 | 94 | ## 五、参考资料 95 | 1. 周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 96 | 2. [sklearn Swiss Roll](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_swiss_roll.html) 97 | 3. [sklearn Isomap](https://scikit-learn.org/stable/modules/generated/sklearn.manifold.Isomap.html) 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /docs/KPCA.md: -------------------------------------------------------------------------------- 1 | # KPCA(kernel-based PCA) 2 | ## 一、介绍 3 | KPCA是PCA加上核技术,解决某些线性不可分的数据在用PCA投影之后不同类的数据会发生交叠,导致后面分类器精度降低的问题。 4 | 5 | ## 二、原理 6 | 7 | ### 2.1 算法原理 8 | 核技术就是将当前空间线性不可分的数据X通过映射φ映射到高维空间(又称为特征空间 feature space)进而线性可分。而KPCA实质上就是在特征空间(feature space)中对数据使用PCA。更具体一点, 9 | [PCA](PCA.md)是要计算原空间中数据的协方差矩阵1/N * XXT,然后进行特征值分解;而KPCA就是要计算特征空间中数据的协方差矩阵C=1/N * φ(X)φ(X)T并进行特征值分解(假设矩阵X和φ(X)每行的均值均为0)。 10 | 11 | **注意**:这里的映射φ是**隐式**的(implicit),不必知道它的具体形式。在核技巧中,对称正定的核函数K(x, y)是已知的,φ是通过核函数K诱导出来的,即K(x, y) = <φ(x), φ(y)>,可以看到φ实质上是将原空间映射到完备的内积空间, 12 | 因此特征空间也称为再生核希尔伯特空间(Reproducing Kernel Hilberte Space, RKHS)。定义核矩阵(kernel matrix) K = φ(X)Tφ(X) = [<φ(xi), φ(xj)>] i, j= 1, 2, 3, ..., m。 13 | 14 | 考虑特征空间数据属性均值不为0的情况,则特征空间协方差矩阵C为: 15 | 16 | ![covariance-matrix](../resources/KPCA/covariance.jpg) 17 | 18 | 可以看到关于核矩阵线性运算得到的矩阵K* = K - 1 / N * K * **1**N×N与特征空间协方差矩阵C相似,进而有相同的特征值λ。由下式可得,相同特征值下, 19 | K*归一化特征向量为u,则协方差矩阵C特征向量为φ(X)u。 20 | 21 | ![C_eigen](../resources/KPCA/C_eigen.jpg) 22 | 23 | 向量φ(X)u归一化为: 24 | 25 | ![uniform_C_eigen](../resources/KPCA/uniform_C_eigen.jpg) 26 | 27 | 算出协方差矩阵的归一化特征向量之后,特征空间的中心化数据需要在较大的特征值上投影,投影坐标v为: 28 | 29 | ![KPCA](../resources/KPCA/KPCA.jpg) 30 | 31 | 至此发现KPCA完全没有使用φ函数,而是直接用核函数K(x, y)和由其定义的核矩阵K完成了在**特征空间上的最大方差投影**计算,具体算法步骤总结如下: 32 | 33 | ``` 34 | 目标:将数据先映射到特征空间,再降维到k维 35 | 1. 定义核函数,算出核矩阵K,和中心化核矩阵K*。 36 | 2. 算出中心化核矩阵K*的特征向量和特征值,取较大的k个特征值对应的k个单位特征向量u1, u2, ..., un。 37 | 3. 算出特征空间投影坐标 vi (i=1,2,...,k)。 38 | ``` 39 | 40 | ### 2.2 常用核函数 41 | 42 | #### 1. 径向基函数核(Radial Basis Function) 43 | 径向基函数核(Radial Basis Function, RBF kernel),也被称为高斯核(Gaussian kernel) 44 | 45 | ![RBF-kernel](../resources/KPCA/RBF.png) 46 | 47 | #### 2. 多项式核函数(Polynomial Kernel) 48 | 49 | ![polynomial-kernel](../resources/KPCA/polynomial.png) 50 | 51 | #### 3. Sigmoid核(Sigmoid Kernel) 52 | 53 | ![sigmoid-kernel](../resources/KPCA/sigmoid_kernel.png) 54 | 55 | ## 三、效果 56 | 57 | ### 1. K = 1,使用RBF核函数,参数γ=2 58 | 59 | ![KPCA](../results/KPCA/gaussian_kenel.png) 60 | 61 | ### 2. K = 1,使用RBF核函数,参数γ=0.5 62 | 63 | ![KPCA](../results/KPCA/gaussian_kenel_2.png) 64 | 65 | ### 3. K = 1,使用Polynomial核函数 66 | 67 | ![KPCA](../results/KPCA/polynomial.png) 68 | 69 | 可以看到数据在原空间线性不可分,如果直接使用PCA降维的话必然导致数据类别的混叠,后续分类器性能下降。而使用KPCA将数据映射到再生核希尔伯特空间之后, 70 | 数据线性可分,再在RKHS中使用PCA降维后的数据可以分离。但使用什么样的核函数、用什么样的参数还需要针对具体任务试验获得。 71 | 72 | ## 四、总结 73 | 核技巧是一套非常优美的技术,具有坚实的理论基础,但是目前仍不能对使用什么核函数、用什么参数给出一个明确的指导,逐渐被数据驱动的深度学习所取代,但核技巧蕴含的思想依然可以给当今机器学习算法设计很多启发。 74 | 75 | ## 五、参考资料 76 | 1. 周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 77 | 2. [https://blog.csdn.net/qq_27231343/article/details/51817866]() -------------------------------------------------------------------------------- /docs/LDA.md: -------------------------------------------------------------------------------- 1 | # 线性判别分析 2 | ## 一、介绍 3 | 线性判别分析(Linear Discriminant Analysis, LDA)是一种监督式线性降维方法,也称为Fisher判别。LDA的思想非常朴素,给定训练集,将训练集投影到某个超平面上, 4 | 使得同类样例的投影点尽可能接近,异类样例点尽量远离。对新样本点进行分类时,将其投影到该超平面上,再根据投影点的位置确定新样本的类别。因此,LDA本身还是一种分类算法。 5 | 6 | ## 二、原理 7 | ### 2.1 二分类 8 | 给定训练集D = {(xi, yi)} i=1, 2, ..., N,yi∈{0, 1}。Xi, μi, Σi(i∈{0, 1})分别表示属于i标签的训练集集合,对应均值和协方差矩阵。若将数据投影到超平面w上,则 9 | 中心投影坐标为wTμ0和wTμ1,协方差矩阵为wTΣ0w和wTΣ1w。 10 | 11 | 为使投影到超平面上后类内紧密,类间远离,定义类内散度矩阵("within-class scatter matrix"): 12 | 13 | ![within-class-matrix](../resources/LDA/lda_within.png) 14 | 15 | 定义类间散度矩阵: 16 | 17 | ![between-class-matrix](../resources/LDA/lda_between.png) 18 | 19 | 称下式为关于Sb和Sw的广义瑞利熵: 20 | 21 | ![generalized-rayleigh-quotient](../resources/LDA/generalized_rayleigh_quotient.png) 22 | 23 | 而要实现类内紧密,类间原理的效果,可以将问题转化为如下优化函数: 24 | 25 | ![optimizing](../resources/LDA/optimizing.png) 26 | 27 | 由lagrange乘子法可得W的闭式解是Sw-1Sb的d'个最大非0广义特征值对应特征向量组成的矩阵。 28 | 将W视为一个投影矩阵,则相当于将样本投影(降维)到d’维空间。**注意**:d'≤ N - 1,因为Sw-1Sb最多有N-1个非零广义特征值。 29 | 30 | ### 2.2 多分类 31 | LDA多分类问题和二分类问题的优化形式和求解方式相同,唯一的区别在于类内散度矩阵Sw和类间散度矩阵Sb的定义。 32 | 33 | 类内散度矩阵(within-class scatter matrix)是二分类中Sw的一个自然推广: 34 | 35 | ![within-class](../resources/LDA/lda_within_2.png) 36 | 37 | 而类间散度矩阵(between-class scatter matrix)则需要用全局散度矩阵诱导出来(也是二分类LDA的推广): 38 | 39 | ![between-class](../resources/LDA/lda_between_2.png) 40 | 41 | 其中μ代表训练集中所有样本的均值。 42 | 43 | ### 2.3 概率理解LDA 44 | LDA不仅是一个监督式线性降维算法,本身还是一个**分类器**。LDA假设每一类的观测样本都来自一个均值不同,方差相同的正态分布,在满足这些假设的情况下,LDA分类器是贝叶斯分类器, 45 | 达到最优分类。 46 | 47 | 在LDA中我们假设P(x|c)服从多元正态分布,密度函数为: 48 | 49 | ![P(X|C)](../resources/LDA/prob/likelihood.png) 50 | 51 | 令每一类的协方差矩阵都相同即Σc = Σ,令先验分布为P(c),则P(c|x)由Bayes公式可得: 52 | 53 | ![P(C|X)](../resources/LDA/prob/bayes.png) 54 | 55 | 对P(C)P(X|C)取对数并丢掉常数项,可得线性判别函数: 56 | 57 | ![Discriminant-function](../resources/LDA/prob/linear_discrimilant_function.png) 58 | 59 | 而对应LDA分类(也是Bayes分类器)为h*(x) = argmaxc δc(x),下面考虑LDA分类器的决策边界: 60 | 61 | ![linear-classifier](../resources/LDA/prob/linear_classifier.png) 62 | 63 | 上述参数Σ、μi、P(C)可以用训练集样本进行点估计。可以看到LDA降维就是将数据朝着分类边界法向量上投影,降维后决策有两种方式:一是直接考虑投影的中心点, 64 | 最近中心点就是其类别,这种方式没有考虑P(C)的先验;另一种是考虑P(C)先验,用线性决策函数来计算对应类别。 65 | 66 | ## 三、效果 67 | ### 1. moon数据 68 | 69 | ![moon](../results/LDA/moon.png) 70 | 71 | ### 2. regression数据 72 | 73 | ![regression](../results/LDA/regression.png) 74 | 75 | 76 | ## 四、总结 77 | 1. 与[PCA](PCA.md)降维结果对比可以发现,PCA没有考虑数据标签的信息,让数据投影后方差最大;而LDA则考虑了训练集数据的标签信息,让数据朝着类内紧密、类间分离的方向投影。 78 | 2. LDA从贝叶斯决策理论的角度来看,当两类数据满足高斯分布且协方差矩阵相等时,LDA可达到最优分类。 79 | 3. 类间散度矩阵的迹也叫类间方差,OTSU图像分割算法就是最大化类间方差算法。 80 | 81 | **补充**:[QDA](QDA.md) 82 | 83 | ## 五、参考资料 84 | [1]周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 85 | 86 | [2][https://blog.csdn.net/VictoriaW/article/details/78275394](https://blog.csdn.net/VictoriaW/article/details/78275394) 87 | -------------------------------------------------------------------------------- /docs/LLE.md: -------------------------------------------------------------------------------- 1 | # 局部线性嵌入 2 | ## 一、介绍 3 | 局部线性嵌入(Locally Linear Embedding)和Isomap一样是非线性的无监督降维方法,属于流形学习的范畴,流形学习的介绍见[Isomap](ISOMAP.md)。Isomap算法假定嵌入映射是一个保测地距离映射。 4 | 而LLE则认为嵌入映射让邻域之间的线性关系得到保持。假定高维空间样本点xi能通过它的邻域样本,xj,xk,xl的坐标线性组合而重构出来: 5 | 6 | xi = wijxj + wikxk + wilxl 7 | 8 | LLE希望上式的重构关系在降维后得到保持。 9 | 10 | 综上,LLE利用了微分流形局部和某个(低维)欧式空间拓扑同胚的性质,某个点的可以利用局域点线性表出,并假设嵌入(embedding)映射是一个保局域线性结构的映射。 11 | 12 | ## 二、原理 13 | 14 | ### 2.1 算法流程 15 | 16 | ![LLE-algorithm](../resources/LLE/LLE_algorithm.png) 17 | 18 | ``` 19 | 目标:将数据由d维降至d'维 20 | 1. 寻找每个样本的k个近邻点。 21 | 2. 由每个样本的近邻点计算局部重建权值矩阵W。 22 | 3. 由局部重建权值矩阵W计算样本点的降维(嵌入)坐标。 23 | ``` 24 | ### 2.2 计算权值重建矩阵 25 | Notation:对数据点xi∈Rd,其k个邻域点为xij,wij为对应的重建权重(也是待求参数),Qi为数据点xi邻域点指标集。 26 | 27 | ![LLE](../resources/LLE/LLE.png) 28 | 29 | 将上式写成矩阵形式并用lagrange乘子法求解,有闭式解: 30 | 31 | ![LLE](../resources/LLE/lle_2.jpg) 32 | 33 | 计算出每个数据点对应权值后可以用N×N矩阵W表示,满足(W)ji = wij,不在k近邻位置元素的权重记为0 34 | 35 | ## 2.3 计算低维坐标 36 | 37 | Notation: Zi为降维后的坐标(待优化参数),wij在2.2已求,属于已知变量。 38 | 39 | ![LLE](../resources/LLE/LLE_3.png) 40 | 41 | 同2.2,将上述优化写成矩阵的形式后,得: 42 | 43 | ![LLE](../resources/LLE/LLE_4.png) 44 | 45 | 对M做特征值分解,Z就是M最小d'个特征值对应特征向量矩阵的转置。**注意**:0天然是M最小的特征值,对应特征向量为[1, 1, ..., 1],所以如果将0包括在内,则会导致降维实际上少一个自由度, 46 | 因此常常取**非0最小的d'个特征值**对应特征向量作为降维坐标Z。 47 | 48 | ## 三、效果 49 | ### 1. 原始数据 swiss roll(1000 points) 50 | 51 | ![result](../results/LLE/origin_data.png) 52 | 53 | ### 2. K=30,包括0特征值 54 | 55 | ![result](../results/LLE/LLE_k_30.png) 56 | 57 | ### 3. K=30,不包括0特征值 58 | 59 | ![result](../results/LLE/LLE_k_30_without0.png) 60 | 61 | ## 四、总结 62 | LLE相对ISOMAP来说,运算速度要快非常多,但从降维的效果可以看出,LLE降维数据有一大部分是重叠的,降维效果没有Isomap好。这是因为LLE只是保持数据局域的线性结构, 63 | 而Isomap保持了全局的测地距离。 64 | 65 | ## 五、参考资料 66 | [1]周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 67 | 68 | [2][https://blog.csdn.net/scott198510/article/details/76099630](https://blog.csdn.net/scott198510/article/details/76099630) 69 | 70 | -------------------------------------------------------------------------------- /docs/MDS.md: -------------------------------------------------------------------------------- 1 | # MDS(多维缩放算法) 2 | ## 一、介绍 3 | MDS(Multiple Dimensional Scaling)是一种无监督的线性降维算法。 4 | 5 | ## 二、原理 6 | 线性降维就是降维变换是一个线性变换,xnew=Px,其中P是一个矩阵。对降维后数据做出不同的假设,即得到不同的P。比如[PCA](PCA.md)对降维后数据做最大可分性假设, 7 | 而MDS算法对降维数据的假设是,原像空间中数据之间的距离应尽量等于像空间中的**欧式距离**。MDS本质上是将数据从一个任意的度量空间映射到一个低维的欧式空间,且映射尽量保距离。 8 | 9 | 假设m个样本在原像空间的距离矩阵为D∈Rm×m,dij代表样本点xi和xj之间的距离。我们的目标是获得样本在k维空间的表示,Z∈Rk×m,且k维空间任意 10 | 两个样本的欧式距离等于原像空间的距离,即||zi-zj||2=dij。由于欧式距离可以由欧式空间的内积诱导,这里将D距离矩阵转换成降维后样本的内积矩阵B = ZTZ(又称为Gram矩阵): 11 | 12 | ![D2B](../resources/MDS/D2B_1.png) 13 | ![D2B](../resources/MDS/D2B.jpg) 14 | 15 | 求出内积矩阵B之后(B是对称(半)正定矩阵),对B做特征值分解B=VAVT,而矩阵ZT = VA0.5的行向量就可以当做m维欧式空间的坐标。接着再将m维欧式空间降维到k维欧式空间, 16 | 具体做法是取A最大的k个特征值对应的特征向量,ZkT=VkAk的行向量就是样本在k维空间的坐标。之所以这样做,是因为对欧式空间保距离和保内积是等价的, 17 | 而取最大的K个特征值可以在矩阵Frobenius范数、二范数、Nuclear范数诱导的度量下最小化内积矩阵的变化。 18 | 19 | 综上,可以认为MDS是将任意度量空间的数据点先等距映射到m维欧式空间,再从m维欧式空间在内积矩阵变动最小的情况下降维到k维欧式空间。具体算法流程如下: 20 | 21 | ``` 22 | 1. 求原像空间m个数据点的距离矩阵D 23 | 2. 求等距变换下m维欧式空间的内积矩阵B 24 | 3. 对B矩阵做特征值分解,取最大的k个特征值对应特征向量,Zk行向量作为降维后k维欧式空间数据点的坐标 25 | ``` 26 | 27 | ## 三、效果 28 | 29 | ### 1. 降维维度为2,原像空间采取欧式距离 30 | 31 | ![to-2-dimension](../results/MDS/to2dim.png) 32 | 33 | 可以看到MDS在输入维度和输出维度相同的情况下,对原数据做了一个刚体变换。 34 | 35 | ### 2. 降维维度为1,原像空间采取欧式距离 36 | 37 | ![to-1-dimension](../results/MDS/to1dim.png) 38 | 39 | 大部分数据点间的相对位置大小得到保持,但部分点也有失真,比如降维后点2和点7之间的距离就过小。 40 | 41 | ## 四、总结 42 | MSD降维基于的假设是降维欧式空间样本点之间的欧式距离应和原度量空间中样本之间的距离尽量相同。为此,先构造距离矩阵D,然后用D诱导出一个对称(半)正定的内积矩阵B,再对B进行特征值分解, 43 | 将VA0.5行向量作为坐标,并用B较大的特征值对应特征向量来降维(VkAk0.5的行向量作为新坐标)。 44 | 45 | 将对称半正定矩阵进行特征值分解,VA0.5作为数据新的表示(坐标)的思想在核方法中也有体现,还有比如在主题模型TSD中, 46 | 采用svd分解后作为文本,单词数据新的表示进行降维。 47 | 48 | 参考资料[2]提出用SGD优化算法求解该问题,优化目标为min||D‘-D||,这种方法在样本较多时容易陷入局部最优解,但它直接比较距离矩阵,而不是像MDS那样间接地比较内积矩阵B。 49 | 个人认为SGD优化方法更通用也容易扩展到非线性保距降维,但是MDS算法更深刻,将线性保距离降维问题转换成一个简单的线性代数问题,更elegant。 50 | 51 | 52 | 53 | 54 | ## 五、参考资料 55 | [1]周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 56 | 57 | [2][https://blog.csdn.net/zhangweiguo_717/article/details/69663452](https://blog.csdn.net/zhangweiguo_717/article/details/69663452) 58 | 59 | -------------------------------------------------------------------------------- /docs/PCA.md: -------------------------------------------------------------------------------- 1 | # 主成分分析(PCA) 2 | ## 一、介绍 3 | 主成分分析(Principal Component Analysis, PCA)是一种最常用的降维技术,它属于**无监督的线性降维算法**,常用于高维数据的预处理。 4 | 5 | ## 二、原理 6 | 所谓线性降维就是降维变换是一个线性变换,即xnew=Px,其中P是一个矩阵。对降维后数据点做出不同的假设,即得到不同的P。 7 | 8 | Notation: 采样得到x∈Rd维数据,共n个样本,构成d行n列矩阵X∈Rd×n。将d维向量降为k维,PCA的做法是选择k个归一化正交基,张成一个K维超平面W, 9 | 使得数据投影到这个k维超平面之后,样本点新坐标WTx能尽可能的分开(方差最大化),表述成优化问题即: 10 | 11 | ![PCA-max-variance](../resources/PCA/max_variance.png) 12 | 13 | 用拉格朗日乘子法求解,得到最优解W*为协方差矩阵XXT前k个最大的特征值对应特征向量作为列向量组成的矩阵,2.1和2.2给出两种等价的PCA算法。 14 | 15 | 16 | ### 2.1 协方差矩阵特征值分解 17 | 18 | 设有n条d维数据,构成d行n列矩阵X 19 | 1. 将X的每一行(每一个特征)减去该行均值(中心化) 20 | 2. 求出协方差矩阵S = 1/mXXT 21 | 3. 对协方差矩阵进行特征值分解S = QAQT 22 | 4. 取前k个最大特征值对应的特征向量按列构成矩阵Qk 23 | 5. P = QkTX即为降维到k维后的数据 24 | 25 | 降维后数据的协方差矩阵为PPT = QkTX XTQk = Ak, 26 | 即新数据的协方差矩阵是一个k维对角阵,且对角元素为对角阵A的最大k个特征值。 27 | 28 | ### 2.2 奇异值分解 29 | 30 | 设有n条d维数据,构成d行n列矩阵X 31 | 1. 将X的每一行(每一个特征)减去该行均值(中心化) 32 | 2. 对X进行奇异值分解,X = U∑VT,取X的最大k个奇异值对应的左奇异向量构成矩阵Uk 33 | 3. P = UkTX即为降维到k维后的数据 34 | 35 | 可以证明降维后的数据协方差矩阵为对角阵,对角元素为最大k个奇异值的平方。2.1和2.2的做法是完全等价的,相比而言奇异值分解更简洁, 36 | 使用右奇异矩阵可以对列进行降维,使用左奇异矩阵可以对行进行降维。 37 | 38 | ## 三 效果 39 | 40 | ![PCA-result](../results/PCA/PCA.png) 41 | 42 | 可以赋予PCA一个几何解释,即朝着方差最大的k个正交方向上“投影“。但要注意的是,PCA是一种无监督的数据降维方法,在降维的过程中没有考虑数据的标签。 43 | 虽然方差较大的k个正交方向在大多数时候都能保存数据的信息,丢弃的是方差较小的噪声,但也不排除对于某些数据(见下图),最能分辨数据标签的特征的方差较小,而在投影过程中,这些特征被抹去了, 44 | 进而导致数据后续分类效果较差。所以,在使用PCA的时候,需要比较降维前后学习器的性能,若性能有所提高,则认为降维起到了作用。 45 | 46 | ![PCA-result](../results/PCA/PCA_2.png) 47 | 48 | ## 四、总结 49 | PCA的算法可以基于两个性质进行等价推导,一是前面介绍的**最大可分性**,即投影到超平面后新坐标的方差最大,这是比较常见的推导PCA算法的假设。还有一种是**最近重构性**,即样本点到这个超平面的距离最近, 50 | 表述成优化问题的形式即: 51 | 52 | ![PCA-min-distance](../resources/PCA/min_distance.png) 53 | 54 | 由于Frobenius范数满足||A||F2 = trace(ATA),将Frobenius范数写成矩阵的迹的形式可得与最大可分性同样的优化形式,说明了这两种不同表述的等价性。 55 | 56 | 57 | ## 五、参考资料 58 | [1] 周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.(第十章) 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/QDA.md: -------------------------------------------------------------------------------- 1 | # 二次判别分析 2 | ## 一、介绍 3 | 二次判别分析(Quadratic Discriminant Analysis)是经典的分类器,与[LDA](LDA.md)不同的地方在于,二次判别分析是二次决策曲面,而线性判别分析是线性决策面, 4 | QDA的缺点是它不能用作降维技术。 5 | 6 | ## 二、原理 7 | 8 | 在QDA中我们假设P(x|c)服从多元正态分布,密度函数为: 9 | 10 | ![P(X|C)](../resources/LDA/prob/likelihood.png) 11 | 12 | 与LDA不同的是,QDA不再假设各个类别之间协方差矩阵相等。令先验分布为P(c),则P(c|x)由Bayes公式可得: 13 | 14 | ![P(C|X)](../resources/LDA/prob/bayes.png) 15 | 16 | 对P(C)P(X|C)取对数并丢掉常数项,可得二次判别函数: 17 | 18 | ![Discrimilant-function](../resources/QDA/discrimilant_function.png) 19 | 20 | 和LDA一样,利用该判别函数做判别即得到QDA二次分类边界,这里不再赘述。 21 | 22 | ## 三、总结 23 | 24 | ### 3.1 LDA和QDA的对比 25 | LDA和QDA模型的选择是一个偏差-方差均衡的问题,LDA模型更简单,因此具有更低的方差;而如果LDA假设K类具有相同的方差是一个非常糟糕的假设,那么LDA会产生非常大的偏差。 26 | 一般来说,如果训练样本较少,使用模型更简单的LDA更好。而如果训练集很大,则应选择QDA,因为这时大规模训练集会帮助降低模型的方差。 27 | 28 | ### 3.2 LDA、逻辑回归、QDA、KNN对比 29 | 按照分类边界来分的话,逻辑回归和LDA都是线性决策边界,两种方法的区别在于,逻辑回归假设数据X|C服从指数分布,使用极大似然估计;而LDA假设X|C服从正态分布,用替换法 (频率替换法和矩估计法) 进行参数估计。 30 | 逻辑回归和LDA会产生相似的分类边界,但不一定相同。当数据满足LDA假设的高斯分布时,LDA比逻辑回归效果更好;当数据满足逻辑回归假设的指数分布时,逻辑回归效果更好。 31 | 32 | QDA和KNN分类器产生的都是非线性的决策边界,KNN是一种非参数方法,对决策边界形状没有做出任何的假设,只对数据X所处的度量空间做了假设,因此当决策边界是高度非线性的时候,用KNN会优于LDA和逻辑回 33 | 归;而另一方面,KNN 模型没有给出哪些预测变量是重要的,可解释性较差。QDA可以看成KNN和逻辑回归、LDA之间的一种折中的方法,QDA的决策边界是二次的,所以它比线性方法应用范围更广,虽然不如KNN决策边界光滑,但 34 | QDA在固定训练数据量的问题上一般比KNN有更好的效果。 35 | 36 | 37 | 38 | ## 四、参考资料 39 | [1] James G , D Witten, Hastie T , et al. An Introduction to Statistical Learning: With Applications in R[M]. 2013. 40 | -------------------------------------------------------------------------------- /docs/UMAP.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 统一流形逼近投影(UMAP)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## 一、介绍\n", 15 | "UMAP全称uniform manifold approximation and projection,统一流形逼近与投影,是继ISOMAP,LLE之后,流形学习在数据降维算法领域的又一力作。\n", 16 | "\n", 17 | "UMAP和t-SNE降维算法除了用在数据预处理之外,还用在**可视化高维数据**上,UMAP有非常快的计算速度(比t-SNE更快),可以实现大数据集的降维。" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "## 二、算法细节\n", 25 | "\n", 26 | "UMAP对数据有三点假设:\n", 27 | "\n", 28 | "* 数据均匀地分布在一个黎曼流形上。\n", 29 | "* 黎曼度量是局部不变的。\n", 30 | "* 黎曼流形是局部连接的。\n", 31 | "\n", 32 | "通过这些假设,UMAP使用模糊拓扑结构表示高维数据。给定数据的一些低维表示,可以使用类似的过程来构造等价的拓扑表示。\n", 33 | "UMAP在低维空间中优化数据坐标,以**最小化两个拓扑表示之间的交叉熵**,从而使得低维空间的表示可以同时保证局部和全局的结构。\n", 34 | "\n", 35 | "### 2.1 构造带权无向图\n", 36 | "UMAP首先将高维数据集$X = \\{x_i\\}_i$表示成一个带权无向图,具体来说,首先构造数据点的$k$-邻居图$\\hat{G} = (V, E, W)$(带权有向图),边的权重可以直观地理解为是该有向边存在的概率。\n", 37 | "\n", 38 | "$$P_{i|j} = exp(-\\frac{d(x_i, x_j)-\\rho_i}{\\sigma_i})$$\n", 39 | "\n", 40 | "其中$\\rho_i$表示第$i$个数据点与其最近邻之间的距离,$\\sigma_i$是一个归一化系数。接着进行对称化操作,把权重变为如下形式,此时边变成无向边,权重可以直观地理解为至少有一边存在的概率。\n", 41 | "\n", 42 | "$$P_{ij}=P_{i|j} + P_{j|i} - P_{i|j}P_{j|i}$$\n", 43 | "\n", 44 | "### 2.2 构造低维表示并优化\n", 45 | "\n", 46 | "构造了数据集$X$的带权无向图表示之后,再给出数据集低维空间(降维空间)图权重的定义方法,UMAP采取了类似$T$-分布的形式\n", 47 | "\n", 48 | "$$q_{ij} = (1 +a(y_i - y_j)^{2b})^{-1}$$\n", 49 | "\n", 50 | "UMAP使用二元交叉熵(CE)作为损失函数,去最小化高维空间中的带权无向图和低维空间中带权无向图对应边之间权重的差异。\n", 51 | "\n", 52 | "$$CE(P, Q) = \\sum_{i}\\sum_{j}[P_{ij}log(\\frac{P_{ij}}{Q_{ij}}) - (1 - P_{ij})log(\\frac{1 - P_{ij}}{1 - Q_{ij}})]$$\n", 53 | "\n", 54 | "UMAP使用SGD优化算法来优化二元交叉熵。" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "### 2.3 UMAP中的超参数\n", 62 | "UMAP中有三个超参数\n", 63 | "* 降维空间维度$d$。\n", 64 | "* 构造$k$-邻居图的$k$。\n", 65 | "* 最小允许距离$d_{min}$,用来防止降维空间中数据之间距离过近,从而导致点云稠密,降低可视化效果。" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "## 三、总结\n", 73 | "\n", 74 | "ISOMAP也是流形学习在降维算法上的应用,ISOMAP也需要先将流形表示成一个$k$-邻居图,然后在该图上计算节点之间的图上最短路径,作为流形上测地距离的近似,构造距离矩阵,然后用MDS算法进行降维。ISOMAP是一种**保测地距离的降维算法**。ISOMAP算法较慢,计算图上任意两点之间距离需要花费较多时间。\n", 75 | "\n", 76 | "相比之下,UMAP要高明地多,它放弃了保持任意两点之间测地距离这一全局结构,而是仅保持$k-$邻居图权重这一局部结构,但对降维可视化来说这已经足够了。" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "## 四、参考资料\n", 84 | "\n", 85 | "[1] UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction[J]. The Journal of Open Source Software, 2018, 3(29):861." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [] 94 | } 95 | ], 96 | "metadata": { 97 | "kernelspec": { 98 | "display_name": "Python 3", 99 | "language": "python", 100 | "name": "python3" 101 | }, 102 | "language_info": { 103 | "codemirror_mode": { 104 | "name": "ipython", 105 | "version": 3 106 | }, 107 | "file_extension": ".py", 108 | "mimetype": "text/x-python", 109 | "name": "python", 110 | "nbconvert_exporter": "python", 111 | "pygments_lexer": "ipython3", 112 | "version": "3.7.6" 113 | } 114 | }, 115 | "nbformat": 4, 116 | "nbformat_minor": 4 117 | } 118 | -------------------------------------------------------------------------------- /resources/KPCA/C_eigen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/C_eigen.jpg -------------------------------------------------------------------------------- /resources/KPCA/KPCA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/KPCA.jpg -------------------------------------------------------------------------------- /resources/KPCA/RBF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/RBF.png -------------------------------------------------------------------------------- /resources/KPCA/covariance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/covariance.jpg -------------------------------------------------------------------------------- /resources/KPCA/polynomial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/polynomial.png -------------------------------------------------------------------------------- /resources/KPCA/sigmoid_kernel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/sigmoid_kernel.png -------------------------------------------------------------------------------- /resources/KPCA/uniform_C_eigen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/KPCA/uniform_C_eigen.jpg -------------------------------------------------------------------------------- /resources/LDA/generalized_rayleigh_quotient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/generalized_rayleigh_quotient.png -------------------------------------------------------------------------------- /resources/LDA/lda_between.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/lda_between.png -------------------------------------------------------------------------------- /resources/LDA/lda_between_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/lda_between_2.png -------------------------------------------------------------------------------- /resources/LDA/lda_within.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/lda_within.png -------------------------------------------------------------------------------- /resources/LDA/lda_within_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/lda_within_2.png -------------------------------------------------------------------------------- /resources/LDA/optimizing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/optimizing.png -------------------------------------------------------------------------------- /resources/LDA/prob/bayes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/prob/bayes.png -------------------------------------------------------------------------------- /resources/LDA/prob/likelihood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/prob/likelihood.png -------------------------------------------------------------------------------- /resources/LDA/prob/linear_classifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/prob/linear_classifier.png -------------------------------------------------------------------------------- /resources/LDA/prob/linear_discrimilant_function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LDA/prob/linear_discrimilant_function.png -------------------------------------------------------------------------------- /resources/LLE/LLE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LLE/LLE.png -------------------------------------------------------------------------------- /resources/LLE/LLE_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LLE/LLE_3.png -------------------------------------------------------------------------------- /resources/LLE/LLE_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LLE/LLE_4.png -------------------------------------------------------------------------------- /resources/LLE/LLE_algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LLE/LLE_algorithm.png -------------------------------------------------------------------------------- /resources/LLE/lle_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/LLE/lle_2.jpg -------------------------------------------------------------------------------- /resources/MDS/D2B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/MDS/D2B.jpg -------------------------------------------------------------------------------- /resources/MDS/D2B_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/MDS/D2B_1.png -------------------------------------------------------------------------------- /resources/PCA/max_variance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/PCA/max_variance.png -------------------------------------------------------------------------------- /resources/PCA/min_distance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/PCA/min_distance.png -------------------------------------------------------------------------------- /resources/QDA/discrimilant_function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/resources/QDA/discrimilant_function.png -------------------------------------------------------------------------------- /results/Isomap/isomap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/Isomap/isomap.png -------------------------------------------------------------------------------- /results/Isomap/isomap_k_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/Isomap/isomap_k_5.png -------------------------------------------------------------------------------- /results/Isomap/origin_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/Isomap/origin_data.png -------------------------------------------------------------------------------- /results/KPCA/gaussian_kenel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/KPCA/gaussian_kenel.png -------------------------------------------------------------------------------- /results/KPCA/gaussian_kenel_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/KPCA/gaussian_kenel_2.png -------------------------------------------------------------------------------- /results/KPCA/polynomial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/KPCA/polynomial.png -------------------------------------------------------------------------------- /results/LDA/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/LDA/moon.png -------------------------------------------------------------------------------- /results/LDA/regression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/LDA/regression.png -------------------------------------------------------------------------------- /results/LLE/LLE_k_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/LLE/LLE_k_30.png -------------------------------------------------------------------------------- /results/LLE/LLE_k_30_without0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/LLE/LLE_k_30_without0.png -------------------------------------------------------------------------------- /results/LLE/origin_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/LLE/origin_data.png -------------------------------------------------------------------------------- /results/MDS/to1dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/MDS/to1dim.png -------------------------------------------------------------------------------- /results/MDS/to2dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/MDS/to2dim.png -------------------------------------------------------------------------------- /results/PCA/PCA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/PCA/PCA.png -------------------------------------------------------------------------------- /results/PCA/PCA_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GCaptainNemo/Dimension-Reduction-Algorithms/566df26731d71a2e09514ab58847527c6ae60dae/results/PCA/PCA_2.png -------------------------------------------------------------------------------- /src/ISLR/eleven.R: -------------------------------------------------------------------------------- 1 | ## ISLR chapter4 question11 2 | 3 | ###### a ###### 4 | library(ISLR) 5 | attach(Auto) 6 | mpg01=rep(1,times=392) 7 | mpg01[mpg".format(source, destination)) 41 | return 42 | else: 43 | # 中间经过节点 44 | mid = path_matrix[source, destination] 45 | ShortestPath.print_floyd_path(path_matrix, source, mid) 46 | ShortestPath.print_floyd_path(path_matrix, mid, destination) 47 | 48 | @staticmethod 49 | def djikstra_algorithm(adjacency_matrix, obj_vertice): 50 | n = adjacency_matrix.shape[0] 51 | musk_lst = [False for _ in range(n)] 52 | dist_lst = [np.inf for _ in range(n)] 53 | parent_lst = [-1 for _ in range(n)] 54 | src_vertice = obj_vertice 55 | dist_lst[src_vertice] = 0 56 | while False in musk_lst: 57 | musk_lst[src_vertice] = True 58 | for i in range(n): 59 | if adjacency_matrix[src_vertice, i] != np.inf: 60 | if dist_lst[src_vertice] + adjacency_matrix[src_vertice, i] < dist_lst[i]: 61 | dist_lst[i] = dist_lst[src_vertice] + adjacency_matrix[src_vertice, i] 62 | parent_lst[i] = src_vertice 63 | min_dist = np.inf 64 | for j in range(n): 65 | if musk_lst[j] == False and dist_lst[j] < min_dist: 66 | min_dist = dist_lst[j] 67 | src_vertice = j 68 | print(dist_lst) 69 | print(parent_lst) 70 | return dist_lst, parent_lst 71 | 72 | @staticmethod 73 | def print_djikstra_path(parent_lst, obj_vertex): 74 | # 从源节点到目标节点(obj_vertex)的路径 75 | a = obj_vertex 76 | lst = [] 77 | while parent_lst[a] != -1: 78 | lst.append(parent_lst[a]) 79 | a = parent_lst[a] 80 | lst.reverse() 81 | print("lst = ", lst) 82 | 83 | for i in lst: 84 | print(i, "->") 85 | print(obj_vertex) 86 | 87 | 88 | class ISOMAP: 89 | def __init__(self, k): 90 | """ 91 | :param k: reduced dimension R^d -> R^k 92 | """ 93 | self.reduced_dimension = k 94 | 95 | def make_data(self): 96 | self.X_data, t = make_swiss_roll(1000, noise=0, random_state=0) 97 | ward = AgglomerativeClustering(n_clusters=6, linkage='ward').fit(self.X_data) 98 | self.Y_data = ward.labels_ 99 | 100 | def construct_graph(self, k): 101 | """ 102 | :param k: k-nearest neighbour 103 | :return: adjacency matrix 104 | """ 105 | kd_tree = KDTree(self.X_data.copy()) 106 | n = self.X_data.shape[0] 107 | adjacency_matrix = np.ones([n, n]) * np.inf 108 | for i in range(n): 109 | # 由于最近邻查询邻居包含自身,需要去除 110 | dist_tuple, index_tuple = kd_tree.query(self.X_data[i, :], k=k+1, p=2) 111 | dist_lst = list(dist_tuple) 112 | index_lst = list(index_tuple) 113 | remove_index = index_lst.index(i) 114 | print(i, index_lst[remove_index]) 115 | dist_lst.pop(remove_index) 116 | index_lst.pop(remove_index) 117 | for index, value in enumerate(index_lst): 118 | adjacency_matrix[i, value] = dist_tuple[index] 119 | return adjacency_matrix 120 | 121 | def Isomap(self, knn=5): 122 | adjacency_matrix = self.construct_graph(knn) 123 | dist_matrix, _ = ShortestPath.floyd_algorithm(adjacency_matrix) 124 | self.D = dist_matrix 125 | print("self.D = ", self.D) 126 | self.MDS() 127 | 128 | def MDS(self): 129 | self.B = self.construct_innerprod_matrix() 130 | if self.B is None: 131 | return 132 | # A是对角阵,Q是特征向量矩阵 133 | # 由于内积矩阵是对称半正定矩阵,用svd求特征值、特征向量 134 | u, sigma, vT = np.linalg.svd(self.B) 135 | Qk = u[:, :self.reduced_dimension] 136 | Ak = np.diag(sigma[:self.reduced_dimension] ** 0.5) 137 | self.new_data = Qk @ Ak 138 | print("new_data.shape = ", self.new_data.shape) 139 | 140 | def construct_innerprod_matrix(self): 141 | inf_ = np.where(self.D == np.inf) 142 | if inf_[0].shape[0] != 0: 143 | print("shape = ", self.D.shape) 144 | print("not connected graph!", self.D[inf_]) 145 | return 146 | 147 | innerprod_matrix = np.zeros(self.D.shape) 148 | length = self.D.shape[0] 149 | meandist2 = np.mean(list(map(lambda x: x**2, self.D))) 150 | meandist2_column_lst = [] 151 | for j in range(length): 152 | meandist2_column_lst.append(np.mean(list(map(lambda x: x**2, 153 | self.D[:, j])))) 154 | for i in range(length): 155 | meandist2_i_row = np.mean(list(map(lambda x: x**2, self.D[i, :]))) 156 | for j in range(i, length): 157 | meandist2_j_column = meandist2_column_lst[j] 158 | innerprod_matrix[i, j] = -0.5 * (self.D[i, j] ** 2 - meandist2_i_row - 159 | meandist2_j_column + meandist2) 160 | innerprod_matrix[j, i] = innerprod_matrix[i, j] 161 | return innerprod_matrix 162 | 163 | def result(self): 164 | fig = plt.figure() 165 | # plt.title("Origin data") 166 | 167 | ax = p3.Axes3D(fig) 168 | ax.view_init(7, -80) 169 | 170 | for l in np.unique(self.Y_data): 171 | ax.scatter(self.X_data[self.Y_data == l, 0], self.X_data[self.Y_data == l, 1], 172 | self.X_data[self.Y_data == l, 2], 173 | color=plt.cm.jet(float(l) / np.max(self.Y_data + 1)), 174 | s=20, edgecolor='k') 175 | plt.show() 176 | # ax = plt.gca() 177 | # ax.axis("equal") 178 | plt.figure() 179 | plt.title("MDS") 180 | if self.reduced_dimension == 2: 181 | print("self.new_data = ", self.new_data) 182 | for l in np.unique(self.Y_data): 183 | plt.scatter(self.new_data[self.Y_data == l, 0], self.new_data[self.Y_data == l, 1], 184 | color=plt.cm.jet(float(l) / np.max(self.Y_data + 1)), 185 | s=20, edgecolor='k') 186 | else: 187 | for l in np.unique(self.Y_data): 188 | plt.scatter(self.new_data[self.Y_data == l, 0], 189 | color=plt.cm.jet(float(l) / np.max(self.Y_data + 1)), 190 | s=20, edgecolor='k') 191 | ax = plt.gca() 192 | ax.axis("equal") 193 | plt.show() 194 | 195 | 196 | if __name__ == "__main__": 197 | a = ISOMAP(2) # 降至两维 198 | a.make_data() 199 | a.Isomap(10) # 用KNN建图 200 | a.result() 201 | 202 | 203 | -------------------------------------------------------------------------------- /src/KPCA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # author: 11360 4 | # datetime: 2021/3/14 12:18 5 | 6 | 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | from sklearn.datasets import make_circles 10 | 11 | 12 | class KPCA: 13 | def __init__(self, k): 14 | """ 15 | :param k: reduced dimension R^d -> R^k 16 | """ 17 | self.reduced_dimension = k 18 | 19 | def make_data(self): 20 | self.X_data, self.Y_data = make_circles(n_samples=100, shuffle=True, noise=0.1, random_state=None, factor=0.2) 21 | # self.X_data, self.Y_data = make_moons(100, noise=.04, random_state=0) 22 | self.X_data = self.X_data - np.mean(self.X_data, 0) 23 | 24 | def KPCA(self): 25 | # kernel_function = lambda x1, x2: np.exp(-np.linalg.norm(x1 - x2) ** 2 * 0.5) 26 | kernel_function = lambda x1, x2: (x1 @ x2 + 1) ** 3 27 | kernel_matrix = self.construct_kernel_matrix(kernel_function) 28 | # 中心化后的kernel matrix 29 | num = self.X_data.shape[0] 30 | K_star = kernel_matrix - 1 / num * kernel_matrix @ \ 31 | np.mat(np.ones([num, num])) 32 | eig_value, eig_vector = np.linalg.eig(K_star) 33 | eig_value = eig_value.real 34 | # 最大的d'个特征值对应特征向量 35 | sorted_index = np.argsort(-eig_value)[:self.reduced_dimension] 36 | print(eig_value[sorted_index]) 37 | print(max(eig_value)) 38 | U = eig_vector[:, sorted_index].real 39 | diag = np.diag(1 / np.sqrt(np.diag(U.T @ kernel_matrix @ U))) 40 | self.new_mat = diag @ U.T @ K_star 41 | 42 | def construct_kernel_matrix(self, kernel_function=None): 43 | if not kernel_function: 44 | return 45 | num = self.X_data.shape[0] 46 | kernel_matrix = np.zeros([num, num]) 47 | for i in range(num): 48 | for j in range(i, num): 49 | kernel_matrix[i, j] = kernel_function(self.X_data[i, :], 50 | self.X_data[j, :]) 51 | kernel_matrix[j, i] = kernel_matrix[i, j] 52 | return kernel_matrix 53 | 54 | def result(self): 55 | plt.subplot(121) 56 | plt.title("Origin data") 57 | one_index = np.where(self.Y_data == 1) 58 | zero_index = np.where(self.Y_data == 0) 59 | plt.scatter(self.X_data[one_index, 0], self.X_data[one_index, 1], c='r') 60 | plt.scatter(self.X_data[zero_index, 0], self.X_data[zero_index, 1], c='b') 61 | 62 | ####################################### 63 | plt.subplot(122) 64 | plt.title("KPCA dimension=1 kernel=polynomial") 65 | if self.reduced_dimension == 2: 66 | for i in range(self.new_mat.shape[1]): 67 | if self.Y_data[i] == 1: 68 | plt.scatter(self.new_mat[0, i], 69 | self.new_mat[1, i], c='r') 70 | else: 71 | plt.scatter(self.new_mat[0, i], 72 | self.new_mat[1, i], c='b') 73 | else: 74 | for i in range(self.new_mat.shape[1]): 75 | if self.Y_data[i] == 1: 76 | plt.scatter(self.new_mat[0, i], 0, c='r') 77 | else: 78 | plt.scatter(self.new_mat[0, i], 0, c='b') 79 | ax = plt.gca() 80 | ax.axis("equal") 81 | plt.show() 82 | 83 | 84 | if __name__ == "__main__": 85 | a = KPCA(1) 86 | a.make_data() 87 | a.KPCA() 88 | a.result() 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/LDA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # author: 11360 4 | # datetime: 2021/3/11 18:58 5 | 6 | 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | import scipy 10 | from sklearn.datasets import make_moons, make_regression 11 | 12 | 13 | class LDA: 14 | def __init__(self, k): 15 | """ 16 | :param k: reduced dimension R^d -> R^k 17 | """ 18 | self.reduced_dimension = k 19 | 20 | def make_data(self, regression=False): 21 | if regression: 22 | x, y = make_regression(n_samples=50, n_features=1, 23 | n_targets=1, noise=1.5, random_state=1, bias=0) 24 | y = np.array([y]).T 25 | print(y.shape) 26 | self.X_data = np.concatenate([x, y], axis=1) 27 | new_X_data = self.X_data - np.array([[15, 13]]) 28 | 29 | self.X_data = np.concatenate([self.X_data, new_X_data], axis=0) 30 | # self.X_data = self.X_data - np.mean(self.X_data, 0) 31 | y = np.array([1 for _ in range(50)]) 32 | new_y = np.array([0 for _ in range(50)]) 33 | self.Y_data = np.concatenate([y, new_y], axis=0) 34 | print(self.X_data.shape) 35 | else: 36 | self.X_data, self.Y_data = make_moons(100, noise=.04, random_state=0) 37 | 38 | def cal_within(self): 39 | """ 40 | calculate within-class scatter matrix 41 | :return: Sw 42 | """ 43 | class_name = np.unique(self.Y_data) 44 | class_num = len(np.unique(self.Y_data)) 45 | attribute_dimension = self.X_data.shape[1] 46 | Sw = np.zeros([attribute_dimension, attribute_dimension]) 47 | for i in range(class_num): 48 | xi = self.X_data[np.where(self.Y_data == class_name[i])[0], :] 49 | miui = np.array([np.mean(xi, axis=0)]) 50 | print(xi.shape) 51 | Sw += xi.T @ xi 52 | Sw -= xi.shape[0] * miui.T @ miui 53 | return Sw 54 | 55 | def cal_between(self, Sw): 56 | """ 57 | calculate class-between scatter matrix 58 | Sb = St - Sw 59 | :return: Sb 60 | """ 61 | St = self.X_data.T @ self.X_data 62 | miu = np.array([np.mean(self.X_data, axis=0)]) 63 | St = St - self.X_data.shape[0] * miu.T @ miu 64 | Sb = St - Sw 65 | # miu0 = np.array([np.mean(self.X_data[np.where(self.Y_data == 0)[0], :], axis=0)]) 66 | # miu1 = np.array([np.mean(self.X_data[np.where(self.Y_data == 1)[0], :], axis=0)]) 67 | # print("miu0.shape = ", miu0.shape) 68 | # Sb = (miu0 - miu1).T @(miu0 - miu1) 69 | return Sb 70 | 71 | def LDA(self): 72 | Sw = self.cal_within() 73 | Sb = self.cal_between(Sw) 74 | eigval, eig_vec = scipy.linalg.eig(Sb, Sw) 75 | eigval = eigval.real 76 | index_sorted = np.argsort(eigval)[-self.reduced_dimension:] 77 | self.Projective_matrix = eig_vec[:, index_sorted] 78 | mat = np.mat(self.X_data).T 79 | self.new_mat = self.Projective_matrix.T @ mat 80 | 81 | def result(self, regression=False): 82 | if regression == False: 83 | translate_vector = [- self.Projective_matrix[1, 0] * 2, self.Projective_matrix[0, 0] * 2] 84 | else: 85 | translate_vector = [- self.Projective_matrix[1, 0] * 400, self.Projective_matrix[0, 0] * 400] 86 | 87 | for i in range(self.new_mat.shape[1]): 88 | if self.Y_data[i] == 1: 89 | plt.scatter(self.new_mat[0, i] * self.Projective_matrix[0, 0] + translate_vector[0], 90 | self.new_mat[0, i] * self.Projective_matrix[1, 0] + translate_vector[1], c='r') 91 | else: 92 | plt.scatter(self.new_mat[0, i] * self.Projective_matrix[0, 0] + translate_vector[0], 93 | self.new_mat[0, i] * self.Projective_matrix[1, 0] + translate_vector[1], c='b') 94 | one_index = np.where(self.Y_data == 1) 95 | zero_index = np.where(self.Y_data == 0) 96 | plt.scatter(self.X_data[one_index, 0], self.X_data[one_index, 1], c='r') 97 | plt.scatter(self.X_data[zero_index, 0], self.X_data[zero_index, 1], c='b') 98 | ax = plt.gca() 99 | ax.axis("equal") 100 | ax.annotate("", 101 | xy=(translate_vector[0], translate_vector[1]), 102 | xytext=(0, 0), 103 | arrowprops=dict(arrowstyle="->", color="k")) 104 | if regression: 105 | plt.text(translate_vector[0] * 0.9, translate_vector[1] * 0.95 + 10, 106 | r'projection', fontdict={'size': 8, 'color': 'k'}) 107 | else: 108 | plt.text(translate_vector[0] * 0.95 + 0.05, translate_vector[1] * 0.95, 109 | r'projection', fontdict={'size': 8, 'color': 'k'}) 110 | plt.show() 111 | 112 | 113 | if __name__ == "__main__": 114 | a = LDA(1) 115 | a.make_data() 116 | # a.make_data(True) 117 | 118 | a.LDA() 119 | a.result() 120 | # a.result(True) 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/LLE.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # author: 11360 4 | # datetime: 2021/3/14 13:41 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from sklearn.datasets import make_swiss_roll 9 | from scipy.spatial import KDTree 10 | import mpl_toolkits.mplot3d.axes3d as p3 11 | from sklearn.cluster import AgglomerativeClustering 12 | 13 | 14 | class LLE: 15 | def __init__(self, k): 16 | """ 17 | :param k: reduced dimension R^d -> R^k 18 | """ 19 | self.reduced_dimension = k 20 | 21 | def make_data(self): 22 | """ 23 | 构造swiss roll数据 24 | """ 25 | self.X_data, t = make_swiss_roll(1000, noise=0, random_state=0) 26 | ward = AgglomerativeClustering(n_clusters=6, linkage='ward').fit(self.X_data) 27 | self.Y_data = ward.labels_ 28 | 29 | def construct_graph(self, k): 30 | """ 31 | :param k: k-nearest neighbour 32 | :return: adjacency index matrix(每个数据点k个近邻点的索引) 33 | """ 34 | kd_tree = KDTree(self.X_data.copy()) 35 | n = self.X_data.shape[0] 36 | adjacency_index_matrix = np.ones([n, k], dtype=int) 37 | for i in range(n): 38 | dist_tuple, index_tuple = kd_tree.query(self.X_data[i, :], k=k+1, p=2) 39 | index_lst = list(index_tuple) 40 | # print(i in index_lst) 41 | index_lst.remove(i) 42 | adjacency_index_matrix[i, :] = np.array([index_lst]) 43 | return adjacency_index_matrix 44 | 45 | def LLE(self, knn=5, remove_zero=True): 46 | adjacency_index_matrix = self.construct_graph(knn) 47 | n = self.X_data.shape[0] 48 | W = np.zeros([n, n]) # 关于所有点的权值矩阵,不在近邻则权值为0 49 | for i in range(n): 50 | linjin_matrix = self.X_data[adjacency_index_matrix[i, :], :] 51 | Zi = linjin_matrix - self.X_data[i, :] 52 | covariance_matrix = Zi @ Zi.T 53 | # wi是wij组成的列向量,即关于节点i邻近点权重组成的向量 54 | wi = np.linalg.pinv(covariance_matrix) @ np.ones([knn, 1]) / \ 55 | np.sum(np.linalg.pinv(covariance_matrix)) 56 | for j in range(knn): 57 | # (W)j,i = wij 58 | W[adjacency_index_matrix[i, j], i] = wi[j] 59 | M = (np.eye(n) - W) @ (np.eye(n) - W).T 60 | # 注意:M是对称半正定矩阵,用svd求特征值更好 61 | eigen_vector, eigen_value, vT = np.linalg.svd(M) 62 | # 从小到大,取M的d'个最小特征值对应特征向量 63 | if remove_zero: 64 | self.new_data = eigen_vector[:, -self.reduced_dimension-1:-1] 65 | else: 66 | self.new_data = eigen_vector[:, -self.reduced_dimension:] 67 | 68 | 69 | def result(self): 70 | fig = plt.figure() 71 | ax = p3.Axes3D(fig) 72 | ax.view_init(7, -80) 73 | for l in np.unique(self.Y_data): 74 | ax.scatter(self.X_data[self.Y_data == l, 0], self.X_data[self.Y_data == l, 1], 75 | self.X_data[self.Y_data == l, 2], 76 | color=plt.cm.jet(float(l) / np.max(self.Y_data + 1)), 77 | s=20, edgecolor='k') 78 | plt.show() 79 | plt.figure() 80 | plt.title("LLE") 81 | if self.reduced_dimension == 2: 82 | 83 | for l in np.unique(self.Y_data): 84 | plt.scatter(self.new_data[self.Y_data == l, 0], self.new_data[self.Y_data == l, 1], 85 | color=plt.cm.jet(float(l) / np.max(self.Y_data + 1)), 86 | s=20, edgecolor='k') 87 | else: 88 | for l in np.unique(self.Y_data): 89 | plt.scatter(self.new_data[self.Y_data == l, 0], 90 | color=plt.cm.jet(float(l) / np.max(self.Y_data + 1)), 91 | s=20, edgecolor='k') 92 | ax = plt.gca() 93 | ax.axis("equal") 94 | plt.show() 95 | 96 | 97 | if __name__ == "__main__": 98 | a = LLE(2) # 降至两维 99 | a.make_data() 100 | a.LLE(30) # KNN 101 | a.result() 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/MDS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # author: 11360 4 | # datetime: 2021/3/12 0:38 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from sklearn.datasets import make_moons, make_regression 9 | 10 | 11 | class MDS: 12 | def __init__(self, k): 13 | """ 14 | :param k: reduced dimension R^d -> R^k 15 | """ 16 | self.reduced_dimension = k 17 | 18 | def make_data(self, regression=False): 19 | if regression: 20 | x, y = make_regression(n_samples=50, n_features=1, 21 | n_targets=1, noise=1.5, random_state=1, bias=0) 22 | y = np.array([y]).T 23 | print(y.shape) 24 | self.X_data = np.concatenate([x, y], axis=1) 25 | new_X_data = self.X_data - np.array([[15, 13]]) 26 | 27 | self.X_data = np.concatenate([self.X_data, new_X_data], axis=0) 28 | self.X_data = self.X_data - np.mean(self.X_data, 0) 29 | y = np.array([1 for _ in range(50)]) 30 | new_y = np.array([0 for _ in range(50)]) 31 | self.Y_data = np.concatenate([y, new_y], axis=0) 32 | print(self.X_data.shape) 33 | else: 34 | self.X_data, self.Y_data = make_moons(10, noise=.04, random_state=0) 35 | 36 | def MDS(self): 37 | self.D = self.construct_distance_matrix() 38 | self.B = self.construct_innerprod_matrix() 39 | # A是对角阵,Q是特征向量矩阵 40 | # self.B是对称正定矩阵,用svd分解算特征值、特征向量更好 41 | u, sigma, vT = np.linalg.svd(self.B) 42 | Qk = u[:, :self.reduced_dimension] 43 | Ak = np.diag(sigma[:self.reduced_dimension] ** 0.5) 44 | self.new_data = Qk @ Ak 45 | print(self.new_data.shape) 46 | 47 | def construct_distance_matrix(self, distance=None): 48 | length = self.X_data.shape[0] 49 | distance_matrix = np.zeros([length, length]) 50 | if distance == None: 51 | dis = lambda x, y: np.linalg.norm(x-y) 52 | else: 53 | dis = distance 54 | for i in range(length): 55 | for j in range(i, length): 56 | distance_matrix[i, j] = dis(self.X_data[i, :], self.X_data[j, :]) 57 | distance_matrix[j, i] = distance_matrix[i, j] 58 | return distance_matrix 59 | 60 | def construct_innerprod_matrix(self): 61 | innerprod_matrix = np.zeros(self.D.shape) 62 | length = self.D.shape[0] 63 | meandist2 = np.mean(list(map(lambda x: x**2, self.D))) 64 | meandist2_column_lst = [] 65 | for j in range(length): 66 | meandist2_column_lst.append(np.mean(list(map(lambda x: x**2, 67 | self.D[:, j])))) 68 | for i in range(length): 69 | meandist2_i_row = np.mean(list(map(lambda x: x**2, self.D[i, :]))) 70 | for j in range(i, length): 71 | meandist2_j_column = meandist2_column_lst[j] 72 | innerprod_matrix[i, j] = -0.5 * (self.D[i, j] ** 2 - meandist2_i_row - 73 | meandist2_j_column + meandist2) 74 | innerprod_matrix[j, i] = innerprod_matrix[i, j] 75 | return innerprod_matrix 76 | 77 | def result(self): 78 | plt.subplot(1, 2, 1) 79 | plt.title("Origin data") 80 | one_index = np.where(self.Y_data == 1) 81 | zero_index = np.where(self.Y_data == 0) 82 | plt.scatter(self.X_data[one_index, 0], self.X_data[one_index, 1], c='r') 83 | plt.scatter(self.X_data[zero_index, 0], self.X_data[zero_index, 1], c='b') 84 | translate = 0.1 85 | for i in range(self.X_data.shape[0]): 86 | plt.text(self.X_data[i, 0], self.X_data[i, 1] + translate, 87 | i, fontdict={'size': 8, 'color': 'k'}) 88 | 89 | ax = plt.gca() 90 | ax.axis("equal") 91 | plt.subplot(1, 2, 2) 92 | plt.title("MDS") 93 | if self.reduced_dimension == 2: 94 | for i in range(self.new_data.shape[0]): 95 | if self.Y_data[i] == 1: 96 | plt.scatter(self.new_data[i, 0], self.new_data[i, 1], c='r') 97 | plt.text(self.new_data[i, 0], self.new_data[i, 1] + translate, 98 | i, fontdict={'size': 8, 'color': 'k'}) 99 | else: 100 | plt.scatter(self.new_data[i, 0], self.new_data[i, 1], c='b') 101 | plt.text(self.new_data[i, 0], self.new_data[i, 1] + translate, 102 | i, fontdict={'size': 8, 'color': 'k'}) 103 | else: 104 | for i in range(self.new_data.shape[0]): 105 | if self.Y_data[i] == 1: 106 | plt.scatter(self.new_data[i, 0], 0, c='r') 107 | plt.text(self.new_data[i, 0], translate, 108 | i, fontdict={'size': 8, 'color': 'k'}) 109 | else: 110 | plt.scatter(self.new_data[i, 0], 0, c='b') 111 | plt.text(self.new_data[i, 0], translate, 112 | i, fontdict={'size': 8, 'color': 'k'}) 113 | 114 | ax = plt.gca() 115 | ax.axis("equal") 116 | plt.show() 117 | 118 | 119 | if __name__ == "__main__": 120 | a = MDS(1) 121 | a.make_data() 122 | a.MDS() 123 | a.result() 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/PCA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # author: 11360 4 | # datetime: 2021/3/11 18:58 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from sklearn.datasets import make_moons, make_regression 9 | 10 | 11 | class PCA: 12 | def __init__(self, k): 13 | """ 14 | :param k: reduced dimension R^d -> R^k 15 | """ 16 | self.reduced_dimension = k 17 | 18 | def make_data(self, regression=False): 19 | if regression: 20 | x, y = make_regression(n_samples=50, n_features=1, 21 | n_targets=1, noise=1.5, random_state=1, bias=0) 22 | y = np.array([y]).T 23 | print(y.shape) 24 | self.X_data = np.concatenate([x, y], axis=1) 25 | new_X_data = self.X_data - np.array([[15, 13]]) 26 | 27 | self.X_data = np.concatenate([self.X_data, new_X_data], axis=0) 28 | self.X_data = self.X_data - np.mean(self.X_data, 0) 29 | y = np.array([1 for _ in range(50)]) 30 | new_y = np.array([0 for _ in range(50)]) 31 | self.Y_data = np.concatenate([y, new_y], axis=0) 32 | print(self.X_data.shape) 33 | else: 34 | self.X_data, self.Y_data = make_moons(100, noise=.04, random_state=0) 35 | self.X_data = self.X_data - np.mean(self.X_data, 0) 36 | 37 | def PCA(self): 38 | mat = np.mat(self.X_data).T 39 | centeralized_mat = mat - np.mean(mat, 1) 40 | # np.linalg.svd输出奇异值由大到小排列 41 | u, sigma, v = np.linalg.svd(centeralized_mat) 42 | self.Projective_matrix = u[:, 0:self.reduced_dimension] 43 | print(self.Projective_matrix) 44 | self.new_mat = self.Projective_matrix.T @ centeralized_mat 45 | 46 | def result(self, regression=False): 47 | if regression == False: 48 | translate_vector = [- self.Projective_matrix[1, 0], self.Projective_matrix[0, 0]] 49 | else: 50 | translate_vector = [- self.Projective_matrix[1, 0] * 100, self.Projective_matrix[0, 0] *100] 51 | 52 | for i in range(self.new_mat.shape[1]): 53 | if self.Y_data[i] == 1: 54 | # plt.scatter(self.new_mat[0, i], 0, c='r') 55 | plt.scatter(self.new_mat[0, i] * self.Projective_matrix[0, 0] + translate_vector[0], 56 | self.new_mat[0, i] * self.Projective_matrix[1, 0] + translate_vector[1], c='r') 57 | else: 58 | # plt.scatter(self.new_mat[0, i], 0, c='b') 59 | plt.scatter(self.new_mat[0, i] * self.Projective_matrix[0, 0] + translate_vector[0], 60 | self.new_mat[0, i] * self.Projective_matrix[1, 0] + translate_vector[1], c='b') 61 | one_index = np.where(self.Y_data == 1) 62 | zero_index = np.where(self.Y_data == 0) 63 | plt.scatter(self.X_data[one_index, 0], self.X_data[one_index, 1], c='r') 64 | plt.scatter(self.X_data[zero_index, 0], self.X_data[zero_index, 1], c='b') 65 | ax = plt.gca() 66 | ax.axis("equal") 67 | ax.annotate("", 68 | xy=(translate_vector[0], translate_vector[1]), 69 | xytext=(0, 0), 70 | # xycoords="figure points", 71 | arrowprops=dict(arrowstyle="->", color="k")) 72 | if regression: 73 | plt.text(translate_vector[0] * 0.9, translate_vector[1] * 0.95 + 10, 74 | r'projection', fontdict={'size': 8, 'color': 'k'}) 75 | else: 76 | plt.text(translate_vector[0] * 0.95 + 0.05, translate_vector[1] * 0.95, 77 | r'projection', fontdict={'size': 8, 'color': 'k'}) 78 | plt.show() 79 | 80 | 81 | if __name__ == "__main__": 82 | a = PCA(1) 83 | a.make_data(True) 84 | a.PCA() 85 | a.result(True) 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/QDA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # author: 11360 4 | # datetime: 2021/3/11 18:58 5 | --------------------------------------------------------------------------------