西瓜书习题试答-第10章-降维与度量学习

试答系列:“西瓜书”-周志华《机器学习》习题试答
系列目录
[第01章:绪论]
[第02章:模型评估与选择]
[第03章:线性模型]
[第04章:决策树]
[第05章:神经网络]
[第06章:支持向量机]
第07章:贝叶斯分类器
第08章:集成学习
第09章:聚类
第10章:降维与度量学习
第11章:特征选择与稀疏学习
第12章:计算学习理论(暂缺)
第13章:半监督学习
第14章:几率图模型
(后续章节更新中...)html



10.1 编程实现k近邻分类器,在西瓜数据集3.0α上比较其分类边界与决策树边界之异同。

:编程代码附后。运行结果以下(考虑了k=1,3,5,以及分别采用曼哈顿(p=1),欧式(p=2)和切比雪夫(p=50)三种距离):
在这里插入图片描述
上图中,“+”和“-”离散点分别表明训练数据样本点,黑色线条为决策树边界,红色线条为kNN边界。
讨论:python

  1. 要说kNN边界与决策树边界的异同,实在看不出来什么值得说道的地方,除了众所周知的,决策树边界线段平行于坐标轴。
  2. 当k=1时训练偏差为零;当k愈来愈大时,分类边界趋向平缓,会出现训练样本误分类的状况,此时为欠拟合。可见,k越小越容易过拟合,k越大越容易欠拟合。
  3. 不一样p值的距离计算下,差异不太明显。

10.2 令err、err*分别表示最近邻分类器与贝叶斯最优分类器的指望错误率,试证实

\[err^\ast\leq err\leq err^\ast(2-\frac{|y|}{|y|-1}err^*)\tag{10.40} \]

证实:首先来理解一下这两个指望错误率,它们都表示成“错误率=1-正确率”的形式,所以问题转化成理解正确率。
对于预测样本\(x\),贝叶斯最优分类器的决策结果\(c^*\),若是预测正确,意味着这个样本的真实类别恰好也是\(c^*\),而该样本以\(P(c^*|x)\)的几率属于\(c^*\)类别,所以预测正确的几率即为\(P(c^*|x)\),而错误率\(err^*=1-P(c^*|x)\)
最近邻分类器的决策结果等于近邻样本\(z\)相同的类别,假设这个共同的类别为\(c\),若是这个决策结果是正确的,意味着\(x\)样本和\(z\)样本恰好都属于\(c\)类,这个事件发生的几率是\(P(c|x)P(c|z)\)。考虑各类不一样的\(c\)值,把它们加起来即是总的指望正确率\(=\sum_c P(c|x)P(c|z)\),所以错误率\(err=1-\sum_c P(c|x)P(c|z)\)
接下来证实上面的不等式:算法

\[\begin{aligned} err&=1-\sum_cP(c|x)P(c|z)\\ &\geq 1-\sum_cP(c^*|x)P(c|z)\\ &=1-P(c^*|x)\cdot\sum_cP(c|z)\\ &=1-P(c^*|x)\\ &=err* \end{aligned}\]

第2行利用了关系\(P(c|x)\leq P(c^*|x)\),第4行利用了关系\(\sum_cP(c|z)=1\)
不等式左半边得证。编程

\[\begin{aligned} err&=1-\sum_cP(c|x)P(c|z)\\ &≈1-\sum_cP^2(c^|x)\\ &=1-P^2(c^*|x)-\sum_{c\neq c^\ast}P^2(c|x)\\ &\leq 1-P^2(c^*|x)- \frac{[\sum_{c\neq c^\ast}P(c|x)]^2}{|y|-1}\\ &=1-(1-err^\ast)^2-\frac{(err^\ast)^2}{|y|-1} \\ &=err^\ast (2-\frac{|y|}{|y|-1}err^\ast) \end{aligned}\]

第4行利用了不等式关系:\(\frac{\sum a_i^2 }{n} \geq (\frac{\sum a_i}{n})^2, \ \ a_i\geq 0,\ i=1,2,\cdots ,n\);第5行利用了关系\(\sum_{c\neq c^\ast}P(c|x)=1-P(c^*|x)=err^*\)
不等式右半边得证。网络

