1. 程式人生 > 實用技巧 >Fancy PCA影象擴充總結(附程式碼)

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,觀察效果如下。

所以我認為引數的具體值,應該根據自己的需求來進行調節。如果有人有調節出比較好的引數值,歡迎在評論區留言告訴我,謝謝!