Analiza TDA: Wykrywanie anomalii w szeregach czasowych
Author
M. Janiga
Wprowadzenie do Analizy Topologicznej Danych (TDA)
Analiza Topologiczna Danych (TDA) pozwala nam spojrzeć na szeregi czasowe nie jako na zwykłe wykresy, ale jako na kształty geometryczne “zanurzone” w przestrzeni wielowymiarowej. Głównym celem tej analizy jest sprawdzenie, czy narzędzia topologiczne (takie jak homologia persystentna) potrafią skutecznie odróżnić sygnał uporządkowany od chaosu oraz czy mogą służyć jako system wczesnego ostrzegania przed awariami.
Kluczowe pojęcia
Zanurzenie (Takens Embedding): Przekształcamy jednowymiarowy sygnał w chmurę punktów w przestrzeni wielowymiarowej (np. 3D).
Filtracja (Vietoris-Rips): Budujemy struktury geometryczne wokół punktów, zwiększając promień “kulek” wokół nich.
Diagram Persystencji: Rejestrujemy momenty narodzin i śmierci cech topologicznych (np. pętli). Cechy “długowieczne” to istotny sygnał, “krótkowieczne” to szum.
Entropia Persystentna: Jedna liczba podsumowująca stopień skomplikowania (chaosu) w strukturze topologicznej danych.
Ustawienia wstępne
Wczytujemy niezbędne biblioteki oraz konfigurujemy ujednolicony styl i wymiary wizualizacji.
import sysimport osimport numpy as npimport matplotlib.pyplot as pltimport plotly.graph_objects as goimport plotly.io as pioimport seaborn as snsfrom gtda.time_series import TakensEmbeddingfrom gtda.homology import VietorisRipsPersistencefrom gtda.diagrams import PersistenceEntropyfrom gtda.plotting import plot_diagramfrom sklearn.pipeline import Pipeline# --- Konfiguracja wizualizacji ---# Definicja standardowych wymiarówFIG_WIDTH =8FIG_HEIGHT =4PLOTLY_HEIGHT =500# Ujednolicony styl Matplotlib/Seabornsns.set_theme(style="whitegrid")plt.rcParams.update({'figure.figsize': (FIG_WIDTH, FIG_HEIGHT),'axes.titlesize': 14,'axes.labelsize': 12,'xtick.labelsize': 10,'ytick.labelsize': 10,'lines.linewidth': 1.5,'legend.fontsize': 11,'figure.dpi': 100})# Stałe kolorystyczne (Semantyczne)COLOR_HEALTHY ="#1f77b4"# NiebieskiCOLOR_ANOMALY ="#d62728"# CzerwonyCOLOR_MIXED ="#9467bd"# Fioletowy# Ujednolicony styl Plotlypio.templates.default ="plotly_white"print(f"Python executable: {sys.executable}")
Zaczynamy od wygenerowania dwóch skrajnych sygnałów: idealnej sinusoidy (reprezentującej zdrowy, cykliczny proces) oraz całkowicie losowego szumu (reprezentującego awarię lub chaos).
t = np.linspace(0, 10, 500)periodic_signal = np.sin(t).reshape(1, -1)random_signal = np.random.rand(500).reshape(1, -1)plt.figure()plt.plot(t, periodic_signal[0], label="Zdrowe zachowanie (Cykl)", color=COLOR_HEALTHY)plt.plot(t, random_signal[0], label="Awaria (Chaos)", color=COLOR_ANOMALY, alpha=0.6)plt.title("Porównanie sygnałów w dziedzinie czasu")plt.xlabel("Czas")plt.ylabel("Amplituda")plt.legend(loc="upper right")plt.show()
Figure 1: Porównanie zdrowego sygnału (cykl) i awarii (chaos)
Geometria danych (Takens Embedding)
Aby zastosować TDA, musimy zamienić wykres 2D w chmurę punktów w 3D. Wykorzystujemy do tego twierdzenie Takensa. Parametry time_delay (przesunięcie) oraz dimension (liczba wymiarów) zostały tutaj dobrane eksperymentalnie tak, aby uzyskać czytelną strukturę geometryczną.
TE = TakensEmbedding(time_delay=10, dimension=3)periodic_3d = TE.fit_transform(periodic_signal)random_3d = TE.fit_transform(random_signal)# Wizualizacja 3D dla sinusoidyfig = go.Figure(data=[go.Scatter3d( x=periodic_3d[0,:,0], y=periodic_3d[0,:,1], z=periodic_3d[0,:,2], mode='markers', marker=dict(size=3, color=COLOR_HEALTHY, opacity=0.8))])fig.update_layout( title="Chmura punktów w 3D (Sygnał periodyczny)", height=PLOTLY_HEIGHT, margin=dict(l=0, r=0, b=0, t=60), scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'))fig.show()
Figure 2: Zanurzenie 3D (Takens Embedding) sinusoidy - idealna pętla
Homologia i Diagramy Persystencji
Algorytm VietorisRipsPersistence analizuje, jak dziury w chmurze punktów pojawiają się i znikają w miarę zmiany skali (“puchnięcia” punktów).
Figure 3: Diagram persystencji dla sygnału periodycznego
Interpretacja diagramu
Punkt znajdujący się daleko od przekątnej (duża persystencja) to sygnał topologiczny. Punkty blisko przekątnej to szum. Sinusoida posiada jedną wielką “dziurę” w środku swojej pętli 3D, co idealnie widać na diagramie powyżej.
Entropia Topologiczna jako miara chaosu
Entropia Persystentna pozwala nam określić, jak skomplikowany (nieuporządkowany) jest dany diagram.
PE = PersistenceEntropy()entropy_periodic = PE.fit_transform(periodic_diagrams)entropy_random = PE.fit_transform(random_diagrams)print(f"Entropia dla cyklu: {entropy_periodic[0][0]:.4f}")print(f"Entropia dla chaosu: {entropy_random[0][0]:.4f}")
Entropia dla cyklu: 0.0478
Entropia dla chaosu: 7.1972
Analiza sygnału zaszumionego
Sprawdźmy, jak zanieczyszczenie sygnału wpływa na jego geometrię i diagram persystencji.
(a) Zaszumiony cykl: Sygnał, Geometria i Persystencja
(b)
(c)
Figure 4
Analiza krocząca i Reguły Decyzyjne
W rzeczywistym systemie monitoringu musimy ustalić próg (threshold), którego przekroczenie oznacza alarm.
# Podział sygnału zaszumionego na oknawindows = mixed_signal.reshape(5, 100)pipeline = Pipeline([ ('embedding', TakensEmbedding(time_delay=3, dimension=3)), ('persistence', VietorisRipsPersistence(homology_dimensions=[1])), ('entropy', PersistenceEntropy())])window_entropies = pipeline.fit_transform(windows)# Wyznaczmy próg (np. na podstawie średniej z pierwszych okien + 2*odchylenie)# Tu dla przykładu ustalimy go sztywno na 7.0THRESHOLD =7.0plt.figure()plt.plot(window_entropies.squeeze(), marker='o', linestyle='--', color=COLOR_MIXED, label="Aktualna Entropia")plt.axhline(y=THRESHOLD, color=COLOR_ANOMALY, linestyle=':', label="Próg Alarmowy")plt.title("Monitoring entropii w czasie")plt.xlabel("Numer okna")plt.ylabel("Entropia")plt.legend()plt.ylim(0, 10)plt.show()
Figure 5: Monitorowanie entropii z wyznaczonym progiem alarmowym
Eksperyment: Wykrywanie narastającej awarii
Symulujemy stopniowy wzrost szumu. Zobaczymy, w którym momencie system przekroczy próg bezpieczeństwa oraz jak wizualnie rozpada się struktura geometryczna sygnału.