前言:
流行学习主要用于聚类,分类和回归算法,例如人脸识别(旋转不变性,光照不变性)
流行是几何中一个概念,它是高维空间中的几何结构,即高维空间中点构成的集合。
简单的理解为二维空间的曲线,三维空间的曲面
假设有一个N维空间的流行M,流行学习降维要实现如下映射
, 映射到(k<N)
早期想把LLE,LE,LPP,ISOmap, MDS 写在一篇,后来实现的时候发现篇幅过大,就分开来写了。
目录:
- 应用
- 算法思想
- 算法推导
- 算法流程
- 算法实现
- 一 LLE 应用
LLE(Locally Linear Embedding) 将高维数据投影到低维空间中,并保持局部线性关系。
应用于人脸图像,手写数字图像,自然语言处理。
- 二 算法思想
高维空间的样本点xi,可由近邻的样本点线性重构出来,如上图
: 为行向量,是样本
: 为列向量
上面式子也可以改为矩阵形式:
LLE算法希望在低维空间中保持如此的线性重构关系
- 三 原理推导:
预置条件:
: 的k个近邻下标集合
: 重构系数,为列向量 ,,为k行1列的向量
,,k*k的矩阵
: 为行向量,代表样本
k: 的长度
3.1 定义高维空间的损失函数:
根据预置条件:
因为有约束,作拉格朗日对偶求极值
, 其中,为k行1列的矩阵
求偏导数:
设
..........................式1
根据约束条件, 对上式进行归一化,求出
求出, 带入式1 可以得到高维重构系数
3.2 映射到d维
低维度空间映射模型
,
其中 :
代表取对应的列,例如
W: 是 低维度的稀疏矩阵,每一列代表一个xi的邻值权值分布情况 Wi 代表取对应的列
则:
令
加上约束条件,拉格朗日对偶变换后,求极小值
求偏导数
可以看出来Y就是特征向量。
注意:
1: 跟PCA 降维相反,这里特征值要去从小到大的排序对应的d个特征向量
2: 零特征值对应的特征向量为e,所以要去非零特征值对应的特征向量
证明:
e:代表1列全为1的列矩阵
左式:
因为e 非0,所以特征向量为0的特征值对应特征向量为e
其中
四 : 算法主要流程:
1: 计算每个样本的k个最临近
2: 计算对应Wi
3: 重构低维度稀疏矩阵W
4: 计算低维度矩阵M:
5: 获取M矩阵对应的特征值,特征值向量
排序后,选择最大的d个非零特征值对应的特征向量
注意事项:
1: 计算Wi的时候,要计算可逆矩阵,这里面有不同的求法,一般都会遇到奇异矩阵。
不同的求法,误差也主要产生在这里面
2: 非零特征值 定义不同,也会导致不同的结果
3: k 的取值影响也很大
4: 每次Wi重构后,可以计算一下重构的误差大小
五 算法实现:
1: 直接调用sklearn库函数方式:
LLE 后效果
Code:
# -*- coding: utf-8 -*-
"""
Created on Thu Oct 10 16:50:58 2019
@author: chengxf2
"""
from time import time
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.ticker import NullFormatter
from sklearn import manifold, datasets
import pickle
file = "d:\\1.txt"
"""
保存的数据集格式
Args
data: 训练样本
color: 样本点对应的color
"""
class Data:
def __init__(self, data, color):
self.trainData = data
self.dataColor = color
"""
绘制点
Args:
Args
data: 训练样本
color: 样本点对应的color
"""
def Draw(data, color):
fig = plt.figure(figsize=(6, 5))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(data[:, 0], data[:, 1], data[:, 2], c=color, cmap=plt.cm.hot)
ax.view_init(10, -70)
ax.set_xlabel("$x_1$", fontsize=18)
ax.set_ylabel("$x_2$", fontsize=18)
ax.set_zlabel("$x_3$", fontsize=18)
plt.show()
"""
从文件中读取数据
Args
file: 文件路劲
return
trainData: 训练样本
color: 样本点对应的color
"""
def LoadFile(file):
f = open(file, 'rb')
data = pickle.load(f)
f.close()
trainData = data.trainData
color = data.dataColor
Draw(trainData, color)
return trainData, color
"""
保存文件
Args
file: 文件名
data: 样本
color: 颜色
return
None
"""
def SaveData(file, data ,color):
DataInfo = Data(data,color)
f = open("d:\\1.txt",'wb')
pickle.dump(DataInfo, f,0)
f.close()
"""
生成保存流行数据
Args
None
return
None
"""
def SWData():
n_points = 500
data, color = datasets.samples_generator.make_s_curve(n_points, random_state=0)
Draw(data, color)
SaveData(file, data,color)
"""
局部线性嵌入降维
Args
data: 数据集
color: 颜色
"""
def LLE(data,color):
n_components = 2 ##降低后的维度
n_neighbors = 15 ##邻近的个数
t0 = time() #计时开始
lle = manifold.LocallyLinearEmbedding(n_neighbors, n_components,max_iter=1)
X_reduced =lle.fit_transform(data)
print(" reconstruction_error:\t ", lle.reconstruction_error_)
print(" \n lle.hessian_tol:\t ", lle.hessian_tol)
print(" neighbors_algorithm:\t ", lle.method)
#print(" embedding_:\t ", lle.embedding_)
plt.title("Unrolled swiss roll using LLE", fontsize=14)
print("x_reduced ",np.shape(X_reduced), "x_reduced[0",X_reduced[0:2])
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=color, cmap=plt.cm.hot)
plt.xlabel("$z_1$", fontsize=18)
plt.ylabel("$z_2$", fontsize=18)
plt.grid(True)
#save_fig("lle_unrolling_plot")
plt.show()
SWData()
data, color = LoadFile(file)
x =data.tolist()
LLE(x, color)
二 自己实现方法:
# -*- coding: utf-8 -*-
"""
Created on Thu Oct 10 16:50:58 2019
@author: chengxf2
"""
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import proj3d
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import LocallyLinearEmbedding
import pickle
import copy
"""
保存的数据集格式
Args
data: 训练样本
color: 样本点对应的color
"""
class Data:
def __init__(self, data, color):
self.trainData = data
self.dataColor = color
"""
绘制点
Args:
Args
data: 训练样本
color: 样本点对应的color
"""
def Draw(data, color, D3):
if True ==D3:
fig = plt.figure(figsize=(6, 5))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(data[:, 0], data[:, 1], data[:, 2], c=color, cmap=plt.cm.hot)
ax.view_init(10, -70)
ax.set_xlabel("$x_1$", fontsize=18)
ax.set_ylabel("$x_2$", fontsize=18)
ax.set_zlabel("$x_3$", fontsize=18)
else:
plt.title("Unrolled swiss roll using LLE", fontsize=14)
plt.scatter(data[:, 0], data[:, 1], c=color, cmap=plt.cm.hot)
plt.xlabel("$z_1$", fontsize=18)
plt.ylabel("$z_2$", fontsize=18)
plt.grid(True)
#save_fig("lle_unrolling_plot")
plt.show()
"""
从文件中读取数据
Args
file: 文件路劲
return
trainData: 训练样本
color: 样本点对应的color
"""
def LoadFile(file):
f = open(file, 'rb')
data = pickle.load(f)
f.close()
trainData = data.trainData
color = data.dataColor
Draw(trainData, color,True)
print("shape ", np.shape(trainData))
print("tp :",type(trainData))
return trainData, color
"""
获取k邻近点
Args
data: 数据集
x_i: 样本点
i: 当前样本点index
k: 近邻点取值个数
m: 样本个数
"""
def GetNk(data,x_i, i,k,m):
#m
XDict ={}
for j in range(m):
if i ==j:
continue
x_j = data[j]
X = x_i-x_j
dist = np.linalg.norm(X)
XDict[j]=dist
jDict = sorted(XDict.items(), key=lambda d:d[1])
jList =[item[0] for item in jDict]
# print("\n i ",i, "\t NearJ: ",jList[0:k])
return jList[0:k]
"""
通过奇异分解求解逆矩阵
Args
Zi
"""
def SvdInv(Z):
m,n = np.shape(Z)
U,V,T = np.linalg.svd(Z)
invV = np.zeros((n,n))
#tol=1E-8 ##防止特征值为0
alpha = 1e-3
for i in range(n):
if np.abs(V[i]<alpha):
invV[i,i] = alpha
else:
a= V[i]
invV[i,i] = 1.0/a
invZ = T.T*invV*U.T
return invZ
def GetLoss(xi, xj, wi):
xiNew = wi.T*xj
a =xi-xiNew
loss = np.linalg.norm(a)
return loss
"""
"""
def GetwL(wNear, wH,m,K):
wL = np.zeros((m,m))
for i in range(m):
QJ = wNear[i] ##邻近矩阵的左边
for j in QJ:##QJ
index = QJ.index(j)
# print("wi ",wH[i], "\t ",j)
wij = wH[i][index]
wL[j,i]=wij ##列矩阵
return wL
"""
低维线性嵌入
Args
data: 数据集
color: 标签
k: 邻近点取的个数
"""
def LLE(data, color, k):
#zero =6.113835700142957e-07
zero = 3.5508457154521093e-07
n_components = 2
m,n = np.shape(data)
onek = np.ones((k,1))
wNear =[]
wH=[]
###step1 计算k 邻近####
print("\n step1 计算邻近:")
for i in range(m):
xi = data[i]
# print("\n xi : ",xi)
QJ = GetNk(data, xi, i, k, m)
wNear.append(QJ)
##计算Zi
xj =[]
for j in QJ:
x_j = data[j]
xj.append(x_j.tolist()[0])
A = xi -xj ##Matrix
Zi =A*A.T##10*10
InvZ = SvdInv(Zi)
a = InvZ*onek
b = onek.T*a
wi = a/b
#loss = GetLoss(xi, xj, wi)
wH.append(wi.T.tolist()[0])
#print("\n i: ",wi.T.tolist()[0])
##求解低维度稀疏矩阵
wL = GetwL(wNear, wH, m,k)
# print("\n 低维度稀疏矩阵: \n ",wL)
I = np.mat(np.eye(m,m))
matA = I-wL
##计算M
print("\n step3 : M 计算完毕")
M = np.dot(matA, matA.T)
# for col in range(m):
# print("\n step2: 计算稀疏矩阵%d \n "%col,M[:,col])
#print("\n sp M: ", np.shape(M), "\t tyep ", type(M))
#print("\n 低维度稀疏矩阵: \n ",M)
##求解特征值特征向量 a 特征值, b特征向量
print("\n step4 : 计算特征值特征向量")
a, b = np.linalg.eig(M)
lista = list(a)
Y =np.zeros((m,n_components))
a1 = copy.deepcopy(a)
a1.sort()
print("m: ",m)
print("shape Y:::::::::: ", np.shape(Y))
col = 0
for lamb in a1: ##从小到大排序
if np.abs(lamb)>zero and col<n_components:
j = lista.index(lamb)
Y[:,col]= b[:,j].reshape(1,m)
col = col+1
print("\n step5 : 取Y")
return Y
#reverse = True 降序 , reverse = False 升序(默认)
def Test():
matA = np.mat([[1,2],
[2,2]])
matB = np.mat([[1,2],
[2,2]])
a, b = np.linalg.eig(matA)
#print("\n a: \n ",a)
#print("\n b: \n ",b)
for i in range(len(a)):
lamb = a[i]
x = b[:,i]
y = lamb*x
y1 = matA*x
print("\n ================\n")
print("\n left :\n ",y.T, "\n right: \n",y1.T)
def Train():
file = "d:\\1.txt"
data,color = LoadFile(file)
dataMat = np.mat(data)
Y= LLE(dataMat,color,15)
data2 = np.array(Y)
Draw(data2, color, False)
#Test()
Train()
参考文档
https://www.cnblogs.com/pinard/p/6266408.html?utm_source=itdadao&utm_medium=referral
https://wenku.baidu.com/view/674e73ab647d27284a735143.html?from=search
https://www.cnblogs.com/jiangxinyang/p/9314256.html
https://blog.csdn.net/elma_tww/article/details/88143633