Comparar pantallas para encontrar errores visuales durante los test del software

Uno de los problemas de los test automáticos para probar aplicaciones es que no se guían por el aspecto visual de la aplicación. Puedes tener un botón torcido, un texto que no se lee o un color que no corresponde y los test serán correctos, sin embargo para el usuario es importante que el botón que tiene que pulsar sea visible o que pueda leer ese texto ilegible.

Es una tarea difícil de automatizar. Programar código para comprobar la correcta disposición de todos los elementos es algo muy costoso. Y tener una IA que detecte posible elementos erróneos puede sonar muy interesante pero es aun más costoso y complicado. Así que vamos a optar por una solución más sencilla, comparar una imagen que sabemos que esta bien con una captura de pantalla de la aplicación durante las pruebas. Luego calcularemos las diferencias entre ambas imágenes.

Hay múltiples librerías para realizar la comparación de la imagen como pixelmatch o Resemble.js. Aunque estas librerías tienen bastante funciones el algoritmo básico de comparación es muy sencillo. Se comparan uno a uno los pixel de la imagen de referencia con los de la imagen capturada, se fija un valor de umbral. Si la diferencia entre ambos pixel es mayor que el valor umbral se considera que ese pixel es distinto y se marca.

Para que funcione bien la imagen que se usa como modelo ha de ser idéntica a la que se obtiene de la captura de pantalla. Podéis estar pensando que siempre se puede reescalar una de la dos imágenes pero generalmente eso mete «ruido» en los bordes de los elementos de la imagen que el algoritmo de comparación detecta como diferencias.

Los pasos a segir son los siguientes:

  1. Captura de pantalla
  2. Cargar imagen modelo correspondiente
  3. Crear una tercera imagen comparando los pixeles de las dos anteriores. Cada pixel cuya diferencia supere cierto valor fijado se marcara como «diferente», por ejemplo poniéndolo en rojo.
  4. Buscar si existe alguna diferencia en la imagen resultado de la comparación
  5. Si existe alguna diferencia se deja la imagen con el resultado de la comparación y se avisa al usuario para que la revise.

El principal problemas son los datos que cambian cada vez que se realiza el test. Por ejemplo: ids de elementos, fechas, horas, elementos generados en parte o completamente por procesos (pseudo)aleatorios. Estos elementos dejan «una mancha» en el resultado de la comparación. Una solución para evitar estas manchas es «taparlas» para ello vamos a usar plantillas con una zona de un color especial que cuando el algoritmo las lee ignora.

Otra técnica para ignorar «pequeñas cantidades de error» es dividir la imagen en cuadrados, por ejemplo de 16×16. En cada cuadrado hay un total de 256 pixeles, solo lanzaremos una advertencia si la cantidad de pixeles marcados como diferentes supera cierto número. Por ejemplo el 20%, o lo que es lo mismo 51 pixeles. Con esto logramos que solo diferencias «notables» se consideren.

Este es un sistema realmente simple para ayudar a resolver el problema de comprobar el aspecto visual de la aplicación o la web durante los test. Si bien no resuelve todo el problema y sigue siendo necesario un humano para verificar las imágenes marcadas como errores. Agiliza mucho el proceso de comprobar el correcto aspecto visual de la aplicación o web.

Calcular la media aritmética, media geométrica, media armónica y media cuadrática en Arduino

Vamos a ver como implementar más funcione estadísticas en un entorno tan limitado como Arduino. Para ello necesitamos usar formas acumulativas de cálculo. En este caso acumulativas se refiere a que no tengan que calcularse de nuevo todos los valores cada vez que se añade uno nuevo, esto nos ahorra gran cantidad de cálculos y de espacio en memoria.

Media aritmética

Es o que normalmente llamamos «media». Corresponde con la suma de cada uno de los valores de muestra dividido entre el numero de valores:

\frac{1}{n} \sum_{} x

Ya la vimos como calcularla de forma acumulativa, vamos a recordarlo rápidamente:

mean = mean + (x-mean)/n);

Media geométrica

Es la raiz enesima del producto de cada uno de los valores:

\sqrt[n]{\prod_{} x}

Vamos a desarrollar nuestro cálculo acumulativo a partir del modelo acumulativo para calcularla que desarrollan en este articulo.

Resumiendo, calculamos la media de ln(x) usando la formula de la media acumulada vista antes:

meanLn = meanLn + ((log(x)-meanLn)/n);

