1. 程式人生 > >機器學習 | 吳恩達機器學習第八週程式設計作業(Python版)

機器學習 | 吳恩達機器學習第八週程式設計作業(Python版)

實驗指導書   下載密碼:higl

本篇部落格主要講解,吳恩達機器學習第八週的程式設計作業,主要包含KMeans實驗和PCA實驗兩部分。原始實驗使用Matlab實現,本篇部落格提供Python版本。

目錄

1.實驗包含的檔案

2.KMeans實驗

3.K-means實驗完整程式碼

4.PCA實驗

5.PCA實驗完整程式碼


1.實驗包含的檔案

檔名稱 含義
ex7.py K-means實驗主程式
ex7_pca.py PCA實驗主程式
ex7data1.mat PCA實驗資料集
ex7data2.mat

K-means實驗資料集

ex7faces.mat 人臉資料集
bird_small.png 示例圖片
displayData.py 視覺化資料
runkMeans.py 執行K-means演算法
pca.py 執行PCA
projectData.py
將原始資料對映到低維空間
recoverData.py 將壓縮資料恢復到原始資料
findClosestCentroids.py 找到最近的簇
computeCentroids.py 更新聚類中心
kMeansInitCentroids.py 初始化k-means的初始聚類中心

完成紅色部分程式的關鍵程式碼。

2.KMeans實驗

  • 開啟KMeans實驗主程式ex7.py
'''第1部分 為每個樣本點找到離他最近的聚類中心'''

print('Finding closest centroids.')

data = scio.loadmat('ex7data2.mat') #載入矩陣格式的資料
X = data['X']  #提取輸入特徵矩陣


k = 3  # 隨機初始化3個聚類中心
initial_centroids = np.array([[3, 3], [6, 2], [8, 5]])

#找到離每個樣本最近的初始聚類中心序號
idx = fc.find_closest_centroids(X, initial_centroids)

print('Closest centroids for the first 3 examples: ')
print('{}'.format(idx[0:3]))
print('(the closest centroids should be 0, 2, 1 respectively)')
  • 編寫findClosestCentroids.py  簇分配
def find_closest_centroids(X, centroids):
    
    K = centroids.shape[0]  #聚類中心數量

    m = X.shape[0]  #樣本數

  
    idx = np.zeros(m) #儲存m個樣本對應的最近的聚類中心序號

    for i in range(m):
        a=(X[i]-centroids).dot((X[i]-centroids).T)  #得到一個方陣  對角線上的元素為該樣本點到每個聚類中心的距離
        idx[i]=np.argsort(a.diagonal())[0]  #取出對角線元素 對其索引進行排序  返回離該樣本最近的聚類中心的序號

    return idx

驗證正確性:

  • 更新聚類中心

'''第2部分 更新聚類中心'''

print('Computing centroids means.')

centroids = cc.compute_centroids(X, idx, k) #在簇分配結束後 對每個簇的樣本點重新計算聚類中心

print('Centroids computed after initial finding of closest centroids: \n{}'.format(centroids))
print('the centroids should be')
print('[[ 2.428301 3.157924 ]')
print(' [ 5.813503 2.633656 ]')
print(' [ 7.119387 3.616684 ]]')
  • 編寫computeCentroids.py
def compute_centroids(X, idx, K):
   
    (m, n) = X.shape #m為樣本數 n為每個樣本的特徵數

    centroids = np.zeros((K, n)) #儲存新的聚類中心的位置 

    for i in range(K):
        centroids[i]=np.mean(X[idx==i],axis=0)   #對每個簇 計算新的聚類中心 axis=0對每一列求均值

    return centroids

驗證正確性:

  • 執行k-means演算法
'''第3部分 執行k-means聚類演算法'''
print('Running K-Means Clustering on example dataset.')

#載入資料集
data = scio.loadmat('ex7data2.mat') 
X = data['X']


K = 3   #聚類中心數量
max_iters = 10  #設定外迴圈迭代次數

initial_centroids = np.array([[3, 3], [6, 2], [8, 5]])  #初始化聚類中心

centroids, idx = km.run_kmeans(X, initial_centroids, max_iters, True) #執行k-means演算法 返回最終聚類中心位置即每個樣本點所屬的聚類中心
#並把中間過程以及最終聚類效果視覺化
print('K-Means Done.')
  • 檢視runKMeans.py
