Creando realidad aumentada en el navegador

Image

Hace muchos años atrás (Como 10) hablaba en un evento público sobre el futuro de algunas tecnologías. Que ya había empresas que estaba apostando a ellas y que las herramientas eran cada vez más accesibles y, por lo tanto, más fáciles de usar e implementar.

Hoy, cuando se habla de realidad aumentada siempre pensamos en celulares y aplicaciones al estilo PokemonGO. Aplicaciones construidas con Unity3D, ARKit y otros. Pero nos olvidamos de los principios de la realidad aumentada y las posibilidades que nos brindaba con equipos de casi costo nulo (Nulo en el sentido de que cualquier computadora o dispositivo con cámara podía ejecutarlas).

En fin… este tipo de tecnología nunca murió y, por el contrario, con mayor poder de cómputo en los dispositivos volvió con más fuerza.

Hoy (Hace unos días, pero es el momento en el que escribo el post en contraposición de cuando será publicado :)), me crucé con AR.js, una herramienta que, en conjunto con otras como Three.js y AFrame, nos permite llevar el mundo de la realidad aumentada a la Web. Y cuando digo “Web” me refiero a HTML + JavaScript puro y duro.

Por esto decidí hacer un pequeño ejemplo (Foto que acompaña el post) y paso a dejarlo por acá:

<!DOCTYPE html&gt;
<html lang="en"&gt;
    <head&gt;
        <meta charset="UTF-8"&gt;
        <meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
        <meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
        <title&gt;Document</title&gt;
    </head&gt;
    <script src="https://aframe.io/releases/0.6.0/aframe.min.js"&gt;</script&gt;
    <script src="https://jeromeetienne.github.io/AR.js/aframe/build/aframe-ar.js"&gt;</script&gt;
    <body style='margin : 0px; overflow: hidden;'&gt;
        <a-scene embedded arjs&gt;
            <a-assets&gt;
                <a-asset-item id="avo" src="avo/Avocado.gltf"&gt;</a-asset-item&gt;
            </a-assets&gt;
            <a-marker-camera preset='hiro'&gt;
                <a-entity gltf-model="#avo" scale="0.3 0.3 0.3"&gt;</a-entity&gt;
            </a-marker-camera&gt;
        </a-scene&gt;
    </body&gt;
</html&gt;

Anuncios

Perceptrón (Inteligencia Artificial) con JavaScript

Ufff! Sí, más de un año desde el último post y, viendo las estadísticas del blog (Claramente) la gente que lo consulta es casi nula. Pero, los que me han seguido sabrán que mudé toda la creación de contenido a mi canal de YouTube: https://youtube.com/user/lacosagorda

En el canal subo un video (Como mínimo) por semana así que todo sigue activo. De cualquier manera, vuelvo por acá y no es que vaya a decir lo que suelo y luego me muerdo a mi mismo porque no puedo cumplirlo. Sí, lo que voy a decir es que iré dejando por acá esas cosas que hago de vez en cuando por puro gusto y que, por algún motivo, no he puesto en videos.

Como ya saben, me gusta explorar más allá de las herramientas que podamos usar. No quiero quedarme con el hecho de poder saber usar tal o cual framework: Quiero hacerlo por mí mismo (Aunque luego use un framework).

En fin, como el mundo de la inteligencia artificial está más movido que nunca y todo el mundo quiere usar este u otro framework, me puse en la tarea de comenzar a crear (Y entender) uno de los conceptos más comunes dentro del mundo de la IA: Cómo crear mi propia red neuronal que pueda aprender por su cuenta y sepa “predecir” determinados resultados en base a entrenamiento.

Así que, para poder iniciar, había que arrancar con la unidad más básica que se usa en este tipo de redes, junto a la función más comunmente usada. Estos son el “perceptrón” y la función “sigmoide”.

Por supuesto, no voy a entrar en detalle sobre cada uno de estas cosas porque (Y queda dicho para todos los siguientes posts que vengan), no quiero escribir tanto detallando cada una de estas ideas, las cuales requieren muchas páginas (Pero muchas). Si hace falta más información siempre se podrá acudir a una búsqueda en Google 🙂

