在 TensorFlow.org 上查看
|
在 Google Colab 运行
|
在 GitHub 上查看源代码
|
下载笔记本
|
简介
此笔记本使用 TensorFlow Core 低级 API 展示了 TensorFlow 作为高性能科学计算平台的能力。访问 Core API 概述以详细了解 TensorFlow Core 及其预期用例。
本教程探讨奇异值分解 (SVD) 技术及其在低秩逼近问题中的应用。SVD 用于分解实数或复数矩阵,并在数据科学中具有多种用例,例如图像压缩。本教程的图像来自 Google Brain 的 Imagen 项目。
安装
import matplotlib
from matplotlib.image import imread
from matplotlib import pyplot as plt
import requests
# Preset Matplotlib figure sizes.
matplotlib.rcParams['figure.figsize'] = [16, 9]
import tensorflow as tf
print(tf.__version__)
2022-12-14 22:07:37.487775: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory 2022-12-14 22:07:37.487870: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory 2022-12-14 22:07:37.487879: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly. 2.11.0
SVD 基础知识
矩阵 \({\mathrm{A} }\) 的奇异值分解由以下因式分解确定:
\[{\mathrm{A} } = {\mathrm{U} } \Sigma {\mathrm{V} }^T\]
其中
- \(\underset{m \times n}{\mathrm{A} }\):输入矩阵,其中 \(m \geq n\)
- \(\underset{m \times n}{\mathrm{U} }\):正交矩阵,\({\mathrm{U} }^T{\mathrm{U} } = {\mathrm{I} }\),包含各个列 \(u_i\),表示 \({\mathrm{A} }\) 的左奇异向量
- \(\underset{n \times n}{\Sigma}\):对角矩阵,包含各个对角条目 \(\sigma_i\),表示\({\mathrm{A} }\)的奇异值
- \(\underset{n \times n}{ {\mathrm{V} }^T}\):正交矩阵,\({\mathrm{V} }^T{\mathrm{V} } = {\mathrm{I} }\),包含各个行 \(v_i\),表示 \({\mathrm{A} }\) 的右奇异向量
当 \(m < n\) 时,\({\mathrm{U} }\) 和 \(\Sigma\) 的维度均为 \((m \times m)\),而 \({\mathrm{V} }^T\) 的维度为 \((m \times n)\)。
TensorFlow 的线性代数软件包具有一个函数 tf.linalg.svd,可用于计算一个或多个矩阵的奇异值分解。首先,定义一个简单的矩阵并计算其 SVD 因式分解。
A = tf.random.uniform(shape=[40,30])
# Compute the SVD factorization
s, U, V = tf.linalg.svd(A)
# Define Sigma and V Transpose
S = tf.linalg.diag(s)
V_T = tf.transpose(V)
# Reconstruct the original matrix
A_svd = U@S@V_T
# Visualize
plt.bar(range(len(s)), s);
plt.xlabel("Singular value rank")
plt.ylabel("Singular value")
plt.title("Bar graph of singular values");

