过程讲解
- 首先对数据集矩阵中心化,用每个维度的数分别减去该维度的均值。
- 计算协方差矩阵,当数据中心化以后,矩阵 X \mathbf{X} X的协方差矩阵就等于 1 n \frac{1}{n} n1 X \mathbf{X} X X T \mathbf{X}^\mathrm{T} XT = 1 n ∑ i , j = 1 n x i T ⋅ x j \frac{1}{n}\sum_{i,j=1}^{n}x_i^T\cdot x_j n1∑i,j=1nxiT⋅xj。
- 特征分解得到协方差矩阵的特征值和特征向量。假设通过线性变换
w
1
w_1
w1使
x
i
x_i
xi做线性变化的投影,我们需要最大化投影后的方差
1
n
∑
i
=
1
n
(
w
1
T
x
i
)
2
=
1
n
∑
i
=
1
n
w
1
T
x
i
x
i
T
w
1
=
1
n
w
1
T
∑
i
=
1
n
x
i
x
i
T
w
1
=
w
1
T
⋅
S
⋅
w
1
\frac{1}{n}\sum_{i=1}^{n}(w_1^Tx_i)^2 = \frac{1}{n}\sum_{i=1}^{n}w_1^Tx_ix_i^Tw_1 = \frac{1}{n}w_1^T\sum_{i=1}^{n}x_ix_i^Tw_1 = w_1^T\cdot S\cdot w_1
n1∑i=1n(w1Txi)2=n1∑i=1nw1TxixiTw1=n1w1T∑i=1nxixiTw1=w1T⋅S⋅w1,即求
max
w
1
w
1
T
⋅
S
⋅
w
1
\max_{w_1}w_1^T\cdot S \cdot w_1
maxw1w1T⋅S⋅w1
s . t . ∣ ∣ w 1 ∣ ∣ 2 2 = 1 s.t. ||w_1||_2^2=1 s.t.∣∣w1∣∣22=1
( ( w 1 T x i ) (w_1^Tx_i) (w1Txi)的结果是一个数,数的转置还是该数本身,所以 ( w 1 T x i ) 2 (w_1^Tx_i)^2 (w1Txi)2可以写成这里的 ( w 1 T x i ) ( w i T x i ) T = w 1 T x i x i T w 1 (w_1^Tx_i)(w_i^Tx_i)^T = w_1^Tx_ix_i^Tw_1 (w1Txi)(wiTxi)T=w1TxixiTw1,当然也可以写成 ( w 1 T x i ) T ( w i T x i ) (w_1^Tx_i)^T(w_i^Tx_i) (w1Txi)T(wiTxi),这里采用前者便于分出协方差矩阵)
利用拉格朗日乘子法并对 w 1 w_1 w1求导可得 S w 1 = λ w 1 Sw_1=\lambda w_1 Sw1=λw1
λ , w 1 \lambda ,w_1 λ,w1是S的特征值和特征向量,这里S是协方差矩阵。
则X投影后的方差就是 λ \lambda λ,即协方差矩阵的特征值。 - 将所有特征值从大到小排列,将相应的特征向量随之排列,可取特征向量的前K行组成的矩阵 ( w 1 , w 2 , . . . , w k ) (w_1,w_2,...,w_k) (w1,w2,...,wk) 乘以原始数据矩阵X,就的到了降维后的数据矩阵。
代码实现
数据获取(数据集在这里下载link,搜wine.data,国内网较慢)
df_wine = pd.read_csv('wine.data',header=None)
df_wine.shape
df_wine.columns = ['Class label','Alcohol','Mail acid','Ash',
'Alcalinity of ash','Magnesium','Total phenols',
'Flavanoids','Nonflavanoid phenols','Proanthocyanins',
'Color intensity','Hue',
'OD280/OD315 of diluted wines','Proline']
- 标准化数据
sc = StandardScaler()
#对数据集进行标准化(一般情况下我们在训练集中进行均值和方差的计算,直接在测试集中使用)
X_train_std = sc.fit_transform(X_train)#先fit,训练sc参数,计算出均值和方差,再transform,使数据标准化、归一化
X_test_std = sc.transform(X_test)
- 计算协方差
cov_mat = np.cov(X_train_std.T)
- 特征分解
eigen_vals,eigen_vecs = np.linalg.eig(cov_mat) #对协方差矩阵进行特征值分解,返回的是特征值和特征向量
可以绘图看看特征的比例
#特征值之和
tot = sum(eigen_vals)
#对特征进行从大到小排序,并计算所占的比例
var_exp = [(i/tot) for i in sorted(eigen_vals,reverse=True)]
#累计求和
cum_var_exp = np.cumsum(var_exp)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.figure()
plt.bar(range(1,14), var_exp, alpha=0.5,align='center',
label='特征值分布')
plt.step(range(1,14), cum_var_exp,where='mid',
label='累计特征值')
plt.ylabel('特征值比例')
plt.xlabel('特征index')
plt.legend(loc='best')
结果
- 特征降维
# 创建列表,由(eigenvalue,eigenvector)元素构成
eigen_pairs = [(np.abs(eigen_vals[i]),eigen_vecs[:,i])
for i in range(len(eigen_vals))]
eigen_pairs.sort(key=lambda k : k[0],reverse=True)#key 指定一个函数,
#按照函数规则进行排序,这里是按照k[0]从大到小排序,即按照特征值从大到小排序
#取前两个特征值对应的特征向量作为主成分
#元组=》[13,]=》[13,1] =》[13,2]
w = np.hstack((eigen_pairs[0][1][:,np.newaxis],
eigen_pairs[1][1][:,np.newaxis]))
#全部特征压缩
X_train_pca = X_train_std.dot(w)
可以展示看看
colors = ['r','b','g']
markers = ['s','x','o']
for l ,c, m in zip(np.unique(y_train),colors, markers):
plt.scatter(X_train_pca[y_train == l,0],
X_train_pca[y_train == l,1],
c=c, label=l,marker=m)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
结果