04 機器學習算法之主成分分析PCA

機器學習算法之主成分分析PCA

目錄:

  • 1.PCA簡介
  • 2.PCA的實現
  • 3.sklearn中的PCA進行降維
  • 4.MNIST手寫數字集
  • 5.使用sklearn中的PCA進行去噪處理
  • 6.人臉特徵臉的提取

一、PCA簡介

PCA(Principal Component Analysis):也是一個梯度分析的應用,不僅是機器學習的算法,也是統計學的經典算法。

  • 一個非監督的機器學習算法
  • 主要用於數據的降維
  • 通過降維:可以發現更便於人類理解的特徵
  • 其他應用:可視化;去噪

1.小例子

例如:下面的一個兩個特徵的訓練集,現在我們想選擇一個特徵,扔掉另外一個特徵

那麼,我們給出分別扔掉特徵一和特徵而的兩種方案,很明顯右邊這種的效果會更好一些,因爲扔掉 特徵2之後,點之間的分佈情況更接近與原圖,但這還不是更好的。

我們希望的是,有一根直線,是斜着的,我們希望將所有的點都映射到這條直線上,那麼這個時候我們就成功的將二維降到了一維,與此同時,這些點更加趨近與原來的點的分佈情況。

2.方差

  • 那麼如何找到這個樣本間間距最大的軸?
  • 如何定義樣本間間距?

事實上有一個指標可以定義樣本間的距離,就是方差(方差:描述樣本整體之間的疏密的一個指標,方差越大,代表樣本之間越稀疏,方差越小,代表樣本之間越緊密)

3.推導

a.demean

將樣例的均值歸爲0(歸0:所有樣本減去他們的均值),使得均值爲0,這樣可以簡化方差的公式

b.映射

X(i)映射到w的距離實際上就是X(i)與w的點乘(藍色的線),根據定義推導,其值實際上就是Xproject

c.目標函數

4.與線性迴歸的區別

a.線性迴歸

b.主成分分析

總之:

  • 主成分分析法的兩個軸都是特徵,線性迴歸y軸是目標結果值
  • 主成分分析法的點事垂直於方差軸直線的,線性迴歸的點是垂直於x軸的

二、PCA的實現

1.高維數據向低維數據轉換

def demean(x):
    return x-np.mean(x,axis=0)
class PCA:
    
    def __init__(self,n_components):
        """初始化PCA"""
        assert n_components>=1, "n_components must be vaild"
        self.n_components = n_components
        self.components_ = None
        
    def fit(self,X,eta=0.01,n_iters=1e4):
        """獲得數據集X的前n個元素"""
        assert self.n_components<=X.shape[1],\
            "n_components must be greater then the feature number of X"
        def f(w,X):
            return np.sum((X.dot(w)**2))/len(X)

        def df(w,X):
            return X.T.dot(X.dot(w)) * 2. /len(X)

        def direction(w):
            """計算單位向量"""
            return w / np.linalg.norm(w)

        def first_componet( X,inital_w,eta,n_iters = 1e4,epsilon=1e-8):
            w = direction(inital_w)
            cur_iter = 0

            while cur_iter < n_iters:
                gradient = df(w,X)
                last_w = w
                w = w + eta * gradient
                # 注意1:每次求單位向量
                w = direction(w) 
                if abs(f(w,X)-f(last_w,X)) < epsilon:
                    break

                cur_iter = cur_iter+1
            return w
        
        X_pca = demean(X)

        self.components_ = np.empty(shape=(self.n_components,X.shape[1]))
        for i in range(self.n_components):
            initial_w = np.random.random(X_pca.shape[1])
            w = first_componet(X_pca,initial_w,eta)
            self.components_[i,:] = w
            
            X_pca = X_pca - X_pca.dot(w).reshape(-1,1) *w
        return self
    
    
    def transform(self,X):
        """將給定的X映射到各個主成分分量中"""
        assert X.shape[1] == self.components_.shape[1]
        
        return X.dot(self.components_.T)
    
    def inverse_transform(self,X):
        """將給定的X反映射會原來的特徵空間"""
        assert X.shape[1] == self.components_.shape[0]
        
        return X.dot(self.components_)

    def __repr__(self):
        return "PCA(n_components=%d)" % self.n_components

