Svelare la complessità un nuovo approccio all’apprendimento manifold utilizzando l’iniezione di rumore

Rivelare la complessità un nuovo approccio all'apprendimento manifold mediante l'iniezione di rumore

Nel mondo della scienza dei dati, i dati ad alta dimensionalità presentano sia una sfida che un’opportunità. Mentre forniscono un tesoro di relazioni e schemi che possono essere modellati e trasformati, senza una pulizia e una selezione accurate possono diventare schiaccianti per analizzare e trarre conclusioni da: “La maledizione della dimensione”. Sebbene istintivamente si possa propendere per l’Analisi delle Componenti Principali per incorporare i dati in uno spazio di dimensioni più piccole, potresti rendere ancora più difficile il problema dei tuoi dati e una tecnica di incorporamento non lineare potrebbe essere l’opzione più appropriata. Tuttavia, occorre fare attenzione nella scelta della giusta tecnica non lineare, poiché una scelta sbagliata potrebbe portare a incorporamenti eccessivamente adattati o semplicemente non adatti all’uso. In questo articolo, avrò l’opportunità di discutere un approccio innovativo alla comprensione del manifold all’interno dei dati ad alta dimensionalità, in modo che noi, come scienziati dei dati, possiamo prendere decisioni quantitative informate sulla struttura sottostante dei nostri dati complessi.

Inizierò spiegando cos’è il manifold learning e fornirò un riassunto di alto livello ma informativo di quattro tecniche di incorporamento lineari e non lineari molto popolari. Da ciò otterremo una comprensione approfondita delle assunzioni fatte in ciascun caso e delle implicazioni che queste hanno sugli incorporamenti efficaci. Coprirò anche alcuni esempi in Python su come applicare il mio approccio analitico di iniezione di rumore per valutare i manifold e il tipo di inferenze che possono essere fatte. Alla fine di questo articolo, avrai una comprensione approfondita delle diverse tecniche di manifold learning e dei passaggi che puoi compiere per comprendere veramente la struttura sottostante dei tuoi dati.

Apprendimento del manifold

Prima di addentrarci in queste tecniche di manifold learning, è importante capire esattamente cos’è un manifold? Nel nostro contesto, il manifold è una rappresentazione approssimativa della struttura del nostro spazio ad alta dimensionalità, che potrebbe avere relazioni locali e/o globali con altri punti dati vicini. L’avvertenza è che inizialmente non conosciamo la vera struttura all’interno del nostro spazio N-dimensionale e spesso siamo costretti a fare assunzioni implicite sulla relazione tra i punti dati durante l’incorporamento dei nostri dati. A differenza dell’apprendimento del manifold in matematica (geometria riemanniana), dove è possibile trovare un mapping esplicito da uno spazio all’altro.

Il successo di un modello di apprendimento automatico, in termini di prestazioni e intuizioni basate sui dati, è fondamentalmente soggetto ai dati che gli passiamo. Mentre passare più informazioni può consentire a questi algoritmi di trovare relazioni e schemi più complessi, comporta anche problemi che vengono generalmente definiti sotto la frase della maledizione della dimensionalità:

  • Modelli sovradattati: All’aumentare della dimensionalità dei dati, i successivi modelli di apprendimento automatico possono fallire nel generalizzare la vera relazione all’interno dei dati, sovradattandosi al rumore e agli outlier.
  • Dilatazione delle relazioni tra punti: In spazi di caratteristiche complesse e ampie, non è raro che alcune aree diventino così sparse da essere difficili da modellare o così concentrate da oscurare informazioni chiave.
  • Aumento della complessità computazionale: La maggior parte degli algoritmi di apprendimento automatico non scala bene all’aumentare del numero di caratteristiche, portando a un aumento del tempo di calcolo o della memoria richiesta durante l’addestramento del modello.

Per superare questo problema, dobbiamo ridurre il numero di caratteristiche che consideriamo o mappare i dati in uno spazio di dimensioni inferiori preservando il maggior numero possibile di informazioni chiave. Nella sezione seguente riassumeremo e esploreremo diverse tecniche (lineari e non lineari).

