[PYTHON] J'ai essayé de regrouper les données ECG en utilisant la méthode K-Shape

Résumé de cet article

base de données

  • Cette fois, nous utiliserons les archives de classification des séries chronologiques UCR de l'Université de Californie à Riverside.

--ECG est connu pour favoriser la détection des maladies cardiaques

  • Le contenu de ** ECGFiveDays_TRAIN.tsv ** à utiliser est le suivant.

DataFrame


df = pd.read_table("ECGFiveDays_TRAIN.tsv",header=None)
df.head(10)

image.png

Cliquez ici pour le résumé des données

Summary


import numpy as np
import matplotlib.pyplot as plt
from tslearn.utils import to_time_series_dataset

data_train = np.loadtxt("ECGFiveDays_TRAIN.tsv")
X_train = to_time_series_dataset(data_train[:,1:])
print("Nombre total de données de séries chronologiques: ",len(data_train))
print("Nombre de cours: ", len(np.unique(data_train[:,0])))
print("Durée de la série chronologique: ",len(data_train[0,1:]))

----------------------
#Nombre total de données de séries chronologiques:  23
#Nombre de cours:  2
#Durée de la série chronologique:  136

Un total de 23 données chronologiques avec 136 clichés sont inclus. Il n'y a aucune valeur manquante.

Vizualize


%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
for i in range(0,3):
    if data_train[i,0]==2.0:
        plt.figure(figsize=(18, 8))
        print("Plot",i,"Class",data_train[i,0])
        plt.plot(data_train[i],c='r')
        plt.show()

image.png

image.png

Étant donné que ces étiquettes sont fixées par des experts en ECG, les classes 1.0 et 2.0 sont presque impossibles à distinguer aux yeux d'un amateur. Aussi, en complément, selon Readme.md, qui est téléchargé avec ECGFiveDays_TRAIN.tsv, cet électrocardiogramme est des données ECG d'un homme de 67 ans mesurées en 1990.

Prétraitement

Normaliser les données de chaque série temporelle afin qu'elles aient une moyenne de 0 et un écart type de 1 afin de les amener en forme de K. Ici, je vais normaliser l'ECG de Plot0, class1.0, le représenter graphiquement et le comparer.

Normalisation


from tslearn.preprocessing import TimeSeriesScalerMeanVariance
from tslearn.utils import to_time_series_dataset
X_train = to_time_series_dataset(data_train[:,1:])
X_train = TimeSeriesScalerMeanVariance(mu=0.,std=1.).fit_transform(X_train)
plt.figure(figsize=(18, 8))
plt.plot(X_train[1],c='magenta',alpha=0.8,label="normalized")
plt.plot(data_train[1],c='blue',alpha=0.8,label="original")
plt.legend(fontsize=17)

image.png

  • La figure ci-dessous montre le graphique rouge de la classe 2.0 par rapport au graphique normalisé. image.png

À propos de la méthode K-Shape

Je vais résumer brièvement avec 3 points.

--Une méthode de clustering basée sur la forme pour les données chronologiques

  • Fondamentalement la même chose que k-means, mais la façon de mesurer la distance est différente
  • Ce qui suit est un papier en forme de K. Écrit en 2015, l'algorithme lui-même est relativement nouveau ( https://dl.acm.org/doi/10.1145/2723372.2737793)

Maintenant, la formation

Exécutez avec 2 clusters, 100 itérations maximum et 100 formations.

Train


from tslearn.clustering import KShape
ks = KShape(n_clusters=2,max_iter=100,n_init=100,verbose=0)
ks.fit(X_train)
y_pred = ks.fit_predict(X_train)
print(y_pred)

---
#[0 1 0 0 1 0 0 1 0 1 1 1 1 0 0 0 1 0 1 1 1 0 1]

