機器學習 | 吳恩達機器學習第八週程式設計作業(Python版)
阿新 • • 發佈:2018-11-20
實驗指導書 下載密碼:higl
本篇部落格主要講解,吳恩達機器學習第八週的程式設計作業,主要包含KMeans實驗和PCA實驗兩部分。原始實驗使用Matlab實現,本篇部落格提供Python版本。
目錄
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