Analisi delle Componenti Principali

L’Analisi delle Componenti Principali (PCA) è probabilmente il metodo più famoso per incorporare o ridurre la dimensionalità di un dataset, probabilmente basato sull’explicability inferita dal suo approccio statistico. Ci sono molti altri articoli disponibili online che approfondiscono questo algoritmo, ma ai fini di questo articolo ho indicato di seguito i passaggi principali.

Il punto chiave è che PCA cerca di preservare le relazioni tra tutti i punti dati assumendo un manifold lineare e mappando i dati su N componenti principali ortogonali che sono una combinazione lineare delle caratteristiche originali. Lo fa prima standardizzando i dati, centrando intorno alla media e ridimensionando di conseguenza in modo che la varianza sia consistente su tutte le variabili:

dove Xⱼ è lo spazio delle caratteristiche originali X per tutte le caratteristiche j, μ e σ sono la media e la deviazione standard di Xⱼ rispettivamente. L’algoritmo calcola quindi la matrice di covarianza, S, dei dati standardizzati

esprimendo come ogni variabile correla con ogni altra variabile. PCA quindi esegue la decomposizione degli autovalori della matrice di covarianza per determinare gli autovalori, λᵢ, e gli autovettori, vᵢ

Una matrice, W, è definita da questi autovettori, ordinati per autovalori decrescenti. La proiezione finale dei dati trasformati, Y, è semplicemente il prodotto di Z e W.

In sintesi, PCA fornisce un modo per scoprire la struttura interna dei dati in modo che preservi al meglio e spieghi la varianza (cioè massimizzare le informazioni attraverso il minor numero di dimensioni). Ogni autovalore è proporzionale alla porzione di varianza e quindi la nostra matrice W impone che il primo componente principale proiettato contenga la maggior varianza e ogni componente ortogonale successiva una frazione inferiore.

Local Linear Embedding

Prima di passare ad approcci non lineari più avanzati, iniziamo con ciò che probabilmente è il più semplice da capire, Local Linear Embedding (LLE). In sostanza, LLE assume che un determinato punto dati e i suoi vicini possano essere approssimativamente rappresentati e mappati su un piano tangente lineare sulla superficie in modo che siano combinazioni lineari l’uno dell’altro. I pesi per mappare i raggruppamenti di vicinato sul piano sono regolati per minimizzare l’errore dalla trasformazione (vedi sotto) e questo processo viene ripetuto per ogni punto dati. Pertanto, mentre c’è linearità locale in un vicinato di punti dati, la non linearità verrà catturata globalmente.

Come data scientist è necessario definire il numero di vicini più prossimi, k, che richiede una sintonizzazione accurata. Con questo valore a disposizione, il primo problema di ottimizzazione da risolvere è l’array di pesi Wᵢⱼ che mappa ogni punto dati Xᵢ sul piano tangente come combinazione lineare dei suoi vicini:

soggetto, per ogni i, al vincolo:

che garantisce la conservazione della geometria locale con pesi che si sommano a 1. Da questo la nostra incorporazione, Yᵢ, è semplicemente il prodotto di questi pesi e dello spazio originale Xᵢ, garantendo al contempo che la seguente funzione di costo di incorporazione sia minimizzata

Ciò garantisce che la rappresentazione a dimensioni inferiori conservi al meglio i pesi locali nello spazio originale. Sebbene questa sia una soluzione piuttosto elegante per catturare le relazioni non lineari, c’è il rischio di incorporazioni corrotte se il nostro valore per k non è sintonizzato correttamente o se ci sono parti del nostro spazio N-dimensionale che sono sparse. In seguito esploreremo altre tecniche di incorporazione non lineare che utilizzano una combinazione di relazioni locali e globali per formare l’incorporazione finale, che nella maggior parte dei casi le rende più robuste.

Spectral Embedding

