什麼是奇異值分解(Singular Value Decomposition)
奇異值分解對我來說是探索矩陣本質的過程,也是相當重要的矩陣分解之一。
還記得在高中、大學的線性代數課裡頭,最常見的矩陣分解就是 LU 分解了。
當時我一直想不明白,求解線性方程式 ,不就先求 A 的逆矩陣就好了,為什麼要大費周章去分解矩陣?授課時也很少提起矩陣分解的目的,反而是強調手算,留下了非常不好的回憶。這部分會在後續的章節提起,請大家敬請期待~
回到正題,奇異值分解可以將一個矩陣拆分為三個矩陣:
其中 U 為 AA^T 的特徵向量所構成的矩陣,U 一定會是個正交矩陣。
是對角矩陣,是 AA^T 或 A^TA 的特徵值開根號,由於 AA^T 的特性,特徵值一定是非負數。通常會將特徵值從大到小排列。
V^T 是 A^TA 的特徵向量所構成的矩陣,也會是個正交矩陣。
以工程師的角度,大致上可以這樣理解,每個矩陣都可以被拆分為這三個操作:
U: 旋轉
S: 放大或縮小
V: 旋轉
然而如果從圖片的角度來想,可以想成每個圖片都是由無數個更簡單的特徵向量經由旋轉跟放大縮小組合而成的。
應用
SVD 很常用在各個領域當中,介紹幾個常見的應用,最後我們以圖片壓縮當作範例做收尾。
主成分分析
有時候我們在分析資料時,特徵數過多導致沒辦法很直覺地看到資料之間的關係,在資料分析裡頭一個常見的方法是主成分分析。核心概念是將數據的共變異數矩陣投影到特徵向量(主成分)上,達到降維的目的。
https://link.medium.com/hqd7YIs1SCb
推薦系統
Netflix 為了改善自家的推薦系統精確度,曾經舉辦過競賽,最後由 BellKor’s Pragmatic Chaos team 改善了 10% 拿下冠軍。當時一個突破點就是有個隊伍使用了 SVD 改善了算法之後,其他隊伍開始紛紛使用 SVD。
在推薦系統當中,使用者的喜好應該只會受到少數幾個因素影響,且因為在推薦系統當中常常會有缺失值的問題,因此在 Netflix 競賽的過程中甚至還發明出了 Funk SVD,解決了在推薦系統中通常會面臨稀疏矩陣的問題,同時只拆分成兩個矩陣。
詳細的解說可以參考此網站
圖片壓縮
我們可以將圖片想成一個充滿像素的矩陣,對圖片的矩陣做奇異值分解,相當於找出對此圖片最重要的特徵向量的特徵值。我們如果把前幾個奇異值挑出來,把不重要的去除掉,就可以減少矩陣大小,同時又獲得不錯的效果。
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
image = Image.open('./pizza.jpg')
image_array = np.array(image.convert('L')) # 轉成灰階比較容易處理
U, S, VT = np.linalg.svd(image_array) # 奇異值分解
k = 50 # 取前 50 個奇異值
# 把原來的圖片矩陣改成 U * S(前 50 個奇異值) * Vt
image_compressed = U[:, :k] @ np.diag(S[:k]) @ VT[:k, :]
image_compressed = np.clip(image_compressed, 0, 255)
image_compressed = image_compressed.astype('uint8')
plt.imshow(image_compressed, cmap='gray')
plt.show()
先看看 k = 5 的效果:
好像有點輪廓,但看不出來是什麼
再來看看 k = 50 的效果:
已經可以看出來是個披薩,但解析度不太好
再來看看 k = 200 的效果:
解析度瞬間高很多。
最後看看原圖,是個好吃的披薩!
後記
奇異值分解背後涉及的主題很多,如果要完全理解,從線性轉換、特徵值、矩陣對角化、對角矩陣、正定矩陣都需要有一定的概念才行。因此省略了很多說明與數學證明。
不過對於工程上的應用來說,我們只要知道奇異值分解可以看透矩陣本質,任何矩陣都可以被拆解成三個小矩陣,根據需求我們可以將特徵值最大的幾個特徵向量挑出來,就可以大幅減少使用空間,同時拼湊出矩陣的完整面貌。