def run_kmeans(X, initial_centroids, max_iters, plot): #plot設定是否進行視覺化 
    if plot:
        plt.figure()

    (m, n) = X.shape #m樣本數  n樣本特徵數
    K = initial_centroids.shape[0]  #聚類中心數量
    centroids = initial_centroids
    previous_centroids = centroids
    idx = np.zeros(m)  #存放每個樣本所屬的聚類中心序號

    # 執行k-means
    for i in range(max_iters):  #外迴圈
        print('K-Means iteration {}/{}'.format((i + 1), max_iters))  

        idx = fc.find_closest_centroids(X, centroids) #第一個內迴圈 為每個樣本找到最近的聚類中心
        
        if plot:
            plot_progress(X, centroids, previous_centroids, idx, K, i) #畫出此時簇分配的狀態
            previous_centroids = centroids
            input('Press ENTER to continue')

        centroids = cc.compute_centroids(X, idx, K) #第2個內迴圈  更新聚類中心

    return centroids, idx  #返回最終聚類中心的位置  和每個樣本所屬的聚類中心序號


def plot_progress(X, centroids, previous, idx, K, i):
    plt.scatter(X[:, 0], X[:, 1], c=idx, s=15)   #不同聚類中心用不同的顏色表示 

    plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', c='black', s=25) #標出聚類中心

    for j in range(centroids.shape[0]):  #為更新後的聚類中心和之前的聚類中心連線
        draw_line(centroids[j], previous[j])

    plt.title('Iteration number {}'.format(i + 1))


def draw_line(p1, p2):
    plt.plot(np.array([p1[0], p2[0]]), np.array([p1[1], p2[1]]), c='black', linewidth=1)
  • 最終的聚類效果和聚類中心的移動過程

  • 使用k-means壓縮圖片
'''第4部分 執行k-means聚類演算法 壓縮圖片'''
print('Running K-Means clustering on pixels from an image')

#載入圖片
image = io.imread('bird_small.png')
image = img_as_float(image)

# 圖片大小
img_shape = image.shape


X = image.reshape(img_shape[0] * img_shape[1], 3) #把圖片轉換成3個列向量構成的矩陣  每個列向量代表每個顏色通道的所有畫素點 

#可以設定不同的引數 觀察效果
K = 16 #聚類中心數量
max_iters = 10 #外迴圈迭代次數

#初始化聚類中心位置很重要  初始化不同  最終聚類效果也會不同
initial_centroids = kmic.kmeans_init_centroids(X, K) 

# 執行k-means
centroids, idx = km.run_kmeans(X, initial_centroids, max_iters, False) #False不進行視覺化

print('K-Means Done.')

input('Program paused. Press ENTER to continue')


print('Applying K-Means to compress an image.')

# 得到最終聚類結束後 每個樣本所屬的聚類中心序號
idx = fc.find_closest_centroids(X, centroids)

#用idx做索引
idx=idx.astype(int) #將數值型別轉換為整型
idx=idx.tolist()  #將陣列轉換為列表  

X_recovered = centroids[idx]  #將每個樣本點位置轉換為它所屬簇的聚類中心的位置  實現壓縮

X_recovered = np.reshape(X_recovered, (img_shape[0], img_shape[1], 3)) #把影象轉換為之前的維度

io.imsave('compress.png',X_recovered) #儲存壓縮後的圖片檔案
plt.subplot(2, 1, 1)  #視覺化原始圖片
plt.imshow(image)
plt.title('Original')

plt.subplot(2, 1, 2)  #壓縮後的圖片
plt.imshow(X_recovered)
plt.title('Compressed, with {} colors'.format(K))

input('ex7 Finished. Press ENTER to exit')
  • 編寫kMeansInitCentroids.py 
def kmeans_init_centroids(X, K):
    #隨機初始化聚類中心
    centroids = np.zeros((K, X.shape[1]))  

    #初始化聚類中心為資料集中的樣本點
    centroids=X[np.random.randint(0,X.shape[0],K)]

    return centroids
  • 圖片壓縮效果