Spectral Embedding (SE), conosciuto anche come Laplacian Eigenmap embedding, forma un grafo di similarità che collega tutti i punti dati insieme, che sono poi pesati in base alla similarità spettrale tra i punti. In questo modo, SE non solo conserva la relazione locale (come fa LLE) ma il grafo connesso garantisce che le relazioni globali siano anche considerate. La dipendenza dalle proprietà spettrali osservate nel Laplaciano del grafo consente a questo algoritmo di scoprire strutture non lineari complesse che altre tecniche potrebbero non riuscire a identificare.

Il primo passo di questo algoritmo è costruire il grafo:

dove Wᵢⱼ è la larghezza dell’arco tra i nodi i e j, e σ è un parametro personalizzabile per controllare la larghezza dei vicinati. Da questo la matrice Laplaciana del grafo, L, è definita come L=D-W dove W è la matrice di adiacenza del grafo e D è la matrice diagonale dei gradi con le voci seguenti:

Applicando l’ortogonalità e un vincolo di centratura all’incorporamento finale, l’algoritmo esegue la decomposizione degli autovalori sulla matrice laplaciana per identificare gli autovalori e gli autovettori per incorporare i dati di conseguenza.

Mappatura di funzioni isometriche

La tecnica di mappatura non lineare dell’ultimo manifollo che sarà trattata è la Mappatura di Funzioni Isometriche (ISOMAP), un potente metodo di incorporamento non lineare che offre leggermente più personalizzazione rispetto a quelle sopra citate. L’approccio algoritmico assume che tu possa rappresentare lo spazio ad alta dimensionalità con un grafo di vicinato connesso, in cui la distanza tra i nodi è geodetica. Il metodo applica quindi la riduzione multidimensionale (MDS) per trovare una rappresentazione a dimensionalità inferiore dei dati in modo che le distanze tra i nodi siano preservate il più possibile.

Nel costruire il grafo puoi scegliere di imporre limiti sia sul numero di vicini più prossimi da considerare sia sulla distanza euclidea relativa di un punto dati dai suoi vicini. Questi vincoli devono essere adeguatamente calibrati, ad esempio, non troppo grandi da formare bordi shortcut (mancando informazioni strutturali chiave) ma anche non troppo piccoli da non riuscire a creare un grafo connesso. Se queste condizioni di vicinato sono soddisfatte, viene stabilito un collegamento tra due nodi. Con il grafo, G, l’algoritmo calcola quindi la distanza geodetica, Dᵢⱼ, tra tutte le coppie di punti utilizzando un algoritmo del percorso più breve, f, come Dijkstra o Floyd:

Il passaggio finale consiste nel mappare i dati nel sottospazio che comporta l’applicazione di MDS a Dᵢⱼ. Se ricordiamo quanto detto in precedenza nella nostra panoramica di PCA, valutavamo la matrice di covarianza prima della decomposizione degli autovalori. MDS si differenzia leggermente calcolando una matrice di Gram, B, relativa alla matrice di centratura H:

dove e è un vettore di uno e n è il numero di punti dati. L’incorporamento finale deriva dagli d autovettori che corrispondono agli autovalori più grandi:

Iniezione di rumore

