編程作業(python)| 吳恩達機器學習(7)K-means與PCA

作業及代碼:https://pan.baidu.com/s/1L-Tbo3flzKplAof3fFdD1w 密碼:oin0
本次作業的理論部分:吳恩達機器學習(八)聚類與降維(K-Means,PCA)
編程環境:Jupyter Notebook

  :  + \color{#f00}{***\ 點擊查看\ :吳恩達機器學習 \ —— \ 整套筆記+編程作業詳解\ ***}

K-means 聚類

案例1: 給定一個二維數據集,使用kmeans進行聚類。數據集:data/ex7data2.mat

1. K-means 計算過程

import numpy as np
import scipy.io as sio
import matplotlib.pyplot as plt

data1 = sio.loadmat('./data/ex7data2.mat')
X = data1['X']

plt.scatter(X[:,0],X[:,1]) # 繪製原始數據
plt.show()

在這裏插入圖片描述

1. 獲取樣本點所屬類別

計算每個樣本點與聚類中心的距離:
c(i):=j that minimizes x(i)μj2c^{(i)}:=j \quad \text { that minimizes } \quad\left\|x^{(i)}-\mu_{j}\right\|^{2}

def find_centroids(X,centros):
    
    idx = [] #存放樣本所屬類別索引,即 0,1,2    
    for i in range(len(X)):
        # (2,) (k,2) -> (k,2)
        dist =np.linalg.norm((X[i] - centros),axis=1) #(k,) # 每個樣本點與聚類中心計算距離(axis=1表示按列計算)
        id_i = np.argmin(dist)      # 距離排序,取最小距離在數組中的索引號(0,1,2),即將樣本點分給對應聚類中心
        idx.append(id_i)
        
    return np.array(idx)# 返回值爲每個樣本點的所屬類別索引

init_centros= np.array([[3, 3], [6, 2], [8, 5]]) # 人爲給定(隨機初始化)3個初始聚類中心
idx = find_centroids(X,centros)				 # 每個樣本點都找到所屬類別

#繪製首次聚類結果
plt.scatter(X[:,0],X[:,1],c=idx,cmap='rainbow')#數據集根據索引idx來區分顏色
plt.scatter(init_centros[:,0],init_centros[:,1],
			s=200,marker='x',c=[0,1,2],cmap='rainbow')#末尾參數表示符號大小,形狀,顏色

在這裏插入圖片描述

2. 計算重心點

def compute_centros(X,idx,k):
    
    centros = [] # 用於存放聚類中心
    
    for i in range(k):
        centros_i = np.mean(X[idx == i],axis=0)
        centros.append(centros_i)
        
    return np.array(centros)

centros = compute_centros(X,idx,k=3) # 三個聚類中心由初始給定值 變成 當前的平均值

plt.scatter(X[:,0],X[:,1],c=idx,cmap='rainbow')#數據集根據索引idx來區分顏色
plt.scatter(init_centros[:,0],init_centros[:,1],
            s=200,marker='x',c=[0,1,2],cmap='rainbow')#初始聚類中心
plt.scatter(centros[:,0],centros[:,1],s=200,marker='x',c='k')# 新的聚類中心

 黑色X即爲初始聚類的重心點:
在這裏插入圖片描述

3. 運行kmeans,重複執行1和2

def run_kmeans(X,centros,iters):
    
    k = len(centros)
    centros_all = []
    centros_all.append(centros)# 記錄初始聚類中心
    centros_i = centros # 初始聚類中心
    for i in range(iters):
        idx = find_centroids(X,centros_i)    # 獲取樣本點所屬類別
        centros_i = compute_centros(X,idx,k) # 更新聚類中心
        centros_all.append(centros_i)        # 記錄聚類中心
        
    return idx,np.array(centros_all)

4. 繪製數據集聚類結果&聚類中心的移動軌跡

def plot_data(X,centros_all,idx):
    plt.figure()
    plt.scatter(X[:,0],X[:,1],c=idx,cmap='rainbow')#數據集根據索引idx來區分顏色
    
    # centros_all是3維數組,三個維度分別是迭代次數,類別索引,特徵
    plt.plot(centros_all[:,:,0], #橫軸
    		centros_all[:,:,1],  #縱軸
    		'kx--')				 # k表示黑色,x表示聚類中心,--表示連接線

idx,centros_all = run_kmeans(X,init_centros,iters=10) 
plot_data(X,centros_all,idx)
plt.scatter(centros_all[10,:,0],centros_all[10,:,1],s=100,marker='o',c='k')#最終的聚類中心

在這裏插入圖片描述

2. 觀察初始聚類點的位置對聚類效果的影響

# 在訓練樣本 X-中隨機選取 k個樣本作爲初始聚類中心
def init_centros(X,k):
    index = np.random.choice(len(X),k)
    return X[index]

init_centros(X,k=3)

for i in range(4):
    idx,centros_all = run_kmeans(X,init_centros(X,k=3),iters=10)
    plot_data(X,centros_all,idx)
	plt.scatter(centros_all[10,:,0],centros_all[10,:,1],s=200,marker='o',c='k')#最終的聚類中心

由圖可知,不同的初始化位置會得到不同的聚類結果(黑色●),且第一種情況陷入了局部最優。

在這裏插入圖片描述


3. 使用 k-means 壓縮圖像

  • 原始圖像:24位真彩色圖像,每個像素有RGB三個通道,每個通道有256個強度值(每個像素可以有 282828=2242^8*2^8*2^8=2^{24} 種色彩)。

  • 壓縮目標:將每個像素通道的強度值壓縮爲16種

通俗的講,原圖像每個像素點的單個通道有256種顏色可供選擇,而我們要用K-means算法選16種顏色,用於圖片壓縮。把原始圖片的每個像素看作一個數據樣本,然後利用K-means算法去找分組最好的16種顏色。

# 原始圖像
from skimage import io
image = io.imread('data/bird_small.png')
plt.imshow(image)

在這裏插入圖片描述

# 原始圖像數據集
data = sio.loadmat('data/bird_small.mat')
>>> data.keys()
> dict_keys(['__header__', '__version__', '__globals__', 'A'])
A = data['A']
>>> A.shape
> (128, 128, 3) #128×128個像素點,每個像素點有3個通道強度值(範圍是0-255)
# 特徵歸一化
A = A / 255
A = A.reshape(-1,3) #將A轉換爲二維數組,且列數爲3
>>> A.shape # 128*128=16348行(像素點數),3列(通道數)
> (16384, 3)

k = 16
idx,centros_all = run_kmeans(A,init_centros(A,k=16),iters=20)獲取原始數據的16個聚類中心
centros = centros_all[-1]#最後的聚類中心

im = np.zeros(A.shape)
for i in range(k):
    im[idx==i] = centros[i] # 使同一類點全都相等於聚類中心點
im = im.reshape(128,128,3)  # 將聚類後的數據重構爲圖像格式
plt.imshow(im)

在這裏插入圖片描述



PCA 降維

1. PCA 計算過程

j計算過程請參考 :聚類與降維(K-Means,PCA)# 2.2 節

數據集:data/ex7data1.mat。

要求:將二維數據降到一維。

import numpy as np
import scipy.io as sio
import matplotlib.pyplot as plt

mat = sio.loadmat('./data/ex7data1.mat')
X = mat['X']
>>> X.shape
> (50, 2)

plt.scatter(X[:,0],X[:,1])
plt.show()

在這裏插入圖片描述
1. 對X去均值化,使得特徵的數值在可比較的範圍之內

X_demean = X - np.mean(X,axis=0)
plt.scatter(X_demean[:,0],X_demean[:,1])
plt.show()

在這裏插入圖片描述
2. 計算協方差矩陣
Sigma=1mXTXSigma=\frac{1}{m} X^{T} X

Sigma = X_demean.T @ X_demean / len(X)

3. 利用奇異值分解(SVD)來分解協方差矩陣,得到特徵矩陣 Un×nU_{n×n}
[U,S,V]=svd(Sigma)[U, S, V]= svd(Sigma)Un×n=[u(1)u(2)u(3)u(n)]U_{n \times n}=\left[\begin{array}{ccccc} | & | & | & | & | \\ u^{(1)} & u^{(2)} & u^{(3)} & \ldots & u^{(n)} \\ | & | & | & | & | \end{array}\right]

U,S,V = np.linalg.svd(Sigma)
>>> U
> array([[-0.76908153, -0.63915068], # 可以發現兩個特徵向量正交
       [-0.63915068,  0.76908153]])

plt.figure(figsize=(7,7))
plt.scatter(X_demean[:,0],X_demean[:,1])
plt.plot([0,U1[0]],[0,U1[1]],c='r') 		# 紅線代表主成分特徵向量
plt.plot([0,U[:,1][0]],[0,U[:,1][1]],c='k') # 黑線代表次成分特徵向量
plt.show()

在這裏插入圖片描述
4. 實現降維
z(i)=UreduceT x(i)=[u(1)u(2)u(3)u(k)]Tx(i)z^{(i)}=U_{r e d u c e}^{T}\ x^{(i)}=\left[\begin{array}{ccccc} | & | & | & | & | \\ u^{(1)} & u^{(2)} & u^{(3)} & \ldots & u^{(k)} \\ | & | & | & | & | \end{array}\right]^{T}x^{(i)}

U_reduce = U[:,0] # 取第一列作爲降維後的特徵向量 #(2,)
U_reduce = U_reduce.reshape((2,1)) 			   #(2,1)
z = X_demean @ U_reduce 
>>> z.shape
> (50,1) # 可以看出維度由(50,2)降至(50,1)

5. 還原數據
xapprox=Ureducezxx_{a p pr o x}=U_{r e d u c e} \cdot z\approx x

X_approx = z @ U_reduce.T + np.mean(X,axis=0) #(50,2) 加上均值是爲了還原到原始座標系
plt.scatter(X[:,0],X[:,1])
plt.scatter(X_approx[:,0],X_approx[:,1])

在這裏插入圖片描述

2. 使用 PCA 對圖像降維

數據集:data/ex7faces.mat,要求將特徵降至 k = 36 維

import numpy as np
import scipy.io as sio
import matplotlib.pyplot as plt

mat = sio.loadmat('data/ex7faces.mat')
X = mat['X']
>>> X.shape
> (5000, 1024)

可視化前100個人臉原始圖像:

def plot_100_images(X):
    fig, axs = plt.subplots(ncols=10, nrows=10, figsize=(10,10))
    for c in range(10):
        for r in range(10):
            axs[c,r].imshow(X[10*c + r].reshape(32,32).T,cmap = 'Greys_r') #顯示單通道的灰度圖
            axs[c,r].set_xticks([])
            axs[c,r].set_yticks([])

plot_100_images(X)

在這裏插入圖片描述

means = np.mean(X,axis=0)
X_demean = X - means
Sigma = X_demean.T @ X_demean / len(X)
U,S,V = np.linalg.svd(Sigma)

U_reduce  = U[:,:36]
z = X_demean @ U_reduce 
>>> z.shape
> (5000, 36)

X_approx  = z @ U_reduce.T  
plot_100_images(X_appox)

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章