3.K-means實驗完整程式碼

下載連結    下載密碼:qhbm

4.PCA實驗

  • 開啟PCA實驗主程式ex7_pca.py
'''第1部分 載入資料集 並可視化'''
#小資料集方便視覺化
print('Visualizing example dataset for PCA.')

data = scio.loadmat('ex7data1.mat')
X = data['X'] #兩個特徵 

# 視覺化
plt.figure()
plt.scatter(X[:, 0], X[:, 1], facecolors='none', edgecolors='b', s=20)
plt.axis('equal')
plt.axis([0.5, 6.5, 2, 8])
  • 視覺化效果

  • 實現PCA演算法
'''第2部分 實現PCA 進行資料壓縮'''

print('Running PCA on example dataset.')

# 在PCA之前 要對特徵進行縮放
X_norm, mu, sigma = fn.feature_normalize(X)

# 執行PCA 返回U矩陣  和S矩陣
U, S = pca.pca(X_norm)

#對比兩個不同的特徵向量 U[:,0]更好 投影誤差最小  U中的各個特徵向量(列)都是正交的  2D->1D 取前1個特徵向量 作為Ureduce
rk.draw_line(mu, mu + 1.5 * S[0] * U[:, 0]) 
rk.draw_line(mu, mu + 1.5 * S[1] * U[:, 1])

print('Top eigenvector: \nU[:, 0] = {}'.format(U[:, 0])) #利用PCA得到的特徵向量矩陣Ureduce(降維後子空間的基)
print('You should expect to see [-0.707107 -0.707107]')
  • 檢視特徵縮放程式featureNormalize.py
def feature_normalize(X):
    mu = np.mean(X, 0)  #對特徵矩陣每一列求均值
    sigma = np.std(X, 0, ddof=1)  #特徵矩陣每一列求標準差
    X_norm = (X - mu) / sigma  #特徵矩陣每一列的元素減去該列均值  除以該列標準差  得到特徵縮放後的矩陣

    return X_norm, mu, sigma
  • 編寫pca.py
def pca(X):
   
    (m, n) = X.shape  #m 樣本數  n特徵數

    U = np.zeros((n,n)) #U 為n*n的矩陣
    S = np.zeros(n)  #S也是n*n的對角矩陣  只不過svd返回的是其對角線的非0元素 
    #計算協方差矩陣
    Sigma=(1/m)*(X.T.dot(X))
    #對協方差矩陣進行奇異值分解
    U,S,V=scipy.linalg.svd(Sigma)
   
    return U, S
  • 視覺化降維後的特徵向量(子空間的基向量)

驗證程式正確性:

  • 得到降維後的樣本點並進行壓縮重放
'''第3部分 得到降維後的樣本點 再進行壓縮重放'''
print('Dimension reductino on example dataset.')

# 視覺化特徵縮放後的資料集
plt.figure()
plt.scatter(X_norm[:, 0], X_norm[:, 1], facecolors='none', edgecolors='b', s=20)
plt.axis('equal')
plt.axis([-4, 3, -4, 3])

# 將2維資料對映到1維
K = 1
Z = pd.project_data(X_norm, U, K)
print('Projection of the first example: {}'.format(Z[0]))
print('(this value should be about 1.481274)')

X_rec = rd.recover_data(Z, U, K) #將降維後的1維資料 轉換為2維(在特徵向量上的投影點)

print('Approximation of the first example: {}'.format(X_rec[0]))
print('(this value should be about [-1.047419 -1.047419])')

# 畫出特徵縮放後的樣本在特徵向量上的投影點 並在2者之間連線
plt.scatter(X_rec[:, 0], X_rec[:, 1], facecolors='none', edgecolors='r', s=20)
for i in range(X_norm.shape[0]):
    rk.draw_line(X_norm[i], X_rec[i])
  • 編寫降維程式projectData.py
def project_data(X, U, K): #得到降維後的樣本點

    Z = np.zeros((X.shape[0], K)) #降維後的特徵矩陣 Z:m*K X:m*n

    Z=X.dot(U[:,:K])

    return Z
  • 編寫壓縮重放程式recoverData.py