Fino ad ora abbiamo analizzato una selezione di metodi lineari e non lineari per incorporare i dati in uno spazio a dimensione inferiore, facendo determinate ipotesi sul manifollo sottostante, ma come possiamo sapere quale metodo sta catturando informazioni utili, specialmente quando lavoriamo con dati ad alta dimensionalità che non possiamo visualizzare. Un approccio per valutare le prestazioni di qualsiasi tecnica di incorporamento da un punto di vista quantitativo è utilizzare, quello che io definisco, l’iniezione di rumore. Con questo approccio applichiamo quantità variabili di rumore allo spazio originale e monitoriamo l’impatto che ha sugli incorporamenti. Il principio sottostante è che all’aumentare della quantità di rumore (distorsione) nello spazio originale arriverà un punto in cui gli algoritmi di apprendimento di manifold non riusciranno a catturare la vera struttura sottostante. Osservando come gli incorporamenti rispondono a quantità variabili di rumore è facile identificare quanto bene ciascuna tecnica stia modellando la struttura sottostante nel dataset. Di seguito puoi trovare un riassunto passo passo su come fare questa analisi, con due esempi in Python per rendere l’idea più chiara:

  1. Genera dataset sostitutivi dal dataset originale con l’aggiunta di rumore gaussiano, la varianza del quale scaliamo per ciascun dataset sostitutivo successivo.
  2. Incorpora questi dataset utilizzando una selezione di tecniche di apprendimento di manifold.
  3. Per ciascuna tecnica confronta gli incorporamenti con rumore iniettato con l’incorporamento dello spazio originale (senza rumore additivo sintetico) utilizzando l’analisi di procrustes, un metodo statistico popolare per confrontare due forme. Questo metodo di valutazione ruota, scala e trasla uno spazio sull’altro con l’obiettivo di minimizzare la somma dei quadrati delle differenze tra ciascun punto dati. Questa differenza è una misura di similarità, che verrà analizzata per osservare come ogni metodo di incorporamento si comporta quando sottoposto a rumore.
  4. Il passo finale consiste nel tracciare il cambiamento della distanza procrustes rispetto alla scala del rumore aggiuntivo sintetico, consentendoci di trarre conclusioni sulle prestazioni di ciascuna tecnica.

Applichiamo i passaggi sopra descritti al set di dati classico a curva-S:

import matplotlib.pyplot as pltfrom sklearn import manifold, datasetsimport numpy as npfrom scipy.spatial import procrustesfrom sklearn.decomposition import PCAdef compute_procrustes_distances(data, embedding_technique, max_noise, noise_step=0.05):    """    Calcola le distanze di Procrustes per una serie di livelli di rumore gaussiano.    Parametri:        data (np.array): Il set di dati originale.        embedding_technique (object): Un'istanza di una tecnica di apprendimento di manifolds.        max_noise (float): Livello massimo di rumore da aggiungere.        noise_step (float): Passo incrementale per l'aggiunta di rumore.    Restituisce:        list: Una lista delle distanze di Procrustes per ogni livello di rumore.    """    base_embedding = embedding_technique.fit_transform(data)    noise_levels = np.arange(0, max_noise, noise_step)    distances = []    for noise in noise_levels:        noisy_data = data + np.random.normal(-noise, noise, data.shape)        noisy_embedding = embedding_technique.fit_transform(noisy_data)        _, _, disparity = procrustes(base_embedding, noisy_embedding)        distances.append(disparity)        return distancesdef plot_data(X, colour):    """    Traccia il set di dati in 3D.    Parametri:        X (np.array): Punti dei dati.        colour (np.array): Mappatura del colore per i punti dei dati.    """    fig = plt.figure(figsize=(30, 10))    ax = fig.add_subplot(111, projection='3d')    ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=colour, cmap=plt.cm.Spectral)    ax.view_init(4, -72)    plt.show()def plot_procrustes_distances(noise_range, distances, labels):    """    Traccia le distanze di Procrustes per diverse tecniche di embedding.    Parametri:        noise_range (np.array): Intervallo dei livelli di rumore.        distances (dict): Dizionario delle distanze per ogni tecnica di embedding.        labels (list): Lista di etichette per ogni tecnica di embedding.    """    plt.figure(figsize=(10, 6))    for label in labels:        plt.plot(noise_range, distances[label], label=label)    plt.xlabel('Intervallo del rumore')    plt.ylabel('Distanza di Procrustes')    plt.title('Confronto delle tecniche di embedding')    plt.legend()    plt.show()# Genera e traccia il set di dati a curva-SX, colour = datasets.make_s_curve(1000, random_state=0)plot_data(X, colour)# Calcola le distanze di Procrustes per diverse tecniche di embeddingmax_noise = 2noise_range = np.arange(0, max_noise, 0.05)embedding_techniques = {    "PCA": PCA(2),    "ISOMAP": manifold.Isomap(n_components=2),    "LLE": manifold.LocallyLinearEmbedding(n_neighbors=10, n_components=2),    "SE": manifold.SpectralEmbedding(n_components=2, n_neighbors=10)}distances = {label: compute_procrustes_distances(X, technique, max_noise) for label, technique in embedding_techniques.items()}# Traccia le distanze di Procrustes calcolateplot_procrustes_distances(noise_range, distances, embedding_techniques.keys())

