1. 导入数据
一共有40个人脸,每个人脸10张图片,8张用于训练,2张用于测试。所以训练集有320张图片,测试集有80张图片。图像的分辨率为112*92,我们将其reshape为(1,10304)。由此得到训练集(320,10304),测试集(180,10304)
#%%导入数据 import numpy as np import cv2 import os.path as osp import os from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score #训练集路径 osp.abspath(".") #导入训练数据 train_x,train_y = [],[] for i in os.listdir(osp.join(osp.realpath("."),"train_1")): img = cv2.imread(osp.join(osp.realpath("."),"train_1",i),0) train_x.append(np.reshape(img,(1,-1))) a,_ = i.split("_") train_y.append(a) test_x,test_y = [],[] for i in os.listdir(osp.join(osp.realpath("."),"test")): img = cv2.imread(osp.join(osp.realpath("."),"test",i),0) test_x.append(np.reshape(img,(1,-1))) a,_ = i.split("_") test_y.append(a) train_x = np.array(train_x,dtype = np.float32).squeeze(1) train_y = np.array(train_y) test_x = np.array(test_x,dtype = np.float32).squeeze(1) test_y = np.array(test_y)
2. PCA降维
在训练时,最初采用的是特征值分解的方法,但是存在的问题是,因为维度较高,特征分解时间较长,并且分解后的特征向量存在虚数,不利于识别。因此,我采用了第二种方法,SVD分解的方法**(保留前80个维度)**,具体的步骤请参照李航博士《统计学习原理》第二版一书(page336),这里不再一一赘述。
class PCA: #定义K近邻的值必须大于1 def __init__(self): self.x_train_fit = None self.y_train_fit = None self.u = None self.s = None self.vh = None def fit(self,x_train,y_train): self.x_train_fit = x_train self.y_train_fit = y_train return self #中心化,建立模型 def Centralization(self,X): Centra = X - np.mean(self.x_train_fit,axis = 0) return Centra def model(self): #定义一个新矩阵 X_ = (1/np.sqrt(len(self.Centralization(self.x_train_fit))))*self.Centralization(self.x_train_fit) #进行奇异值分解 self.u, self.s, self.vh = np.linalg.svd(X_) #降维 def transform(self,X,K): X = self.Centralization(X) X_dunction = (((self.vh).T[:,:K]).T).dot(X.T).T return X_dunction #%% pca = PCA() pca.fit(train_x,train_y) pca.model() k=80 X_test_dunction_1 = pca.transform(test_x,k) X_train_dunction_1 = pca.transform(train_x,k)
3. LDA降维
LDA也可以推广到了多分类的任务当中。假定存在 N NN个类,且第i ii 类示例数为m i m_im
i
我们先定义"全局散度矩阵"St=Sb+Sw=i=1∑m(xi−μ)(xi−μ)T
其中u uu是所有示例样本的均值xi为第i ii类样本的示例。
我们还要重新定义多分类任务的类内散度S w S_wS 即每个类别的散度矩阵之和:
S w = ∑ i = 1 N S w i \mathbf{S}_{w}=\sum_{i=1}^{N} \mathbf{S}_{w_{i}}
其中
S w i = ∑ x ∈ X i ( x − μ i ) ( x − μ i ) T \mathbf{S}_{w_{i}}=\sum_{\boldsymbol{x} \in X_{i}}\left(\boldsymbol{x}-\boldsymbol{\mu}_{i}\right)\left(\boldsymbol{x}-\boldsymbol{\mu}_{i}\right)^{\mathrm{T}}
由以上公式可以知道
S b = S t − S w = ∑ i = 1 N m i ( μ i − μ ) ( μ i − μ ) T
Sb=St−Sw=∑i=1Nmi(μi−μ)(μi−μ)T
Sb=St−Sw=∑i=1Nmi(μi−μ)(μi−μ)T
其中S b S_bS
b
为类间散度,m i m_im
i
表示的为类别,在本题目中有40个类别。u i u_iu
i
是同一类别下的特征均值。
一般的我们可以利用如下公式,进行求解,其中W WW就是一个投影矩阵,我们可以选取特征值最大的前K个,用于特征的降维。
S b W = λ S w W \mathbf{S}_{b} \mathbf{W}=\lambda \mathbf{S}_{w} \mathbf{W}
#%%导入数据 #%%算法的判别 #需要计算肋间方差和类内方差 import collections #求取所用示例的均值向量(k,),k表示降维后的数据 mean_u = np.mean(X_train_dunction_1,axis = 0) #求取类间散度矩阵,和类内散度矩阵 S_b = np.zeros((X_train_dunction_1.shape[1],X_train_dunction_1.shape[1])) S_w = np.zeros((X_train_dunction_1.shape[1],X_train_dunction_1.shape[1])) get_class = collections.Counter(train_y).keys() for classes in get_class: x_i = np.array([x_i for x_i,i in zip(X_train_dunction_1,train_y) if i == classes]) mean_u_i = np.mean(x_i,axis = 0) #类间散度矩阵 S_b = S_b + (mean_u_i - mean_u).reshape(-1,1).dot((mean_u_i - mean_u).reshape(1,-1)) #类内散度矩阵,西瓜书公式3.41,3.42 S_w = S_w + (x_i - mean_u_i).T.dot(x_i - mean_u_i) #类间散度矩阵,西瓜书公式3.43 S_b = S_b * len(get_class) #进行特征值分解 eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(S_w).dot(S_b)) #对特征值进行由高到低的排序 eig_vecs = np.array([eig_vecs[:,i] for i in np.argsort(-eig_vals)]) #%%求取准确度 for k in range(1,40): X_train_dunction_2 = X_train_dunction_1.dot(eig_vecs[:,:k]) X_test_dunction_2 = X_test_dunction_1.dot(eig_vecs[:,:k]) accu = 0 for i in range(len(test_y)): a = [np.linalg.norm(X_test_dunction_2[i,:] - X_train_dunction_2[j,:]) for j in range(len(train_y))] min_dix = np.argmin(a) if train_y[min_dix] == test_y[i]: accu += 1 print("保留前{}维的准确度为{}".format(k,accu/80))
结果
保留前1维的准确度为0.0875 保留前2维的准确度为0.425 保留前3维的准确度为0.575 保留前4维的准确度为0.7125 保留前5维的准确度为0.8 保留前6维的准确度为0.85 保留前7维的准确度为0.875 保留前8维的准确度为0.8875 保留前9维的准确度为0.875 保留前10维的准确度为0.8625 保留前11维的准确度为0.875 保留前12维的准确度为0.9125 保留前13维的准确度为0.9375 保留前14维的准确度为0.9125 保留前15维的准确度为0.9375 保留前16维的准确度为0.95 保留前17维的准确度为0.9375 保留前18维的准确度为0.9125 保留前19维的准确度为0.9625 保留前20维的准确度为0.9625 保留前21维的准确度为0.9625 保留前22维的准确度为0.9625 保留前23维的准确度为0.9625 保留前24维的准确度为0.975 保留前25维的准确度为0.975 保留前26维的准确度为0.975 保留前27维的准确度为0.9625 保留前28维的准确度为0.9625 保留前29维的准确度为0.9625 保留前30维的准确度为0.95 保留前31维的准确度为0.9625 保留前32维的准确度为0.95 保留前33维的准确度为0.9625 保留前34维的准确度为0.9625 保留前35维的准确度为0.9625 保留前36维的准确度为0.9625 保留前37维的准确度为0.95 保留前38维的准确度为0.9625 保留前39维的准确度为0.975
有测试结果得出,在本实验中保留前24维,准确度可以达到最佳。