Para calcula la media geométrica a partir de este valor vasta con elevar el numero e al valor calculado:

Necesitaras declarar el número e:

const double e=2.71828;

Media armónica

Se calcula dividiendo el numero de muestras entre el sumatorio de uno partido por el valor de cada muestra. (Si no te has enterado, tranquilo, no me he enterado ni yo y soy el que lo ha escrito). Se ve mejor con la fórmula:

\frac{n}{\sum_{} 1/x}

En lugar de usar la versión acumulativa vamos a optar por aprovecharnos de la relación entre las distintas medias:

harmonica = \frac{geometrica^2}{aritmetica}

En código:

harmonicMean =  pow(geometricMean(), 2)/mean();

Media Cuadrática

Es la raíz cuadra del sumatorio del cuadrado de los valores:

\sqrt{\frac{1}{n} \sum_{} x^2}

Para calcularlo usamos la misma formula que para la media aritmética pero aplicada al cuadrado del valor:

mean2 = mean2 + (((x*x)-mean2)/n);

Luego para obtener el valor final solo hemos de calcular la raíz cuadrada de la misma:

sqrt(mean2):

Puede encontrar el código de la implementación de todo esto en este proyecto de github.

De regresión lineal a regresión logística en Arduino

Ya hemos visto como calcular la regresión lineal en Arduino y como a partir de esta calcular diversos tipos de regresiones. Lo que vamos a ver aquí es usar un truco para convertir la regresión lineal en regresión logística basandonos en la función sigmoide.

La regresión lineal se usa como clasificador binario entre dos conjuntos. En el caso ideal de regresion logística para cualqueir valor de x devuelve un valor de y que es 0 o 1 dependiendo de a que clase pertenezca. Pero en la vida real rara vez suele ser un «caso ideal» y hay valores para los que devolverá un valor comprendido entre 0 y 1. Este resultado puede interpretarse como la probabilidad de que sea del grupo representado por el valor 1 o cuadno esto carezca de sentido simplemente tomar cualqueir valor mayor de 0,5 como del grupo del 1 y cualquie valor por debajo como del grupo del 0.

La función sigmoide se define como:

1 / 1 + e^{-y}

El valor de y lo podemos sacar de la regresión lineal:

y = mx +b

Juntandolo todo:

1 / 1 + e^{-(mx+b)}

Veamos las diferencias entre ambas fórmulas:

Regresión lineal (verde) comparada con regresión logística (naranja)

Regresión Lineal:

  • Su fórmula define una linea
  • No está acotada, no tiene un valor máximo ni mínimo
  • Se usa para estimar valores.
  • Devuelve un valor numérico

Regresión logística:

  • Su fórmula define una "S"
  • Esta acotada entre 1 y 0
  • Se usa para clasificar un valor en uno de dos grupos. Clasificador binario.
  • El resultado que devuelve se puede interpretar de dos maneras: como probabilidad de pertenecer a un grupo si se toma el valor directamente o como pertenencia absoluta a un grupo u otro si se considera que cuando el valor obtenido este por encima de 0,5 se pertenece a uno y por debajo al otro.

Forma de implementarlo

La forma de implementar esto en un Arduino es aprovechar la librería que ya tenemos de regresión lineal y que nos soluciona los problemas de memoria y tiempo de cálculo que tienen los cálculos estadísticos en Arduino. Simplemente una vez que nuestro sistema aprenda el modelo lineal basta con transformar el resultado que devuelve este modelo para convertir su respuesta a la de una regresión logística.

    double exp = linealRegression.calculate(x)*-1; 
    return 1/1+pow(e, exp);

La implementación del código se puede encontrar en la librería Regressino

Utilidad

¿Tiene sentido transformar una regresión lineal en un modelo de regresión logística?. Aunque esta conversión se puede realizar para cualquier regresión lineal no tiene sentido hacerlo. Solo tiene sentido usarlos cuando se quiera entrenar un clasificador binario y haya dos grupos de elementos claramente diferenciables. Entonces se puede calcular la recta de regresión y convertirla en una regresión logística que funcione como clasificador.

Tampoco va servir para calsificar cualquier grupo de elementos, han de ser linealmente separables. dicho de forma más intuitiva, tienen que poder separarse trazando una linea recta entre ellos.

En definitiva, sin ser una opción ideal, es suficiente buena y útil como para plantearse su uso.