Ovviamente, nelle applicazioni del mondo reale non avremo accesso alla vera struttura sottostante come nel caso di questo esempio fittizio, ma per ora assumiamo di non conoscere la vera struttura. Cosa possiamo dedurre dal grafico sopra? Fondamentalmente una buona tendenza sarebbe quella che assomiglia ad una curva sigmoide, in cui inizialmente si osserva una resistenza ad una piccola quantità di rumore con una embedding molto simile allo spazio originale, tuttavia arriverà un punto critico in cui questo non sarà più vero poiché il rumore spezzerà la struttura sottostante. In questo momento ci aspetteremmo un aumento repentino della distanza di Procrustes con embeddings successivi che catturano il rumore con poche o nessuna informazione significativa. Prendendo ciò in considerazione, insieme al grafico sopra, possiamo riassumere quanto segue:

PCA: Anche se la distanza di Procrustes aumenta con il rumore, la tendenza è piuttosto lineare e quindi probabilmente non sta catturando informazioni sufficienti sulla vera struttura. Senza prendere in considerazione le altre tendenze, questo da solo sarebbe un forte indicatore che è richiesto un embedding non lineare.

LLE: Vediamo una scarsa resilienza anche a piccole quantità di rumore, probabilmente a causa di una violazione dell’assunzione chiave di linearità locale. Aumentare k, il numero di vicini più prossimi, potrebbe ridurre la fragilità di questa incorporazione, ma comporta un potenziale costo di perdita di dettaglio (informazione) nel sottospazio risultante.

ISOMAP: Inizialmente le prestazioni di questa tecnica di incorporazione sembrano buone, ma all’aumentare del rumore diventa evidente che manca l’acquisizione di informazioni (con un trend lineare oltre il livello di rumore: 0.25).

SE: Tra tutti i metodi esplorati, SE ha le migliori prestazioni, anche se richiede un accordo per ottenere una calibrazione ottimale.

In generale, si potrebbe concludere che la struttura dei nostri dati risiede chiaramente su una varietà non lineare e che la non linearità sembra essere catturata in modo ragionevole con SE e ISOMAP. Considerando ciò e la scarsa performance di LLE, potremmo inferire una curvatura significativa per la varietà nello spazio originale. Esploriamo questo con un altro esempio:

# Genera e traccia il set di dati S-curvex, color = datasets.make_swiss_roll(1000, random_state=0)plot_data(X, color)# Calcola le distanze di Procrustes per diverse tecniche di incorporazionemax_noise = 4noise_range = np.arange(0, max_noise, 0.05)embedding_techniques = {    "PCA": PCA(2),    "ISOMAP": manifold.Isomap(n_components=2),    "LLE": manifold.LocallyLinearEmbedding(n_neighbors=10, n_components=2),    "SE": manifold.SpectralEmbedding(n_components=2, n_neighbors=10)}distances = {label: compute_procrustes_distances(X, technique, max_noise) for label, technique in embedding_techniques.items()}# Traccia le distanze di Procrustes calcolateplot_procrustes_distances(noise_range, distances, embedding_techniques.keys())

