局部线性嵌入(LLE)算法的详细推导及Python实现

  局部线性嵌入(Local Linear Embedding, LLE)算法是一种经典的流形学习算法,不同于等度量映射(Isometric Mapping, Isomap)算法考虑结点的全局连接信息,LLE算法只考虑每个结点的k近邻信息,所以速度比Isomap快很多。LLE的思想如下图所示,LLE在低维空间保持了原始高维空间样本邻域内的线性关系,这也是局部线性的一个最直接的解释:高维数据在局部上具有欧式空间的性质,满足线性相关性。
局部线性嵌入(LLE)算法的详细推导及Python实现
  给定LLE算法的输入χ={x1,x2,...,xN}RD×N\chi=\{x_1, x_2,...,x_N\}\in R^{D\times N}χ={x1​,x2​,...,xN​}∈RD×N,其中xiRD×1,i=1,...,Nx_i\in R^{D\times1},i=1,...,Nxi​∈RD×1,i=1,...,N,即有NNN个样本,每个样本都是维度为DDD的列向量。算法输出为Y={y1,y2,...,yN}Rd×NY=\{y_1, y_2,...,y_N\}\in R^{d\times N}Y={y1​,y2​,...,yN​}∈Rd×N,其中d<Dd<Dd<D,yiRd×1,i=1,...,Ny_i\in R^{d\times1},i=1,...,Nyi​∈Rd×1,i=1,...,N。假定原始输入空间的向量xix_ixi​满足xi=j=1kwjixjix_i=\sum_{j=1}^k w_{ji}x_{ji}xi​=∑j=1k​wji​xji​,xjix_{ji}xji​为xix_ixi​的第jjj个近邻点,wji(0,1)w_{ji}\in(0,1)wji​∈(0,1)为权重,那么LLE算法认为在低维空间同样应该保持上述线性关系:yi=j=1kwjiyjiy_i=\sum_{j=1}^k w_{ji}y_{ji}yi​=∑j=1k​wji​yji​,所以LLE算法的核心就是如何根据高维空间的邻域线性关系求解具有同邻域线性关系的低维流形嵌入。
  首先看高维空间。LLE算法第一步是求出原始输入空间的每个样本与其邻域的线性关系,即argminWi=1Nxij=1kwjixji2\mathop{\arg\min}_{W} \sum_{i=1}^N \| x_i-\sum_{j=1}^k w_{ji}x_{ji} \|^2argminW​i=1∑N​∥xi​−j=1∑k​wji​xji​∥2 s.t. j=1kwji=1s.t. \ \sum_{j=1}^k w_{ji}=1s.t. j=1∑k​wji​=1记Φ(W)=i=1Nxij=1kwjixji2=i=1Nj=1k(xixji)wji2=i=1N(XiNi)Wi2=i=1NWiT(XiNi)T(XiNi)Wi\begin{aligned} \Phi(W) &= \sum_{i=1}^N \| x_i-\sum_{j=1}^k w_{ji}x_{ji} \|^2 \\ &= \sum_{i=1}^N \| \sum_{j=1}^k (x_i-x_{ji}) w_{ji} \|^2 \\ &= \sum_{i=1}^N \| (X_i-N_i)W_i \|^2 \\ &= \sum_{i=1}^N W_i ^T(X_i-N_i)^T(X_i-N_i)W_i \end{aligned}Φ(W)​=i=1∑N​∥xi​−j=1∑k​wji​xji​∥2=i=1∑N​∥j=1∑k​(xi​−xji​)wji​∥2=i=1∑N​∥(Xi​−Ni​)Wi​∥2=i=1∑N​WiT​(Xi​−Ni​)T(Xi​−Ni​)Wi​​ s.t.  WiT1k=1s.t. \ \ W_i^T \boldsymbol {1}_k=1s.t.  WiT​1k​=1 其中Xi,NiRD×k,WiRk×1X_i,N_i\in R^{D\times k},W_i\in R^{k\times 1}Xi​,Ni​∈RD×k,Wi​∈Rk×1。记S=(XiNi)T(XiNi)Rk×kS=(X_i-N_i)^T(X_i-N_i) \in R^{k\times k}S=(Xi​−Ni​)T(Xi​−Ni​)∈Rk×k,则上述优化目标变成了:Φ(W)=i=1NWiTSWi \Phi(W)= \sum_{i=1}^N W_i ^TSW_i Φ(W)=i=1∑N​WiT​SWi​ s.t.  WiT1k=1s.t. \ \ W_i^T \boldsymbol {1}_k=1s.t.  WiT​1k​=1 写出该优化目标的拉格朗日方程: L=WiTSWi+λ(WiT1k1) L=W_i ^TSW_i + \lambda (W_i^T \boldsymbol {1}_k-1) L=WiT​SWi​+λ(WiT​1k​−1) 求LLL对WiW_iWi​的偏导数,并令其等于零:LWi=2SWi+λ1k=0\frac{\partial L}{\partial W_i} =2SW_i+\lambda \boldsymbol {1}_k=0 ∂Wi​∂L​=2SWi​+λ1k​=0 则可以求出WiW_iWi​: Wi=Si11k1kTSi11kW_i=\frac{S_i^{-1} \boldsymbol {1}_k}{\boldsymbol {1}_k ^TS_i^{-1} \boldsymbol {1}_k} Wi​=1kT​Si−1​1k​Si−1​1k​​ 上式中的分母1kTSi11k\boldsymbol {1}_k ^TS_i^{-1} \boldsymbol {1}_k1kT​Si−1​1k​是为了对WiRk×1W_i \in R^{k \times 1}Wi​∈Rk×1进行归一化,使得WiW_iWi​各维度之和为1,所以这里可以忽略系数λ\lambdaλ,总体的权重系数WWW矩阵为W={W1,W2,...,WN}Rk×NW=\{ W_1, W_2,...,W_N \} \in R^{k \times N}W={W1​,W2​,...,WN​}∈Rk×N。
  然后看低维空间Y={y1,y2,...,yN}Rd×NY=\{y_1, y_2,...,y_N\}\in R^{d\times N}Y={y1​,y2​,...,yN​}∈Rd×N,YYY的每一列都是一个样本,样本长度为ddd,共NNN个样本,因为低维空间要保证局部线性与原始输入空间相同,所以可以表示为:argminYi=1Nyij=1kwjiyji2\mathop{\arg\min}_{Y} \sum_{i=1}^N \| y_i-\sum_{j=1}^k w_{ji}y_{ji} \|^2argminY​i=1∑N​∥yi​−j=1∑k​wji​yji​∥2 s.t. i=1Nyi=0i=1NyiyiT=NId×d\begin{aligned} s.t. \ & \sum_{i=1}^N y_i=0 \\ & \sum_{i=1}^N y_i y_i^T=NI_{d \times d} \end{aligned}s.t. ​i=1∑N​yi​=0i=1∑N​yi​yiT​=NId×d​​ 这里有两个约束条件,第一个是说YYY被中心化,所有的样本在同一个维度上的数值之和为0,第二个约束条件指的是YYY中的低维样本yiy_iyi​都是单位向量。现建立稀疏矩阵W={wij}RN×NW'=\{ w'_{ij} \} \in R^{N \times N}W′={wij′​}∈RN×N,且wij={wij,   xjkNN(xi)0,       otherwise w'_{ij}=\left\{ \begin{aligned} &w_{ij}, \ \ \ x_j \in kNN(x_i)\\ &0, \ \ \ \ \ \ \ otherwise\\ \end{aligned} \right. wij′​={​wij​,   xj​∈kNN(xi​)0,       otherwise​ 因此,j=1Nwjiyji=j=1kwjiyji=YWi\sum_{j=1}^N w'_{ji}y_{ji} = \sum_{j=1}^k w_{ji}y_{ji} = YW'_ij=1∑N​wji′​yji​=j=1∑k​wji​yji​=YWi′​ 上式中,YRd×NY \in R^{d \times N}Y∈Rd×N每一列代表一个样本,WiRN×1W '_i\in R^{N \times 1}Wi′​∈RN×1为矩阵WW'W′的列向量,得到的结果刚好是yiRd×1y_i \in R^{d \times 1}yi​∈Rd×1。记Φ(Y)=i=1Nyij=1kwjiyji2=i=1NY(IiWi)2=i=1N[Y(IiWi)(IiWi)TYT]=tr(Y(IiWi)(IiWi)TYT)\begin{aligned} \Phi(Y) &= \sum_{i=1}^N \| y_i-\sum_{j=1}^k w_{ji}y_{ji} \|^2 \\ &= \sum_{i=1}^N \| Y(I_i-W'_i) \|^2 \\ &= \sum_{i=1}^N [Y(I_i-W'_i)(I_i-W'_i)^TY ^T] \\ &= tr(Y(I_i-W'_i)(I_i-W'_i)^TY ^T) \end{aligned}Φ(Y)​=i=1∑N​∥yi​−j=1∑k​wji​yji​∥2=i=1∑N​∥Y(Ii​−Wi′​)∥2=i=1∑N​[Y(Ii​−Wi′​)(Ii​−Wi′​)TYT]=tr(Y(Ii​−Wi′​)(Ii​−Wi′​)TYT)​ 这里用到了矩阵的一个技巧,假定A=[a1,a2,,...,an]A=[a_1, a_2, ,...,a_n]A=[a1​,a2​,,...,an​],anRd×1a_n \in R^{d \times 1}an​∈Rd×1为列向量,则i=1Nai2=i=1NaiTai=tr(AAT)\sum_{i=1}^N a_i^2 = \sum_{i=1}^N a_i^T a_i = tr(AA^T)∑i=1N​ai2​=∑i=1N​aiT​ai​=tr(AAT)(结果最后为一个数值,即i=1dj=1NAij2\sum_{i=1}^d \sum_{j=1}^N A_{ij}^2∑i=1d​∑j=1N​Aij2​),可以使用三元数组举一个toy problem,篇幅所限就不列举出来了。所以同样,i=1NyiTyi=tr(YYT)\sum_{i=1}^N y_i^T y_i = tr(YY^T)∑i=1N​yiT​yi​=tr(YYT)。令M=(IiWi)(IiWi)TRN×NM=(I_i-W'_i)(I_i-W'_i)^T\in R^{N \times N}M=(Ii​−Wi′​)(Ii​−Wi′​)T∈RN×N,则优化问题可写为:min  Φ(Y)=tr(YMYT)min \ \ \Phi(Y)=tr(YMY^T)min  Φ(Y)=tr(YMYT) s.t.  tr(YYTNId×d)=0s.t. \ \ tr(YY^T-NI_{d \times d})=0s.t.  tr(YYT−NId×d​)=0 写出拉格朗日方程:L=tr[YMYT+λ(YYTNId×d)]L=tr[YMY^T+\lambda(YY^T-NI_{d \times d})]L=tr[YMYT+λ(YYT−NId×d​)] 对YYY求导并令其等于零,则可以得到MYT+2λYT=0MY_T+2 \lambda Y_T=0MYT​+2λYT​=0 MYT=λYTMY_T=\lambda' Y_TMYT​=λ′YT​ 然后,借鉴PCA的思想,要得到降维后的ddd维数据集,只需要取矩阵MMM最小的ddd个特征值对应的特征向量组成的矩阵YRd×NY \in R^{d\times N}Y∈Rd×N,由于MMM最小的特征值一般为000,所以去掉最小的特征值,选择MMM的第222到第d+1d+1d+1特征值对应的特征向量。
  然后把代码贴出来,详细代码见我的GitHub,首先是求距离矩阵然后计算K近邻的索引:

