1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-fastai-num-linalg-v2-zh

Клонировать/Скачать
4.md 14 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 30.11.2024 04:49 05baa78

Сейчас мы объединим наши слова с первыми 10 000 слов из всего нашего словаря (некоторые слова уже есть в нём), и создадим матрицу встраивания.

embeddings = np.concatenate((vecs[my_word_indices], vecs[:10000,:]), axis=0); embeddings.shape

# (10030, 100)

Визуализация слов в 3D

Слова имеют 100 измерений, и нам нужен способ визуализировать их в трёхмерном пространстве.

Мы будем использовать метод главных компонент (PCA), который широко используется и имеет множество применений, включая визуализацию многомерных данных в более низком измерении!

PCA

from collections import defaultdict
from sklearn import decomposition

components = get_components(embeddings, categories, my_word_indices)
plotly_3d(components.T[:len(my_words),:], categories, "pca.html")

# (30, 10030)

IFrame('pca.html', width=600, height=400)

Случайное проецирование

Принцип Джонсона-Линденштрауса: в высоком пространстве можно вложить небольшое количество точек в пространство с меньшей размерностью таким образом, что расстояния между точками сохраняются (это доказывается с помощью случайной проекции).

Полезно иметь возможность уменьшить размерность данных, сохраняя при этом расстояния. Принцип Джонсона-Линденштрауса является классическим результатом такого типа.

embeddings.shape

# (10030, 100)

rand_proj = embeddings @ np.random.normal(size=(embeddings.shape[1], 40)); rand_proj.shape

# (10030, 40)

# pca = decomposition.PCA(n_components=3).fit(rand_proj.T)
# components = pca.components_
components = get_components(rand_proj, categories, my_word_indices)
plotly_3d(components.T[:len(my_words),:], categories, "pca-rand-proj.html")

# (30, 10030)

IFrame('pca-rand-proj.html', width=600, height=400)

Часть вторая: случайный SVD для удаления фона

Наша цель сегодня:

Загрузка и форматирование данных

Давайте используем данные из задачи BMC 2012 по удалению фона, видео 003.

Импортируем необходимые библиотеки:

import imageio
imageio.plugins.ffmpeg.download()

import moviepy.editor as mpe
import numpy as np
import scipy

%matplotlib inline
import matplotlib.pyplot as plt

scale = 0.50   # adjust the ratio to change the resolution of the image
dims = (int(240 * scale), int(320 * scale))
fps = 60      # frames per second

M = np.load("movie/med_res_surveillance_matrix_60fps.npy")

print(dims, M.shape)

# (120, 160) (19200, 6000)

plt.imshow(np.reshape(M[:,140], dims), cmap='gray');

plt.figure(figsize=(12, 12))
plt.imshow(M, cmap='gray')

# <matplotlib.image.AxesImage at 0x7f601f315fd0>

В будущем вы можете загрузить сохранённое содержимое:

U = np.load("U.npy")
s = np.load("s.npy")
V = np.load("V.npy")

Что такое U, S и V?

U.shape, s.shape, V.shape

# ((19200, 6000), (6000,), (6000, 6000))

Проверяем, являются ли они разложением M.

reconstructed_matrix = U @ np.diag(s) @ V

np.allclose(M, reconstructed_matrix)

# True

Да.

Удаление фона

low_rank = np.expand_dims(U[:,0], 1) * s[0] * np.expand_dims(V[0,:], 0)

plt.figure(figsize=(12, 12))
plt.imshow(low_rank, cmap='gray')

# <matplotlib.image.AxesImage at 0x7f1cc3e2c9e8>

plt.imshow(np.reshape(low_rank[:,0], dims), cmap='gray');

Как получить человека на переднем плане?

plt.imshow(np.reshape(M[:,0] - low_rank[:,0], dims), cmap='gray');

Скорость SVD для матриц разных размеров

s — диагональная матрица с диагональными элементами.

np.set_printoptions(suppress=True, precision=4)

import timeit
import pandas as pd

m_array = np.array([100, int(1e3), int(1e4)])
n_array = np.array([100, int(1e3), int(1e4)])

index = pd.MultiIndex.from_product([m_array, n_array], names=['# rows', '# cols'])

pd.options.display.float_format = '{:,.3f}'.format
df = pd.DataFrame(index=m_array, columns=n_array)

# %%prun
for m in m_array:
    for n in n_array:      
        A = np.random.uniform(-40,40,[m,n])  
        t = timeit.timeit('np.linalg.svd(A, full_matrices=False)', number=3, globals=globals())
        df.set_value(m, n, t)

df/3
100 1000 10000
100 0,006 0,009 0,043
1000 0,004 0,259 0,992
10000 0,019 0,984 218,726

Отлично! Но...