En fin, un perceptrón; en este caso para operaciones tipo OR; nos sirve para poder categorizar entre dos tipos de elementos. La función sigmoide, por otro lado, le ayuda al perceptrón para marcar el umbral de activación para una operación dada en dicho perceptrón.

Básicamente obtendremos un 1 o un 0 si la operación se acerca al valor calculado/esperado. (De nuevo, estoy tratando de explicarlo lo más simple posible para no escribir toneladas de texto)

En definitiva, les dejo el “perceptrón“:

var perceptron = (function() {

    var neuro = function() {
        
        this._seed = 1;
        this._threshold = 1;
        this._bias = 1;
        this._learnRate = 0.1;
        this._weights = [];
        this._neuralData = [];
    };

    neuro.prototype._random = function() {
        var x = Math.sin(this._seed++) * 10000;
        return x - Math.floor(x);
    };

    neuro.prototype._multiply = function (inputs) {
        var result = 0;

        for (var i = 0; i &amp;lt; inputs.length; i++) {
            result += inputs[i] * this._weights[i];
        }

        result += this._threshold * this._weights[this._weights.length - 1];

        return result;
    },

    neuro.prototype._sigmoid = function(x) {
        return 1 / (1 + Math.pow(Math.E, -x));
    };

    neuro.prototype._retrain = function() {
        var neuralDataLength = this._neuralData.length;
        var hasSuccess = true;

        for (var i = 0; i &amp;lt; neuralDataLength; i++) {
            var trainData = this._neuralData.shift();

            hasSuccess = this.train(trainData.input, trainData.target) &amp;amp;&amp;amp; hasSuccess;
        }

        return hasSuccess;
    };

    neuro.prototype.exercise = function(iterations) {
        var i = 0;

        while (i++ &amp;lt; iterations) {
            var trainResult = this._retrain();

            if (trainResult === true) {
                console.log('Retraining ended after ' + i + ' attempts');
                break;
            }
        }
    };

    neuro.prototype.train = function (inputs, expected) {
        
        while (this._weights.length &amp;lt; inputs.length) {
            this._weights.push(this._random());
        }

        //neuron bias
        if (this._weights.length === inputs.length) {
            this._weights.push(this._bias);
        }

        var preceptionResult = this.process(inputs); //this.percieve(inputs);
        this._neuralData.push({input: inputs, target: expected, prev: preceptionResult})

        if (preceptionResult !== expected) {
            for (var i = 0; i &amp;lt; this._weights.length; i++) { var input = (i == inputs.length) ? this._threshold : inputs[i]; var adjustment = (expected - preceptionResult) * this._learnRate * input; this._weights[i] += adjustment; if (isNaN(this._weights[i])) throw new Error('Weight out of boundaries'); } return false; } return true; }; neuro.prototype.process = function (inputs) { return Number(this._sigmoid(this._multiply(inputs, this._weights)) &amp;gt; 0.5);
    };

    return new neuro();
}());

Para poder utilizar este objeto lo primero que deberemos hacer es entrenar nuestro perceptrón:

perceptron.train([0, 0], 0);
perceptron.train([0, 1], 0);
perceptron.train([1, 0], 0);

El primer parámetro en la función “train” es un vector con los diferentes (En este caso) valores que representan un modelo válido y el segundo, el resultado. Esto es, si tenemos los elementos del primero parámetro, esperamos como resultado el segundo parámetro. Para el primer caso, si tenemos un “0, 0”, esperamos “0” como respuesta.

perceptron.exercise(10000);

El siguiente paso es entrenar intensivamente el perceptrón con los datos enviados. Esto simplemente aplicará la función interna N cantidad de veces hasta encontrar un valor aproximado que pudiese dar los resultados esperados.

Una vez entrenado, ya podemos “preguntarle”:

console.log(perceptron.process([1, 1]));

En este caso, el resultado será “1”. Que, en base al modelo que hemos armado, es lo que esperamos.