def cal_pairwise_dist(data):
	expand_ = data[:, np.newaxis, :]
	repeat1 = np.repeat(expand_, data.shape[0], axis=1)
	repeat2 = np.swapaxes(repeat1, 0, 1)
	D = np.linalg.norm(repeat1 - repeat2, ord=2, axis=-1, keepdims=True).squeeze(-1)
	return D
	
def get_n_neighbors(data, n_neighbors=10):
	dist = cal_pairwise_dist(data)
	dist[dist < 0] = 0
	n = dist.shape[0]
	N = np.zeros((n, n_neighbors))
	for i in range(n):
		# np.argsort 列表从小到大的索引
		index_ = np.argsort(dist[i])[1:n_neighbors+1]
		N[i] = N[i] + index_
	return N.astype(np.int32)                         # [n_features, n_neighbors]

然后是LLE算法的核心:

def lle(data, n_dims=2, n_neighbors=10):
	N = get_n_neighbors(data, n_neighbors)            # k近邻索引
	n, D = data.shape                                 # n_samples, n_features
	# prevent Si to small
	if n_neighbors > D:
		tol = 1e-3
	else:
		tol = 0
	# calculate W
	W = np.zeros((n_neighbors, n))
	I = np.ones((n_neighbors, 1))
	for i in range(n):                                # data[i] => [1, n_features]
		Xi = np.tile(data[i], (n_neighbors, 1)).T     # [n_features, n_neighbors]
		                                              # N[i] => [1, n_neighbors]
		Ni = data[N[i]].T                             # [n_features, n_neighbors]
		Si = np.dot((Xi-Ni).T, (Xi-Ni))               # [n_neighbors, n_neighbors]
		Si = Si + np.eye(n_neighbors)*tol*np.trace(Si)
		Si_inv = np.linalg.pinv(Si)
		wi = (np.dot(Si_inv, I)) / (np.dot(np.dot(I.T, Si_inv), I)[0,0])
		W[:, i] = wi[:,0]
	W_y = np.zeros((n, n))
	for i in range(n):
		index = N[i]
		for j in range(n_neighbors):
			W_y[index[j],i] = W[j,i]
	I_y = np.eye(n)
	M = np.dot((I_y - W_y), (I_y - W_y).T)
	eig_val, eig_vector = np.linalg.eig(M)
	index_ = np.argsort(np.abs(eig_val))[1:n_dims+1]
	Y = eig_vector[:, index_]
	return Y

  最后贴一张实验效果图,瑞士卷降维的:
局部线性嵌入(LLE)算法的详细推导及Python实现

上一篇:LIS&&LCS&&LCIS


下一篇:摆花