Недостаток: это действительно медленно (мы отбросили много вычислений).

%time u, s, v = np.linalg.svd(M, full_matrices=False)

'''
CPU times: user 5min 38s, sys: 1.53 s, total: 5min 40s
Wall time: 57.1 s
'''

M.shape

# (19200, 6000)

Самый простой вариант рандомизированного SVD

Идея: давайте попробуем использовать меньшую матрицу!

Мы ещё не нашли лучшего универсального метода SVD, поэтому мы просто используем метод, который мы использовали для небольших матриц, матрицы примерно того же размера, что и исходная.

def simple_randomized_svd(M, k=10):
    m, n = M.shape
    transpose = False
    if m < n:
        transpose = True
        M = M.T
        
    rand_matrix = np.random.normal(size=(M.shape[1], k))  # short side by k
    Q, _ = np.linalg.qr(M @ rand_matrix, mode='reduced')  # long side by k
    smaller_matrix = Q.T @ M                              # k by short side
    U_hat, s, V = np.linalg.svd(smaller_matrix, full_matrices=False)
U = Q @ U_hat

if transpose:
    return V.T, s.T, U.T
else:
    return U, s, V

%time u, s, v = simple_randomized_svd(M, 10)

**CPU times: user 3.06 s, sys: 268 ms, total: 3.33 s
Wall time: 789 ms**

U_rand, s_rand, V_rand = simple_randomized_svd(M, 10)

low_rank = np.expand_dims(U_rand[:,0], 1) * s_rand[0] * np.expand_dims(V_rand[0,:], 0)

plt.imshow(np.reshape(low_rank[:,0], dims), cmap='gray');

Мы как получаем людей?

Как этот метод работает?
```py
rand_matrix = np.random.normal(size=(M.shape[1], 10))

rand_matrix.shape
# (6000, 10)

plt.imshow(np.reshape(rand_matrix[:4900,0], (70,70)), cmap='gray');
temp = M @ rand_matrix; temp.shape
# (19200, 10)

plt.imshow(np.reshape(temp[:,0], dims), cmap='gray');
plt.imshow(np.reshape(temp[:,1], dims), cmap='gray');
Q, _ = np.linalg.qr(M @ rand_matrix, mode='reduced'); Q.shape
# (19200, 10)

np.dot(Q[:,0], Q[:,1])
# -3.8163916471489756e-17

plt.imshow(np.reshape(Q[:,0], dims), cmap='gray');
plt.imshow(np.reshape(Q[:,1], dims), cmap='gray');
smaller_matrix = Q.T @ M; smaller_matrix.shape
# (10, 6000)

U_hat, s, V = np.linalg.svd(smaller_matrix, full_matrices=False)

U = Q @ U_hat

plt.imshow(np.reshape(U[:,0], dims), cmap='gray');

Реконструируем маленькую матрицу:

reconstructed_small_M = U @ np.diag(s) @ V

И получаем человека.

plt.imshow(np.reshape(M[:,0] - reconstructed_small_M[:,0], dims), cmap='gray');

Сравнение времени:

from sklearn import decomposition
import fbpca

Полный SVD:

%time u, s, v = np.linalg.svd(M, full_matrices=False)
'''
CPU times: user 5min 38s, sys: 1.53 s, total: 5min 40s
Wall time: 57.1 s
'''

Наш (переупрощённый) randomized_svd:

%time u, s, v = simple_randomized_svd(M, 10)
'''
CPU times: user 2.37 s, sys: 160 ms, total: 2.53 s
Wall time: 641 ms
'''

Sklearn:

%time u, s, v = decomposition.randomized_svd(M, 10)
'''
CPU times: user 19.2 s, sys: 1.44 s, total: 20.7 s
Wall time: 3.67 s
'''

Я бы выбрал fbpca, потому что он быстрее sklearn и более надёжен и точен, чем наша упрощённая реализация.

Вот результаты Facebook Research:

Время и точность в зависимости от k:

import timeit
import pandas as pd

U_rand, s_rand, V_rand = fbpca.pca(M, 700, raw=True)
reconstructed = U_rand @ np.diag(s_rand) @ V_rand

np.linalg.norm(M - reconstructed)
# 1.1065914828881536e-07

plt.imshow(np.reshape(reconstructed[:,140], dims), cmap='gray');
pd.options.display.float_format = '{:,.2f}'.format
k_values = np.arange(100,1000,100)
df_rand = pd.DataFrame(index=["time", "error"], columns=k_values)

# df_rand = pd.read_pickle("svd_df")

for k in k_values:
    U_rand, s_rand, V_rand = fbpca.pca(M, k, raw=True)
    reconstructed = U_rand @ np.diag(s_rand) @ V_rand
    df_rand.set_value("error", k, np.linalg.norm(M - reconstructed))
    t = timeit.timeit('fbpca.pca(M, k)', number=3, globals=globals())
    df_rand.set_value("time", k, t/3)

df_rand.to_pickle("df_rand")

df_rand
100 200 300 400 500 600 700 800 900 1000
time 2.07 2.57 3.45 6.44 7.99 9.02 10.24 11.70 13.30 10.87
error 58,997.27 37,539.54 26,569.89 18,769.37 12,559.34 6,936.17 0.00 0.00 0.00 0.00
df = pd.DataFrame(index=["error"], columns=k_values)

for k in k_values:
    reconstructed = U[:,:k] @ np.diag(s[:k]) @ V[:k,:]
    df.set_value("error", k, np.linalg.norm(M - reconstructed))

df.to_pickle("df")

fig, ax1 = plt.subplots()
ax1.plot(df.columns, df_rand.loc["time"].values, 'b-', label="randomized SVD time")
ax1.plot(df.columns, np.tile([57], 9), 'g-', label="SVD time")
ax1.set_xlabel('k: # of singular values')
# Make the y-axis label, ticks

В этом тексте описывается процесс работы с матрицами и их разложениями с использованием различных методов и библиотек. Текст содержит фрагменты кода на языке Python, а также результаты вычислений и сравнения времени выполнения различных алгоритмов. **Метод randomized_range_finder находит ортогональную матрицу, диапазон которой приблизительно равен диапазону матрицы A (шаг 1 нашего алгоритма).**

Для этого мы используем LU и QR разложения, которые рассмотрим позже.

Я использовал исходный код sklearn.extmath.randomized_svd в качестве руководства.
```py
# Вычисление ортогональной матрицы, диапазон которой примерно равен диапазону A
# power_iteration_normalizer может быть safe_sparse_dot (быстрый, но нестабильный), LU (средний) или QR (медленный, но наиболее точный)
def randomized_range_finder(A, size, n_iter=5):
    Q = np.random.normal(size=(A.shape[1], size))

    for i in range(n_iter):
        Q, _ = linalg.lu(A @ Q, permute_l=True)
        Q, _ = linalg.lu(A.T @ Q, permute_l=True)

    Q, _ = linalg.qr(A @ Q, mode='economic')
    return Q