Pour un total de 23 séries chronologiques, nous avons pu mettre une étiquette de prédiction sur laquelle des deux classes serait classée. Pour les hyper paramètres liés à K-Shape, reportez-vous à l'URL suivante. https://tslearn.readthedocs.io/en/stable/gen_modules/clustering/tslearn.clustering.KShape.html

Évaluation

L '** indice foncier ajusté ** est utilisé comme échelle d'évaluation pour le regroupement des séries chronologiques. En un mot, cette métrique mesure «dans quelle mesure les attributions de cluster correspondent entre le clustering prévu et le clustering réel».

Ici, le score ajusté de Scikit-learn est utilisé. https://scikit-learn.org/stable/modules/generated/sklearn.metrics.adjusted_rand_score.html

Évaluation(Pour les données d'entraînement)


from sklearn.metrics import adjusted_rand_score
ars = adjusted_rand_score(data_train[:,0],y_pred)
print("Indice foncier ajusté: ",ars)
---
#Indice foncier ajusté:  0.668041237113402

Ensuite, recherchez les ars des données de test.

Évaluation(Pour les données de test)


import numpy as np
import matplotlib.pyplot as plt
from tslearn.utils import to_time_series_dataset

data_test = np.loadtxt("ECGFiveDays_TEST.tsv")
X_test = to_time_series_dataset(data_test[:,1:])
pred_test = ks.predict(X_test)
ars = adjusted_rand_score(data_test[:,0],pred_test)
print("Indice foncier ajusté: ",ars)
---
#Indice foncier ajusté:  0.06420517898316379

J'ai un score très bas.

Score proche de 0=Score lorsque attribué au hasard

En raison de la logique, on ne peut jamais dire que c'est un bon modèle. On pense que la raison pour laquelle le score des données de test est faible est que le nombre de données est extrêmement faible. Augmentez donc le nombre de données et essayez la même chose.

Formation / évaluation à l'aide des données ECG5000

Ce qui suit utilise ** ECG5000_TRAIN.tsv ** et ** ECG500_TEST.tsv ** empaquetés dans l'archive UCR.

Jeu de données ECG5000


import numpy as np
import matplotlib.pyplot as plt
from tslearn.utils import to_time_series_dataset
from sklearn.model_selection import train_test_split

data_test_ = np.loadtxt("ECG5000_TEST.tsv")
data_train_ = np.loadtxt("ECG5000_TRAIN.tsv")
data_joined = np.concatenate((data_train_,data_test_),axis=0)
data_train_,data_test_ =train_test_split(data_joined,test_size=0.2,random_state=2000) 

X_train_ = to_time_series_dataset(data_train_[:,1:])
X_test_ = to_time_series_dataset(data_test_[:,1:])

print("Nombre total de données de séries chronologiques: ",len(data_train_))
print("Nombre de cours: ", len(np.unique(data_train_[:,0])))
print("Durée de la série chronologique: ",len(data_train_[0,1:]))

---
#Nombre total de données de séries chronologiques:  4000
#Nombre de cours:  5
#Durée de la série chronologique:  140

Cliquez ici pour le nombre de données appartenant au cluster

Détails du cluster


print("Classe 1.Nombre de données appartenant à 0",len(data_train_[data_train_[:,0]==1.0]))
print("Classe 1.Nombre de données appartenant à 0",len(data_train_[data_train_[:,0]==2.0]))
print("Classe 1.Nombre de données appartenant à 0",len(data_train_[data_train_[:,0]==3.0]))
print("Classe 1.Nombre de données appartenant à 0",len(data_train_[data_train_[:,0]==4.0]))
print("Classe 1.Nombre de données appartenant à 0",len(data_train_[data_train_[:,0]==5.0]))
---
#Classe 1.Nombre de données appartenant à 0 2342
#Classe 1.Nombre de données appartenant à 0 1415
#Classe 1.Nombre de données appartenant à 0 74
#Classe 1.Nombre de données appartenant à 0153
#Classe 1.Nombre de données appartenant à 0 16

Cela ressemble à ceci dans un graphique

Visualisation de chaque cluster


%matplotlib inline
import matplotlib.pyplot as plt

for j in np.unique(data_train_[:,0]):
    plt.figure(figsize=(28,2))
    dataPlot = data_train_[data_train_[:,0]==j]
    cnt = len(dataPlot) 
    dataPlot = dataPlot[:,1:].mean(axis=0)
    print(" Class ",j," Count ",cnt)
    plt.plot(dataPlot,c='b')
    plt.show()

image.png

Nous nous formerons et évaluerons comme avant

Entraînement/Évaluation(Pour les données de test)


from tslearn.preprocessing import TimeSeriesScalerMeanVariance
from tslearn.utils import to_time_series_dataset
from tslearn.clustering import KShape
from sklearn.metrics import adjusted_rand_score
X_train_ = to_time_series_dataset(data_train_[:,1:])
X_train_ = TimeSeriesScalerMeanVariance(mu=0.,std=1.).fit_transform(X_train_)
X_test_ = to_time_series_dataset(data_test_[:,1:])
X_test_ = TimeSeriesScalerMeanVariance(mu=0.,std=1.).fit_transform(X_test_)

ks = KShape(n_clusters=5,max_iter=100,n_init=100,verbose=1,random_state=2000)
ks.fit(X_train_)
preds_test = ks.predict(X_test_)
ars_ = adjusted_rand_score(data_test_[:,0],preds_test)
print("Indice foncier ajusté: ",ars_)
---
#Indice foncier ajusté:  0.4984050982000773

L'indice de terrain d'évaluation appliqué aux données de test était d'environ 0,498 </ font>. Par rapport à l'application de données précédente, c'est une différence dans la boue des nuages. En augmentant la taille de l'ensemble d'entraînement de 23 à 4000, un modèle beaucoup plus précis a été créé.

Enfin, regardons les données de test

Visualisation des résultats de classification


for yi in range(5):
    plt.figure(figsize=(20,35))
    plt.subplot(5, 1, 1 + yi)
    for xx in X_test_[preds_test == yi]:
        plt.plot(xx.ravel(), alpha=0.1,c='blue')
    cnt = len(X_test_[preds_test == yi]) 
    
    plt.plot(ks.cluster_centers_[yi].ravel(), "red")
    print(" Class ",yi," Count ",cnt)
    plt.title("Cluster %d" % (yi + 1) )

plt.tight_layout()
plt.show()

Voici une visualisation des résultats de classification des données de test (la ligne rouge est le centre de gravité) image.png

image.png

image.png

image.png

image.png

Résumé

  • Cette fois, j'ai essayé le regroupement de séries chronologiques en utilisant des données K-Shape et ECG basées sur la forme.
  • En augmentant le nombre de données, le score de l'indice foncier ajusté a explosé. --En plus de K-Shape, vous pouvez également créer un cluster avec k-means et DBSCAN, je voudrais donc les comparer à l'avenir.

Supplément

Mesure de distance en forme de K

SBD(\vec{x},\vec{y})=1-\underset{w}{max}\frac{CC_w(\vec{x},\vec{y})}{\sqrt{R_0(\vec{x},\vec{x})}\sqrt{R_0(\vec{y},\vec{y})}}

--Introduire la similarité et la corrélation mutuelle $ CC_w $ qui correspondent bien aux données de séries chronologiques

  • $ SBD $ signifie la distance de $ \ vec {x}, \ vec {y} $

Qu'est-ce que tslearn?

tslearn est un package Python pour l'analyse de séries chronologiques à l'aide de l'apprentissage automatique. En plus de K-Shape, divers algorithmes d'analyse de séries chronologiques sont inclus.

Les références

  • https://www.slideshare.net/kenyanonaka/k-shapes-zemiyomi
  • https://dl.acm.org/doi/10.1145/2723372.2737793

Recommended Posts