10.3 在对高维数据降维以前应先进行“中心化”,常见的是将协方差矩阵\(XX^T\)转化为\(XHH^TX^T\),其中\(H=I-\frac{1}{m}11^T\),试析其效果。

:至关于将\(X\)变为\(X^{\prime}\):app

\[\begin{aligned} X^\prime&=XH\\ &=X(I-\frac{1}{m}11^T)\\ &=X-\frac{1}{m}X11^T\\ &=X-\frac{1}{m}\begin{bmatrix}x_1,&x_2,&\cdots\end{bmatrix}\begin{bmatrix}1\\1\\\vdots\end{bmatrix}\begin{bmatrix}1&1&\cdots\end{bmatrix}\\ &=X-\frac{1}{m}\sum_i x_i\begin{bmatrix}1&1&\cdots\end{bmatrix}\\ &=X-\bar{x}\begin{bmatrix}1&1&\cdots\end{bmatrix}\\ &=\begin{bmatrix}x_1-\bar{x},&x_2-\bar{x},&\cdots\end{bmatrix} \end{aligned}\]

其效果即是中心化\(x_i^{\prime}=x_i-\bar{x}\)机器学习

10.4 在实践中,协方差矩阵\(XX^T\)的特征值分解常由中心化后的样本矩阵X的奇异值分解代替,试述其缘由。

:首先看一下,确实能够经过SVD(奇异值)分解获得协方差矩阵的特征值和特征向量。由附录A.33式有,任意实矩阵\(A\in R^{m\times n}\)均可以分解为函数

\[A=U\Sigma V^T \]

其中U和V分别为m阶和n阶的酉矩阵,\(\Sigma\)是m×n矩阵,对角元之外元素均为零,因而有:学习

\[AA^T=U\Sigma V^TV\Sigma^TU^T=U\Sigma\Sigma^TU^T\\ A^TA=V\Sigma^TU^TU\Sigma V^T=V\Sigma^T\Sigma V^T\]

所以,\(U\)的列向量是\(AA^T\)的特征向量,\(V\)的列向量是\(A^TA\)的特征向量,\(\Sigma\)矩阵的非零对角元\(\sigma_{ii}\)的平方即为\(AA^T\)\(A^TA\)的共同非零特征值。spa

在前面原理介绍部分采用的10.2和10.4等图中,数据维度d=2或3,而样本数m远大于数据维数,m>>d。然而在实际状况中,既然须要降维,一般维度d很大,好比对于100张100*100的图片,m=100,d=10000,此时的协方差矩阵\(XX^T\)的shape为\(R^{10000\times10000}\),矩阵维度较大。而\(X\in R^{10000\times100}\),对其进行SVD(奇异值)分解的计算成本较低一些。

另外,参考博友Vic时代的解答:
其实对X进行奇异值分解,也要消耗与\(XX^T\)相同的10000×10000的存储空间。
能够先求\(X^TX\in R^{100\times 100}\)的特征分解,获得特征值\(\lambda\)和特征向量\(\nu\),那么\(\lambda\)\(X\nu\)分别也是\(XX^T\)的特征值和特征向量。
由于\(X^TX\nu=\lambda\nu\),等式左右两边左乘\(X\)获得,\(XX^T(X\nu)=\lambda (X\nu)\)

10.5 降维中涉及的投影矩阵一般要求是正交的。试述正交、非正交投影矩阵用于降维的优缺点。

