Un recorrido paso a paso de la clasificación entre participantes e intraparticipantes realizada con datos de sensores portátiles de los corredores.
Los datos de carrera recopilados mediante sensores portátiles pueden proporcionar información sobre el rendimiento y la técnica general de un corredor. Los datos que provienen de estos sensores suelen ser series temporales por naturaleza. Este tutorial recorre una tarea de detección de fatiga en la que se utilizan métodos de clasificación de series de tiempo en un conjunto de datos en ejecución. En este tutorial, los datos de la serie temporal se utilizan en su formato sin procesar en lugar de extraer características de la serie temporal. Esto genera una dimensión adicional en los datos y, por lo tanto, los algoritmos tradicionales de aprendizaje automático que utilizan los datos en un formato vectorial tradicional no funcionan bien. Por lo tanto, es necesario utilizar algoritmos de series de tiempo específicos.
Los datos contienen datos de captura de movimiento de corredores en condiciones normales y fatigadas. Los datos se recopilaron utilizando Unidades de Medición Inercial (IMU) en el University College Dublin, Irlanda. Los datos utilizados en este tutorial se pueden encontrar en https://zenodo.org/records/7997851 . Los datos presentan una tarea de clasificación binaria en la que intentamos predecir entre “fatigado” y “no fatigado”. En este tutorial, utilizamos los paquetes especializados de Python, Aprendizaje científico; un conjunto de herramientas para el aprendizaje automático en Python y tiempo de espera; una biblioteca creada específicamente para el aprendizaje automático de series temporales.
El conjunto de datos contiene múltiples canales de datos. Aquí, modelamos el problema como un problema univariado por simplicidad y, por lo tanto, solo se utiliza un canal de datos. Seleccionamos la señal de aceleración de magnitud ya que es la señal de mejor rendimiento. [1, 2]. La señal de magnitud es la raíz cuadrada de la suma al cuadrado de cada uno de los componentes direccionales.
Puede encontrar información más detallada sobre la recopilación y el procesamiento de datos en los siguientes artículos, [1, 2].
Para resumir, en este tutorial:
- Se realiza una tarea de clasificación de series temporales utilizando una técnica de clasificación de series temporales de última generación en datos recopilados por sensores portátiles.
- Se realiza una comparación entre el uso de modelos interparticipantes (globalizados) y modelos intraparticipantes (personalizados) para la detección de fatiga en corredores.
Configuración de la tarea de clasificación.
Primero, necesitamos cargar los datos necesarios para el análisis. Para esta evaluación utilizamos los datos de “Accel_mag_all.csv”. Usamos pandas para cargar los datos. Asegúrate de haber descargado este archivo desde https://10.5281/zenodo.7997850 .
import pandas as pdfilename = "Accel_mag_all.csv"
data = pd.read_csv(filename, header = None)
Se requieren algunas funciones de los paquetes sktime y sklearn, por lo que las cargamos a continuación antes de comenzar el análisis:
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
A continuación, separamos las etiquetas y el número de participante. Los datos se representarán mediante matrices desde aquí.
import numpy as npX = data.iloc[:,2:].values
y = data[1].values
participant_no = data[0].values
Para esta tarea, utilizaremos la transformación Rocket junto con un clasificador de regresión Ridge. Rocket es una técnica de última generación para la clasificación de series temporales [3]. Rocket funciona mediante la generación de núcleos convolucionales aleatorios que convolucionan a lo largo de la serie temporal para producir un mapa de características. Luego se utiliza un clasificador lineal simple como el clasificador Ridge en este mapa de características. Se puede crear una canalización que primero transforme los datos usando Rocket, estandarice las características y finalmente use Ridge Classifier para realizar la clasificación.
rocket_pipeline_ridge = make_pipeline(
Rocket(random_state=0),
StandardScaler(),
RidgeClassifierCV(alphas=np.logspace(-3, 3, 10))
)
Clasificación globalizada
En aplicaciones donde tenemos datos de varios participantes, usar todos los datos juntos significaría que los datos de un individuo pueden aparecer tanto en conjuntos de entrenamiento como de prueba. Para evitar esto, generalmente se realiza un análisis de dejar un sujeto fuera (LOSO) donde el modelo se entrena en todos los participantes menos uno y se prueba en el participante excluido. Esto se repite para cada participante. Este método probaría la capacidad del modelo para generalizar entre los participantes.
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)
Imprimir un resumen de los resultados de arriba:
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)}")
El resultado del código anterior:
Global Model Results
mean accuracy: 0.5919805636306338
standard deviation: 0.10360659996594646
minimum accuracy: 0.4709480122324159
maximum accuracy: 0.8283582089552238
La precisión de este análisis LOSO es notablemente baja y algunos conjuntos de datos arrojan resultados tan pobres como las conjeturas aleatorias. Esto sugiere que los datos de un participante pueden no generalizarse bien a otro participante. Este es un problema que ocurre comúnmente cuando se trabaja con datos de sensores personales, ya que la técnica del ejercicio y la fisiología general son diferentes de un individuo a otro. Además, en esta aplicación, la forma en que una persona compensa la fatiga puede ser diferente de cómo otra persona compensa la fatiga. A ver si podemos mejorar el rendimiento personalizando los modelos.
Clasificación personalizada
Al construir modelos personalizados, la predicción se realiza en función de los datos del individuo. Al dividir los datos de series temporales en conjuntos de tren y de prueba, debe hacerse de manera que los datos no se mezclen. Para hacer esto, dividimos cada clase en trenes y conjuntos de prueba individuales para preservar la proporción de cada clase en el tren y los conjuntos de prueba y al mismo tiempo preservar la naturaleza de serie temporal de los datos. Los datos de los primeros dos tercios de la ejecución se utilizan para entrenar el modelo para que prediga en el último tercio de la ejecución.
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))
Imprima un resumen de los resultados anteriores como antes:
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)}")
Salida del código anterior:
Personalised Model Results
mean accuracy: 0.9517626092184379
standard deviation: 0.07750979452994386
minimum accuracy: 0.7037037037037037
maximum accuracy: 1.0
Al personalizar los modelos se observa una mejora drástica en el rendimiento. Por tanto, en esta aplicación, queda claro que existen dificultades para generalizar de una persona a otra.
Conclusión
Para realizar una clasificación de los datos de series temporales de los sensores portátiles, se utilizó la técnica más moderna, Rocket. Este análisis mostró que en este dominio la personalización de los modelos conduce a modelos de clasificación con mejor rendimiento.
La figura anterior muestra una gran mejora en el rendimiento gracias al uso de modelos personalizados donde, para muchos participantes, el rendimiento casi se duplica. Es probable que las diferencias en fisiología y técnica de carrera de una persona a otra contribuyan a este comportamiento. Desde el punto de vista del usuario, tanto el modelo global como el personalizado tendrían beneficios dependiendo de la aplicación. Por ejemplo, en entornos clínicos donde es necesario controlar la técnica de ejercicio de un usuario individual, un modelo personalizado puede resultar útil. Sin embargo, recopilar suficientes datos de un solo individuo para una predicción precisa puede resultar difícil y, por lo tanto, para muchas aplicaciones, los modelos globales serían ideales.
El código presentado en este tutorial también se puede encontrar en github: https://github.com/bahavathyk/TSC_for_Fatigue_Detection
Referencias:
[1] B. Kathirgamanathan, T. Nguyen, G. Ifrim, B. Caulfield, P. Cunningham. Explicación de la fatiga en corredores mediante análisis de series temporales de datos de sensores portátiles, XKDD 2023: 5.º taller internacional sobre descubrimiento de conocimientos explicables en minería de datos, ECML PKDD, 2023, http://xkdd2023.isti.cnr.it/papers/223.pdf
[2] B. Kathirgamanathan, B. Caulfield y P. Cunningham, “Hacia modelos globalizados para la clasificación de ejercicios utilizando unidades de medición inercial”, 2023 IEEE 19.ª Conferencia internacional sobre redes de sensores corporales (BSN), Boston, MA, EE. UU., 2023, págs. 1– 4, doi: 10.1109/BSN58485.2023.10331612.
[3] A. Dempster, F. Petitjean y GIWebb. ROCKET: clasificación de series temporales excepcionalmente rápida y precisa utilizando núcleos convolucionales aleatorios. Minería de datos y descubrimiento de conocimientos, 34(5):1454–1495, 2020.