Fancy PCA影象擴充總結(附程式碼)
Fancy PCA影象擴充總結(附程式碼)
論文連結:ImageNet Classification with Deep Convolutional Neural Networks
PCA原理參考:PCA (主成分分析)詳解 (寫給初學者)
示例程式碼:fancy_pca.py
幾天前,老師叫我使用PCA(主成分分析)方法來做影象擴充的任務,在此之前對這方面基本不瞭解,故參考了上述連結的論文(4.1小節第三段)。論文中沒有講述PCA具體的原理,如果對PCA原理不太清楚的話,可以參考上述第二個連結。好了,下面開始步入正題。
為什麼需要影象擴充?
當訓練網路的訓練集較少時,容易造成過擬合,所以常常通過影象擴充來生成更大的資料集,能夠使得神經網路學習到更多的特徵,進而從根本上提升網路的效能。
Fancy PCA
通常影象具有RGB三個通道,例如一張256 * 256的彩色影象具有(256, 256, 3)的尺寸,每一個通道形成一組資料,將影象尺寸改成((256*256), 3),進而將影象轉換成一個三維向量。然後對其執行PCA,得到一個3 * 3的協方差矩陣,通過該矩陣求得特徵值與特徵向量,特徵值與特徵向量按從大到小的順序進行排序。計算公式如下:
其中[P1,P2,P3]表示特徵向量,[λ1,λ2,λ3]表示特徵值。在論文中α是一個服從均值為0,標準差為0.1的高斯分佈。對於同一張影象,α = α1 = α2 = α3。上述公式得到的結果是一個尺寸為(3, 1)的向量,原影象的每個畫素(R, G, B)都加上這個向量得到新的目標影象。
下面將通過跟隨程式碼一步步理解Fancy PCA理解
1. 畫素值歸一化
opencv讀到的影象畫素值落在[0, 255]且為整數,在計算PCA之前我們需要將影象畫素值進行歸一化。
orig_img = img.astype(float).copy() # 複製原影象,為了方便最後畫素相加
img = img / 255.0
2. 將歸一化的影象轉化成三維向量
按照RGB每一個通道作為一組資料,將RGB影象轉化成一個三維向量。
img_rs = img.reshape(-1, 3)
3. 求協方差矩陣
# 樣本矩陣中心化,即每一維度減去該維度的均值 img_centered = img_rs - np.mean(img_rs, axis=0) # 將新的樣本矩陣乘上它的轉置,然後除以(N - 1),N代表維度 # cov方法的功能就是通過樣本矩陣求得協方差矩陣 img_cov = np.cov(img_centered, rowvar=False)
4. 求出特徵值與特徵向量
由於協方差矩陣的尺寸為[3 * 3],故特徵向量的尺寸為[3 * 3],特徵值的尺寸為[1 * 3]
# eig_vals: 特徵值
# eig_vecs: 特徵向量
# 每一列為一組
eig_vals, eig_vecs = np.linalg.eigh(img_cov)
5. 對特徵值從大到小進行排序,並使特徵向量與對應特徵值的位置保持一致
# 求出特徵值排序從大至小排序的序號
# eig_vals=[15, 12, 28] --> sort_perm=[2,0,1]
sort_perm = eig_vals[::-1].argsort()
# 對特徵值從大到小進行排序
eig_vals[::-1].sort()
# 特徵向量與對應特徵值的位置保持一致
eig_vecs = eig_vecs[:, sort_perm]
6. 將特徵向量[P1, P2, P3]與公式中[α1λ1, α2λ2, α3λ3]相乘
# m1 --> [P1, P2, P3]
m1 = np.column_stack((eig_vecs))
# m2 --> [α1λ1, α2λ2, α3λ3]
# alpha_std --> 高斯分佈的標準差,作為方法引數傳進來
m2 = np.zeros((3, 1))
alpha = np.random.normal(0, alpha_std)
m2[;,0] = alpha * eig_vals[:]
# 將m1與m2進行矩陣相乘,得到add_vect
# add_vect即為最終加到原影象的每個畫素點上的向量
add_vect = np.matmul(m1, m2)
7. 將add_vect加到原影象的每一個畫素
# 畫素值相加
for idx in range(3):
orig_img[...,idx] += add_vect[idx]
# 對超出[0, 255]範圍的畫素值進行調整
# value < 0 --> value = 0
# value > 255 --> value = 255
# 0 <= value <= 255 --> 值不變
orig_img = np.clip(orig_img, 0.0, 255.0)
# 對值取整
orig_img = orim_img.astype(np.uint8)
將最終得到的orig_img進行返回即可。
標準差引數alpha_std調整
在實現的過程中,按照論文將標準差設定為0.1時,發現得到的新影象與原影象幾乎沒有差異,於是將add_vect打印出來進行觀察。
print(add_vect)
out:
[[ 0.01741496]
[-0.01558548]
[ 0.01257316]]
由於原影象的畫素值在[0, 255]之間,而add_vect的值太小了,加上去後對原影象幾乎沒有什麼影響,於是試著將alpha_std增大至100,觀察效果如下。
所以我認為引數的具體值,應該根據自己的需求來進行調節。如果有人有調節出比較好的引數值,歡迎在評論區留言告訴我,謝謝!