2.準備數據

import numpy as np
import matplotlib.pyplot as plt
# 100個數據,二維的特徵
x = np.empty((100,2))
x[:,0] = np.random.uniform(0.,100.,size=100)
x[:,1] = 0.75 * x[:,0] + 3.+ np.random.normal(0.,10.,size=100)

3.轉爲一維數據

pca = PCA(n_components=1)
pca.fit(x)
PCA(n_components=1)
pca.components_
array([[0.75498921, 0.65573721]])
x_reduction = pca.transform(x)
x_reduction.shape
(100, 1)

4.維度恢復

x_restore = pca.inverse_transform(x_reduction)
x_restore.shape
(100, 2)

5.展示圖

import matplotlib.pyplot as plt
%matplotlib inline

plt.scatter(x[:,0],x[:,1],color="b",alpha=0.5)
plt.scatter(x_restore[:,0],x_restore[:,1],color="r",alpha=0.5)
<matplotlib.collections.PathCollection at 0x11c002b4c50>

在這裏插入圖片描述

三、sklearn中的PCA進行降維

1.前期準備

# 導入模塊
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
# 加載數據集
digits = datasets.load_digits()
x = digits.data
y = digits.target
# 分割數據集
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(x,y,random_state=666)
print(x_train.shape,type(x_train))
print(y_train.shape,type(y_train))
print(x_test.shape,type(x_test))
print(y_test.shape,type(y_test))
(1347, 64) <class 'numpy.ndarray'>
(1347,) <class 'numpy.ndarray'>
(450, 64) <class 'numpy.ndarray'>
(450,) <class 'numpy.ndarray'>

2.使用64個維度的數據集,訓練KNN算法

%%time
from sklearn.neighbors import KNeighborsClassifier

knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train,y_train)
Wall time: 19.9 ms
knn_clf.score(x_test,y_test)
0.9866666666666667

3.使用PCA進行降維,訓練KNN算法

a.降到二維

%%time 
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
pca.fit(x_train)
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
Wall time: 78.8 ms
%%time
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train_reduction,y_train)
Wall time: 2 ms
knn_clf.score(x_test_reduction,y_test)
0.6066666666666667

b.降維的指標

pca.explained_variance_ratio_
array([0.14566817, 0.13735469])

具體降到幾維,提供了一個指標

explained_variance_ratio_-解釋方差的比例

  • 0.14566817 代表第一個主成分可以解釋14%的原數據
  • 0.13735469 代表第二個主成分可以解釋13%的原數據

兩個主成分加起來可以解釋百分之27的原數據,而其他的信息丟失了

可以使用explained_variance_ratio_這個參數來查看每個主成分所解釋的原數據,來判斷要取多少個主成分

pca = PCA(n_components=64)
pca.fit((x_train))
PCA(copy=True, iterated_power='auto', n_components=64, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)
pca.explained_variance_ratio_
array([1.45668166e-01, 1.37354688e-01, 1.17777287e-01, 8.49968861e-02,
       5.86018996e-02, 5.11542945e-02, 4.26605279e-02, 3.60119663e-02,
       3.41105814e-02, 3.05407804e-02, 2.42337671e-02, 2.28700570e-02,
       1.80304649e-02, 1.79346003e-02, 1.45798298e-02, 1.42044841e-02,
       1.29961033e-02, 1.26617002e-02, 1.01728635e-02, 9.09314698e-03,
       8.85220461e-03, 7.73828332e-03, 7.60516219e-03, 7.11864860e-03,
       6.85977267e-03, 5.76411920e-03, 5.71688020e-03, 5.08255707e-03,
       4.89020776e-03, 4.34888085e-03, 3.72917505e-03, 3.57755036e-03,
       3.26989470e-03, 3.14917937e-03, 3.09269839e-03, 2.87619649e-03,
       2.50362666e-03, 2.25417403e-03, 2.20030857e-03, 1.98028746e-03,
       1.88195578e-03, 1.52769283e-03, 1.42823692e-03, 1.38003340e-03,
       1.17572392e-03, 1.07377463e-03, 9.55152460e-04, 9.00017642e-04,
       5.79162563e-04, 3.82793717e-04, 2.38328586e-04, 8.40132221e-05,
       5.60545588e-05, 5.48538930e-05, 1.08077650e-05, 4.01354717e-06,
       1.23186515e-06, 1.05783059e-06, 6.06659094e-07, 5.86686040e-07,
       1.71368535e-33, 7.44075955e-34, 7.44075955e-34, 7.15189459e-34])