def recover_data(Z, U, K):  #進行壓縮重放
    
    X_rec = np.zeros((Z.shape[0], U.shape[0])) #原始樣本在特徵向量上的投影點 X_rec:m*n Z:m*K  U:n*n

    X_rec=Z.dot(U[:,:K].T)

    return X_rec
  • 視覺化效果

驗證程式正確性:

  • 載入並可視化人臉資料
'''第4部分 載入並可視化人臉資料集'''

print('Loading face dataset.')

data = scio.loadmat('ex7faces.mat')
X = data['X'] #得到輸入特徵矩陣  
print(X.shape[1]) #特徵為1024維
disp.display_data(X[0:100]) #視覺化前100個人臉
  • 視覺化效果

  • 視覺化人臉資料的特徵向量
'''第5部分 視覺化人臉資料的特徵向量'''
print('Running PCA on face dataset.\n(this might take a minute or two ...)')

X_norm, mu, sigma = fn.feature_normalize(X) #對輸入特徵矩陣進行特徵縮放

#執行PCA演算法
U, S = pca.pca(X_norm)

#視覺化前36個特徵向量(每個向量1024維)
disp.display_data(U[:, 0:36].T)
  • 視覺化效果

  • 對人臉資料進行降維(1024->100)
'''第6部分 對人臉資料進行降維 從1024維降到100維'''
print('Dimension reduction for face dataset.')

K = 100
Z = pd.project_data(X_norm, U, K)  #得到降維後的特徵矩陣(樣本點)

print('The projected data Z has a shape of: {}'.format(Z.shape)) #m*100
  • 視覺化降維後,再壓縮重放後的人臉資料與原資料比較
'''第7部分 視覺化降維後,再壓縮重放的人臉資料和原始資料比較'''
print('Visualizing the projected (reduced dimension) faces.')

K = 100
X_rec = rd.recover_data(Z, U, K) #壓縮重放

#視覺化原始資料
disp.display_data(X_norm[0:100])
plt.title('Original faces')
plt.axis('equal')

#壓縮到100維  再壓縮重放後的資料
disp.display_data(X_rec[0:100])
plt.title('Recovered faces')
plt.axis('equal')

PCA要求投影誤差最小,所以2者應該是差不多的:

  • 利用PCA視覺化高維資料

PCA可以把高維資料降至低維再進行視覺化:

'''第8部分 利用PCA視覺化高維資料'''
image = io.imread('bird_small.png') #讀取圖片
image = img_as_float(image)

img_shape = image.shape

X = image.reshape((img_shape[0] * img_shape[1], 3))  #將圖片格式轉換為包含3列(3個顏色通道)的矩陣
K = 16   #聚類中心數量
max_iters = 10  #外迴圈迭代次數
initial_centroids = kmic.kmeans_init_centroids(X, K)  #初始化K個聚類中心
centroids, idx = km.run_kmeans(X, initial_centroids, max_iters, False) #執行k-means,得到最終的聚類中心和每個樣本點所屬的聚類中心序號


selected = np.random.randint(X.shape[0], size=1000) #隨機選擇1000(可以更改)個樣本點 每個樣本點3維

#視覺化3維資料  不同顏色表示每個樣本點的所屬的簇
cm = plt.cm.get_cmap('RdYlBu')
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[selected, 0], X[selected, 1], X[selected, 2], c=idx[selected],cmap=cm, s=15, vmin=0, vmax=K)
plt.title('Pixel dataset plotted in 3D. Color shows centroid memberships')

input('Program paused. Press ENTER to continue')

#利用PCA把3維資料 降至2維 進行視覺化

X_norm, mu, sigma = fn.feature_normalize(X)  #對特徵矩陣X 進行特徵縮放

#呼叫pca 3D->2D
U, S = pca.pca(X_norm)
Z = pd.project_data(X_norm, U, 2)  #得到降維後的特徵矩陣

# 視覺化2維資料  不同顏色表示每個樣本點的所屬的簇
plt.figure()
plt.scatter(Z[selected, 0], Z[selected, 1], c=idx[selected].astype(np.float64), cmap=cm,s=15)
plt.title('Pixel dataset plotted in 2D, using PCA for dimensionality reduction')
  • 視覺化效果

5.PCA實驗完整程式碼

下載連結       下載密碼:yazu