Classificazione delle serie temporali per il rilevamento della fatica nei corridori: un tutorial |  di K Bahavathy |  Dicembre 2023

 | Intelligenza-Artificiale

Una procedura dettagliata della classificazione inter-partecipante e intra-partecipante eseguita sui dati dei sensori indossabili dei corridori

Immagine dell’autore

I dati di corsa raccolti utilizzando sensori indossabili possono fornire informazioni sulle prestazioni di un corridore e sulla tecnica generale. I dati che provengono da questi sensori sono solitamente serie temporali per natura. Questo tutorial illustra un’attività di rilevamento della fatica in cui i metodi di classificazione delle serie temporali vengono utilizzati su un set di dati in esecuzione. In questo tutorial, i dati delle serie temporali vengono utilizzati nel loro formato non elaborato anziché estrarre funzionalità dalle serie temporali. Ciò porta a una dimensione aggiuntiva nei dati e quindi i tradizionali algoritmi di apprendimento automatico che utilizzano i dati in un formato vettoriale tradizionale non funzionano bene. Pertanto è necessario utilizzare algoritmi specifici per serie temporali.

I dati contengono dati di motion capture di corridori in condizioni normali e affaticate. I dati sono stati raccolti utilizzando le unità di misurazione inerziale (IMU) presso l’University College di Dublino, in Irlanda. I dati utilizzati in questo tutorial sono reperibili all’indirizzo https://zenodo.org/records/7997851 . I dati presentano un’attività di classificazione binaria in cui proviamo a prevedere tra “Affaticato” e “Non affaticato”. In questo tutorial utilizziamo i pacchetti Python specializzati, Scikit-impara; un toolkit per l’apprendimento automatico su Python e sktime; una libreria creata appositamente per il machine learning per le serie temporali.

Il set di dati contiene più canali di dati. Qui modelliamo il problema come un problema univariato per semplicità e quindi viene utilizzato solo un canale di dati. Selezioniamo il segnale di accelerazione della grandezza in quanto è il segnale con le migliori prestazioni (1, 2). Il segnale di grandezza è la radice quadrata della somma quadrata di ciascuna delle componenti direzionali.

Informazioni più dettagliate sulla raccolta e sul trattamento dei dati sono reperibili nei seguenti documenti, (1, 2).

Per riassumere, in questo tutorial:

  • Un’attività di classificazione delle serie temporali viene eseguita utilizzando una tecnica di classificazione delle serie temporali all’avanguardia sui dati raccolti dai sensori indossabili.
  • Viene effettuato un confronto tra l’uso di modelli inter-partecipante (globalizzati) e modelli intra-partecipante (personalizzati) per il rilevamento della fatica nei corridori.

Impostazione dell’attività di classificazione

Per prima cosa dobbiamo caricare i dati richiesti per l’analisi. Per questa valutazione utilizziamo i dati di “Accel_mag_all.csv”. Usiamo i panda per caricare i dati. Assicurati di aver scaricato questo file da https://10.5281/zenodo.7997850 .

import pandas as pd

filename = "Accel_mag_all.csv"
data = pd.read_csv(filename, header = None)

Sono necessarie alcune funzioni dei pacchetti sktime e sklearn, quindi le carichiamo di seguito prima di iniziare l’analisi:

from sktime.transformations.panel.rocket import Rocket
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import RidgeClassifierCV, LogisticRegression, LogisticRegressionCV
from sklearn.model_selection import LeaveOneGroupOut

Successivamente, separiamo le etichette e il numero del partecipante. I dati saranno rappresentati da array da qui.

import numpy as np

X = data.iloc(:,2:).values

y = data(1).values
participant_no = data(0).values

