Introdução
Neste laboratório, aprenderemos a transformar um sinal como uma combinação esparsa de ondas de Ricker usando métodos de codificação esparsa. A onda de Ricker (também conhecida como chapéu mexicano ou a segunda derivada de uma gaussiana) não é um núcleo particularmente bom para representar sinais de partes constantes como este. Portanto, pode-se ver o quanto adicionar diferentes larguras de átomos importa e, portanto, motiva a aprendizagem do dicionário para melhor se ajustar ao seu tipo de sinais.
Vamos comparar visualmente diferentes métodos de codificação esparsa usando o estimador SparseCoder. O dicionário mais rico à direita não é maior em tamanho, uma subamostragem mais pesada é realizada para permanecer na mesma ordem de grandeza.
Dicas de Máquina Virtual
Após o início da VM, clique no canto superior esquerdo para mudar para a aba Notebook para acessar o Jupyter Notebook para praticar.
Às vezes, pode ser necessário esperar alguns segundos para que o Jupyter Notebook termine de carregar. A validação de operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se você enfrentar problemas durante o aprendizado, sinta-se à vontade para perguntar ao Labby. Forneça feedback após a sessão e resolveremos prontamente o problema para você.
Importar Bibliotecas
Começaremos importando as bibliotecas necessárias.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import SparseCoder
Definir Funções de Onda de Ricker
Vamos definir funções para gerar uma onda de Ricker e um dicionário de ondas de Ricker.
def ricker_function(resolution, center, width):
"""Onda de Ricker (chapéu mexicano) subamostrada discreta"""
x = np.linspace(0, resolution - 1, resolution)
x = (
(2 / (np.sqrt(3 * width) * np.pi**0.25))
* (1 - (x - center) ** 2 / width**2)
* np.exp(-((x - center) ** 2) / (2 * width**2))
)
return x
def ricker_matrix(width, resolution, n_components):
"""Dicionário de ondas de Ricker (chapéu mexicano)"""
centers = np.linspace(0, resolution - 1, n_components)
D = np.empty((n_components, resolution))
for i, center in enumerate(centers):
D[i] = ricker_function(resolution, center, width)
D /= np.sqrt(np.sum(D**2, axis=1))[:, np.newaxis]
return D
Gerar um Sinal
Vamos gerar um sinal e visualizá-lo usando Matplotlib.
resolution = 1024
subsampling = 3 ## fator de subamostragem
width = 100
n_components = resolution // subsampling
## Gerar um sinal
y = np.linspace(0, resolution - 1, resolution)
primeiro_quarto = y < resolution / 4
y[primeiro_quarto] = 3.0
y[np.logical_not(primeiro_quarto)] = -1.0
## Visualizar o sinal
plt.figure()
plt.plot(y)
plt.title("Sinal Original")
plt.show()
Calcular um Dicionário de Ondas
Vamos calcular um dicionário de ondas e visualizá-lo usando Matplotlib.
## Calcular um dicionário de ondas
D_fixed = ricker_matrix(width=width, resolution=resolution, n_components=n_components)
D_multi = np.r_[
tuple(
ricker_matrix(width=w, resolution=resolution, n_components=n_components // 5)
for w in (10, 50, 100, 500, 1000)
)
]
## Visualizar o dicionário de ondas
plt.figure(figsize=(10, 5))
for i, D in enumerate((D_fixed, D_multi)):
plt.subplot(1, 2, i + 1)
plt.imshow(D, cmap=plt.cm.gray, interpolation="nearest")
plt.title("Dicionário de Ondas (%s)" % ("largura fixa" if i == 0 else "larguras múltiplas"))
plt.axis("off")
plt.show()
Codificação Esparsa
Vamos realizar codificação esparsa no sinal usando diferentes métodos e visualizar os resultados.
## Lista os diferentes métodos de codificação esparsa no seguinte formato:
## (título, algoritmo_transformação, alpha_transformação,
## n_coefs_não_zero_transformação, cor)
estimators = [
("OMP", "omp", None, 15, "navy"),
("Lasso", "lasso_lars", 2, None, "turquoise"),
]
lw = 2
plt.figure(figsize=(13, 6))
for subplot, (D, title) in enumerate(
zip((D_fixed, D_multi), ("largura fixa", "larguras múltiplas"))
):
plt.subplot(1, 2, subplot + 1)
plt.title("Codificação esparsa contra dicionário %s" % title)
plt.plot(y, lw=lw, linestyle="--", label="Sinal original")
## Faz uma aproximação wavelet
for title, algo, alpha, n_nonzero, color in estimators:
coder = SparseCoder(
dictionary=D,
transform_n_nonzero_coefs=n_nonzero,
transform_alpha=alpha,
transform_algorithm=algo,
)
x = coder.transform(y.reshape(1, -1))
density = len(np.flatnonzero(x))
x = np.ravel(np.dot(x, D))
squared_error = np.sum((y - x) ** 2)
plt.plot(
x,
color=color,
lw=lw,
label="%s: %s coefs não nulos,\n%.2f erro" % (title, density, squared_error),
)
## Desvios de limiarização suave
coder = SparseCoder(
dictionary=D, transform_algorithm="threshold", transform_alpha=20
)
x = coder.transform(y.reshape(1, -1))
_, idx = np.where(x != 0)
x[0, idx], _, _, _ = np.linalg.lstsq(D[idx, :].T, y, rcond=None)
x = np.ravel(np.dot(x, D))
squared_error = np.sum((y - x) ** 2)
plt.plot(
x,
color="darkorange",
lw=lw,
label="Limiarização com desvio:\n%d coefs não nulos, %.2f erro"
% (len(idx), squared_error),
)
plt.axis("tight")
plt.legend(shadow=False, loc="best")
plt.subplots_adjust(0.04, 0.07, 0.97, 0.90, 0.09, 0.2)
plt.show()
Resumo
Neste laboratório, aprendemos como transformar um sinal como uma combinação esparsa de ondas de Ricker usando métodos de codificação esparsa e o estimador SparseCoder. Também comparamos diferentes métodos de codificação esparsa e visualizamos os resultados.