正交有两个好处:

  1. 降维和重构变换计算方便。好比,在PCA中,设新坐标系中基矢为\(\{w_1,w_2,\cdots,w_{d^\prime}\}\),样本\(x\)在新坐标系中的坐标为\(z\),经过\(z\)重构的样本坐标为\(\hat{x}=Wz\)。如今假设\(\{w_i\}\)是一组线性无关的基矢,可是未必正交归一,那么为了知足“最近重构”,须要\(\min_z|\hat{x}-x|=min_z|Wz-x|\),该问题有解析解:\(z^*=(W^TW)W^Tx\)。若是\(\{w_i\}\)彼此正交归一,便有\(W^TW=I\),因而\(z^*=W^Tx\)
  2. 变换后的Z的不一样坐标之间是“去相关”的。咱们已经知道,在PCA中,变换后,在新的特征空间中,不一样特征之间是“不相关”的,也就是协方差矩阵\(ZZ^T\)是对角化的,非对角元素为零。如今,假设有一组基矢\(\{w_i^\prime\}\)不是正交化的,设为\(W^\prime\),它能够由正交化的\(W\)线性表出:\(W^\prime=WA\),从“最近重构”的角度,二者的重构效果应该等同:\(W^{\prime }z'=Wz\),因而\(z^\prime=A^{-1}z\)\(Z^\prime Z^{\prime T}=A^{-1}ZZ^T{A^{-1}}^T\),此时协方差的非对角元就未必为零了。

其实至于不一样特征之间“去相关”有什么好处,如今没有相关的实践应用,很差体会,留待之后有所体会的时候再回来补充吧。
关于正交的缺点方面,大概也就是“去相关”后的缺点吧,某些状况下也许特征之间彻底去相关未必是好事。一样须要慢慢实践、体会。如今想到的例子,好比:一我的的身高和体重是正相关的,经过PCA方法大概能够获得“身体年龄”和“肥瘦度”两个相互独立的特征,可是,或许在一个特定任务中,直接用身高和体重两个特征更容易一些。

10.6 试使用MATLAB中的PCA函数对Yale人脸数据集进行降维,并观察前20个特征向量所对应的图像。

  1. Yale人脸数据集在vision.uscd.edu这个地址不太好下载,能够从这里下载,提取码:uuae 。Yale人脸数据有15人,每人11张照片。原始照片以下:
    在这里插入图片描述
  2. 详细编程代码附后。这里采用Python语言编写,PCA利用sklearn中的PCA类实现。
    图片文件数据读取能够参考这篇博文
  3. PCA前20个特征向量可视化效果:
    在这里插入图片描述
    利用这20个特征向量来重构回去的样本图片效果:
    在这里插入图片描述

10.7 试述核化线性降维与流形学习之间的联系及优缺点。(暂缺)

10.8 k近邻图与ε近邻图存在的短路和断路问题会给Isomap形成困扰,试设计一个方法缓解该问题。(暂缺)

10.9 试设计一个方法为新样本找到LLE降维后的低维坐标。(暂缺)

10.10 试述如何确保度量学习产生的距离能知足距离度量的四条基本性质。(暂缺)


附:编程代码

习题10.1(Python)

# -*- coding: utf-8 -*-
"""
Created on Mon Apr 27 11:04:11 2020

@author: Administrator
"""

import numpy as np
import matplotlib.pyplot as plt

#设置绘图时显示中文
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def kNN(X,Y,Xpre,k,p=2):
    # k近邻算法
    # X:样本数据
    # Y:样本标记
    # Xpre:待预测样本
    # k:k近邻的k值
    # p:计算距离所采用的闵可夫斯基距离的p值
    mt,n=X.shape             #训练样本数和特征数
    Xpre=np.array(Xpre).reshape(-1,n)
    mp=Xpre.shape[0]         #预测样本数
    dist=np.zeros([mp,mt])   #存储预测样本和训练样本之间的距离
    for i in range(mt):
        dist[:,i]=(((abs(Xpre-X[i]))**p).sum(axis=1))**(1/p)
    neighbor=np.argsort(dist,axis=1)   #训练样本按距离远近排序的索引号
    neighbor=neighbor[:,:k]            #只取前k个做为最近邻
    Ypre=Y[neighbor]
    return (Ypre.sum(axis=1)>=0)*2-1   #西瓜3.0α仅两类,故可如此计算

# 西瓜3.0α 样本数据
X=np.array([[0.697,0.46],[0.774,0.376],[0.634,0.264],[0.608,0.318],[0.556,0.215],
   [0.403,0.237],[0.481,0.149],[0.437,0.211],[0.666,0.091],[0.243,0.267],
   [0.245,0.057],[0.343,0.099],[0.639,0.161],[0.657,0.198],[0.36,0.37],
   [0.593,0.042],[0.719,0.103]])
Y=np.array([1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1])

# 执行kNN算法
# 尝试 k=1,3,5,p=1,2,30的不一样状况
ks=[1,3,5]
ps=[1,2,50]  #p=1为曼哈顿距离,p=2为欧式距离,p=50(→∞)为切比雪夫距离
for i,k in enumerate(ks):
    for j,p in enumerate(ps):
        # kNN算法预测结果
        x0=np.linspace(min(X[:,0]),max(X[:,0]),60)
        x1=np.linspace(min(X[:,1]),max(X[:,1]),60)        
        X0,X1=np.meshgrid(x0,x1)
        Xpre=np.c_[X0.reshape(-1,1),X1.reshape(-1,1)]
        Ypre=kNN(X,Y,Xpre,k,p).reshape(X0.shape)
        # 画图
        plt.subplot(len(ks),len(ps),i*len(ps)+j+1)
        #plt.axis('equal')
        plt.title('k=%d,p=%d'%(k,p))
        plt.xlabel('密度')
        plt.ylabel('含糖率')
        # 画样本点
        plt.scatter(X[Y==1,0],X[Y==1,1],marker='+',s=30,label='好瓜')
        plt.scatter(X[Y==-1,0],X[Y==-1,1],marker='_',s=30,label='坏瓜')
        # 画决策树边界 (直接根据教材上图4.10和4.11肯定边界曲线坐标)
        plt.plot([0.381,0.381,0.56,0.56,max(X[:,0])],
                  [max(X[:,1]),0.126,0.126,0.205,0.205],'k',label='决策树边界')
        # 画kNN边界
        plt.contour(X0,X1,Ypre,1,colors='r',s=2)
plt.show()

习题10.6(Python)

# -*- coding: utf-8 -*-
"""
Created on Sat May 16 21:03:47 2020

@author: Administrator
"""
import numpy as np
from PIL import Image
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

#==========读取Yale图片数据===========
#Yale人脸数据为15人,每人11张照片
#下载到的Yale文件存储规律是:
#    Yale文件夹下有名称分别为1~15的15个子文件夹,
#    每一个子文件夹下有s1.bmp~s11.bmp的11张图片

rootpath='Yale'  #Yale根文件夹所在路径,我这里将Yale文件夹放在当前目录下,若其余位置,改为相应路径便可
X=[]             #存储图片数据
for person in range(15):
    for num in range(11):
        path=rootpath+'/'+str(person+1)+'/s'+str(num+1)+'.bmp'
        img=Image.open(path)
        X.append(np.array(img).reshape(-1))
X=np.array(X)

#==========观察这15人的图片============
#只显示第一张图片s1
for i in range(3):
    for j in range(5):
        plt.subplot(3,5,i*5+j+1)
        plt.imshow(X[(i*5+j)*11,:].reshape(100,100),cmap='gray')
        plt.axis('off')
        plt.title('%d'%(i*5+j+1))
plt.show()

#========PCA主成分分析(d'=20)==========
pca=PCA(n_components=20)
Z=pca.fit_transform(X)   #输入X的shape为m×d,与教材中相反
W=pca.components_        #特征向量W,shape为d'×d,与教材中相反

#====可视化观察特征向量所对应的图像======
for i in range(5):
    for j in range(4):
        plt.subplot(4,5,i*4+j+1)
        plt.imshow(W[i*4+j,:].reshape(100,100),cmap='gray')
        plt.axis('off')
        plt.title('w%d'%(i*4+j+1))
plt.show()

#========观察重构后的15人图片===========
#只显示第一张图片s1
X_re=pca.inverse_transform(Z)
for i in range(3):
    for j in range(5):
        plt.subplot(3,5,i*5+j+1)
        plt.imshow(X_re[(i*5+j)*11,:].reshape(100,100),cmap='gray')
        plt.axis('off')
        plt.title('%d'%(i*5+j+1))
plt.show()