Estadísticas básicas en Arduino

Como ya vimos en el post sobre regresión lineal en Arduino, el principal problema que plantea Arduino para realizar cálculos estadísticos es la escasa capacidad de memoria y cálculo que tiene. Para ello en lugar de guardar todos los datos vamos a usar formulas que permiten aproximar los valores estadísticos que vamos a utilizar sin gastar casi recursos, la idea es guardar solo una aproximación.

En el siguiente enlace puedes encontrar la librería SimpleStatisticsArduino de Arduino que implenta lo explicado en este texto.

Para la varianza y la media usaremos las siguientes formulas que tratan de aproximar

numeroDeMuestras++;
media += (nuevoValor – media)/numeroDeMuestras;
media2 += (value^2 – media2)/numeroDeMuestras;
varianza = media2 – media^2;

Con estos valores podemos aproximar la suma de todas los datos:

suma = media*numeroDeMuestras;

La desviación estándar :

desviacionEstandar = sqrt(varianza);

Otros dos valores que podemos almacenar de forma muy sencilla y casi sin costes es el valor mínimo y máximo. Cada nuevo valor se comprueba:

if(minimo > nuevoValor){
minimo = nuevoValor;
}
if(maximo < nuevoValor){
maximo = nuevoValor;
}

Ahora con estos dos valores podemos calcular el valor central, que no es lo mismo que la media:

centro = (maximo – minimo) / 2;

Con esta estrategia solo necesitamos 6 variables para almacenar los datos sobre los que se calcula la estadística.

Estadística con dos variables en Arduino

Tenemos dos variables X e Y, partiendo de los cálculos del apartado anterior para cada una ahora podemos calcular los valores conjuntos, para ello debemos de almacenar dos variables más necesarias para calcular la covarianza:

mediaXY += ((XY)-mediaXY)/numeroDeMuestras;
covarianza = mediaXY – (mediaXmediaY);

Ahora con la covarianza podemos calcular la correlación:

correlacion = covarianza / (desviacionEstandarX * desviacionEstandarY);

Con estos datos podemos calcular los parámetros de la regresión lineal:

m = covarianza / varianzaX;
b = mediaY – m*mediaX;

Y la propia regresión:

y = m*x + b;

Si buscas una implementación de la idea de este post pero optimizada exclusivamente para la regresión lineal puede mirar la librería Regressino.

Por último podemos calcular el centroide que no es nada mas que el centro de cada una de las variables X e Y.

centroide = [centroX, centroY]

De esta forma se pueden calcular bastantes valores sin consumir casi memoria o recursos

Como convertir una aplicación de MS-DOS en una aplicación web.

Seguro que todos los que tenemos cierta edad tenemos programas desarrollados para MS-DOS que siempre hemos querido recuperar pero nunca hemos tenido tiempo de volver a programarlos en algo más moderno. En mi caso eran varios videojuegos que había desarrollado cuando era adolescente.

Para ello vamos a usar dos herramientas: DOSBox, JS-DOS (en este caso la versión 6.22).

Preparando la aplicación

Lo primero de todo es montarse DOSBox en tu ordenador e instalar la aplicación que queramos usar en la web. Vamos a llamarla programa.exe. Una vez instalada la iniciamos y la configuramos tal y como queramos que se ejecute desde la web. Si necesitamos incluir algún fichero para que lo abra la aplicación ahora es el momento.

Una vez terminado el paso necesitamos comprimir todos los ficheros necesarios para que funcione la aplicación en un fichero zip. Lo ideal sería que el ejecutable programa.exe quede directamente en la raíz del zip para que al descomprimirlo no quede dentro de ninguna carpeta.

Todo este proceso no es necesario que lo hagamos desde DOSBox, para instalar la aplicación hay que «montar’ una carpeta del ordenador como si fuera una unidad de disco de DOSBox, puedes comprimir allí los ficheros.

Ejecutandola en una web

Vamos a suponer que el fichero se llama programa.zip. Ahora vamos a preparar la web.

Necesitaremos un canvas en el que se ejecutará el emulador de MS-DOS, para referenciarlo vamos a usar el id «jsdos». Para fijar de el tamaño del canvas usaremos CSS.

Debajo se puede ver el código completo:

<!doctype html>

<head>
    <meta charset="utf-8">
    <title>Programa.exe</title>
    <script src="js-dos.js"></script>
    <style>
        #jsdos, .dosbox-container {
            width: 800px;
            height: 600px;
        }
    </style>
