一、奇異值分解(SVD)簡介
??SVD適用于對任意矩陣進行矩陣分解,是一種重要的矩陣分解方法,
??SVD對應的公式為:
A
m
×
n
=
U
m
×
n
S
m
×
n
V
m
×
n
T
{ A_{m \times n}=U_{m \times n } S_{m \times n } V^T_{m \times n}}
Am×n?=Um×n?Sm×n?Vm×nT? ,簡記為:
A
=
U
S
V
T
{ A=USV^T}
A=USVT,其中
U
{U}
U 和
V
{V}
V 是正交矩陣(酉矩陣),即滿足
U
T
U
=
E
m
×
m
{U^TU=E_{m \times m}}
UTU=Em×m? ,
V
T
V
=
E
n
×
n
{V^TV=E_{n \times n}}
VTV=En×n?,
??
U
{U}
U矩陣:左奇異矩陣,列由
A
A
T
{AA^T}
AAT 的特征向量組成,且特征向量為單位向量;
??
S
{S}
S矩陣:奇異值矩陣,對角元素來源于
A
A
T
{AA^T}
AAT 或
A
T
A
{A^TA}
ATA 的特征值的平方根,并且按降序排列,值越大可以理解為越重要;
??
V
{V}
V矩陣:右奇異矩陣,列由
A
T
A
{A^TA}
ATA 的特征向量組成,且特征向量為單位向量;
二、python實作與驗證
import numpy as np
from numpy import linalg as la
A = np.array([[1,5,7,6,1],[2,1,10,4,4],[3,6,7,5,2]])
U, S, VT = la.svd(A)
Sigma = np.zeros(np.shape(A))
Sigma[:len(S),:len(S)] = np.diag(S) # 生成奇異值矩陣,該矩陣的對角元素為奇異值
print('左奇異值矩陣:\n', U)
print('奇異值:', S)
print('奇異值矩陣:\n', Sigma)
print('右奇異值矩陣的轉置:\n', VT)
print('--------------------------------------')
# 利用SVD重構矩陣
B = U.dot(Sigma.dot(VT))
print('原矩陣A:\n', A)
print('重構后的矩陣B:\n', B)
print('原矩陣A與重構后的矩陣B是否相同', np.allclose(A,B))
輸出為:
左奇異值矩陣:
[[-0.55572489 0.40548161 -0.72577856]
[-0.59283199 -0.80531618 0.00401031]
[-0.58285511 0.43249337 0.68791671]]
奇異值: [18.53581747 5.0056557 1.83490648]
奇異值矩陣:
[[18.53581747 0. 0. 0. 0. ]
[ 0. 5.0056557 0. 0. 0. ]
[ 0. 0. 1.83490648 0. 0. ]]
右奇異值矩陣的轉置:
[[-0.18828164 -0.37055755 -0.74981208 -0.46504304 -0.22080294]
[ 0.01844501 0.76254787 -0.4369731 0.27450785 -0.38971845]
[ 0.73354812 0.27392013 -0.12258381 -0.48996859 0.36301365]
[ 0.36052404 -0.34595041 -0.43411102 0.6833004 0.30820273]
[-0.5441869 0.2940985 -0.20822387 -0.0375734 0.7567019 ]]
--------------------------------------
原矩陣A:
[[ 1 5 7 6 1]
[ 2 1 10 4 4]
[ 3 6 7 5 2]]
重構后的矩陣B:
[[ 1. 5. 7. 6. 1.]
[ 2. 1. 10. 4. 4.]
[ 3. 6. 7. 5. 2.]]
原矩陣A與重構后的矩陣B是否相同 True
三、利用SVD進行影像壓縮
??實作對原影像進行SVD,使用更少的像素表示原影像,實作影像壓縮,
??(1)讀取影像,將圖片像素分解成三個矩陣,分別為RGB;
??(2)對RGB三個通道分別進行SVD并壓縮:先進行SVD得到對應的奇異值,然后按照一定標準進行奇異值篩選,即保留
S
{S}
S 矩陣的前
k
{k}
k 個,有兩種方式可以進行篩選,一種是取整體奇異值的百分比,另一種是取奇異值之和的百分比,然后進行重構,得到壓縮后的影像;
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
'''
函式功能:SVD并還原壓縮后的資料,
引數說明:data代表原始矩陣percent代表奇異值總和的百分比
'''
def get_approx_SVD1(data,percent):
U, s, VT = np.linalg.svd(data) # 進行SVD
Sigma = np.zeros(np.shape(data))
Sigma[:len(s),:len(s)] = np.diag(s)
count = (int)(sum(s))*percent
k = -1 # k是奇異值總和的百分比的個數
curSum = 0 # 初值為第一個奇異值
while curSum <= count :
k += 1
curSum += s[k]
D = U[:,:k].dot(Sigma[:k,:k].dot(VT[:k,:])) #SVD還原后的資料
D[D<0] = 0
D[D>255] = 255
return np.rint(D).astype("uint8")
'''
函式功能:SVD并還原壓縮后的資料,
引數說明:data代表原始矩陣percent代表奇異值個數的百分比
'''
def get_approx_SVD2(data,percent):
U, s, VT = np.linalg.svd(data) # 進行SVD
Sigma = np.zeros(np.shape(data))
Sigma[:len(s),:len(s)]=np.diag(s) # 獲得奇異值矩陣
k = (int)(percent*len(s)) # k是奇異值個數的百分比的個數
D = U[:,:k].dot(Sigma[:k,:k].dot(VT[:k,:])) # SVD還原后的資料
D[D < 0] = 0
D[D > 255] = 255
return np.rint(D).astype("uint8")
'''
函式功能:匯入影像,進行SVD壓縮,并重構影像
引數說明:filename代表檔案名,p代表百分比,
get_approx_SVD代表呼叫的SVD篩選方法
'''
def rebuild_img(filename,p,get_approx_SVD):
img = Image.open(filename, 'r') # 打開檔案
a = np.array(img) # 獲得色素值
R0 = a[:, :, 0] # 獲得紅色的色素值
G0 = a[:, :, 1] # 獲得綠色的色素值
B0 = a[:, :, 2] # 獲得藍色的色素值
R = get_approx_SVD(R0,p) # 對紅色進行SVD還原
G = get_approx_SVD(G0,p) # 對綠色進行SVD還原
B = get_approx_SVD(B0,p) # 對藍色進行SVD還原
I = np.stack((R, G, B), 2)
img_svd = Image.fromarray(I)
return img_svd
filename="qin.jpg"
img = Image.open(filename).convert('RGB')
img_svd1 = rebuild_img(filename,0.2,get_approx_SVD1)
img_svd2 = rebuild_img(filename,0.4,get_approx_SVD1)
img_svd3 = rebuild_img(filename,0.6,get_approx_SVD1)
img_svd4 = rebuild_img(filename,0.8,get_approx_SVD1)
img_svd5 = rebuild_img(filename,1.0,get_approx_SVD1)
plt.figure(1)
plt.subplot(2,3,1)
plt.imshow(img)
plt.axis('off'),plt.title('原圖')
plt.subplot(2,3,2)
plt.imshow(img_svd1)
plt.axis('off'),plt.title('p=0.2')
plt.subplot(2,3,3)
plt.imshow(img_svd2)
plt.axis('off'),plt.title('p=0.4')
plt.subplot(2,3,4)
plt.imshow(img_svd3)
plt.axis('off'),plt.title('p=0.6')
plt.subplot(2,3,5)
plt.imshow(img_svd4)
plt.axis('off'),plt.title('p=0.8')
plt.subplot(2,3,6)
plt.imshow(img_svd5)
plt.axis('off'),plt.title('p=1.0')
輸出為:

利用get_approx_SVD2,同樣可以得到:

由此可知,在相同的百分比下,按奇異值個數重構,可以保留更多的資訊,
參考資料
人工智能數學基礎(北京大學出版社)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/336388.html
標籤:其他
上一篇:串口通信小試牛刀
