白板推导系列Pytorch-线性判别分析(LDA)
导入所需的包
import torch
from sklearn.linear_model import Perceptron
import torch.nn.functional as F
import matplotlib.pyplot as plt
生成数据集
def create_dataset(n_samples=1000):
x0 = torch.normal(2,1,size=(n_samples//2,2),dtype=torch.float32)
y0 = torch.zeros(n_samples//2,dtype=torch.float32)
x1 = torch.normal(-2,1,size=(n_samples-n_samples//2,2),dtype=torch.float32)
y1 = torch.ones(n_samples-n_samples//2,dtype=torch.float32)
#合并数据x,y
x=torch.cat((x0,x1),0)
y=torch.cat((y0,y1),0)
return x,y
X,y = create_dataset(1000)
plt.scatter(X[:,0],X[:,1],c=y)
准备一个可视化w直线的函数
def plot_line(X,y,w):
plt.scatter(X[:, 0], X[:, 1], c=y)
x = torch.tensor([-4,4],dtype=torch.float32)
y = w[1] / w[0] * x
plt.plot(x, y, color='red')
plt.show()
定义LDA类
class LDA:
def __init__(self):
pass
def fit(self,X,y):
# 分离样本
X = X.numpy()
X1 = torch.tensor([X[i] for i in range(len(X)) if y[i] == 0],dtype=torch.float32)
X2 = torch.tensor([X[i] for i in range(len(X)) if y[i] == 1],dtype=torch.float32)
# 分别求均值
mju1 = torch.mean(X1, dim=0)#求中心点
mju2 = torch.mean(X2, dim=0)
# 分别求协方差
cov1 = torch.zeros(size=(len(mju1),len(mju1)),dtype=torch.float32)
cov2 = torch.zeros(size=(len(mju2),len(mju2)),dtype=torch.float32)
# 注意,下面的view一定要加上,否则会出问题
for i in range(len(X1)):
cov1 += torch.matmul((X1[i]-mju1).view(len(mju1),-1),(X1[i]-mju1).T.view(-1,len(mju1)))
for i in range(len(X2)):
cov2 += torch.matmul((X2[i]-mju2).view(len(mju2),-1),(X2[i]-mju2).T.view(-1,len(mju2)))
# 求和得到Sw
Sw = cov1/len(X1) + cov2/len(X2)
# Sw的逆乘以均值差,注意同样要加view,把向量转换成矩阵。为了应对Sw不可逆的情况,采用伪逆
w = torch.matmul(torch.pinverse(Sw), (mju1 - mju2).view(len(mju1),-1)) # 计算w
# w单位化
w = F.normalize(w,p=2,dim=0)
def transform(self,X):
X = torch.tensor(X,dtype=torch.float32)
X = torch.matmul(X,self.w)
return X
降维,计算原数据的投影
lda = LDA()
# 计算w
lda.fit(X,y)
# 计算投影
X_new = lda.transform(X)
投影前
plot_line(X,y,lda.w)
投影后(y随机生成)
plt.scatter(X_new[:, 0], torch.randn(size=(X_new.shape[0],)),marker='o',c=y)
plt.show()
利用sklearn进行LDA降维
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda = LinearDiscriminantAnalysis(n_components=1)
lda.fit(X,y)
X_new = lda.transform(X)
plt.scatter(X_new[:, 0], torch.randn(size=(X_new.shape[0],)),marker='o',c=y)
plt.show()
使用sklearn的lda生成的投影跟自己实现的lda投影正好相反,但不影响分类
利用skearn进行感知机分类
# fit_intercept表示是否需要截距
clf = Perceptron(fit_intercept=True,n_iter_no_change=100,shuffle=False)
# 训练
clf.fit(X_new[:800],y[:800])
# 评估
clf.score(X_new[800:],y[800:])