</head>

<body>
    <canvas id="jsdos"></canvas>   
    <script>
    Dos(document.getElementById("jsdos"), {
        autolock: true
    }).ready(function (fs, main) {
        fs.extract("programa.zip").then(function () {
            main(["-c", "programa.exe"]).then(function (ci) {              
                window.ci = ci;
            });
        });
    });
    </script>
</body>

</html>

Su funcionamiento es sencillo y se puede entender sin demasiadas explicaciones, algunos puntos que se necesitan aclarar:

fs.extract(«programa.zip») descomprime el fichero zip en el directorio raiz C:

main([«-c», «programa.exe»]) el primer comando va a C: y el segundo ejecuta programa.exe

window.ci = ci; Permite acceder a la API de DosCommandInterface desde cualquier parte de la web.

autolock: true Sirve para capturar el ratón automáticamente cuando se hace click sobre el canvas. Si tu aplicación no usa el ratón para nada puedes prescindir de ponerlo.

Algunas funciones interesantes

  • Entra y salir de modo pantalla completa ci.fullscreen(), ci.exitFullscreen()
  • Realiza una captura de pantalla ci.screenshot()
  • Simular pulsacioenes de teclas ci.simulateKeyPress(keyCode), ci.simulateKeyEvent(keyCode, pressed)
  • Ejecutar comandos ci.shell([cmd1, cmd2, …])

Problemas

Aquí recopilo un listado de cosas que me han dado problemas:

  • Resoluciones raras de pantalla. Forzar la aplicación en formatos que distorsionan mucho el formato 4:3 habitual de los monitores de esa época
  • El sonido no siempre va tan bien como sería deseable
  • El ratón, muchas aplicaciones de MS-DOS usan falsos ratones, imágenes que superponen donde debería estar el ratón. Algunos dan problemas.
  • El uso desde el móvil no está resuelto, aunque no es algo fácil de solucionar. El punto bueno es que te dan la herramientas básicas para que trates de resolverlo.

Ideas interesantes

Algunas ideas interesantes que se me ocurrieron pero no he tenido oportunidad o tiempo de intentaras.

  • Aunque JS-DOS ofrece alguna formas de ejecutar múltiples comandos hay soluciones como los ficheros de procesamiento por lotes .bat
  • Se pueden asociar comandos de MS-DOS, pulsaciones de teclas o del ratón a acciones en la web esto puede dar lugar a interesantes formas de interactuar con la aplicación (incluso de hacerla más accesible)
  • Un gamepad virtual sobre el canvas donde se ejecuta la aplicación. Para usar juegos desde el móvil es imprescindible
  • Usando Apache Cordoba o PhoneGap se podría convertir la web en una aplicación móvil. Aunque no sé si tiene utilidad más allá de entretenerse un rato probando
  • Lo mismo que el punto anterior pero en el escritorio con electron

Optimizar la comparación de distancias entre varios puntos

La función distancia se usa muy a menudo en muchos algoritmos para calcular la distancia entre puntos en el espacio. Hay algoritmos que hacen un uso intensivo de esta función como k-nn, k-means, o casi cualquier método que incluya buscar puntos cercanos. Por lo que reducir el coste de calcularla reporta una mayoría considerable de rendimiento.

Distintas distancias

Por suerte podemos usar funciones distancia diferentes. Una función distancia tiene que cumplir las siguientes condiciones cuando
x != y:

dist(x,x) = 0
dist(x,y) != 0
dist(x,y) = dist(y,x)
dist(x,y) <= dist(x,z) + dist(z,y)

Para calcular la distancia entre dos puntos es habitual usar la distancia euclídea:

dist(A,B) = √∑(Ai-Bi)²

Podemos hacer una primera mejora, si solo necesitamos comparar distancias podemos ahorrarnos la raíz cuadrada. Obtenemos una función distancia perfectamente válida y que puede reemplazar la euclídea

Pero hay una mejora más eliminar los cuadrados de las restas. Sin embargo hay un detalle a tener en cuenta, el cuadro de un número negativo es positivo, por lo que no basta con eliminarlos hay que reemplazarlos por el valor absoluto.

Esta parece un mejora menor pero en caso de espacios de muchas dimensiones puede llegar a notarse.

Pero no somos los primeros en llegar a esta conclusión, es conocida como distancia Manhattan:

