Livre rouge de Rumor's, Série Machine Learning Professional "Détection des anomalies et détection des changements" (http://ide-research.net/book/support.html#kodansha) Il s'agit d'un article sur l'écriture des graphiques du chapitre 1 en Python. ..
Comme ça, je dessine la précision de l'échantillon et la courbe ROC avec animation.
Le code complet est ici
Seules des explications simples sont données ici, donc si vous souhaitez en savoir plus, achetez le livre!
J'ai généré des données similaires en Python, pas exactement les mêmes données, ou j'ai recherché et tracé les données. (Surtout, il était difficile de trouver les données d'électrocardiogramme [^ 1] ...: sweat_smile :)
[^ 1]: Veuillez noter que la partie rouge anormale est traitée et que les données ECG ne sont pas correctes. L'URL pour obtenir les données est décrite sur GitHub.
Le code entier qui rend ceci est ici
À partir de la figure 1.2, j'ai essayé de visualiser la pertinence en reliant le degré d'anomalie et la fonction d'indicateur et le dessin.
De plus, le seuil du point de branchement est calculé comme le point où les valeurs de la fonction de densité sont les mêmes, et $ \ tau $ dans ce cas est représenté par la barre horizontale dans le quatrième graphique.
Le code entier qui rend ceci est ici
#Génération aléatoire
rd.seed()
n = 1000
d_0 = rd.normal(135, 18, n) #Données normales
d_1 = rd.normal(80, 30, n) #Données anormales
#Calcul de la moyenne et de l'écart type
m_0 = np.mean(d_0)
sd_0 = np.sqrt(np.var(d_0))
m_1 = np.mean(d_1)
sd_1 = np.sqrt(np.var(d_1))
#Génération de données sur l'axe X
xx = np.linspace(0,300, 5000)
#Fonction de densité de distribution normale
density_0 = st.norm.pdf(xx, m_0, sd_0)
density_1 = st.norm.pdf(xx, m_1, sd_1)
#Calcul des anomalies
abnormaly_score = np.log(density_1) - np.log(density_0)
def balance_position(x_min, x_max, f1, f2, EPS=0.00001):
if abs(f1(x_max) - f2(x_max)) > EPS:
center = (x_min + x_max)/2.
if np.sign(f1(x_max) - f2(x_max)) * np.sign(f1(center) - f2(center)) < 0:
x_min = center
else:
x_max = center
x_max = balance_position(x_min, x_max, f1, f2)
else:
return x_max
return x_max
mark = balance_position(0, 200, lambda x:st.norm.pdf(x, m_0, sd_0), lambda x: st.norm.pdf(x, m_1, sd_1))
print "mark:", mark
tau_pos = np.argsort(np.abs(xx - mark))[0]
print "tau pos:", tau_pos
tau = abnormaly_score[tau_pos]
print "tau:", tau
tau2_pos = np.max(np.argsort(np.abs(abnormaly_score - tau))[0:2])
print "tau2_pos:",tau2_pos
tau2 = abnormaly_score[tau2_pos]
print "tau2:",tau2
mark2 = xx[tau2_pos]
print "mark2:",mark2
#----------------Processus de dessin-----------------#
n_row = 5 #Nombre de lignes dans le graphique
plt.subplots(n_row, 1, sharex=True,figsize=(12,12))
gs = gridspec.GridSpec(n_row, 1, height_ratios=[3,3,3,3,3])
axs = [plt.subplot(gs[i]) for i in range(n_row) ]
#Dessin de la première zone
axs[0].hist(d_1, bins=40, color="r", alpha=0.6, range=(0,300), label="data 1")
axs[0].hist(d_0, bins=40, color="b", alpha=0.6, range=(0,300), label="data 0")
axs[0].legend(loc='best')
#Dessin de la deuxième zone
axs[1].plot(xx, get_density(d_1, xx), "r--", alpha=.4 , lw=2, label=r"density of data 1")
axs[1].plot(xx, get_density(d_0, xx), "b--", alpha=.4 , lw=2, label=r"density of data 0")
axs[1].legend(loc='best')
axs[1].plot([0,300],[0,0],"k")
axs[1].plot(xx, density_1, c="r", alpha=.5 )
axs[1].plot(xx, density_0, c="b", alpha=.5 )
axs[1].fill_between(xx[0:tau_pos], density_0[0:tau_pos], color="lightblue", zorder = 500, alpha=.6)
axs[1].set_ylim(0,np.max(density_0)*1.1)
axs[1].plot([mark ,mark],[-100,200], "k--", lw=.5)
axs[1].plot([mark2 ,mark2],[-100,200], "k--", lw=.5)
#Dessin de la troisième zone
axs[2].plot(xx, -np.log(density_0), c="b", alpha=.5, label=r"$\ln p({\bf x}'|y=0,\mathcal{D})$")
axs[2].plot(xx, np.log(density_1), c="r", alpha=.5, label=r"$-\ln p({\bf x}'|y=1,\mathcal{D})$")
axs[2].plot([mark ,mark],[-110,200], "k--", lw=.5)
axs[2].plot([mark2 ,mark2],[-110,200], "k--", lw=.5)
axs[2].set_ylim(-25,40)
axs[2].legend(loc='best')
#4ème dessin de zone
axs[3].plot(xx, abnormaly_score, c="purple", alpha=.6, label=r"$$ \ln{ p({\bf x}'|y=1,\mathcal{D})\over p({\bf x}'|y=0,\mathcal{D})} $$")
axs[3].set_ylim(-5,5)
axs[3].plot([mark ,mark],[-100,200], "k--", lw=.5)
axs[3].plot([mark2 ,mark2],[-100,200], "k--", lw=.5)
axs[3].plot([0 ,300],[tau, tau], "k", lw=.5)
axs[3].legend(loc='best')
#Dessin de la cinquième zone
axs[4].fill_between(xx[0:tau_pos], np.ones_like(xx[0:tau_pos]), color="blue", zorder = 500, alpha=.6)
axs[4].fill_between(xx[tau2_pos:], np.ones_like(xx[tau2_pos:]), color="blue", zorder = 500, alpha=.6)
axs[4].plot([mark, mark],[-110,200], "k--", lw=.5)
axs[4].plot([mark2, mark2],[-110,200], "k--", lw=.5)
axs[4].text(10, 1.3, r"$I[a(x) \ge \tau]$")
axs[4].set_ylim(0,5)
#Plage fixe de x dans tous les domaines
for ax in axs:
ax.set_xlim(0,300)
plt.subplots_adjust(hspace=0)
De manière similaire à la figure 1.2, la fonction de densité de la distribution normale estimée à partir de l'histogramme, la densité du noyau, la moyenne / dispersion est dessinée, et la "quantité d'informations" est tracée. Le degré d'anomalie augmente simplement à mesure que la distance par rapport à la moyenne des données augmente.
Le code entier qui rend ceci est ici
#Pour les données non étiquetées
#Génération aléatoire
n = 1000
data = rd.normal(80, 15, n)
#Calcul de la moyenne et de l'écart type
m = np.mean(data)
sd = np.sqrt(np.var(data))
#Génération de données sur l'axe X
xx = np.linspace(0,300, 5000)
#Fonction de densité de distribution normale
density = st.norm.pdf(xx, m, sd)
#----------------Processus de dessin-----------------#
n_row = 3 #Nombre de lignes dans le graphique
xx = np.linspace(0,300, 5000) #Génération de données sur l'axe X
plt.subplots(n_row, 1, sharex=True,figsize=(12,10))
gs = gridspec.GridSpec(n_row, 1, height_ratios=[3,3,3])
axs = [plt.subplot(gs[i]) for i in range(n_row) ]
#Dessin de la première zone
axs[0].hist(data, bins=50, range=(0,300), label="data: a", color="b", alpha=0.5)
axs[0].set_ylim(0,200)
axs[0].legend(loc='best')
#Dessin de la deuxième zone
axs[1].plot(xx, get_density(data, xx), lw=2, linestyle="--", label="density of the data", color="b", alpha=0.4)
axs[1].plot(xx, density, lw=1, label=r"estimated density of norm dist: $p({\bf x}'|\mathcal{D})$", color="b", alpha=0.5)
axs[1].legend(loc='best')
#Dessin de la troisième zone
axs[2].plot(xx, -np.log(density), lw=1, label=r"information:$-\ln p({\bf x}'|\mathcal{D})$", color="b", alpha=0.5)
axs[2].legend(loc='best')
#Plage fixe de x dans tous les domaines
for ax in axs:
ax.set_xlim(0,300)
plt.subplots_adjust( hspace=0)
J'ai essayé d'animer la relation entre la précision normale de l'échantillon, la précision anormale de l'échantillon et la courbe ROC. De plus, les détails de la courbe ROC ont déjà été expliqués dans "[Statistiques] Qu'est-ce que la courbe ROC? ](Http://qiita.com/kenmatsu4/items/550b38f4fa31e9af6f4f) "est également écrit, veuillez donc vous y référer.
Dans cette figure, la précision de l'échantillon est dessinée comme indiqué ci-dessous.
r_0 = \log(1 + x) \\
r_1 = \log(e -x)
Le graphique de la deuxième ligne représente la valeur F et la définition est la suivante.
f \equiv { 2r_0 r_1 \over r_0 + r_1 }
Puisque la courbe ROC est $ (X, Y) = (1 --r_0 (\ tau), \ r_1 (\ tau)) $, la valeur de coordonnée y du point qui trace la ligne rouge utilise $ 1-y $. Les coordonnées sont affichées sur l'animation, et le nombre sur le côté droit des deux correspond à cela.
Le code entier qui rend ceci est ici
def animate(nframe):
global num_frame
sys.stdout.write(str(int(float(nframe)/num_frame*100)) + "%, ")
plt.clf()
#Valeurs minimum et maximum de x
xmin = 0
xmax = np.e -1
#Nombre de divisions de x
sx = num_frame * 2
#emplacement actuel
pos = nframe * 2
#génération de l'axe des x
xx = np.linspace(xmin, xmax, sx)
#Précision de l'échantillon
cx1 = np.log(1+xx)
cx2 = np.log(np.e -xx)
#Premier dessin graphique-----------------------
plt.subplot(311)
plt.title("Sample accuracy. x={0:.2f}".format(xx[pos]))
plt.xlim(xmin, xmax)
plt.ylim(0,1)
#Tracez une courbe
plt.plot(xx,cx1,linewidth=2)
plt.plot(xx,cx2,linewidth=2)
#Dessiner des points et des valeurs de coordonnées
plt.scatter(xx[pos],cx1[pos], c="g", s=40, zorder=100)
plt.text(xx[pos]+.01,cx1[pos]-.05,"{0:.3f}, {1:.3f}".format(cx1[pos], 1-cx1[pos]), size=16)
plt.scatter(xx[pos],cx2[pos], c="b", s=40, zorder=100)
plt.text(xx[pos]-.20,cx2[pos]-.05,"{0:.3f}".format(cx2[pos]), size=16)
#Dessin au trait en pointillé
plt.plot([xx[pos], xx[pos]], [0,1], "k--", alpha=.5, lw=2 )
plt.plot([0, xx[pos]], [cx1[pos],cx1[pos]], "k--", alpha=.5, lw=2 )
plt.plot([0, xx[pos]], [cx2[pos],cx2[pos]], "k--", alpha=.5, lw=2 )
plt.text(.08, .42, r"normal:$r_0$", color="r", size=16)
plt.text(1.2, .42, r"anomalous:$r_1$", color="#2F79B0", size=16)
#Deuxième dessin graphique-----------------------
plt.subplot(312)
plt.title("F-value")
plt.xlim(xmin, xmax)
plt.ylim(0,1)
F = 2*cx1*cx2/(cx1+cx2)
F_pos = 2*cx1[pos]*cx2[pos]/(cx1[pos]+cx2[pos])
plt.scatter(xx[pos], F_pos, c="g", alpha=1, s=50)
plt.plot(xx, F)
plt.plot([xx[pos], xx[pos]], [0,1], "k--", alpha=.5, lw=2 )
plt.tight_layout()
#Troisième dessin graphique-----------------------
plt.subplot(313)
plt.title("ROC Curve.")
plt.xlim(0,1)
plt.ylim(0,1)
#Dessiner des courbes et des points
plt.plot(1-cx1, cx2, linewidth=2, color="b", alpha=.4)
plt.scatter(1-cx1[pos],cx2[pos], c="b", s=40, zorder=100)
plt.text(1-cx1[pos]+.01,cx2[pos]-.05, "{0:.3f}, {1:.3f}".format(1-cx1[pos], cx2[pos]), size=16)
plt.xlabel(r"$1-r_0$", size=16)
plt.ylabel(r"$r_1$", size=16)
plt.tight_layout()
num_frame = 50
fig = plt.figure(figsize=(5,10))
anim = ani.FuncAnimation(fig, animate, frames=num_frame, blit=True)
anim.save('ROC_curve2.gif', writer='imagemagick', fps=3, dpi=60)
"Détection d'anomalies et détection de changement" par Tsuyoshi Ide et Masaru Sugiyama (série professionnelle d'apprentissage automatique) http://ide-research.net/book/support.html
Recommended Posts