├── LICENSE
├── README.md
└── data
├── gaint_panda.bmp
├── gaint_panda.jpg
├── gaint_panda_gray.bmp
├── gaint_panda_rgb.jpg
└── sample_speed_time_series.npz
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Xinyu Chen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 张量计算系列教程
2 |
3 | [](https://opensource.org/licenses/MIT)
4 | 
5 | [](https://github.com/xinychen/tensor-book)
6 |
7 |
8 |
9 |
10 | ## 系列教程
11 |
12 | 1. [《从线性代数到张量计算》(PDF)](https://xinychen.github.io/books/tensor_book.pdf)
13 | 2. [《**机器学习与时空数据建模**》(PDF, 已更新超过100页)](https://xinychen.github.io/books/spatiotemporal_low_rank_models.pdf)
14 |
15 |
16 |
17 | > 该系列教程尚处在更新阶段,欢迎广大读者提供宝贵的反馈意见,如有建议,请在本项目issues区域留言;如需交流,请移步至QQ群(457012422),非诚勿扰。
18 |
19 |
20 |
21 | ## 作者申明
22 |
23 | - 撰写该系列教程的初衷在于传播知识、丰富中文科技文库,为感兴趣的读者提供参考素材。
24 | - 禁止将该系列教程放在其他网站上,唯一下载网址为[https://xinychen.github.io](https://xinychen.github.io)。
25 | - 禁止将该系列教程用于任何形式的商业活动。
26 |
27 |
28 |
29 | 代数结构
30 | ▴ 回到顶部
31 |
32 | **例.** 写出张量的lateral切片与horizontal切片。
33 |
34 | ```python
35 | import numpy as np
36 |
37 | X = np.zeros((2, 2, 2))
38 | X[:, :, 0] = np.array([[1, 2], [3, 4]])
39 | X[:, :, 1] = np.array([[5, 6], [7, 8]])
40 | print('lateral slices:')
41 | print(X[:, 0, :])
42 | print()
43 | print(X[:, 1, :])
44 | print()
45 | print('horizonal slices:')
46 | print(X[0, :, :])
47 | print()
48 | print(X[1, :, :])
49 | print()
50 | ```
51 |
52 | Kronecker分解与Kronecker分解
53 | ▴ 回到顶部
54 |
55 | **例.** 计算Kronecker积并求Kronecker分解。
56 |
57 | - 计算Kronecker积
58 |
59 | ```python
60 | import numpy as np
61 |
62 | A = np.array([[1, 2], [3, 4]])
63 | B = np.array([[5, 6, 7], [8, 9, 10]])
64 | X = np.kron(A, B)
65 | print('X = ')
66 | print(X)
67 | ```
68 |
69 | - 求Kronecker分解
70 |
71 | ```python
72 | def permute(mat, A_dim1, A_dim2, B_dim1, B_dim2):
73 | ans = np.zeros((A_dim1 * A_dim2, B_dim1 * B_dim2))
74 | for j in range(A_dim2):
75 | for i in range(A_dim1):
76 | ans[A_dim1 * j + i, :] = mat[i * B_dim1 : (i + 1) * B_dim1,
77 | j * B_dim2 : (j + 1) * B_dim2].reshape(B_dim1 * B_dim2, order = 'F')
78 | return ans
79 |
80 | X_tilde = permute(X, 2, 2, 2, 3)
81 | print('X_tilde = ')
82 | print(X_tilde)
83 | print()
84 | u, s, v = np.linalg.svd(X_tilde, full_matrices = False)
85 | A_hat = np.sqrt(s[0]) * u[:, 0].reshape(2, 2, order = 'F')
86 | B_hat = np.sqrt(s[0]) * v[0, :].reshape(2, 3, order = 'F')
87 | print('A_hat = ')
88 | print(A_hat)
89 | print()
90 | print('B_hat = ')
91 | print(B_hat)
92 | ```
93 |
94 | **例.** 采用广义 Kronecker 分解重构灰度图像。
95 |
96 | ```python
97 | import numpy as np
98 |
99 | def permute(mat, A_dim1, A_dim2, B_dim1, B_dim2):
100 | ans = np.zeros((A_dim1 * A_dim2, B_dim1 * B_dim2))
101 | for j in range(A_dim2):
102 | for i in range(A_dim1):
103 | ans[A_dim1 * j + i, :] = mat[i * B_dim1 : (i + 1) * B_dim1,
104 | j * B_dim2 : (j + 1) * B_dim2].reshape(B_dim1 * B_dim2, order = 'F')
105 | return ans
106 |
107 | def kron_decomp(mat, A_dim1, A_dim2, B_dim1, B_dim2, rank):
108 | mat_tilde = permute(mat, A_dim1, A_dim2, B_dim1, B_dim2)
109 | u, s, v = np.linalg.svd(mat_tilde, full_matrices = False)
110 | A_hat = np.zeros((A_dim1, A_dim2, rank))
111 | B_hat = np.zeros((B_dim1, B_dim2, rank))
112 | for r in range(rank):
113 | A_hat[:, :, r] = np.sqrt(s[r]) * u[:, r].reshape([A_dim1, A_dim2], order = 'F')
114 | B_hat[:, :, r] = np.sqrt(s[r]) * v[r, :].reshape([B_dim1, B_dim2], order = 'F')
115 | mat_hat = np.zeros(mat.shape)
116 | for r in range(rank):
117 | mat_hat += np.kron(A_hat[:, :, r], B_hat[:, :, r])
118 | return mat_hat
119 | ```
120 |
121 | ```python
122 | from skimage import color
123 | from skimage import io
124 |
125 | img = io.imread('data/gaint_panda.bmp')
126 | imgGray = color.rgb2gray(img)
127 |
128 | io.imshow(imgGray)
129 | plt.axis('off')
130 | plt.imsave('gaint_panda_gray.png', imgGray, cmap = plt.cm.gray)
131 | plt.show()
132 | ```
133 |
134 | ```python
135 | import imageio
136 | import matplotlib.pyplot as plt
137 |
138 | img = io.imread('data/gaint_panda.bmp')
139 | mat = color.rgb2gray(img)
140 | io.imshow(mat)
141 | plt.axis('off')
142 | plt.show()
143 |
144 | A_dim1 = 16
145 | A_dim2 = 32
146 | B_dim1 = 32
147 | B_dim2 = 16
148 | for rank in [5, 10, 50, 100]:
149 | mat_hat = kron_decomp(mat, A_dim1, A_dim2, B_dim1, B_dim2, rank)
150 | mat_hat[mat_hat > 1] = 1
151 | mat_hat[mat_hat < 0] = 0
152 | io.imshow(mat_hat)
153 | plt.axis('off')
154 | plt.imsave('gaint_panda_gray_R{}.png'.format(rank),
155 | mat_hat, cmap = plt.cm.gray)
156 | plt.show()
157 | ```
158 |
159 | 模态积与Tucker张量分解
160 | ▴ 回到顶部
161 |
162 | **例.** 计算张量与矩阵的模态积。
163 |
164 | ```python
165 | import numpy as np
166 |
167 | X = np.zeros((2, 2, 2))
168 | X[:, :, 0] = np.array([[1, 2], [3, 4]])
169 | X[:, :, 1] = np.array([[5, 6], [7, 8]])
170 | A = np.array([[1, 2], [3, 4], [5, 6]])
171 | Y = np.einsum('ijh, ki -> kjh', X, A)
172 | print('frontal slices of Y:')
173 | print(Y[:, :, 0])
174 | print()
175 | print(Y[:, :, 1])
176 | print()
177 | ```
178 |
179 | 低秩时序矩阵模型
180 | ▴ 回到顶部
181 |
182 | **例.** 使用时序矩阵分解对流体流动的动态过程进行预测。
183 |
184 | ```python
185 | import numpy as np
186 | import seaborn as sns
187 | import scipy.io
188 | import matplotlib.pyplot as plt
189 | from matplotlib.colors import ListedColormap, LinearSegmentedColormap
190 | color = scipy.io.loadmat('CCcool.mat')
191 | cc = color['CC']
192 | newcmp = LinearSegmentedColormap.from_list('', cc)
193 |
194 | dense_tensor = np.load('tensor.npz')['arr_0']
195 | np.random.seed(1)
196 | dense_tensor = dense_tensor[:, :, : 150]
197 | M, N, T = dense_tensor.shape
198 |
199 | plt.rcParams['font.size'] = 13
200 | plt.rcParams['mathtext.fontset'] = 'cm'
201 | fig = plt.figure(figsize = (7, 8))
202 | id = np.array([5, 10, 15, 20, 25, 30, 35, 40])
203 | for t in range(8):
204 | ax = fig.add_subplot(4, 2, t + 1)
205 | ax = sns.heatmap(dense_tensor[:, :, id[t] - 1], cmap = newcmp, vmin = -5, vmax = 5, cbar = False)
206 | ax.contour(np.linspace(0, N, N), np.linspace(0, M, M), dense_tensor[:, :, id[t] - 1],
207 | levels = np.linspace(0.15, 15, 30), colors = 'k', linewidths = 0.7)
208 | ax.contour(np.linspace(0, N, N), np.linspace(0, M, M), dense_tensor[:, :, id[t] - 1],
209 | levels = np.linspace(-15, -0.15, 30), colors = 'k', linestyles = 'dashed', linewidths = 0.7)
210 | plt.xticks([])
211 | plt.yticks([])
212 | plt.title(r'$t = {}$'.format(id[t]))
213 | for _, spine in ax.spines.items():
214 | spine.set_visible(True)
215 | plt.show()
216 | fig.savefig("fluid_flow_heatmap.png", bbox_inches = "tight")
217 | ```
218 |
219 | ```python
220 | import numpy as np
221 |
222 | def update_cg(var, r, q, Aq, rold):
223 | alpha = rold / np.inner(q, Aq)
224 | var = var + alpha * q
225 | r = r - alpha * Aq
226 | rnew = np.inner(r, r)
227 | q = r + (rnew / rold) * q
228 | return var, r, q, rnew
229 |
230 | def ell_w(ind, W, X, rho):
231 | return X @ ((W.T @ X) * ind).T + rho * W
232 |
233 | def conj_grad_w(sparse_mat, ind, W, X, rho, maxiter = 5):
234 | rank, dim1 = W.shape
235 | w = np.reshape(W, -1, order = 'F')
236 | r = np.reshape(X @ sparse_mat.T - ell_w(ind, W, X, rho), -1, order = 'F')
237 | q = r.copy()
238 | rold = np.inner(r, r)
239 | for it in range(maxiter):
240 | Q = np.reshape(q, (rank, dim1), order = 'F')
241 | Aq = np.reshape(ell_w(ind, Q, X, rho), -1, order = 'F')
242 | w, r, q, rold = update_cg(w, r, q, Aq, rold)
243 | return np.reshape(w, (rank, dim1), order = 'F')
244 |
245 | def ell_x(ind, W, X, A, Psi, d, lambda0, rho):
246 | rank, dim2 = X.shape
247 | temp = np.zeros((d * rank, Psi[0].shape[0]))
248 | for k in range(1, d + 1):
249 | temp[(k - 1) * rank : k * rank, :] = X @ Psi[k].T
250 | temp1 = X @ Psi[0].T - A @ temp
251 | temp2 = np.zeros((rank, dim2))
252 | for k in range(d):
253 | temp2 += A[:, k * rank : (k + 1) * rank].T @ temp1 @ Psi[k + 1]
254 | return W @ ((W.T @ X) * ind) + rho * X + lambda0 * (temp1 @ Psi[0] - temp2)
255 |
256 | def conj_grad_x(sparse_mat, ind, W, X, A, Psi, d, lambda0, rho, maxiter = 5):
257 | rank, dim2 = X.shape
258 | x = np.reshape(X, -1, order = 'F')
259 | r = np.reshape(W @ sparse_mat - ell_x(ind, W, X, A, Psi, d, lambda0, rho), -1, order = 'F')
260 | q = r.copy()
261 | rold = np.inner(r, r)
262 | for it in range(maxiter):
263 | Q = np.reshape(q, (rank, dim2), order = 'F')
264 | Aq = np.reshape(ell_x(ind, W, Q, A, Psi, d, lambda0, rho), -1, order = 'F')
265 | x, r, q, rold = update_cg(x, r, q, Aq, rold)
266 | return np.reshape(x, (rank, dim2), order = 'F')
267 |
268 | def generate_Psi(T, d):
269 | Psi = []
270 | for k in range(0, d + 1):
271 | if k == 0:
272 | Psi.append(np.append(np.zeros((T - d, d)), np.eye(T - d), axis = 1))
273 | else:
274 | Psi.append(np.append(np.append(np.zeros((T - d, d - k)), np.eye(T - d), axis = 1),
275 | np.zeros((T - d, k)), axis = 1))
276 | return Psi
277 |
278 | def tmf(sparse_mat, rank, d, lambda0, rho, maxiter = 50):
279 | dim1, dim2 = sparse_mat.shape
280 | ind = sparse_mat != 0
281 | W = 0.01 * np.random.randn(rank, dim1)
282 | X = 0.01 * np.random.randn(rank, dim2)
283 | A = 0.01 * np.random.randn(rank, d * rank)
284 | Psi = generate_Psi(dim2, d)
285 | temp = np.zeros((d * rank, dim2 - d))
286 | for it in range(maxiter):
287 | W = conj_grad_w(sparse_mat, ind, W, X, rho)
288 | X = conj_grad_x(sparse_mat, ind, W, X, A, Psi, d, lambda0, rho)
289 | for k in range(1, d + 1):
290 | temp[(k - 1) * rank : k * rank, :] = X @ Psi[k].T
291 | A = X @ Psi[0].T @ np.linalg.pinv(temp)
292 | mat_hat = W.T @ X
293 | return mat_hat, W, X, A
294 |
295 | def var4cast(X, A, d, delta):
296 | dim1, dim2 = X.shape
297 | X_hat = np.append(X, np.zeros((dim1, delta)), axis = 1)
298 | for t in range(delta):
299 | X_hat[:, dim2 + t] = A @ X_hat[:, dim2 + t - np.arange(1, d + 1)].T.reshape(dim1 * d)
300 | return X_hat[:, - delta :]
301 | ```
302 |
303 | ```python
304 | import numpy as np
305 | np.random.seed(1)
306 |
307 | dense_tensor = np.load('tensor.npz')['arr_0']
308 | dense_tensor = dense_tensor[:, :, : 150]
309 | M, N, T = dense_tensor.shape
310 | dense_mat = np.reshape(dense_tensor, (M * N, T), order = 'F')
311 | p = 0.5
312 | sparse_mat = dense_mat * np.round(np.random.rand(M * N, T) + 0.5 - p)
313 |
314 | import time
315 | start = time.time()
316 | delta = 3
317 | rank = 10
318 | d = 1
319 | lambda0 = 1
320 | rho = 1
321 | _, W, X, A = tmf(sparse_mat[:, : T - delta], rank, d, lambda0, rho)
322 | mat_hat = W.T @ var4cast(X, A, d, delta)
323 | end = time.time()
324 | print('Running time: %d seconds'%(end - start))
325 | ```
326 |
327 | ```python
328 | import seaborn as sns
329 | import scipy.io
330 | import matplotlib.pyplot as plt
331 | from matplotlib.colors import ListedColormap, LinearSegmentedColormap
332 | color = scipy.io.loadmat('CCcool.mat')
333 | cc = color['CC']
334 | newcmp = LinearSegmentedColormap.from_list('', cc)
335 |
336 | plt.rcParams['font.size'] = 13
337 | plt.rcParams['mathtext.fontset'] = 'cm'
338 | fig = plt.figure(figsize = (11, 1.8))
339 | i = 1
340 | for t in [147, 148, 149]:
341 | ax = fig.add_subplot(1, 3, i)
342 | ax = sns.heatmap(dense_mat[:, t].reshape((199, 449), order = 'F'),
343 | cmap = newcmp, vmin = -5, vmax = 5, cbar = False)
344 | ax.contour(np.linspace(0, N, N), np.linspace(0, M, M),
345 | dense_mat[:, t].reshape((199, 449), order = 'F'),
346 | levels = np.linspace(0.15, 15, 30), colors = 'k',
347 | linewidths = 0.7)
348 | ax.contour(np.linspace(0, N, N), np.linspace(0, M, M),
349 | dense_mat[:, t].reshape((199, 449), order = 'F'),
350 | levels = np.linspace(-15, -0.15, 30), colors = 'k',
351 | linestyles = 'dashed', linewidths = 0.7)
352 | plt.title(r'$t = {}$'.format(t + 1))
353 | plt.xticks([])
354 | plt.yticks([])
355 | for _, spine in ax.spines.items():
356 | spine.set_visible(True)
357 | i += 1
358 | plt.show()
359 | fig.savefig("fluid_flow_ground_truth.png", bbox_inches = "tight")
360 | ```
361 |
362 | ```python
363 | import seaborn as sns
364 | import scipy.io
365 | import matplotlib.pyplot as plt
366 | from matplotlib.colors import ListedColormap, LinearSegmentedColormap
367 | color = scipy.io.loadmat('CCcool.mat')
368 | cc = color['CC']
369 | newcmp = LinearSegmentedColormap.from_list('', cc)
370 |
371 | plt.rcParams['font.size'] = 13
372 | plt.rcParams['mathtext.fontset'] = 'cm'
373 | fig = plt.figure(figsize = (11, 1.8))
374 | i = 1
375 | for t in range(3):
376 | ax = fig.add_subplot(1, 3, i)
377 | ax = sns.heatmap(mat_hat[:, t].reshape((199, 449), order = 'F'),
378 | cmap = newcmp, vmin = -5, vmax = 5, cbar = False)
379 | ax.contour(np.linspace(0, N, N), np.linspace(0, M, M),
380 | mat_hat[:, t].reshape((199, 449), order = 'F'),
381 | levels = np.linspace(0.15, 15, 30), colors = 'k',
382 | linewidths = 0.7)
383 | ax.contour(np.linspace(0, N, N), np.linspace(0, M, M),
384 | mat_hat[:, t].reshape((199, 449), order = 'F'),
385 | levels = np.linspace(-15, -0.15, 30), colors = 'k',
386 | linestyles = 'dashed', linewidths = 0.7)
387 | plt.title(r'$t = {}$'.format(148+ t))
388 | plt.xticks([])
389 | plt.yticks([])
390 | for _, spine in ax.spines.items():
391 | spine.set_visible(True)
392 | i += 1
393 | plt.show()
394 | fig.savefig("fluid_flow_forecasts.png", bbox_inches = "tight")
395 | ```
396 |
397 | **例.** 使用考虑平滑处理的矩阵分解对灰度图像进行复原。
398 |
399 | ```python
400 | import numpy as np
401 |
402 | def compute_rse(var, var_hat):
403 | return np.linalg.norm(var - var_hat, 2) / np.linalg.norm(var, 2)
404 |
405 | def generate_Psi(n):
406 | mat1 = np.append(np.zeros((n - 1, 1)), np.eye(n - 1), axis = 1)
407 | mat2 = np.append(np.eye(n - 1), np.zeros((n - 1, 1)), axis = 1)
408 | Psi = mat1 - mat2
409 | return Psi
410 |
411 | def update_cg(var, r, q, Aq, rold):
412 | alpha = rold / np.inner(q, Aq)
413 | var = var + alpha * q
414 | r = r - alpha * Aq
415 | rnew = np.inner(r, r)
416 | q = r + (rnew / rold) * q
417 | return var, r, q, rnew
418 |
419 | def ell_w(ind, W, X, Psi1, rho, lmbda):
420 | return X @ ((W.T @ X) * ind).T + rho * W + lmbda * W @ Psi1.T @ Psi1
421 |
422 | def conj_grad_w(sparse_mat, ind, W, X, Psi1, rho, lmbda, maxiter = 5):
423 | rank, dim1 = W.shape
424 | w = np.reshape(W, -1, order = 'F')
425 | r = np.reshape(X @ sparse_mat.T
426 | - ell_w(ind, W, X, Psi1, rho, lmbda), -1, order = 'F')
427 | q = r.copy()
428 | rold = np.inner(r, r)
429 | for it in range(maxiter):
430 | Q = np.reshape(q, (rank, dim1), order = 'F')
431 | Aq = np.reshape(ell_w(ind, Q, X, Psi1, rho, lmbda), -1, order = 'F')
432 | w, r, q, rold = update_cg(w, r, q, Aq, rold)
433 | return np.reshape(w, (rank, dim1), order = 'F')
434 |
435 | def ell_x(ind, W, X, Psi2, rho, lmbda):
436 | return W @ ((W.T @ X) * ind) + rho * X + lmbda * X @ Psi2.T @ Psi2
437 |
438 | def conj_grad_x(sparse_mat, ind, W, X, Psi2, rho, lmbda, maxiter = 5):
439 | rank, dim2 = X.shape
440 | x = np.reshape(X, -1, order = 'F')
441 | r = np.reshape(W @ sparse_mat
442 | - ell_x(ind, W, X, Psi2, rho, lmbda), -1, order = 'F')
443 | q = r.copy()
444 | rold = np.inner(r, r)
445 | for it in range(maxiter):
446 | Q = np.reshape(q, (rank, dim2), order = 'F')
447 | Aq = np.reshape(ell_x(ind, W, Q, Psi2, rho, lmbda), -1, order = 'F')
448 | x, r, q, rold = update_cg(x, r, q, Aq, rold)
449 | return np.reshape(x, (rank, dim2), order = 'F')
450 |
451 | def smoothing_mf(dense_mat, sparse_mat, rank, rho, lmbda, maxiter = 50):
452 | dim1, dim2 = sparse_mat.shape
453 | W = 0.01 * np.random.randn(rank, dim1)
454 | X = 0.01 * np.random.randn(rank, dim2)
455 | ind = sparse_mat != 0
456 | pos_test = np.where((dense_mat != 0) & (sparse_mat == 0))
457 | dense_test = dense_mat[pos_test]
458 | del dense_mat
459 | Psi1 = generate_Psi(dim1)
460 | Psi2 = generate_Psi(dim2)
461 | show_iter = 10
462 | for it in range(maxiter):
463 | W = conj_grad_w(sparse_mat, ind, W, X, Psi1, rho, lmbda)
464 | X = conj_grad_x(sparse_mat, ind, W, X, Psi2, rho, lmbda)
465 | mat_hat = W.T @ X
466 | if (it + 1) % show_iter == 0:
467 | temp_hat = mat_hat[pos_test]
468 | print('Iter: {}'.format(it + 1))
469 | print(compute_rse(temp_hat, dense_test))
470 | print()
471 | return mat_hat, W, X
472 | ```
473 |
474 | ```python
475 | import numpy as np
476 | np.random.seed(1)
477 | import matplotlib.pyplot as plt
478 | from skimage import color
479 | from skimage import io
480 |
481 | img = io.imread('data/gaint_panda.bmp')
482 | imgGray = color.rgb2gray(img)
483 | M, N = imgGray.shape
484 | missing_rate = 0.9
485 |
486 | sparse_img = imgGray * np.round(np.random.rand(M, N) + 0.5 - missing_rate)
487 | io.imshow(sparse_img)
488 | plt.axis('off')
489 | plt.show()
490 | ```
491 |
492 | ```python
493 | lmbda = 1e+1
494 | for rank in [5, 10, 50]:
495 | rho = 1e-1
496 | maxiter = 100
497 | mat_hat, W, X = smoothing_mf(imgGray, sparse_img, rank, rho, lmbda, maxiter)
498 |
499 | mat_hat[mat_hat < 0] = 0
500 | mat_hat[mat_hat > 1] = 1
501 | io.imshow(mat_hat)
502 | plt.axis('off')
503 | plt.imsave('gaint_panda_gray_recovery_90_mf_rank_{}_lmbda_10.png'.format(rank),
504 | mat_hat, cmap = plt.cm.gray)
505 | plt.show()
506 | ```
507 |
508 | ```python
509 | lmbda = 1e-10
510 | for rank in [5, 10, 50]:
511 | rho = 1e-1
512 | maxiter = 100
513 | mat_hat, W, X = smoothing_mf(imgGray, sparse_img, rank, rho, lmbda, maxiter)
514 |
515 | mat_hat[mat_hat < 0] = 0
516 | mat_hat[mat_hat > 1] = 1
517 | io.imshow(mat_hat)
518 | plt.axis('off')
519 | plt.imsave('gaint_panda_gray_recovery_90_mf_rank_{}_lmbda_0.png'.format(rank),
520 | mat_hat, cmap = plt.cm.gray)
521 | plt.show()
522 | ```
523 |
524 | **例.** 使用考虑空间自回归的矩阵分解对灰度图像进行复原。
525 |
526 | ```python
527 | import numpy as np
528 |
529 | def compute_rse(var, var_hat):
530 | return np.linalg.norm(var - var_hat, 2) / np.linalg.norm(var, 2)
531 |
532 | def generate_Psi(n):
533 | mat1 = np.append(np.zeros((n - 1, 1)), np.eye(n - 1), axis = 1)
534 | mat2 = np.append(np.eye(n - 1), np.zeros((n - 1, 1)), axis = 1)
535 | return mat1, mat2
536 |
537 | def update_cg(var, r, q, Aq, rold):
538 | alpha = rold / np.inner(q, Aq)
539 | var = var + alpha * q
540 | r = r - alpha * Aq
541 | rnew = np.inner(r, r)
542 | q = r + (rnew / rold) * q
543 | return var, r, q, rnew
544 |
545 | def ell_w(ind, W, X, Aw, Phi0, Phi1, rho, lmbda):
546 | temp1 = W @ Phi0.T - Aw @ W @ Phi1.T
547 | temp2 = lmbda * temp1 @ Phi0 - lmbda * Aw.T @ temp1 @ Phi1
548 | return X @ ((W.T @ X) * ind).T + rho * W + temp2
549 |
550 | def conj_grad_w(sparse_mat, ind, W, X, Aw, Phi0, Phi1, rho, lmbda, maxiter = 5):
551 | rank, dim1 = W.shape
552 | w = np.reshape(W, -1, order = 'F')
553 | r = np.reshape(X @ sparse_mat.T
554 | - ell_w(ind, W, X, Aw, Phi0, Phi1, rho, lmbda), -1, order = 'F')
555 | q = r.copy()
556 | rold = np.inner(r, r)
557 | for it in range(maxiter):
558 | Q = np.reshape(q, (rank, dim1), order = 'F')
559 | Aq = np.reshape(ell_w(ind, Q, X, Aw, Phi0, Phi1, rho, lmbda), -1, order = 'F')
560 | w, r, q, rold = update_cg(w, r, q, Aq, rold)
561 | return np.reshape(w, (rank, dim1), order = 'F')
562 |
563 | def ell_x(ind, W, X, Ax, Psi0, Psi1, rho, lmbda):
564 | temp1 = X @ Psi0.T - Ax @ X @ Psi1.T
565 | temp2 = lmbda * temp1 @ Psi0 - lmbda * Ax.T @ temp1 @ Psi1
566 | return W @ ((W.T @ X) * ind) + rho * X + temp2
567 |
568 | def conj_grad_x(sparse_mat, ind, W, X, Ax, Psi0, Psi1, rho, lmbda, maxiter = 5):
569 | rank, dim2 = X.shape
570 | x = np.reshape(X, -1, order = 'F')
571 | r = np.reshape(W @ sparse_mat
572 | - ell_x(ind, W, X, Ax, Psi0, Psi1, rho, lmbda), -1, order = 'F')
573 | q = r.copy()
574 | rold = np.inner(r, r)
575 | for it in range(maxiter):
576 | Q = np.reshape(q, (rank, dim2), order = 'F')
577 | Aq = np.reshape(ell_x(ind, W, Q, Ax, Psi0, Psi1, rho, lmbda), -1, order = 'F')
578 | x, r, q, rold = update_cg(x, r, q, Aq, rold)
579 | return np.reshape(x, (rank, dim2), order = 'F')
580 |
581 | def spatial_autoregressive_mf(dense_mat, sparse_mat, rank, rho, lmbda, maxiter = 50):
582 | dim1, dim2 = sparse_mat.shape
583 | W = 0.01 * np.random.randn(rank, dim1)
584 | X = 0.01 * np.random.randn(rank, dim2)
585 | Aw = 0.01 * np.random.randn(rank, rank)
586 | Ax = 0.01 * np.random.randn(rank, rank)
587 | ind = sparse_mat != 0
588 | pos_test = np.where((dense_mat != 0) & (sparse_mat == 0))
589 | dense_test = dense_mat[pos_test]
590 | del dense_mat
591 | Phi0, Phi1 = generate_Psi(dim1)
592 | Psi0, Psi1 = generate_Psi(dim2)
593 | show_iter = 10
594 | for it in range(maxiter):
595 | W = conj_grad_w(sparse_mat, ind, W, X, Aw, Phi0, Phi1, rho, lmbda)
596 | X = conj_grad_x(sparse_mat, ind, W, X, Ax, Psi0, Psi1, rho, lmbda)
597 | Aw = W @ Phi0.T @ np.linalg.pinv(W @ Phi1.T)
598 | Ax = X @ Psi0.T @ np.linalg.pinv(X @ Psi1.T)
599 | mat_hat = W.T @ X
600 | if (it + 1) % show_iter == 0:
601 | temp_hat = mat_hat[pos_test]
602 | print('Iter: {}'.format(it + 1))
603 | print(compute_rse(temp_hat, dense_test))
604 | print()
605 | return mat_hat, W, X, Aw, Ax
606 | ```
607 |
608 | ```python
609 | import numpy as np
610 | np.random.seed(1)
611 | import matplotlib.pyplot as plt
612 | from skimage import color
613 | from skimage import io
614 |
615 | img = io.imread('data/gaint_panda.bmp')
616 | imgGray = color.rgb2gray(img)
617 | M, N = imgGray.shape
618 | missing_rate = 0.9
619 |
620 | sparse_img = imgGray * np.round(np.random.rand(M, N) + 0.5 - missing_rate)
621 | io.imshow(sparse_img)
622 | plt.axis('off')
623 | plt.show()
624 | ```
625 |
626 | ```python
627 | lmbda = 5e-1
628 | for rank in [5, 10, 50]:
629 | rho = 1e-1
630 | maxiter = 100
631 | mat_hat, W, X, Aw, Ax = spatial_autoregressive_mf(imgGray, sparse_img, rank, rho, lmbda, maxiter)
632 |
633 | mat_hat[mat_hat < 0] = 0
634 | mat_hat[mat_hat > 1] = 1
635 | io.imshow(mat_hat)
636 | plt.axis('off')
637 | plt.imsave('gaint_panda_gray_recovery_90_spatial_ar_mf_rank_{}.png'.format(rank),
638 | mat_hat, cmap = plt.cm.gray)
639 | plt.show()
640 | ```
641 |
642 | **例.** 根据卷积定理计算循环卷积。
643 |
644 | ```python
645 | import numpy as np
646 |
647 | x = np.array([0, 1, 2, 3, 4])
648 | y = np.array([2, -1, 3])
649 | fx = np.fft.fft(x)
650 | fy = np.fft.fft(np.append(y, np.zeros(2), axis = 0))
651 | z = np.fft.ifft(fx * fy).real
652 | ```
653 |
654 | **例.** 根据卷积定理计算向量**y**。
655 |
656 | ```python
657 | import numpy as np
658 |
659 | x = np.array([0, 1, 2, 3, 4])
660 | z = np.array([5, 14, 3, 7, 11])
661 | fx = np.fft.fft(x)
662 | fz = np.fft.fft(z)
663 | y = np.fft.ifft(fz / fx).real
664 | ```
665 |
666 | **例.** 根据卷积定理计算二维循环卷积。
667 |
668 | ```python
669 | import numpy as np
670 |
671 | X = np.array([[1, 2, 3, 4], [4, 5, 6, 7],
672 | [7, 8, 9, 10], [10, 11, 12, 13]])
673 | K = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
674 | pad_K = np.zeros(X.shape)
675 | pad_K[: K.shape[0], : K.shape[1]] = K
676 | Y = np.fft.ifft2(np.fft.fft2(X) * np.fft.fft2(pad_K)).real
677 | ```
678 |
679 | **例.** 计算矩阵的F范数与傅立叶变换的F范数。
680 |
681 | ```python
682 | import numpy as np
683 |
684 | X = np.array([[5, 6, 7], [8, 9, 10]])
685 | print('The squared Frobenius norm of the matrix X:')
686 | print(np.linalg.norm(X, 'fro') ** 2)
687 | print()
688 | print('The squared Frobenius norm of the matrix Fx:')
689 | print(np.linalg.norm(np.fft.fft2(X), 'fro') ** 2)
690 | print()
691 | ```
692 |
693 | **例.** 计算循环矩阵的奇异值与L1范数。
694 |
695 | ```python
696 | import numpy as np
697 |
698 | def circ_mat(vec):
699 | n = vec.shape[0]
700 | mat = np.zeros((n, n))
701 | mat[:, 0] = vec
702 | for k in range(1, n):
703 | mat[:, k] = np.append(vec[n - k :], vec[: n - k], axis = 0)
704 | return mat
705 |
706 | x = np.array([0, 1, 2, 3, 4])
707 | Cx = circ_mat(x)
708 | s = np.linalg.svd(Cx, full_matrices = False, compute_uv = False)
709 | print('Singular values of the circulant matrix Cx:')
710 | print(s)
711 | print()
712 | print('Discrete Fourier transform of the vector x:')
713 | print(np.fft.fft(x))
714 | print()
715 | ```
716 |
717 | **例.** 对循环矩阵核范数最小化问题进行求解。
718 |
719 | ```python
720 | import numpy as np
721 |
722 | z = np.array([0, 1, 2, 3, 4])
723 | lmbda = 2
724 | T = z.shape[0]
725 |
726 | h_hat = np.fft.fft(z)
727 | print('h_hat = ')
728 | print(h_hat)
729 | print()
730 | h_hat_abs = np.abs(h_hat)
731 | print('h_hat_abs = ')
732 | print(h_hat_abs)
733 | print()
734 |
735 | temp = h_hat_abs - T / lmbda
736 | temp[temp <= 0] = 0
737 | x = np.fft.ifft(h_hat / h_hat_abs * temp).real
738 | print('x = ')
739 | print(x)
740 | print()
741 | print('The objective function is: ')
742 | print(np.sum(np.abs(np.fft.fft(x)))
743 | + 0.5 * lmbda * np.linalg.norm(x - z, 2) ** 2)
744 | print()
745 | ```
746 |
747 | **例.** 使用循环矩阵核范数最小化算法对灰度图像进行复原。
748 |
749 | ```python
750 | import numpy as np
751 |
752 | def compute_rse(var, var_hat):
753 | return np.linalg.norm(var - var_hat, 2) / np.linalg.norm(var, 2)
754 |
755 | def prox(z, w, lmbda):
756 | T = z.shape[0]
757 | temp = np.fft.fft(z - w / lmbda)
758 | temp1 = np.abs(temp) - T / lmbda
759 | temp1[temp1 <= 0] = 0
760 | return np.fft.ifft(temp / np.abs(temp) * temp1).real
761 |
762 | def update_z(y_train, pos_train, x, w, lmbda, eta):
763 | z = x + w / lmbda
764 | z[pos_train] = (lmbda / (lmbda + eta) * z[pos_train]
765 | + eta / (lmbda + eta) * y_train)
766 | return z
767 |
768 | def update_w(x, z, w, lmbda):
769 | return w + lmbda * (x - z)
770 |
771 | def circ_nnm(y_true, y, lmbda, eta, maxiter = 50):
772 | pos_train = np.where(y != 0)
773 | y_train = y[pos_train]
774 | pos_test = np.where((y_true != 0) & (y == 0))
775 | y_test = y_true[pos_test]
776 | z = y.copy()
777 | w = y.copy()
778 | del y_true, y
779 | show_iter = 10
780 | for it in range(maxiter):
781 | x = prox(z, w, lmbda)
782 | z = update_z(y_train, pos_train, x, w, lmbda, eta)
783 | w = update_w(x, z, w, lmbda)
784 | if (it + 1) % show_iter == 0:
785 | print(it + 1)
786 | print(compute_rse(y_test, x[pos_test]))
787 | print()
788 | return x
789 | ```
790 |
791 | ```python
792 | import numpy as np
793 | np.random.seed(1)
794 | import matplotlib.pyplot as plt
795 | from skimage import color
796 | from skimage import io
797 |
798 | img = io.imread('data/gaint_panda.bmp')
799 | imgGray = color.rgb2gray(img)
800 | M, N = imgGray.shape
801 | missing_rate = 0.9
802 |
803 | sparse_img = imgGray * np.round(np.random.rand(M, N) + 0.5 - missing_rate)
804 | io.imshow(sparse_img)
805 | plt.axis('off')
806 | plt.show()
807 | ```
808 |
809 | ```python
810 | lmbda = 1e-3 * M * N
811 | eta = 100 * lmbda
812 | maxiter = 100
813 | vec_hat = circ_nnm(imgGray.reshape(M * N, order = 'F'),
814 | sparse_img.reshape(M * N, order = 'F'),
815 | lmbda, eta, maxiter)
816 |
817 | vec_hat[vec_hat < 0] = 0
818 | vec_hat[vec_hat > 1] = 1
819 | io.imshow(vec_hat.reshape([M, N], order = 'F'))
820 | plt.axis('off')
821 | plt.imsave('gaint_panda_gray_recovery_90_circ_nnm.png',
822 | vec_hat.reshape([M, N], order = 'F'), cmap = plt.cm.gray)
823 | plt.show()
824 | ```
825 |
826 | **例.** 使用一维低秩拉普拉斯卷积模型对车速时间序列进行重构。
827 |
828 | ```python
829 | import numpy as np
830 |
831 | def compute_mape(var, var_hat):
832 | return np.sum(np.abs(var - var_hat) / var) / var.shape[0]
833 |
834 | def compute_rmse(var, var_hat):
835 | return np.sqrt(np.sum((var - var_hat) ** 2) / var.shape[0])
836 |
837 | def laplacian(n, tau):
838 | ell = np.zeros(n)
839 | ell[0] = 2 * tau
840 | for k in range(tau):
841 | ell[k + 1] = -1
842 | ell[-k - 1] = -1
843 | return ell
844 |
845 | def prox(z, w, lmbda, denominator):
846 | T = z.shape[0]
847 | temp = np.fft.fft(lmbda * z - w) / denominator
848 | temp1 = np.abs(temp) - T / lmbda
849 | temp1[temp1 <= 0] = 0
850 | return np.fft.ifft(temp / np.abs(temp) * temp1).real
851 |
852 | def update_z(y_train, pos_train, x, w, lmbda, eta):
853 | z = x + w / lmbda
854 | z[pos_train] = (lmbda / (lmbda + eta) * z[pos_train]
855 | + eta / (lmbda + eta) * y_train)
856 | return z
857 |
858 | def update_w(x, z, w, lmbda):
859 | return w + lmbda * (x - z)
860 |
861 | def LCR(y_true, y, lmbda, gamma, tau, maxiter = 50):
862 | eta = 100 * lmbda
863 | T = y.shape
864 | pos_train = np.where(y != 0)
865 | y_train = y[pos_train]
866 | pos_test = np.where((y_true != 0) & (y == 0))
867 | y_test = y_true[pos_test]
868 | z = y.copy()
869 | w = y.copy()
870 | denominator = lmbda + gamma * np.fft.fft(laplacian(T, tau)) ** 2
871 | del y_true, y
872 | show_iter = 10
873 | for it in range(maxiter):
874 | x = prox(z, w, lmbda, denominator)
875 | z = update_z(y_train, pos_train, x, w, lmbda, eta)
876 | w = update_w(x, z, w, lmbda)
877 | if (it + 1) % show_iter == 0:
878 | print(it + 1)
879 | print(compute_mape(y_test, x[pos_test]))
880 | print(compute_rmse(y_test, x[pos_test]))
881 | print()
882 | return x
883 | ```
884 |
885 | ```python
886 | import numpy as np
887 | np.random.seed(1)
888 | import time
889 |
890 | missing_rate = 0.9
891 | print('Missing rate = {}'.format(missing_rate))
892 |
893 | dense_vec = np.load('sample_speed_time_series.npz')['arr_0']
894 | T = dense_vec.shape[0]
895 | sparse_vec = dense_vec * np.round(np.random.rand(T) + 0.5 - missing_rate)
896 |
897 | import time
898 | start = time.time()
899 | lmbda = 5e-3 * T
900 | gamma = 2 * lmbda
901 | tau = 2
902 | maxiter = 100
903 | x = LCR(dense_vec, sparse_vec, lmbda, gamma, tau, maxiter)
904 | end = time.time()
905 | print('Running time: %d seconds.'%(end - start))
906 | ```
907 |
908 | ```python
909 | import matplotlib.pyplot as plt
910 | plt.rcParams.update({'font.size': 13})
911 |
912 | fig = plt.figure(figsize = (7.5, 2.2))
913 | ax = fig.add_subplot(111)
914 | plt.plot(dense_vec[: T], 'dodgerblue', linewidth = 2)
915 | plt.xlabel('Time')
916 | plt.ylabel('Speed (mph)')
917 | plt.xlim([0, T])
918 | plt.ylim([54, 65])
919 | plt.xticks(np.arange(0, T + 1, 24))
920 | plt.yticks(np.arange(54, 66, 2))
921 | plt.grid(linestyle = '-.', linewidth = 0.5)
922 | ax.tick_params(direction = 'in')
923 |
924 | plt.savefig('freeway_traffic_speed_obs.pdf', bbox_inches = "tight")
925 | plt.show()
926 |
927 | import matplotlib.pyplot as plt
928 | plt.rcParams.update({'font.size': 13})
929 |
930 | fig = plt.figure(figsize = (7.5, 2.2))
931 | ax = fig.add_subplot(111)
932 | plt.plot(np.arange(0, T), sparse_vec[: T], 'o',
933 | markeredgecolor = 'darkblue', alpha = missing_rate,
934 | markerfacecolor = 'deepskyblue', markersize = 10)
935 | plt.xlabel('Time')
936 | plt.ylabel('Speed (mph)')
937 | plt.xlim([0, T])
938 | plt.ylim([54, 65])
939 | plt.xticks(np.arange(0, T + 1, 24))
940 | plt.yticks(np.arange(54, 66, 2))
941 | plt.grid(linestyle = '-.', linewidth = 0.5)
942 | ax.tick_params(direction = 'in')
943 |
944 | plt.savefig('freeway_traffic_speed_partial_obs.pdf', bbox_inches = "tight")
945 | plt.show()
946 |
947 | import matplotlib.pyplot as plt
948 | plt.rcParams.update({'font.size': 13})
949 |
950 | fig = plt.figure(figsize = (7.5, 2.2))
951 | ax = fig.add_subplot(111)
952 | plt.plot(np.arange(0, T), sparse_vec[: T], 'o',
953 | markeredgecolor = 'darkblue', alpha = missing_rate,
954 | markerfacecolor = 'deepskyblue', markersize = 10)
955 | plt.plot(x[: T], 'red', linewidth = 4)
956 | plt.xlabel('Time')
957 | plt.ylabel('Speed (mph)')
958 | plt.xlim([0, T])
959 | plt.ylim([54, 65])
960 | plt.xticks(np.arange(0, T + 1, 24))
961 | plt.yticks(np.arange(54, 66, 2))
962 | plt.grid(linestyle = '-.', linewidth = 0.5)
963 | ax.tick_params(direction = 'in')
964 |
965 | plt.savefig('freeway_traffic_speed_reconstructed_lap_conv.pdf',
966 | bbox_inches = "tight")
967 | plt.show()
968 |
969 | import matplotlib.pyplot as plt
970 | plt.rcParams.update({'font.size': 13})
971 |
972 | fig = plt.figure(figsize = (7.5, 2.2))
973 | ax = fig.add_subplot(111)
974 | plt.plot(dense_vec[: T], 'dodgerblue', linewidth = 1.5)
975 | plt.plot(np.arange(0, T), sparse_vec[: T], 'o',
976 | markeredgecolor = 'darkblue', alpha = missing_rate,
977 | markerfacecolor = 'deepskyblue', markersize = 10)
978 | plt.plot(x[: T], 'red', linewidth = 4)
979 | plt.xlabel('Time')
980 | plt.ylabel('Speed (mph)')
981 | plt.xlim([0, T])
982 | plt.ylim([54, 65])
983 | plt.xticks(np.arange(0, T + 1, 24))
984 | plt.yticks(np.arange(54, 66, 2))
985 | plt.grid(linestyle = '-.', linewidth = 0.5)
986 | ax.tick_params(direction = 'in')
987 |
988 | plt.savefig('freeway_traffic_speed_reconstructed_vs_true_lap_conv.pdf',
989 | bbox_inches = "tight")
990 | plt.show()
991 | ```
992 |
993 | **例.** 使用一维低秩拉普拉斯卷积模型对灰度图像进行复原。
994 |
995 | ```python
996 | import numpy as np
997 |
998 | def compute_rse(var, var_hat):
999 | return np.linalg.norm(var - var_hat, 2) / np.linalg.norm(var, 2)
1000 |
1001 | def laplacian(n, tau):
1002 | ell = np.zeros(n)
1003 | ell[0] = 2 * tau
1004 | for k in range(tau):
1005 | ell[k + 1] = -1
1006 | ell[-k - 1] = -1
1007 | return ell
1008 |
1009 | def prox(z, w, lmbda, denominator):
1010 | T = z.shape[0]
1011 | temp = np.fft.fft(lmbda * z - w) / denominator
1012 | temp1 = 1 - T / (lmbda * np.abs(temp))
1013 | temp1[temp1 <= 0] = 0
1014 | return np.fft.ifft(temp * temp1).real
1015 |
1016 | def update_z(y_train, pos_train, x, w, lmbda, eta):
1017 | z = x + w / lmbda
1018 | z[pos_train] = (lmbda / (lmbda + eta) * z[pos_train]
1019 | + eta / (lmbda + eta) * y_train)
1020 | return z
1021 |
1022 | def update_w(x, z, w, lmbda):
1023 | return w + lmbda * (x - z)
1024 |
1025 | def lap_conv_1d(y_true, y, gamma, lmbda, eta, tau, maxiter = 50):
1026 | T = y.shape
1027 | pos_train = np.where(y != 0)
1028 | y_train = y[pos_train]
1029 | pos_test = np.where((y_true != 0) & (y == 0))
1030 | y_test = y_true[pos_test]
1031 | z = y.copy()
1032 | w = y.copy()
1033 | denominator = lmbda + gamma * np.fft.fft(laplacian(T, tau)) ** 2
1034 | del y_true, y
1035 | show_iter = 10
1036 | for it in range(maxiter):
1037 | x = prox(z, w, lmbda, denominator)
1038 | z = update_z(y_train, pos_train, x, w, lmbda, eta)
1039 | w = update_w(x, z, w, lmbda)
1040 | if (it + 1) % show_iter == 0:
1041 | print(it + 1)
1042 | print(compute_rse(y_test, x[pos_test]))
1043 | print()
1044 | return x
1045 | ```
1046 |
1047 | ```python
1048 | import numpy as np
1049 | np.random.seed(1)
1050 | import matplotlib.pyplot as plt
1051 | from skimage import color
1052 | from skimage import io
1053 |
1054 | img = io.imread('data/gaint_panda.bmp')
1055 | imgGray = color.rgb2gray(img)
1056 | M, N = imgGray.shape
1057 | missing_rate = 0.9
1058 |
1059 | sparse_img = imgGray * np.round(np.random.rand(M, N) + 0.5 - missing_rate)
1060 | io.imshow(sparse_img)
1061 | plt.axis('off')
1062 | plt.show()
1063 | ```
1064 |
1065 | ```python
1066 | lmbda = 5e-3 * M * N
1067 | gamma = 1 * lmbda
1068 | eta = 100 * lmbda
1069 | tau = 1
1070 | maxiter = 100
1071 | vec_hat = lap_conv_1d(imgGray.reshape(M * N, order = 'F'),
1072 | sparse_img.reshape(M * N, order = 'F'),
1073 | gamma, lmbda, eta, tau, maxiter)
1074 |
1075 | vec_hat[vec_hat < 0] = 0
1076 | vec_hat[vec_hat > 1] = 1
1077 | io.imshow(vec_hat.reshape([M, N], order = 'F'))
1078 | plt.axis('off')
1079 | plt.imsave('gaint_panda_gray_recovery_90_lap_conv_1d.png',
1080 | vec_hat.reshape([M, N], order = 'F'), cmap = plt.cm.gray)
1081 | plt.show()
1082 | ```
1083 |
1084 | **例.** 使用二维低秩拉普拉斯卷积模型对灰度图像进行复原。
1085 |
1086 | ```python
1087 | import numpy as np
1088 |
1089 | def compute_rse(var, var_hat):
1090 | return np.linalg.norm(var - var_hat, 2) / np.linalg.norm(var, 2)
1091 |
1092 | def laplacian(T, tau):
1093 | ell = np.zeros(T)
1094 | ell[0] = 2 * tau
1095 | for k in range(tau):
1096 | ell[k + 1] = -1
1097 | ell[-k - 1] = -1
1098 | return ell
1099 |
1100 | def prox_2d(z, w, lmbda, denominator):
1101 | M, N = z.shape
1102 | temp = np.fft.fft2(lmbda * z - w) / denominator
1103 | temp1 = 1 - M * N / (lmbda * np.abs(temp))
1104 | temp1[temp1 <= 0] = 0
1105 | return np.fft.ifft2(temp * temp1).real
1106 |
1107 | def update_z(y_train, pos_train, x, w, lmbda, eta):
1108 | z = x + w / lmbda
1109 | z[pos_train] = (lmbda / (lmbda + eta) * z[pos_train]
1110 | + eta / (lmbda + eta) * y_train)
1111 | return z
1112 |
1113 | def update_w(x, z, w, lmbda):
1114 | return w + lmbda * (x - z)
1115 |
1116 | def laplacian_conv_2d(y_true, y, lmbda, gamma, eta, tau, maxiter = 50):
1117 | M, N = y.shape
1118 | pos_train = np.where(y != 0)
1119 | y_train = y[pos_train]
1120 | pos_test = np.where((y_true != 0) & (y == 0))
1121 | y_test = y_true[pos_test]
1122 | z = y.copy()
1123 | w = y.copy()
1124 | ell_1 = laplacian(M, tau)
1125 | ell_2 = laplacian(N, tau)
1126 | denominator = lmbda + gamma * np.fft.fft2(np.outer(ell_1, ell_2)) ** 2
1127 | del y_true, y
1128 | show_iter = 10
1129 | for it in range(maxiter):
1130 | x = prox_2d(z, w, lmbda, denominator)
1131 | z = update_z(y_train, pos_train, x, w, lmbda, eta)
1132 | w = update_w(x, z, w, lmbda)
1133 | if (it + 1) % show_iter == 0:
1134 | print(it + 1)
1135 | print(compute_rse(y_test, x[pos_test]))
1136 | print()
1137 | return x
1138 | ```
1139 |
1140 | ```python
1141 | import numpy as np
1142 | np.random.seed(1)
1143 | import matplotlib.pyplot as plt
1144 | from skimage import color
1145 | from skimage import io
1146 |
1147 | img = io.imread('data/gaint_panda.bmp')
1148 | imgGray = color.rgb2gray(img)
1149 | M, N = imgGray.shape
1150 | missing_rate = 0.9
1151 |
1152 | sparse_img = imgGray * np.round(np.random.rand(M, N) + 0.5 - missing_rate)
1153 | io.imshow(sparse_img)
1154 | plt.axis('off')
1155 | plt.imsave('gaint_panda_gray_missing_rate_90.png',
1156 | sparse_img, cmap = plt.cm.gray)
1157 | plt.show()
1158 | ```
1159 |
1160 | ```python
1161 | lmbda = 5e-3 * M * N
1162 | gamma = 1 * lmbda
1163 | eta = 100 * lmbda
1164 | tau = 2
1165 | maxiter = 100
1166 | mat_hat = laplacian_conv_2d(imgGray, sparse_img, lmbda, gamma, eta, tau, maxiter)
1167 |
1168 | mat_hat[mat_hat < 0] = 0
1169 | mat_hat[mat_hat > 1] = 1
1170 | io.imshow(mat_hat)
1171 | plt.axis('off')
1172 | plt.imsave('gaint_panda_gray_recovery_90_gamm1_tau{}.png'.format(tau),
1173 | mat_hat, cmap = plt.cm.gray)
1174 | plt.show()
1175 | ```
1176 |
1177 | **例.** 计算延迟嵌入矩阵的奇异值分解、还原向量**x**。
1178 |
1179 | ```python
1180 | import numpy as np
1181 |
1182 | def delay_embedding(vec, kernel_size):
1183 | n = vec.shape[0]
1184 | mat = np.zeros((n, kernel_size))
1185 | mat[:, 0] = vec
1186 | for k in range(1, kernel_size):
1187 | mat[:, k] = np.append(vec[k :], vec[: k], axis = 0)
1188 | return mat
1189 |
1190 | vec = np.array([0, 1, 2, 3, 4])
1191 | T = vec.shape[0]
1192 | kernel_size = 3
1193 | mat = delay_embedding(vec, kernel_size)
1194 |
1195 | u, s, v = np.linalg.svd(mat, full_matrices = False)
1196 | x_hat = np.zeros(T)
1197 | for r in range(kernel_size):
1198 | fu = np.fft.fft(u[:, r])
1199 | fv = np.fft.fft(np.append(v[r, :], np.zeros(T - kernel_size), axis = 0))
1200 | x_hat += s[r] * np.fft.ifft(fu * fv).real
1201 | x_hat = x_hat / kernel_size
1202 | print(x_hat)
1203 | ```
1204 |
1205 | 或者
1206 | ```python
1207 | import numpy as np
1208 |
1209 | def CircularConv(x, kernel):
1210 | m = x.shape[0]
1211 | n = kernel.shape[0]
1212 | vec = np.zeros(m)
1213 | for t in range(m):
1214 | temp = 0
1215 | for k in range(n):
1216 | temp += x[t - k] * kernel[k]
1217 | vec[t] = temp
1218 | return vec
1219 |
1220 | def DelayEmbedding(vec, kernel_size):
1221 | n = vec.shape[0]
1222 | mat = np.zeros((n, kernel_size))
1223 | mat[:, 0] = vec
1224 | for k in range(1, kernel_size):
1225 | mat[:, k] = np.append(vec[k :], vec[: k], axis = 0)
1226 | return mat
1227 |
1228 | x = np.array([0, 1, 2, 3, 4])
1229 | kernel_size = 3
1230 | mat = DelayEmbedding(x, kernel_size)
1231 | u, s, v = np.linalg.svd(mat, full_matrices = False)
1232 | temp1 = s[0] * CircularConv(u[:, 0], v[0, :]) / 3
1233 | print(temp1)
1234 | print()
1235 | temp2 = s[1] * CircularConv(u[:, 1], v[1, :]) / 3
1236 | print(temp2)
1237 | print()
1238 | temp3 = s[2] * CircularConv(u[:, 2], v[2, :]) / 3
1239 | print(temp3)
1240 | print()
1241 | print(temp1 + temp2 + temp3)
1242 | print()
1243 | ```
1244 |
1245 | 核范数最小化问题
1246 | ▴ 回到顶部
1247 |
1248 |
1249 | ```python
1250 | import numpy as np
1251 |
1252 | def circ_mat(vec):
1253 | n = vec.shape[0]
1254 | mat = np.zeros((n, n))
1255 | mat[:, 0] = vec
1256 | for k in range(1, n):
1257 | mat[:, k] = np.append(vec[n - k :], vec[: n - k], axis = 0)
1258 | return mat
1259 |
1260 | def inv_circ_mat(mat):
1261 | n = mat.shape[0]
1262 | vec = mat[:, 0]
1263 | for k in range(1, n):
1264 | vec += np.append(mat[k :, k], mat[: k, k], axis = 0)
1265 | return vec / n
1266 |
1267 | z = np.array([0, 1, 2, 3, 4])
1268 | T = z.shape[0]
1269 | lmbda = 2
1270 | mat = circ_mat(z)
1271 | u, s, v = np.linalg.svd(mat, full_matrices = False)
1272 | s = s - T / lmbda
1273 | s[s < 0] = 0
1274 | temp = u @ np.diag(s) @ v
1275 | print('The result of singular value thresholding:')
1276 | print(temp)
1277 | print()
1278 | print('The inverse operator of the matrix:')
1279 | x = inv_circ_mat(temp)
1280 | print(x)
1281 | print()
1282 | print('The objective function is: ')
1283 | print(np.sum(np.abs(np.fft.fft(x)))
1284 | + 0.5 * lmbda * np.linalg.norm(x - z, 2) ** 2)
1285 | print()
1286 | ```
1287 |
1288 | ```python
1289 | def conv_mat(vec, kernel_size):
1290 | n = vec.shape[0]
1291 | mat = np.zeros((n, kernel_size))
1292 | mat[:, 0] = vec
1293 | for k in range(1, kernel_size):
1294 | mat[:, k] = np.append(vec[n - k :], vec[: n - k], axis = 0)
1295 | return mat
1296 |
1297 | def inv_conv_mat(mat):
1298 | tau = mat.shape[1]
1299 | vec = mat[:, 0]
1300 | for k in range(1, tau):
1301 | vec += np.append(mat[k :, k], mat[: k, k], axis = 0)
1302 | return vec / tau
1303 | ```
1304 |
1305 |
--------------------------------------------------------------------------------
/data/gaint_panda.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xinychen/tensor-book/66b126528b0f33cd75d3c9952b4692a80d8350af/data/gaint_panda.bmp
--------------------------------------------------------------------------------
/data/gaint_panda.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xinychen/tensor-book/66b126528b0f33cd75d3c9952b4692a80d8350af/data/gaint_panda.jpg
--------------------------------------------------------------------------------
/data/gaint_panda_gray.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xinychen/tensor-book/66b126528b0f33cd75d3c9952b4692a80d8350af/data/gaint_panda_gray.bmp
--------------------------------------------------------------------------------
/data/gaint_panda_rgb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xinychen/tensor-book/66b126528b0f33cd75d3c9952b4692a80d8350af/data/gaint_panda_rgb.jpg
--------------------------------------------------------------------------------
/data/sample_speed_time_series.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xinychen/tensor-book/66b126528b0f33cd75d3c9952b4692a80d8350af/data/sample_speed_time_series.npz
--------------------------------------------------------------------------------