dist(A,B) = ∑|Ai-Bi|

Realmente podemos reemplazar la distancia euclídea por cualquier distancia (tiene que cumplir los cuatro puntos anteriores) siempre que la nueva distancia cumpla que si en la distancia euclídea la distancia entre dos puntos cualesquiera A y B es mayor (o menor) que entre dos puntos cualesquiera C y D, en la nueva distancia también ha de ser mayor (o menor) en la nueva distancia.

Desigualdad triangular

Viendo la cuarta propiedad de las funciones distancia, conocida como desigualdad triangular, podemos usarla para acelerar la comparación entre distancias.

Teniendo tres puntos A,B,C sabemos que:

dist(A,B) + dist(B,C) >= dist(A,C)

|dist(A,B) – dist(B,C) | <= dist(A,C)

Es decir la suma de dos lados de un triángulo es mayor o igual que el tercer lado y el valor absoluto de la resta de dos lados es menor o igual que el tercer lado.

En la imagen de debajo de este texto se puede ver ejemplos visuales:

Operaciones entre distancias

Esto nos sirve para acotar distancias sin calcularlas. Calculando solo dos distancias podemos acotar el valor mínimo y máximo de la tercera lo cual nos puede permitir descartarla sin calcularla.

Por ejemplo, nos dan un punto C y tenemos que averiguar qué otro punto está más cerca A o B. A y B ya los conocíamos así que tenemos precalculada dist(A,B). Calculamos dist(B,C), si dist(B,C) < |dist(A,B) – dist(B,C)| entonces podemos decir que dist(A,C) > dist(B,C) ¡Sin calcularla!

En muchas situaciones tenemos puntos que son previamente conocidos/aprendidos. Por lo que podemos tener una tabla de distancias entre puntos precalculadas y usarlas para acotar distancias sin necesidad de calcular la distancia entre todos los puntos.

El problema es que una tabla de distancias entre puntos puede ocupar demasiado espacio en memoria. Veamos una alternativa, calcular la distancia de todos los puntos al mismo punto. Si no hay muy buenas razones para usar otro punto usaremos el origen de coordenadas (el punto cuyas coordenadas son todas ceros). Es muy poco costoso calcular la distancia de origen a un punto P

dist(O, P) =  √∑Pi² (euclídea)

dist(O, P) =  ∑Pi (Manhattan)

Ahora si tenemos precalculadas las distancias de los puntos a comparar con el origen y nos dan un nuevo punto P. Calculamos dist(O, P) y tenemos precalculado dist(O,A) y queremos saber dist(A, P).

Sabemos que:

dist(O, A) + dist(O, P) >= dist(A, P) >= |dist(O, A) – dist(O, P)|

Max dist(A, P) = dist(O, A) + dist(O, P) 

Min dist(A, P) = |dist(O, A) – dist(O, P)|

Ya tenemos una cota mínima y máxima de cual es la distancia de de A a P. Realmente por si misma no nos sirve de nada pero si hay que comparar con muchos puntos es suficiente pare descartar muchos de ellos.

Comparar distancias

Vamos a ver con una imagen la diferencia entre las tres distancias comentada en este texto. Euclídea, Euclídea² (sin hacer la raíz cuadrada) y distancia Manhattan. En la imagen inferior se puede ver la forma y el área que cubre una «circunferencia» de radio 2. (Tomando como circunferencia como aquel objeto geométrico cuyo todos sus puntos estan a la misma distancia del centro, en este caso 0,0)

El valor de tus datos

Vivimos en la época en que los datos son tan valiosos como el dinero. Es imposible desarrollar modelos de Big data o de aprendizaje maquina sin una gran cantidad de datos (el término «big data» puede ser una pista de lo importantes que son). Obtener estos datos es uno de los problemas más costosos de resolver a la hora de implementar estos modelos.

La necesidad de datos no termina ahí, para sacar rendimiento a estos modelos necesitan datos sobre los que aplicarlos. Imagínate que voy al banco a pedir un crédito y solo tienen mi nombre y apellidos, con eso su departamento de riesgos no va a poder evaluar cuanto «riesgo» supone darme un crédito. Necesitarán que rellene un formulario y les lleve varios documentos que les permitan valorar ese riesgo. En ese caso soy consciente de ello, pero en muchos otros no.

No sirve alimentar al sistema con datos a lo loco, estos datos necesitan tener algunas características:

Cantidad: se necesitan montones de datos para obtener un modelo. Obtenerlos es un proceso complicado y en algunos casos caro. Puede ser que no sea necesario recopilar los datos, que ya estén en bruto, pero que sea necesario «extraerlos». Incluso es posible que digitalizarlos.

Procesar estos datos tiene un coste, no basta con «acumularlo y lanzarlos a los algoritmos», necesitas saber que hacer con ellos y adaptarlos. Eso necesita un equipo de expertos detrás.

Calidad: los datos han de ser ciertos y útiles. O lo que es lo mismo han de tener la mínima cantidad de errores.

Esto hay que tenerlo en cuenta al procesar los datos. Según el método de recopilación puede haber errores al introducir los datos, al transcribirlos, malentendidos o directamente datos falsos. ¿Quién no ha mentido al rellenar un formulario?.

Relevancia: si tenemos muchos datos pero no podemos obtener conclusiones de ellos no nos sirven. Por desgracia es posible que durante la fase de recopilación de datos no se sepa muy bien cuáles son útiles o que surja una especie de «síndromes de Diógenes» de los datos y se recopilen todos los que se pueda. Como consecuencia asume que cuando usas una web o una aplicación todo lo que haces es recopilado.

Por ejemplo, saber el número de pie de todos los que entran a una tienda probablemente no nos resulte útil (quizás, si es una zapatería). Sin embargo lo que han comprado si.

Hay un tipo de datos especialmente valioso que sirve para cruzar entre varias fuentes. Un ejemplo es el número de teléfono móvil. Es un dato que no solemos revelar mucho de darlo al realizar compras por internet, encargos, rellenar formularios para tarjetas descuento, …. Sin embargo puede ser tremendamente valioso ya permite identificar al mismo individuo en distintas fuentes de datos y cruzarlas.

Variedad: de nada sirve tener muchos datos de gran calidad si pertenecen a una muestra pequeña de individuos. Es necesario tener muestras del mayor número de sujetos posibles.

Si los datos están sesgados el modelo también lo estará. Por ejemplo si obtienes los datos a partir de una aplicación de descuentos en el móvil estás dejando de lado a toda la gente que no use smartphones o no tengan suficiente soltura para usarlos o no quiera «instalar cosas raras» en ellos. Los datos de esas personas se vuelven más valiosos para completar el modelo. Quizás podría hacer un concurso donde se participe rellenando una papeleta con los datos personales.

Legales y éticos: es importante no recopilar datos que la ley prohíba recopilar o qué no sea ético hacerlo. Aunque es posible hacer trampas y usar proxys. Los proxys son datos que de forma directa o indirecta permiten «deducir» otros datos. A veces no son 100% exactos pero no importa los algoritmos pueden tolerar cierta cantidad de errores. Un ejemplo tan obvio que solo sirve como ejemplo sería no poder pedir el sexo pero si el nombre. Con el nombre se puede extraer el sexo en la mayoría de los casos. Hay veces que un proxy no es un solo dato sino varios.

Baratos: debido a la cantidad de datos necesarios es importante que obtener cada muestra sea barato. Eso puede condicionar el método elegido para recopilarlos e introducir sesgos.

Muchos datos que necesitan tratamiento manual son procesados por personas con sueldos bastante bajos.

Hasta este punto todo han sido costes. El modelo no dará beneficios hasta que empiece a usarse. Esto no es una justificación para no pagar por los datos, de hecho es probable que te hayan pagado en forma de descuentos, promociones, concursos. El problema es que no sabes que van a hacer con esos datos. ¿Y si se usan en un modelo que acaba siendo usado en perjuicio tuyo?

Una vez creado el modelo para obtener beneficio hay que usarlo. Para ello se necesita obtener tus datos para introducirlos al modelo y obtener un resultado. Lo que este resultado aporte al negocio (más conversiones a clientes, más compras, mejor servicio, ahorro de recursos y costes) es el beneficio que produce.

Cuando alguna empresa anuncie que ha desarrollado un sistema que le va a permitir ahorrar o ganar cantidades ingentes de dinero piensa que tus datos han sido necesario para ello.

Guardar datos de nodeMCU (o arduino) en la nube usando IFTTT

La idea es usar IFTTT, que permite interconectar varios servicios, para que nos sirva de repositorio de datos de nuestros sistemas IoT desarrollados con arduino, esp8266, nodeMCU o similares. ¡Y además gratis!.

Webhooks

Nuestro sistema enviara una petición HTTP (GET o POST) al servicio de IFTTT para ello hay que crearse una cuenta allí y activar un evento por webhook, que recibirá los datos que le enviemos.

Los webhook de IFTTT solo permiten pasar hasta tres valores que ademas tiene que ser value1, value2 y value3.

Incluyo un ejemplo de enviar un petición con nodeMCU con 3 valores:

 
void sendMsg(){ 
  http.begin("http://maker.ifttt.com/[tu codigo]/alarm/with/key/[pon aqui tu key]?value1=1&values2=2&value3=3"); //HTTP 
  int httpCode = http.GET(); 
  if(httpCode > 0){
    Serial.println("Alarm send"); 
  } else {
    Serial.println("Error send alarm"); 
  } 
  delay(1000); 
}
 


IFTTT recibe un nombre de evento, tres parámetros y el timestamp de cuando ha ocurrido el evento:

{{OccurredAt}}
{{EventName}}
{{Value1}}
{{Value2}}
{{Value3}}

Okey tenemos esos cinco parámetros ¿Que podemos hacer con ellos?

El que incluya la hora del evento es muy útil ya que muchos sistemas no tiene un reloj y no pueden saber que hora es.

Hay que tener en cuenta es si tenemos una sola placa emitiendo datos o tenemos varias. Si tenemos varias podemos usar el nombre del evento o uno de los valores para indicar que placa es, es algo muy útil sobre todo cuando una de ellas empieza a dar datos erróneos así resulta fácil filtrarlos.

Puede parecer que un nombre de evento y tres valores son pocos pero para muchos sistemas de IoT son mas que suficientes.

Hoja de cálculo

Mi primera idea es guardar los datos que nos interese en una hoja de calculo de Google. IFTTT nos permite guardar los datos en filas, con una columna cada datos. Hasta 2000 filas, luego no deja de guardarlas, pero crea otra hoja de cálculo.

La ventaja de este sistema es que podemos conectarlo para que genere tablas, gráficas o realice cálculos de forma automática. Con poco trabajo nos podemos montar un sistema que almacene y procese lo datos generando informes y gráficas todo ello gratis.

Fichero de texto

Otra opción es usar un fichero de texto y almacenar los datos estilo CSV, que consiste en separar los valores por comas y ya esta. Se pueden usar varios, en mi caso opte por Dropbox. La ventaja de este tipo de ficheros es que es muy sencillo de procesar por cualquier dispositivo. De hecho hay tres maneras de hacerlo con Dropbox.

  • Sincronización: tener Dropbox sincronizado en el dispositivo con lo que el fichero de actualiza «solo» con cada cambio
  • API: usar la API de Dropbox para acceder a ese fichero
  • Publico: Dropbox permite crear enlaces públicos a ficheros. Una vez generado ese enlace leer es fichero es tan sencillo como hace una petición GET

En el caso de este tipo de ficheros el limite de IFTTT esta en 2 megas de datos.

Redes sociales

Si los datos han de ser compartidos con otras personas, las redes sociales pueden ser una buena solución, por ejemplo una cuanta de Twitter (privada si no quieres que los datos sean accesibles por todos). Además también se puede automatizar el acceso a los datos usando la API de Twitter

Notificaciones

Si los avisos tienen que llegar en tiempo real (o casi) se pueden usar las notificaciones a móvil (ya hice un ejemplo) o si tienen que llegar a un grupo de gente se pueden notificar en un grupo de Telegram. Este mismo sistema se puede usar para notificaciones personales si quieres guardar un histórico de ellas.

Calendario

Este caso es solo una idea, pero en el caso de eventos que ocurran solo de vez en cuando y se quiera tener un registro de cuando ocurrieron se podría usar la integración con alguno de los calendarios.

No resulta útil para eventos muy numerosos ya que es difícil de consultar cuando hay muchos en un día

Entropía

Supongo que la entropía os sonara de la termodinámica y así es. Cuando Shannon desarrollo la teoría de la información llego a una formula similar a la que describe la entropía en los sistema físicos. He aquí la formula:

H(x) = – Σ p(xi) * log2(p(xi))

Es decir, si tenemos una variable x con distintos valores posibles, para cada valor (xi) cuya probabilidad es p(xi) la entropía de ese valor es:

 p(xi) * log2(p(xi))

Y la entropía total es la suma de la entropía de cada valor. A veces a cada posible valor se le llama signo.

Por ejemplo supongamos un semáforo que se pasa el 45% del tiempo en rojo, el 45% en verde y el 10% restante en ambar. La entro pía de cada señal seria:

x p(x) Entropia Formula
verde 0,45 -0.156 log2(0,45)*0.45 
rojo 0.45 -0.156 log2(0,45)*0.45
ambar 0.1 -0.1 log2(0.1)*0.1

Entropia =  -0,412

¿Como se usa esto y qué mide?

Mide la cantidad de bits que «aporta» un dato o lo que es lo mismo, la cantidad de información que transmite. Para entenderlo mejor tomando el ejemplo más simple, un bit con dos valores (0,1) pero cambiando la probabilidad de cada uno, respetando que la suma de ambas siempre es 1 (P(1) = 1 – P(0)):

P(0) P(1) Entropia Formula
0 1 0 -(log2(0)*0 + log2(1)*1)
0.1 0.9 0.46 -(log2(0.1)*0.1 + log2(0.9)*0.9)
0.2 0.8 0.72 -(log2(0.2)*0.2 + log2(0.8)*0.8)
0.3 0.7 0.88 -(log2(0.3)*0.3 + log2(0.7)*0.7)
0.4 0.6 0.97 -(log2(0.4)*0.4 + log2(0.6)*0.6)
0.5 0.5 1 -(log2(0.5)*0.5 + log2(0.5)*0.5)

Para el primer caso que es que el bit siempre sea 1 el valor aportado es 0. ya que no aporta ninguna información. en resumen si sabemos que un signo es más probable que otro aporta menos información que si ambos son equiprobables. 

¿Para qué nos sirve todo esto?. De forma muy simple, cuando haya que elegir entre varias propiedades, la entropía puede ayudarnos a elegir que parámetro escoger

El corazón del aprendizaje máquina

Decenas de algoritmos de aprendizaje que van desde la sencilla regresión lineal a cosas tan complejas como las redes neuronales y casi todos (seguro que hay alguno que no solo por fastidiar) se basan en una misma y sencilla idea. Reducir al mínimo el error total de la función de aprendizaje.

Vamos poco a poco. tenemos una función f(x) que recibe una entrada de valores x = [x0,…,xn] y que tiene unos parámetros p = [p0,….,pi] que configuran la respuesta de la función. Esta función es la que aprende, el proceso de aprendizaje consiste en adaptar los parámetros p para aproximar la salida a una deseada.

Voy a ir al caso del aprendizaje supervisado que es el más sencillo de entender. Hay un conjunto de ejemplos de entrada x cada uno con una «respuesta» Y = [Y0,…,Um] asociada (la respuesta correcta). Lo ideal es que cuando a f se le pasa x devuelva una «respuesta» y = [y0,…,ym] esa respuesta sea lo más parecida posible a Y.

f(x) = y ≈ Y

Esto se ha de cumplir con todos los ejemplos que hay. Ajustamos p para que minimice la suma de los errores de cada muestra de aprendizaje.

min Σ error(y, Y)

  • f es la función que pretende usarse para aprender el modelo.
  • p son los parámetros de f cuyo valor se módica para adaptarse al modelo. Este cambio de valor es la parte que se aprende.
  • x son los valores que recibimos como entrada de los cuales nuestro algoritmo tiene que calcular y
  • Y es el valor real asociado a esos parámetros
  • y es la aproximación calculada por nuestra función f

Para calcular el error se pueden usar diversas funciones. Igual que métodos para aproximar los valores de p. Sin embargo el algoritmo será algo parecido a este:

Inicializamos p
Para cada x
y = f(p, x)
e = error(Y,y)
aproximar(p, e)

El error total no siempre se calcula sumando los errores de cada muestra, pero es algo habitual.

A veces no es tan sencillo y para cada muestra x no hay un valor Y con el que comparar los resultados. El valor puede ser una propiedad de todos los resultados obtenidos. Por ejemplo muchos algoritmos de «clustering» se basan en minimizar la distancia interna entre elementos del cluster y maximizar la distancia externa entre los clusters elegidos.

Todo esto se puede complicar bastante y cambiar algunos detalles, pero en el fondo de todo algoritmo de aprendizaje hay una función de optimización que trata de reducir el error.