Per questo compito, utilizzeremo la trasformazione Rocket insieme a un classificatore di regressione Ridge. Rocket è una tecnica all’avanguardia per la classificazione delle serie temporali (3). Rocket funziona attraverso la generazione di nuclei convoluzionali casuali che vengono contorti lungo le serie temporali per produrre una mappa delle caratteristiche. Su questa mappa delle caratteristiche viene quindi utilizzato un semplice classificatore lineare come il classificatore Ridge. È possibile creare una pipeline che prima trasforma i dati utilizzando Rocket, standardizza le funzionalità e infine utilizza Ridge Classifier per eseguire la classificazione.

rocket_pipeline_ridge = make_pipeline(
Rocket(random_state=0),
StandardScaler(),
RidgeClassifierCV(alphas=np.logspace(-3, 3, 10))
)

Classificazione globalizzata

Nelle applicazioni in cui disponiamo di dati di più partecipanti, l’utilizzo di tutti i dati insieme significherebbe che i dati di un individuo possono essere visualizzati sia nei set di formazione che in quelli di test. Per evitare ciò, viene generalmente eseguita un’analisi LOSO (leave one-subject-out) in cui il modello viene addestrato su tutti i partecipanti tranne uno e testato su un partecipante escluso. Questo viene ripetuto per ogni partecipante. Questo metodo metterebbe alla prova la capacità del modello di generalizzare tra i partecipanti.

logo = LeaveOneGroupOut()

logo.get_n_splits(X, y, participant_no)

Rocket_score_glob = ()
for i, (train_index, test_index) in enumerate(logo.split(X, y, participant_no)):
rocket_pipeline_ridge.fit(X(train_index), y(train_index))

Rocket_score = rocket_pipeline_ridge.score(X(test_index),y(test_index))
Rocket_score_glob = np.append(Rocket_score_glob, Rocket_score)

Stampa di un riepilogo dei risultati dall’alto:

print("Global Model Results")
print(f"mean accuracy: {np.mean(Rocket_score_glob)}")
print(f"standard deviation: {np.std(Rocket_score_glob)}")
print(f"minimum accuracy: {np.min(Rocket_score_glob)}")
print(f"maximum accuracy: {np.max(Rocket_score_glob)}")

L’output del codice precedente:

Global Model Results
mean accuracy: 0.5919805636306338
standard deviation: 0.10360659996594646
minimum accuracy: 0.4709480122324159
maximum accuracy: 0.8283582089552238

L’accuratezza di questa analisi LOSO è notevolmente bassa con alcuni set di dati che producono risultati scadenti quanto ipotesi casuali. Ciò suggerisce che i dati di un partecipante potrebbero non essere generalizzabili bene per un altro partecipante. Questo è un problema che si verifica comunemente quando si lavora con dati di rilevamento personali poiché la tecnica di esercizio e la fisiologia generale sono diverse da un individuo all’altro. Inoltre, in questa applicazione, il modo in cui una persona compensa la fatica può essere diverso da come un’altra persona compensa la fatica. Vediamo se riusciamo a migliorare le prestazioni personalizzando i modelli.

Classificazione personalizzata

Quando si creano modelli personalizzati, la previsione viene effettuata in base ai dati dell’individuo. Durante la suddivisione dei dati delle serie temporali in set di training e test, è necessario farlo in modo che i dati non vengano mescolati. Per fare ciò, abbiamo suddiviso ciascuna classe in singoli set di treni e test per preservare la proporzione di ciascuna classe nei set di treni e test preservando allo stesso tempo la natura di serie temporale dei dati. I dati dei primi due terzi dell’esecuzione vengono utilizzati per addestrare il modello a effettuare previsioni sull’ultimo terzo dell’esecuzione.

Rocket_score_pers = ()
for i, (train_index, test_index) in enumerate(logo.split(X, y, participant_no)):

#print(f"Participant: {participant_no(test_index)(0)}")
label = y(test_index)
X_S = X(test_index)

# Identify the indices for each class
class_0_indices = np.where(label == 'NF')(0)
class_1_indices = np.where(label == 'F')(0)

# Split each class into train and test using indexing
class_0_split_index = int(0.66 * len(class_0_indices))
class_1_split_index = int(0.66 * len(class_1_indices))