Utilizzando le stesse funzioni descritte in precedenza, otteniamo le tendenze delle distanze di Procrustes per ogni tecnica di incorporazione sopra. Chiaramente, la struttura dei dati è significativamente non lineare poiché osserviamo che la PCA fatica a catturare la struttura sottostante. La non linearità costante nello spazio originale è ulteriormente evidenziata anche dalle scarse prestazioni di LLE, probabilmente a causa di una mappatura incoerente dei vicini ai piani tangenti. Ancora una volta, SE e ISOMAP si comportano bene, quest’ultimo leggermente meglio del primo, tendendo verso una distanza di Procrustes di 1 più rapidamente dopo che la struttura diventa compromessa dal rumore. Non sarebbe irragionevole inferire che SE sta catturando un po’ di rumore in tutte le incorporazioni, il che potrebbe essere corretto con un’accordatura dei parametri. La regolazione di questi algoritmi può migliorare la generalizzazione e adattamento dei dati incorporati, ecco un esempio di come farlo per la tecnica ISOMAP sopra:

import numpy as npfrom scipy.spatial import procrustesimport matplotlib.pyplot as pltfrom sklearn import manifold, datasetsdef return_procrustes_distance(data, embedding_technique, max_noise, noise_step=0.05):    """    Calcola la distanza di Procrustes per diversi livelli di rumore.    Parametri:        data (array_like): I dati originali da incorporare.        embedding_technique (object): La tecnica di incorporazione (ad esempio, PCA, SpectralEmbedding).        max_noise (float): Il livello massimo di rumore da aggiungere.        noise_step (float): L'incremento per il livello di rumore.    Returns:        list: Una lista delle distanze di Procrustes per ogni livello di rumore.    """    embeddings = []    distances = []    noise_range = np.arange(0, max_noise, noise_step)    for noise_level in noise_range:        noisy_data = data + np.random.normal(0, noise_level, data.shape)        embedded_data = embedding_technique.fit_transform(noisy_data)        if not embeddings:  # se la lista degli incorporamenti è vuota            embeddings.append(embedded_data)        _, _, disparity = procrustes(embeddings[0], embedded_data)        distances.append(disparity)    return distances# Genera il set di dati S-curvex, _ = datasets.make_swiss_roll(1000, random_state=0)# Parametrimax_noise = 2k_values = [5, 7, 9]  # Diversi valori di k per ISOMAP# Calcola e traccia le distanze di Procrustes per ogni valore di knoise_levels = np.arange(0, max_noise, 0.05)plt.figure(figsize=(10, 6))for k in k_values:    embedding = manifold.Isomap(n_components=2, n_neighbors=k)    procrustes_distances = return_procrustes_distance(X, embedding, max_noise)    plt.plot(noise_levels, procrustes_distances, label=f'ISOMAP (k={k})')plt.xlabel('Livello di Rumore')plt.ylabel('Distanza di Procrustes')plt.title('Distanza di Procrustes in base al Livello di Rumore per Vari k in ISOMAP')plt.legend()plt.show()

Quello sopra è un esempio molto generico di messa a punto, e sicuramente vorrai esplorare altri parametri, ma il principio è che semplicemente regolando k e osservando l’impatto del rumore possiamo iniziare a vedere l’algoritmo generalizzarsi meglio.

Conclusioni

In questo articolo abbiamo esplorato una serie di tecniche di apprendimento a varietà e mostrato come possiamo utilizzare l’iniezione di rumore per comprendere meglio la struttura sottostante nei dati ad alta dimensionalità. Abbiamo raggiunto questo approfondendo la nostra comprensione di come funziona ciascun algoritmo, le assunzioni che sottendono ad ognuno, e analizzando l’influenza del rumore sulle visualizzazioni. Con questa conoscenza a disposizione possiamo prendere decisioni più informate su come pre-elaborare o gestire i dati prima di passarli alle pipeline successive di machine learning. Questo approccio arricchisce anche la nostra comprensione della generalizzazione delle visualizzazioni risultanti e di come potrebbero rispondere al rumore o a possibili deviazioni future dei dati.

Se pianifichi di utilizzare una tecnica di visualizzazione come parte della tua soluzione di machine learning o desideri un modo per approfondire il tuo processo di analisi esplorativa dei dati, l’approccio sopra descritto ti aiuterà a comprendere meglio la struttura nascosta e oscurata in qualsiasi dataset ad alta dimensionalità.

A meno che non sia diversamente indicato, tutte le immagini sono dell’autore.