En traitant un graphique de série temporelle, je voudrais détecter des modèles qui ressemblent à ceci:
En utilisant un exemple de série temporelle comme exemple, je voudrais pouvoir détecter les modèles comme indiqué ici:
Quel type d'algorithme d'intelligence artificielle (je suppose que les techniques d'apprentissage de marchine) dois-je utiliser pour y parvenir? Existe-t-il une bibliothèque (en C/C++) que je peux utiliser?
Voici un exemple de résultat d'un petit projet que j'ai fait pour partitionner des données ECG.
Mon approche était une "commutation HMM autorégressive" (google ceci si vous n'en avez pas entendu parler) où chaque point de données est prédit à partir du point de données précédent en utilisant un modèle de régression bayésien. J'ai créé 81 états cachés: un état indésirable pour capturer des données entre chaque battement et 80 états cachés distincts correspondant à différentes positions dans le motif de battement de cœur. Le motif 80 états ont été construits directement à partir d'un motif de battement unique sous-échantillonné et avait deux transitions - une transition automatique et une transition vers l'état suivant dans le motif. L'état final du modèle est passé à l'état lui-même ou à l'état indésirable.
J'ai entraîné le modèle avec formation Viterbi , en mettant à jour uniquement les paramètres de régression.
Les résultats étaient adéquats dans la plupart des cas. Un champ aléatoire conditionnel de structure similaire fonctionnerait probablement mieux, mais la formation d'un CRF nécessiterait un étiquetage manuel des modèles dans l'ensemble de données si vous ne disposez pas déjà de données étiquetées.
Modifier:
Voici un exemple python - il n'est pas parfait, mais il donne l'approche générale. Il implémente EM plutôt que la formation Viterbi, qui peut être légèrement plus stable. Le jeu de données ecg est de http://www.cs.ucr.edu/~eamonn/discords/ECG_data.Zip
import numpy as np
import numpy.random as rnd
import matplotlib.pyplot as plt
import scipy.linalg as lin
import re
data=np.array(map(lambda l: map(float,filter(lambda x: len(x)>0,re.split('\\s+',l))),open('chfdb_chf01_275.txt'))).T
dK=230
pattern=data[1,:dK]
data=data[1,dK:]
def create_mats(dat):
'''
create
A - an initial transition matrix
pA - pseudocounts for A
w - emission distribution regression weights
K - number of hidden states
'''
step=5 #adjust this to change the granularity of the pattern
eps=.1
dat=dat[::step]
K=len(dat)+1
A=np.zeros( (K,K) )
A[0,1]=1.
pA=np.zeros( (K,K) )
pA[0,1]=1.
for i in xrange(1,K-1):
A[i,i]=(step-1.+eps)/(step+2*eps)
A[i,i+1]=(1.+eps)/(step+2*eps)
pA[i,i]=1.
pA[i,i+1]=1.
A[-1,-1]=(step-1.+eps)/(step+2*eps)
A[-1,1]=(1.+eps)/(step+2*eps)
pA[-1,-1]=1.
pA[-1,1]=1.
w=np.ones( (K,2) , dtype=np.float)
w[0,1]=dat[0]
w[1:-1,1]=(dat[:-1]-dat[1:])/step
w[-1,1]=(dat[0]-dat[-1])/step
return A,pA,w,K
#initialize stuff
A,pA,w,K=create_mats(pattern)
eta=10. #precision parameter for the autoregressive portion of the model
lam=.1 #precision parameter for the weights prior
N=1 #number of sequences
M=2 #number of dimensions - the second variable is for the bias term
T=len(data) #length of sequences
x=np.ones( (T+1,M) ) # sequence data (just one sequence)
x[0,1]=1
x[1:,0]=data
#emissions
e=np.zeros( (T,K) )
#residuals
v=np.zeros( (T,K) )
#store the forward and backward recurrences
f=np.zeros( (T+1,K) )
fls=np.zeros( (T+1) )
f[0,0]=1
b=np.zeros( (T+1,K) )
bls=np.zeros( (T+1) )
b[-1,1:]=1./(K-1)
#hidden states
z=np.zeros( (T+1),dtype=np.int )
#expected hidden states
ex_k=np.zeros( (T,K) )
# expected pairs of hidden states
ex_kk=np.zeros( (K,K) )
nkk=np.zeros( (K,K) )
def fwd(xn):
global f,e
for t in xrange(T):
f[t+1,:]=np.dot(f[t,:],A)*e[t,:]
sm=np.sum(f[t+1,:])
fls[t+1]=fls[t]+np.log(sm)
f[t+1,:]/=sm
assert f[t+1,0]==0
def bck(xn):
global b,e
for t in xrange(T-1,-1,-1):
b[t,:]=np.dot(A,b[t+1,:]*e[t,:])
sm=np.sum(b[t,:])
bls[t]=bls[t+1]+np.log(sm)
b[t,:]/=sm
def em_step(xn):
global A,w,eta
global f,b,e,v
global ex_k,ex_kk,nkk
x=xn[:-1] #current data vectors
y=xn[1:,:1] #next data vectors predicted from current
#compute residuals
v=np.dot(x,w.T) # (N,K) <- (N,1) (N,K)
v-=y
e=np.exp(-eta/2*v**2,e)
fwd(xn)
bck(xn)
# compute expected hidden states
for t in xrange(len(e)):
ex_k[t,:]=f[t+1,:]*b[t+1,:]
ex_k[t,:]/=np.sum(ex_k[t,:])
# compute expected pairs of hidden states
for t in xrange(len(f)-1):
ex_kk=A*f[t,:][:,np.newaxis]*e[t,:]*b[t+1,:]
ex_kk/=np.sum(ex_kk)
nkk+=ex_kk
# max w/ respect to transition probabilities
A=pA+nkk
A/=np.sum(A,1)[:,np.newaxis]
# solve the weighted regression problem for emissions weights
# x and y are from above
for k in xrange(K):
ex=ex_k[:,k][:,np.newaxis]
dx=np.dot(x.T,ex*x)
dy=np.dot(x.T,ex*y)
dy.shape=(2)
w[k,:]=lin.solve(dx+lam*np.eye(x.shape[1]), dy)
#return the probability of the sequence (computed by the forward algorithm)
return fls[-1]
if __name__=='__main__':
#run the em algorithm
for i in xrange(20):
print em_step(x)
#get rough boundaries by taking the maximum expected hidden state for each position
r=np.arange(len(ex_k))[np.argmax(ex_k,1)<3]
#plot
plt.plot(range(T),x[1:,0])
yr=[np.min(x[:,0]),np.max(x[:,0])]
for i in r:
plt.plot([i,i],yr,'-r')
plt.show()
Pourquoi ne pas utiliser un simple filtre adapté? Ou son équivalent statistique général appelé corrélation croisée. Étant donné un modèle connu x(t) et une série chronologique composée bruyante contenant votre modèle décalé a, b, ..., z comme y(t) = x(t-a) + x(t-b) +...+ x(t-z) + n(t).
La fonction de corrélation croisée entre x et y devrait donner des pics dans a, b, ..., z
Je ne sais pas quel package fonctionnerait le mieux pour cela. J'ai fait quelque chose de similaire à un moment au collège où j'ai essayé de détecter automatiquement certaines formes similaires sur un axe x-y pour un tas de graphiques différents. Vous pouvez faire quelque chose comme ceci.
Les étiquettes de classe comme:
Des fonctionnalités telles que:
Weka est une puissante collection de logiciels d'apprentissage automatique, et prend en charge certains outils d'analyse de séries chronologiques, mais je ne connais pas suffisamment le domaine pour recommander une meilleure méthode. Cependant, il est basé sur Java; et vous pouvez appeler Java code de C/C++ sans grand bruit.
Les packages de manipulation de séries chronologiques sont principalement destinés à la bourse. J'ai suggéré Cronos dans les commentaires; Je ne sais pas comment faire de la reconnaissance de formes avec cela, au-delà de l'évidence: tout bon modèle d'une longueur de votre série devrait être capable de prédire qu'après de petites bosses à une certaine distance de la dernière petite bosse, de grosses bosses suivent. Autrement dit, votre série présente une auto-similitude, et les modèles utilisés dans Cronos sont conçus pour la modéliser.
Si cela ne vous dérange pas C #, vous devriez demander une version de TimeSearcher2 aux gens de HCIL - la reconnaissance des formes est, pour ce système, dessiner à quoi ressemble une forme, puis vérifier si votre modèle est assez général pour capturer la plupart des instances avec un faible taux de faux positifs. Probablement l'approche la plus conviviale que vous trouverez; tous les autres nécessitent une formation en statistiques ou en stratégies de reconnaissance de formes.
J'utilise l'apprentissage en profondeur si c'est une option pour vous. Cela se fait en Java, Deeplearning4j . J'expérimente avec LSTM. J'ai essayé 1 couche cachée et 2 couches cachées pour traiter les séries chronologiques.
return new NeuralNetConfiguration.Builder()
.seed(HyperParameter.seed)
.iterations(HyperParameter.nItr)
.miniBatch(false)
.learningRate(HyperParameter.learningRate)
.biasInit(0)
.weightInit(WeightInit.XAVIER)
.momentum(HyperParameter.momentum)
.optimizationAlgo(
OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT // RMSE: ????
)
.regularization(true)
.updater(Updater.RMSPROP) // NESTEROVS
// .l2(0.001)
.list()
.layer(0,
new GravesLSTM.Builder().nIn(HyperParameter.numInputs).nOut(HyperParameter.nHNodes_1).activation("tanh").build())
.layer(1,
new GravesLSTM.Builder().nIn(HyperParameter.nHNodes_1).nOut(HyperParameter.nHNodes_2).dropOut(HyperParameter.dropOut).activation("tanh").build())
.layer(2,
new GravesLSTM.Builder().nIn(HyperParameter.nHNodes_2).nOut(HyperParameter.nHNodes_2).dropOut(HyperParameter.dropOut).activation("tanh").build())
.layer(3, // "identity" make regression output
new RnnOutputLayer.Builder(LossFunctions.LossFunction.MSE).nIn(HyperParameter.nHNodes_2).nOut(HyperParameter.numOutputs).activation("identity").build()) // "identity"
.backpropType(BackpropType.TruncatedBPTT)
.tBPTTBackwardLength(100)
.pretrain(false)
.backprop(true)
.build();
Trouvé quelques choses: