Skip to main content

· 3 min read
Darvin Cotrina

La detección de anomalías es el proceso de identificar patrones inusuales en los datos. Es un problema de aprendizaje no supervisado, lo que significa que no necesitamos tener etiquetas para entrenar nuestro modelo. En cambio, nuestro modelo aprenderá a identificar patrones inusuales en los datos por sí mismo.

La detección de anomalías se puede aplicar a una amplia gama de dominios, como la detección de fraudes con tarjetas de crédito, la detección de fallas en equipos de fabricación o la detección de anomalías médicas.

Estimación de densidad

La detección de anomalías se puede realizar utilizando un modelo de estimación de densidad. La idea es que los datos normales se distribuirán de manera diferente a los datos anormales. Por lo tanto, podemos estimar la densidad de los datos normales y luego identificar los puntos de datos que tienen una densidad significativamente menor como anomalías.

Dado el conjunto de datos de entrenamiento {x(1),x(2),,x(m)}\{x^{(1)}, x^{(2)}, \ldots, x^{(m)} \}, donde cada ejemplo tiene nn características, podemos estimar la densidad de los datos como:

p(x)=p(x1;μ1,σ12)×p(x2;μ2,σ22)××p(xn;μn,σn2)p(x) = p(x_1; \mu_1, \sigma_1^2) \times p(x_2; \mu_2, \sigma_2^2) \times \ldots \times p(x_n; \mu_n, \sigma_n^2) =j=1np(xj;μj,σj2)= \prod_{j=1}^n p(x_j; \mu_j, \sigma_j^2)

Algoritmo de detección de anomalías

  1. Elija las características xix_i que crea que pueden indicar anomalías.

  2. Ajuste los parámetros μ1,,μn,σ12,,σn2\mu_1, \ldots, \mu_n, \sigma_1^2, \ldots, \sigma_n^2 en el conjunto de entrenamiento {x(1),x(2),,x(m)}\{x^{(1)}, x^{(2)}, \ldots, x^{(m)} \}. μ=1mi=1mx(i)\vec{\mu} = \frac{1}{m} \sum_{i=1}^m \vec{x^{(i)}} σ2=1mi=1m(x(i)μ)2\vec{\sigma^2} = \frac{1}{m} \sum_{i=1}^m (\vec{x^{(i)}} - \vec{\mu})^2

  3. Dado un nuevo ejemplo xx, compute p(x)p(x):

p(x)=j=1np(xj;μj,σj2)=j=1n12πσjexp((xjμj)22σj2)p(x) = \prod_{j=1}^n p(x_j; \mu_j, \sigma_j^2) = \prod_{j=1}^n \frac{1}{\sqrt{2\pi}\sigma_j} \exp \left( - \frac{(x_j - \mu_j)^2}{2\sigma_j^2} \right)

  1. Si p(x)<ϵp(x) < \epsilon, marque un ejemplo de anomalía.

image.png

Escoger que caracteristicas usar

En Deteción de Anomalías, se debe escoger que caracteristicas usar, ya que si se usan todas las caracteristicas, el algoritmo no funcionará correctamentem.

Caracteristicas no gaussianas

Cuando encontramos caracteristicas que no son gaussianas, se debe aplicar una transformación a los datos para que se vuelvan gaussianos.

por ejemplo:

  • x1=log(x1)x_1 = \log(x_1)
  • x2=log(x2+c)x_2 = \log(x_2 + c)
  • x3=x3x_3 = \sqrt{x_3}
  • x4=x41/3x_4 = x_4^{1/3}

En python

from scipy.stats import skewnorm
import matplotlib.pyplot as plt

numValues = 1000
maxValue = 100
skewness = 20

randomValues = skewnorm.rvs(a=skewness, loc=maxValue, size=numValues)

randomValues = randomValues - min(randomValues) # cambia el conjunto de datos para que comience en 0
randomValues = randomValues / max(randomValues) # cambia el conjunto de datos para que termine en 1
randomValues = randomValues * maxValue # cambia el conjunto de datos para que termine en maxValue

x = randomValues

fig, ax = plt.subplots(1, 3, figsize=(15, 5))

ax[0].hist(x, bins=50)
ax[0].set_title('X')

# x**2
ax[1].hist(x**2, bins=50)
ax[1].set_title('X^2')

# x**0.4
ax[2].hist(x**0.4, bins=50)
ax[2].set_title('X^0.4')

plt.show()

Error en el analisis para detección de anomalías

El problema más común en la detección de anomalías es que el conjunto de datos de entrenamiento contiene muy pocos ejemplos de anomalías. Por lo tanto, el algoritmo de detección de anomalías no puede aprender lo suficiente sobre los ejemplos de anomalías para identificarlos correctamente en el conjunto de prueba.

· 4 min read
Darvin Cotrina

Las expresiones regulares son una secuencia de caracteres que forman un patrón de búsqueda, principalmente utilizadas para la búsqueda de patrones de cadenas de caracteres u operaciones de sustituciones.

Caracteres especiales

Los caracteres especiales son aquellos que tienen un significado especial para las expresiones regulares. Por ejemplo, el punto y coma (;) es un caracter especial que se utiliza para separar instrucciones en Python. Sin embargo, en las expresiones regulares, el punto y coma (;) es un caracter especial que se utiliza para indicar que el patrón de búsqueda debe coincidir con cualquier caracter.

A continuación se muestra una lista de los caracteres especiales más utilizados en las expresiones regulares:

CaracterDescripción
.Coincide con cualquier caracter
^Coincide con el inicio de una cadena
$Coincide con el final de una cadena
*Coincide con 0 o más ocurrencias del caracter anterior
+Coincide con 1 o más ocurrencias del caracter anterior
?Coincide con 0 o 1 ocurrencia del caracter anterior
{n}Coincide con n ocurrencias del caracter anterior
{n,}Coincide con n o más ocurrencias del caracter anterior
{n,m}Coincide con un rango de ocurrencias del caracter anterior
[]Coincide con cualquier caracter dentro de los corchetes
[^...]Coincide con cualquier caracter que no esté dentro de los corchetes
(…)Agrupa una serie de patrones
Coincide con un espacio en blanco
Coincide con cualquier caracter que no sea un espacio en blanco
Coincide con cualquier caracter alfanumérico
Coincide con cualquier caracter que no sea alfanumérico
Coincide con cualquier caracter numérico
Coincide con cualquier caracter que no sea numérico

Trabajando en python

para trabajar con expresiones regulares en python, se debe importar el módulo re. A continuación se muestra un ejemplo de como utilizar el módulo re para buscar un patrón en una cadena de caracteres:

import re

Encontrar todas las coincidencias

text = "Hola, mi nombre es Juan y mi número de teléfono es 123456789"
pattern = r"mi"

print(re.findall(pattern, text))
pattern = r"\d+"
print(re.findall(pattern, text))
['mi', 'mi']
['123456789']

Sustituir un patrón en una cadena de caracteres

text = "Hol, mi nombre es Juan y mi nUmero de teléfono es 123456789"

text = re.sub(r"Hol", "Hola", text)
print(text)
text = re.sub(r"U", "ú", text)
print(text)
Hola, mi nombre es Juan y mi nUmero de teléfono es 123456789
Hola, mi nombre es Juan y mi número de teléfono es 123456789

Dividir una cadena de caracteres

text = "Hola, mi nombre es Juan y mi número de teléfono es 123456789"

text_split = re.split(r"y", text)
text_split
['Hola, mi nombre es Juan ', ' mi número de teléfono es 123456789']

Python tambien tiene integrado funciones de expresiones regulares en el módulo string.

text = "Hola, mi nombre es Juan y mi número de teléfono es 123456789"
print(text.replace("Juan", "Darvin"))
print(text.split(','))
Hola, mi nombre es Darvin y mi número de teléfono es 123456789
['Hola', ' mi nombre es Juan y mi número de teléfono es 123456789']

Estos son solo alguno de todos los metodos que tiene python para trabajar con expresiones regulares. Para más información, puede consultar la documentación oficial de python en el siguiente enlace: https://docs.python.org/3/library/re.html

· 3 min read
Darvin Cotrina

¿Que es clustering?

Clustering es un método de aprendizaje no supervisado, que consiste en agrupar un conjunto de objetos de tal manera que los objetos del mismo grupo (o cluster) sean más similares (en algún sentido o en algún aspecto) entre sí que los de otros grupos.

k-means

k-means es un algoritmo de clustering que consiste en agrupar un conjunto de objetos de tal manera que los objetos del mismo grupo (o cluster) sean más similares (en algún sentido o en algún aspecto) entre sí que los de otros grupos.

Algoritmo de k-means

  1. Inicializar los centroides de los clusters aleatoriamente (k puntos): μ1,μ2,...,μk\mu_1, \mu_2, ..., \mu_k
repeat{#Asignar puntos a los centroides del clusterfor i=1 to mc(i):=ıˊndice (de 1 a k) del centroide maˊs cercano a x(i)# Mover los centroides de los clustersfor k=1 to kuk:=promedio de los puntos asignados al cluster k}\begin{align} repeat \{ \\ & \# Asignar \ puntos \ a \ los \ centroides \ del \ cluster \\ & \text{for } i = 1 \text{ to } m \\ & \quad c^{(i)} := \text{índice (de 1 a k) del centroide más cercano a } x^{(i)} \\ & \# \ Mover \ los \ centroides \ de \ los \ clusters \\ & \text{for } k = 1 \text{ to } k \\ & \quad u_k := \text{promedio de los puntos asignados al cluster } k \\ \} \\ \end{align}

Convergencia de k-means

k-means optimización objetivo

  • c(i)c^{(i)} = índice del cluster (1, 2, ..., k) al que se asigna el ejemplo x(i)x^{(i)}
  • uku_k = vector de parámetros del centroide del cluster kk
  • μc(i)\mu_{c^{(i)}} = vector de parámetros del centroide del cluster al que se asigna el ejemplo x(i)x^{(i)}

Función de costo

J(c(1),...,c(m),μ1,...,μk)=1mi=1mx(i)μc(i)2J(c^{(1)}, ..., c^{(m)}, \mu_1, ..., \mu_k) = \frac{1}{m} \sum_{i=1}^{m} ||x^{(i)} - \mu_{c^{(i)}}||^2

Objetivo: Encontrar c(1),...,c(m),μ1,...,μkc^{(1)}, ..., c^{(m)}, \mu_1, ..., \mu_k que minimicen JJ.

minc(1),...,c(m),μ1,...,μkJ(c(1),...,c(m),μ1,...,μk)min_{c^{(1)}, ..., c^{(m)}, \mu_1, ..., \mu_k} J(c^{(1)}, ..., c^{(m)}, \mu_1, ..., \mu_k)

Inicializando k-means

  • Seleccionar aleatoriamente kk ejemplos de entrenamiento x(1),...,x(k)x^{(1)}, ..., x^{(k)} que servirán como los centroides iniciales: μ1,...,μk\mu_1, ..., \mu_k.

Elección del número de clusters

¿Cual es el número de clusters óptimo?

Para elegir el número de clusters óptimo se puede utilizar los siguientes 2 métodos:

  • Método del codo: el metodo del codo consiste en graficar el valor de la función de costo JJ en función del número de clusters kk. El número de clusters óptimo será el valor de kk en el que la función de costo JJ se "quiebre" o tenga un cambio de pendiente más pronunciado.

Método del codo

No es una buena métrica para elegir el número de clusters óptimo, ya que no siempre se puede identificar un cambio de pendiente claro en la gráfica, no hay un codo claro.

la elección del número de clusters es subjetiva, depende de la aplicación y del contexto.

· 4 min read
Darvin Cotrina

El comando mágico %timeit en Jupyter Lab es una forma conveniente de medir el tiempo de ejecución de una expresión o una función directamente en tus celdas de código. Puedes utilizar %timeit para obtener rápidamente el tiempo promedio de ejecución y comparar diferentes enfoques de implementación.

1. Uso básico de %timeit

Para utilizar %timeit, simplemente coloca el comando mágico antes de la expresión o función que deseas medir. Por ejemplo, para medir el tiempo de ejecución de la expresión '1 + 1', puedes usar el siguiente código en una celda de Jupyter Lab:

%timeit 1 + 1
10.1 ns ± 0.491 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)

Después de ejecutar la celda, %timeit ejecutará la expresión '1 + 1' varias veces y mostrará el tiempo promedio de ejecución. En este caso, el tiempo promedio de ejecución en unidades de tiempo

2. Tabla de tiempos

AbreviaturaUnidad de tiempo
nsnanosegundos
usmicrosegundos
msmilisegundos
ssegundos
mminutos
hhoras

3. Especificar el número de ejecuciones y repeticiones

Por defecto, %timeit ejecuta la expresión o función 100.000 veces y repite la operación tres veces. Puedes especificar el número de ejecuciones y repeticiones utilizando la sintaxis %timeit -r <repeticiones> -n <ejecuciones>. Por ejemplo, para ejecutar la expresión '1 + 1' 10.000 veces y repetir la operación cinco veces, puedes usar el siguiente código:

%timeit -r5 -n50 1 + 1
25.6 ns ± 5.28 ns per loop (mean ± std. dev. of 5 runs, 50 loops each)

En el comando anterior espesificamps que se ejecute 50 veces en 5 repeticiones

5. Medir el tiempo de ejecución de una función

También puedes utilizar %timeit para medir el tiempo de ejecución de una función. Por ejemplo, para medir el tiempo de ejecución de la función sum() de Python, puedes usar el siguiente código:

def mi_funcion():
# puedes colocar cualquier código aquí
return 1 + 1

Jupyter Lab ejecutara el código y te devolvera el tiempo de ejecución de la función

6. Medir el tiempo de ejecución de una celda

También puedes utilizar %timeit para medir el tiempo de ejecución de una celda completa. Por ejemplo, para medir el tiempo de ejecución de la siguiente celda, puedes usar el siguiente código:

%%timeit
x = 1
x += 1
36.7 ns ± 1.13 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

7. Obtener el tiempo de ejecución como variable

En caso de que desees obtener información más detallada sobre el tiempo de ejecución, podrias asignar el resultado de %timeit a una variable, para esto utilizaremos las opciones -o para almacenar el resultado y -q para silenciar la salida de la celda. Por ejemplo, para obtener el tiempo de ejecución de la expresión '1 + 1' como una variable, puedes usar el siguiente código:

resultado = %timeit -o -q 1 + 1
print(f'El mejor tiempo fue {resultado.best}')
print(f'El peor tiempo fue {resultado.worst}')
El mejor tiempo fue 9.775258000008763e-09
El peor tiempo fue 1.1235137999756262e-08

Hemos visto de forma muy rapida como usar el comando magico %timeit en Jupyter Lab, con expresiones muy sencillas, pero en la practica se utiliza para medir el tiempo de ejecución de funciones y celdas completas, lo cual es muy util para comparar diferentes enfoques de implementación.

· One min read
Darvin Cotrina

Tanto numpy como pandas tienen funciones que permiten aplicar una funcion a un array o dataframe, respectivamente, de forma vectorizada. Esto significa que la funcion se aplica a todos los elementos del array o dataframe, sin necesidad de iterar sobre ellos. Esto es mucho mas eficiente que iterar sobre los elementos, ya que no se necesita hacer un loop en python, sino que la funcion se aplica en C.

import numpy as np
import pandas as pd

# comparación de vectorize de numpy vs apply de pandas

# vectorize de numpy
def f(x):
return x**2 + 1

array = np.arange(100000, dtype=np.int16)

%timeit np.vectorize(f)(array)
# pandas apply
df = pd.DataFrame({'x': array})
%timeit df['x'].apply(f)
24.2 ms ± 1.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
40.7 ms ± 1.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Esta es una comparación muy simple entre ambas formas de aplicar una funcion, pero nos da una idea bastante clara de la diferencia de performance entre ambas, como podemos ver vectorize fue mucho mas rapido que apply.

· 2 min read
Darvin Cotrina

¿Alguna vez has tenido que optimizar el código de un programa? line_profiler es una herramienta que te permite perfilar el código de un programa para encontrar las partes que más tiempo consumen. En este notebook veremos cómo usarla.

Instalación

Como line_profiler no viene instalado por defecto en Anaconda, lo instalaremos con conda:

En la terminal:

pip install line_profiler

En el notebook:

! pip install line_profiler

¿Cómo funciona en Jupyter?

line_profiler es una herramienta que permite perfilar el código de un programa. Esto significa que nos permite ver cuánto tiempo se tarda en ejecutar cada línea de código. Para ello, line_profiler nos permite usar el comando %lprun en Jupyter. Este comando nos permite perfilar una función. Para ello, debemos añadir el decorador @profile a la función que queremos perfilar.

cargar el módulo line_profiler en el notebook:

%load_ext line_profiler
The line_profiler extension is already loaded. To reload it, use:
%reload_ext line_profiler

Perfilando una función

Perfilar una funcion en en jupyter lab ees muy sencillo con el comando %lprun. Para ello vamos a crear una funcion de prueba que calcule el doble de una lista de números:


def funcion_prueba():
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
doble = []
for item in data:
doble.append(item * 2)

return doble
%lprun -f funcion_prueba funcion_prueba()
Timer unit: 1e-07 s

Total time: 8e-06 s

Could not find file C:\Users\WillyCotrina\AppData\Local\Temp\ipykernel_14792\1026023441.py
Are you sure you are running this program from the same directory
that you ran the profiler from?
Continuing without the function's contents.

Line # Hits Time Per Hit % Time Line Contents
==============================================================
1
2 1 7.0 7.0 8.8
3 1 3.0 3.0 3.8
4 9 24.0 2.7 30.0
5 9 43.0 4.8 53.8
6
7 1 3.0 3.0 3.8

Como pudimos notar pefilar una funcion es muy sencillo y extremaente util para optimizar el codigo de un programa.

· 2 min read

El teorema del límite central (TLC) establece que, para una muestra aleatoria de tamaño nn, la distribución de las medias muestrales se aproxima a una distribución normal a medida que nn aumenta.

Sean X1,X2,,XnX_1, X_2, \ldots, X_n una muestra aleatoria de tamaño nn de una población con media μ\mu y varianza σ2\sigma^2. Entonces, para nn suficientemente grande, la variable aleatoria es:

Zn=Xˉμσ/nZ_n = \frac{\bar{X} - \mu}{\sigma / \sqrt{n}}

En python

Para graficar el TLC en python, usaremos un ejemplo de tirar dados.

import numpy as np  
import matplotlib.pyplot as plt
# Tiraremos 10 veces los dados y calcularemos la media
dados = list(range(1,7))
muestra_10 = np.random.choice(dados, size=10, replace=True)
media = np.mean(muestra_10)
print("La media de la muestra es: ", media)
La media de la muestra es:  3.0

Como podemos ver la Media de esta muestra no es 3.5, hora veamos que pasa si hacemos este mismo experimento pero 10 veces.

exp_10 = [np.mean(muestra) for muestra in np.random.choice(dados, size=(10, 10), replace=True)]

# Graficamos el histograma de las medias
plt.hist(exp_10, bins=10, density=True, alpha=0.5)
plt.vlines(3.5, 0, 1, color='red', label='Media teórica')
plt.vlines(np.mean(exp_10), 0, 1, color='green', label='Media muestral')
plt.show()

Ahora veamos que pasa si hacemos este mismo experimento pero 1000 veces.