Здесь представлен наш метод случайного SVD.

def randomized_svd(M, n_components, n_oversamples=10, n_iter=4):

    n_random = n_components + n_oversamples

    Q = randomized_range_finder(M, n_random, n_iter)
    print(Q.shape)
    # проецируем M на (k + p)-мерное пространство с использованием базисных векторов
    B = Q.T @ M
    print(B.shape)
    # вычисляем SVD на тонкой матрице: ширина (k + p)
    Uhat, s, V = linalg.svd(B, full_matrices=False)
    del B
    U = Q @ Uhat
    print(U.shape)

    return U[:, :n_components], s[:n_components], V[:n_components, :]

u, s, v = randomized_svd(vectors, 5)

Тест:

vectors.shape

# (2034, 26576)

Q = np.random.normal(size=(vectors.shape[1], 10)); Q.shape

# (26576, 10)

Q2, _ = linalg.qr(vectors @ Q, mode='economic'); Q2.shape

# (2034, 10)

Q2.shape

# (2034, 10)

Конец теста:

%time u, s, v = randomized_svd(vectors, 5)

'''
CPU times: user 136 ms, sys: 0 ns, total: 136 ms
Wall time: 137 ms
'''

u.shape, s.shape, v.shape

# ((2034, 5), (5,), (5, 26576))

show_topics(v)

'''
['jpeg image edu file graphics images gif data',
 'edu graphics data space pub mail 128 3d',
 'space jesus launch god people satellite matthew atheists',
 'space launch satellite commercial nasa satellites market year',
 'image data processing analysis software available tools display']
'''

Чтобы вычислить ошибку разложения при изменении количества тем, необходимо написать цикл и построить график результатов.

plt.plot(range(0,n*step,step), error)

# [<matplotlib.lines.Line2D at 0x7fe3f8a1b438>]

%time u, s, v = decomposition.randomized_svd(vectors, 5)

'''
CPU times: user 144 ms, sys: 8 ms, total: 152 ms
Wall time: 154 ms
'''

%time u, s, v = decomposition.randomized_svd(vectors.todense(), 5)

'''
CPU times: user 2.38 s, sys: 592 ms, total: 2.97 s
Wall time: 2.96 s
'''

Дополнительные ресурсы:

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-fastai-num-linalg-v2-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-fastai-num-linalg-v2-zh.git
oschina-mirror
wizardforcel-fastai-num-linalg-v2-zh
wizardforcel-fastai-num-linalg-v2-zh
master