plt.plot([i for i in range(x_train.shape[1])],
          [np.sum(pca.explained_variance_ratio_[:i+1]) for i in range(x_train.shape[1])])
[<matplotlib.lines.Line2D at 0x11c01ad7cc0>]

在這裏插入圖片描述

pca = PCA(n_components=30)
pca.fit(x_train)
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
knn_clf.fit(x_train_reduction,y_train)
knn_clf.score(x_test_reduction,y_test)
0.9822222222222222

c.sklearn中的PCA算法支持傳入一個小於1的數來表示我們希望能解釋多少比例的主成分

pca = PCA(0.95)
pca.fit(x_train)
PCA(copy=True, iterated_power='auto', n_components=0.95, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)
pca.n_components_
28
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
%%time
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train_reduction,y_train)
Wall time: 1.99 ms
knn_clf.score(x_test_reduction,y_test)
0.98

d.便於可視化

pca = PCA(n_components=2)
pca.fit(x)
x_reduction = pca.transform(x)
for i in range(10):
    plt.scatter(x_reduction[y==i,0],x_reduction[y==i,1],alpha=0.8)

在這裏插入圖片描述

四、MNIST手寫數字集

1.數據集的前期準備

# 導入模塊
import numpy as np
from sklearn.datasets import fetch_mldata
# 手寫數字集MNIST
mnist = fetch_mldata("MNIST original")
D:\software\Anaconda\workplace\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function fetch_mldata is deprecated; fetch_mldata was deprecated in version 0.20 and will be removed in version 0.22
  warnings.warn(msg, category=DeprecationWarning)
D:\software\Anaconda\workplace\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function mldata_filename is deprecated; mldata_filename was deprecated in version 0.20 and will be removed in version 0.22
  warnings.warn(msg, category=DeprecationWarning)
# 查看數據集MNIST
mnist
{'DESCR': 'mldata.org dataset: mnist-original',
 'COL_NAMES': ['label', 'data'],
 'target': array([0., 0., 0., ..., 9., 9., 9.]),
 'data': array([[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)}
# 數據集及標籤 數據
x,y = mnist.data,mnist.target
import matplotlib.pyplot as plt
%matplotlib inline

idx = np.random.randint(0,len(x))
plt.imshow(x[idx].reshape(28,28))
plt.title(int(y[idx].item()))
Text(0.5, 1.0, '5')

在這裏插入圖片描述

# 訓練集和測試集
x_train = np.array(x[:60000],dtype=float)
y_train = np.array(y[:60000],dtype=float)
x_test = np.array(x[60000:],dtype=float)
y_test = np.array(y[60000:],dtype=float)
print(x_train.shape,type(x_train))
print(y_train.shape,type(y_train))
print(x_test.shape,type(x_test))
print(y_test.shape,type(y_test))
(60000, 784) <class 'numpy.ndarray'>
(60000,) <class 'numpy.ndarray'>
(10000, 784) <class 'numpy.ndarray'>
(10000,) <class 'numpy.ndarray'>

2.直接使用KNN

from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
%time knn_clf.fit(x_train,y_train)
Wall time: 19.1 s





KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=None, n_neighbors=5, p=2,
           weights='uniform')
%time knn_clf.score(x_test,y_test)
Wall time: 12min 47s





0.9688

3.使用PCA對MNIST數據集進行降維

from sklearn.decomposition import PCA

pca = PCA(0.9)
pca.fit(x_train)
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
x_train.shape
(60000, 784)
x_train_reduction.shape
(60000, 87)
knn_clf = KNeighborsClassifier()
%time knn_clf.fit(x_train_reduction,y_train)
Wall time: 441 ms





KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=None, n_neighbors=5, p=2,
           weights='uniform')
%time knn_clf.score(x_test_reduction,y_test)
Wall time: 1min 9s





0.9728