exp_1000 = [np.mean(muestra) for muestra in np.random.choice(dados, size=(1000, 10), replace=True)]
# Graficamos
plt.hist(exp_1000, bins=10, density=True, alpha=0.5)
plt.vlines(3.5, 0, 1, color='red', label='Media teórica')
plt.vlines(np.mean(exp_1000), 0, 1, color='green', label='Media muestral')
plt.show()

Como podemos ver, a medida que aumentamos el número de experimentos, la distribución de las medias muestrales se aproxima a una distribución normal.

caution

Si ejecutas este código en tu computadora, es posible que no obtengas los mismos resultados que yo, ya que los números aleatorios son generados de forma aleatoria.

· 3 min read
Darvin Cotrina

Modelo de arbol de desición

Un árbol de decisión es un modelo de predicción utilizado en el ámbito de la inteligencia artificial, que utiliza un árbol de estructura similar a los diagramas de flujo en donde cada nodo representa una característica (o atributo), cada rama representa una regla de decisión y cada hoja representa el resultado de una decisión. Los árboles de decisión son utilizados comúnmente en minería de datos con el fin de resolver problemas de clasificación.

Ejemplo de un arbol de desición y su estructura:

Arbol de desición

Entropía

¿Qué es la entropía?

La entropía es una medida de incertidumbre. En el contexto de la toma de decisiones, la entropía mide la impureza de un conjunto de ejemplos S. Si S solo contiene ejemplos de una clase, entonces la entropía es 0. Si S contiene una cantidad uniforme de ejemplos de cada clase, entonces la entropía es 1. La entropía de un conjunto S se denota por H (S).

¿Cómo se calcula la entropía?

La entropía de un conjunto S se calcula como:

H(S)=i=1cpilog2piH(S) = -\sum_{i=1}^{c} p_i log_2 p_i

Donde:

  • cc es el número de clases
  • pip_i es la proporción de ejemplos de clase ii en SS
  • log2log_2 es el logaritmo en base 2

Ejemplo de cálculo de entropía

Supongamos que tenemos un conjunto de ejemplos SS con 14 ejemplos de clase 1 y 6 ejemplos de clase 2. La entropía de SS es:

P1=14/20P_1 = 14/20 y P2=6/20P_2 = 6/20

Entonces, la entropía de SS sería: H(S)=(1420log21420+620log2620)0.88H(S) = - \left(\frac{14}{20} \log_2 \frac{14}{20} + \frac{6}{20} \log_2 \frac{6}{20}\right) \approx 0.88.

Ganancia de información

La ganancia de información(IG) se utiliza para decidir qué atributo se utilizará para dividir el conjunto de datos en subconjuntos homogéneos. La ganancia de información se define como la diferencia entre la entropía antes de la división y la entropía después de la división por un atributo. La ganancia de información se denota por IG (S, A) y se calcula como:

IG(S,A)=H(S)vValues(A)SvSH(Sv)IG(S, A) = H(S) - \sum_{v \in Values(A)} \frac{|S_v|}{|S|} H(S_v)

Donde:

  • SS es el conjunto de ejemplos
  • AA es el atributo utilizado para dividir SS en subconjuntos
  • Values(A)Values(A) es el conjunto de valores que puede tomar el atributo AA
  • SvS_v es el subconjunto de SS en el que el atributo AA tiene el valor vv

Indice Gini

El índice de Gini es una medida de impureza utilizada en los árboles de decisión para decidir qué atributo dividir un nodo en dos o más subnodos. El índice de Gini se define como:

Gini(S)=1i=1cpi2Gini(S) = 1 - \sum_{i=1}^{c} p_i^2

Donde:

  • cc es el número de clases
  • pip_i es la proporción de ejemplos de clase ii en SS

Pros y contras de los árboles de decisión

Pros

  • Fácil de entender e interpretar. Los árboles se pueden visualizar.
  • Puede ser muy util para solucionar problemas relacionados con decisiones.
  • Hay menos requisitos de limpieza de datos

Contras

  • Los árboles de decisión pueden ser poco precisos. Pueden ser muy sensibles a pequeños cambios en los datos.