Codificação Esparsa com Dicionário Pré-Computado

Beginner

This tutorial is from open-source community. Access the source code

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.