作業及代碼:https://pan.baidu.com/s/1L-Tbo3flzKplAof3fFdD1w 密碼:oin0
本次作業的理論部分:吳恩達機器學習(八)聚類與降維(K-Means,PCA)
編程環境:Jupyter Notebook
本章目錄
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. 獲取樣本點所屬類別
計算每個樣本點與聚類中心的距離:
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個強度值(每個像素可以有 種色彩)。
-
壓縮目標:將每個像素通道的強度值壓縮爲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 = X_demean.T @ X_demean / len(X)
3. 利用奇異值分解(SVD)來分解協方差矩陣,得到特徵矩陣
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. 實現降維
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. 還原數據
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)