總結:使用PCA進行降維後的數據集進行訓練,不光時間變短了,準確度也變高了,這是因爲在PC的過程中,不僅僅是進行了降維,還在降維的過程中將數據包含的噪音給消除了,這使得我們更加準確的拿到我們數據集對應的特徵,從而使得準確率大大提高了。

五、使用sklearn中的PCA進行去噪處理

1.數據集準備

# 導入模塊
from sklearn import datasets
# 數據集的導入
digits = datasets.load_digits()
x = digits.data
y = digits.target

2.加入噪音

noisy_digits = x + np.random.normal(0,4,size=x.shape)
example_digits = noisy_digits[y==0,:][:10]
for num in range(1,10):
    x_num=noisy_digits[y==num,:][:10]
    example_digits = np.vstack([example_digits,x_num])
example_digits.shape
(100, 64)
# 畫出帶噪音的圖像
# 畫出帶噪音的圖像
def plot_digits(data):
    fig,axes = plt.subplots(10,10,figsize=(10,10),
                            subplot_kw={'xticks':[],'yticks':[]},
    gridspec_kw=dict(hspace=0.1,wspace=0.1))
    for i,ax in enumerate(axes.flat):
        ax.imshow(data[i].reshape(8,8),
                  cmap='binary',interpolation='nearest',clim=(0,16))
        
plot_digits(example_digits)

在這裏插入圖片描述

3.PCA去噪處理

pca = PCA(0.5)
pca.fit(noisy_digits)
PCA(copy=True, iterated_power='auto', n_components=0.5, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)
pca.n_components_
12
components = pca.transform(noisy_digits)
filtered_digits = pca.inverse_transform(components)
plot_digits(filtered_digits)

在這裏插入圖片描述

總結:PCA降低了維度,丟失了信息,同時也去除了一部分噪音

六、人臉特徵臉

1.加載人臉數據庫

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people()

2.人臉數據集的探索

# 人臉數據及特徵
faces.data.shape
(13233, 2914)
# 每一個樣本都以一個二維平面可視化的角度展現出來
faces.images.shape
(13233, 62, 47)
# 隨機獲取36張人臉
random_indexs = np.random.permutation(len(faces.data))
x = faces.data[random_indexs]

example_faces = x[:36,:]
example_faces.shape
(36, 2914)
def plot_faces(data):
    fig,axes = plt.subplots(6,6,figsize=(10,10),
                            subplot_kw={'xticks':[],'yticks':[]},
    gridspec_kw=dict(hspace=0.1,wspace=0.1))
    for i,ax in enumerate(axes.flat):
        ax.imshow(data[i].reshape(62,47),cmap='bone')
        
plot_faces(example_faces)

在這裏插入圖片描述

# 每張人臉對應的人名
faces.target_names
array(['AJ Cook', 'AJ Lamas', 'Aaron Eckhart', ..., 'Zumrati Juma',
       'Zurab Tsereteli', 'Zydrunas Ilgauskas'], dtype='<U35')
# 說明一共包含5749個不同的人的臉
len(faces.target_names)
5749

3.人臉特徵臉的獲取

%%time
from sklearn.decomposition import PCA

x,y = faces.data,faces.target
# 使用隨機的方式來求解出PCA
# 沒有指定n_componets,也就是說想求出所有的主成分
pca = PCA(svd_solver="randomized")
pca.fit(x)
Wall time: 23 s
# 一共2914個維度,所以求出了2194個主成分
pca.components_.shape
(2914, 2914)
plot_faces(pca.components_[:36])

在這裏插入圖片描述

通過求特徵臉

  • 一方面我們可以方便直觀的看出在人臉識別的過程中我們是怎麼樣看到每一張臉對應的特徵的。
  • 另一方面,也可以看出來,其實每一張臉都是這些人臉的一個線性組合,而特徵臉依據重要程度順序排列

而由於fetch_lfw_people這個庫的人臉的分佈是不均勻的,有的人可能只有一張圖片,有的人可能有幾十張,通過下面的方法我們可以取出至少有60張人臉的數據

faces2 = fetch_lfw_people(min_faces_per_person=60)
x,y = faces2.data,faces2.target
print(x.shape)
print(y.shape)
(1348, 2914)
(1348,)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章