tf.einsum 函数可用于根据 tf.linalg.svd 的输出直接计算矩阵重构。
A_svd = tf.einsum('s,us,vs -> uv',s,U,V)
print('\nReconstructed Matrix, A_svd', A_svd)
Reconstructed Matrix, A_svd tf.Tensor( [[0.08873153 0.8545857 0.17131968 ... 0.75293845 0.5527939 0.47924733] [0.700058 0.19823846 0.82784504 ... 0.8284904 0.39280835 0.55723095] [0.2696367 0.8009648 0.20029528 ... 0.01831989 0.4325177 0.51484114] ... [0.03570056 0.29680276 0.67311686 ... 0.35031977 0.68188447 0.56625783] [0.27047342 0.29453754 0.41465694 ... 0.576772 0.31444252 0.23378772] [0.92036074 0.75892895 0.35073748 ... 0.9658414 0.5708384 0.63662905]], shape=(40, 30), dtype=float32)
使用 SVD 进行低秩逼近
矩阵的秩 \({\mathrm{A} }\) 由其各列所跨越的向量空间的维度决定。SVD 可用于逼近具有较低秩的矩阵,这最终会降低存储矩阵表示的信息所需数据的维数。
\({\mathrm{A} }\) 在 SVD 中的秩 r 逼近由以下方程定义:
\[{\mathrm{A_r} } = {\mathrm{U_r} } \Sigma_r {\mathrm{V_r} }^T\]
其中
- \(\underset{m \times r}{\mathrm{U_r} }\):由 \({\mathrm{U} }\) 的前 \(r\) 列组成的矩阵
- \(\underset{r \times r}{\Sigma_r}\):由\(\Sigma\) 中的前 \(r\) 个奇异值组成的对角矩阵
- \(\underset{r \times n}{\mathrm{V_r} }^T\):由 \({\mathrm{V} }^T\) 的前 \(r\) 行组成的矩阵
首先,编写一个函数来计算给定矩阵的秩 r 逼近。这种低秩逼近过程用于图像压缩;因此,计算每个逼近的物理数据大小也很有帮助。为简单起见,假设秩 r 逼近矩阵的数据大小等于计算逼近所需的元素总数。接下来,编写一个函数来呈现原始矩阵 \(\mathrm{A}\)、其秩 r 逼近 \(\mathrm{A}_r\) 和误差矩阵 \(|\mathrm{A} - \mathrm{A}_r|\)。
def rank_r_approx(s, U, V, r, verbose=False):
# Compute the matrices necessary for a rank-r approximation
s_r, U_r, V_r = s[..., :r], U[..., :, :r], V[..., :, :r] # ... implies any number of extra batch axes
# Compute the low-rank approximation and its size
A_r = tf.einsum('...s,...us,...vs->...uv',s_r,U_r,V_r)
A_r_size = tf.size(U_r) + tf.size(s_r) + tf.size(V_r)
if verbose:
print(f"Approximation Size: {A_r_size}")
return A_r, A_r_size
def viz_approx(A, A_r):
# Plot A, A_r, and A - A_r
vmin, vmax = 0, tf.reduce_max(A)
fig, ax = plt.subplots(1,3)
mats = [A, A_r, abs(A - A_r)]
titles = ['Original A', 'Approximated A_r', 'Error |A - A_r|']
for i, (mat, title) in enumerate(zip(mats, titles)):
ax[i].pcolormesh(mat, vmin=vmin, vmax=vmax)
ax[i].set_title(title)
ax[i].axis('off')
print(f"Original Size of A: {tf.size(A)}")
s, U, V = tf.linalg.svd(A)
Original Size of A: 1200
# Rank-15 approximation
A_15, A_15_size = rank_r_approx(s, U, V, 15, verbose = True)
viz_approx(A, A_15)
Approximation Size: 1065

# Rank-3 approximation
A_3, A_3_size = rank_r_approx(s, U, V, 3, verbose = True)
viz_approx(A, A_3)
Approximation Size: 213

正如预期的那样,使用较低的秩会得到不太准确的逼近。然而,这些低秩逼近的质量在现实世界的场景中通常足够好。另请注意,使用 SVD 进行低秩逼近的主要目标是减少数据的维数,而不是减少数据本身的磁盘空间。不过,随着输入矩阵的维度变高,许多低秩逼近也最终受益于缩减的数据大小。这种缩减的好处是该过程适用于图像压缩问题的原因。
图像加载
Imagen 首页上提供了以下图像。Imagen 是由 Google Research 的 Brain 团队开发的文本到图像扩散模型。AI 根据提示创建了这张图像:“一张柯基犬在时代广场骑自行车的照片。它戴着墨镜和沙滩帽。”多么酷啊!您还可以将下面的网址更改为任何 .jpg 链接以加载选择的自定义图像。
首先,读入并呈现图像。读取 JPEG 文件后,Matplotlib 会输出一个形状为 \((m \times n \times 3)\) 的矩阵 \({\mathrm{I} }\),它表示一个二维图像,具有分别对应于红色、绿色和蓝色的 3 个颜色通道。
img_link = "https://imagen.research.google/main_gallery_images/a-photo-of-a-corgi-dog-riding-a-bike-in-times-square.jpg"
img_path = requests.get(img_link, stream=True).raw
I = imread(img_path, 0)
print(
在 TensorFlow.org 上查看
在 Google Colab 运行
在 GitHub 上查看源代码
下载笔记本