X_train = np.concatenate((X_S(class_0_indices(:class_0_split_index)), X_S(class_1_indices(:class_1_split_index))), axis=0)
y_train = np.concatenate((label(class_0_indices(:class_0_split_index)), label(class_1_indices(:class_1_split_index))), axis=0)

X_test = np.concatenate((X_S(class_0_indices(class_0_split_index:)),X_S(class_1_indices(class_1_split_index:))), axis=0)
y_test = np.concatenate((label(class_0_indices(class_0_split_index:)), label(class_1_indices(class_1_split_index:))), axis=0)

rocket_pipeline_ridge.fit(X_train, y_train)

Rocket_score_pers = np.append(Rocket_score_pers, rocket_pipeline_ridge.score(X_test,y_test))

Stampare un riepilogo dei risultati sopra come prima:

print("Personalised Model Results")
print(f"mean accuracy: {np.mean(Rocket_score_pers)}")
print(f"standard deviation: {np.std(Rocket_score_pers)}")
print(f"minimum accuracy: {np.min(Rocket_score_pers)}")
print(f"maximum accuracy: {np.max(Rocket_score_pers)}")

Output dal codice sopra:

Personalised Model Results
mean accuracy: 0.9517626092184379
standard deviation: 0.07750979452994386
minimum accuracy: 0.7037037037037037
maximum accuracy: 1.0

Personalizzando i modelli si vede un drastico miglioramento delle prestazioni. Quindi, in questa applicazione, è chiaro che ci sono difficoltà nel generalizzare da una persona all’altra.

Conclusione

Per eseguire una classificazione dei dati delle serie temporali provenienti dai sensori indossabili è stata utilizzata la tecnica all’avanguardia Rocket. Questa analisi ha mostrato che in questo ambito la personalizzazione dei modelli porta a modelli di classificazione più performanti.

Precisione ottenuta attraverso la classificazione globale rispetto alla classificazione personalizzata per ciascun partecipante

La figura sopra mostra un grande miglioramento delle prestazioni derivante dall’utilizzo di modelli personalizzati in cui per molti partecipanti le prestazioni quasi raddoppiano. È probabile che le differenze nella fisiologia e nella tecnica di corsa da una persona all’altra contribuiscano a questo comportamento. Dal punto di vista dell’utente, sia i modelli globali che quelli personalizzati presenterebbero vantaggi a seconda dell’applicazione. Ad esempio, in contesti clinici in cui è necessario monitorare la tecnica di esercizio di un singolo utente, può essere utile un modello personalizzato. Tuttavia, raccogliere dati sufficienti da un singolo individuo per effettuare una previsione accurata può essere difficile e quindi per molte applicazioni i modelli globali sarebbero l’ideale.

Il codice presentato in questo tutorial può essere trovato anche su github: https://github.com/bahavathyk/TSC_for_Fatigue_Detection

Riferimenti:

(1) B. Kathirgamanathan, T. Nguyen, G. Ifrim, B. Caulfield, P. Cunningham. Spiegare la fatica nei corridori utilizzando l’analisi delle serie temporali sui dati dei sensori indossabili, XKDD 2023: 5th International Workshop on eXplainable Knowledge Discovery in Data Mining, ECML PKDD, 2023, http://xkdd2023.isti.cnr.it/papers/223.pdf

(2) B. Kathirgamanathan, B. Caulfield e P. Cunningham, “Towards Globalized Models for Activity Classification using Inertial Measurement Units”, 19a conferenza internazionale IEEE sulle reti di sensori corporei (BSN), Boston, MA, USA, 2023, 2023, pp 1–4, doi: 10.1109/BSN58485.2023.10331612.

(3) A. Dempster, F. Petitjean e GIWebb. ROCKET: classificazione di serie temporali eccezionalmente veloce e accurata utilizzando kernel convoluzionali casuali. Estrazione dei dati e scoperta della conoscenza, 34(5):1454–1495, 2020.

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *