Indeseabλes2016-07-12T02:14:39+00:00http://indeseables.github.ioIndeseablesindeseables.git@gmail.comAnalisis del ransomware satana2016-07-12T00:00:00+00:00http://indeseables.github.io/2016/07/12/Analisis del ransomware Satana<p>—
layout: post
title: Análisis del ransomware Satana
comments: True
author: Sadfud
tags: [Análisis de malware]
—</p>
<p>Si hay un tipo de malware preferido por los cibercriminales, esa es la familia de los ransomware. Desde la aparicion a finales de 2013 del conocido como “virus de la policia” esta familia de malware no ha parado de crecer, dado que es facil de programar y rentable para los cibercriminales.</p>
<p>A continuacion procederé a analizar detalladamente un nuevo ransomware descubierto a finales de Junio y conocido como “Satana”.
Las muestras en las que se basa el análisis de esta entrada son;
46bfd4f1d581d7c0121d2b19a005d3df Como muestra principal</p>
<p>https://virustotal.com/en/file/683a09da219918258c58a7f61f7dc4161a3a7a377cf82a31b840baabfb9a4a96/analysis/</p>
<p>d236fcc8789f94f085137058311e848b Como muestra de malware unpacked</p>
<h2 id="h2-1informacin-de-versiones">H2 1. Información de versiones</h2>
<p>A día de hoy 12 de Julio de 2016, únicamente se ha distribuido una versión del código malicioso Satana.</p>
<h2 id="h2-2extensiones-a-cifrar">H2 2. Extensiones a cifrar</h2>
<p>Satana cifra los archivos con extensión
.bak .doc .jpg .jpe .txt .tex .dbf .db .xls
.cry .xml .vsd .pdf . csv .bmp .tif .1cd .tax
.gif .gbr .png .mdb .mdf .sdf .dwg .dxf .dgn
.stl .gho .v2i .3ds .ma .ppt .acc .vpd .odt
.ods .rar .zip .7z .cpp .pas .asm</p>
<h2 id="h2-3extension-aadida-a-los-archivos-cifrados">H2 3. Extension añadida a los archivos cifrados</h2>
<p>Tras cifrar los archivos el malware cambia el nombre de los mismos con el formato <dirección de="" email="">__<nombre_original>
![FIG1](https://i.gyazo.com/04f52d0425115f9cf1403f257ab9d7ee.png)</nombre_original></dirección></p>
<h2 id="h2-4anlisis-dinmico-del-cdigo-malicioso">H2 4. Análisis dinámico del código malicioso</h2>
<p>Tras la ejecución del binario malicioso este crea una copia de si mismo en %TEMP% con un nombre aleatorio y posteriormente borra el archivo original. Además crea un archivo de texto bajo el nombre de ¡satana!.txt también en %TEMP%.
Una vez terminado este proceso ejecuta el archivo de nombre aleatorio creado en la carpeta temporal, el cual requiere al usuario permisos de ejecución como administrador.
Cuando el usuario concede los permisos de ejecución, el malware crea 2 entradas en el registro de Windows.
<img src="https://i.gyazo.com/7d9df3a79c12df7a533866b2aacbf0a4.png" alt="FIG4" />
La primera bajo el nombre de “BTC” contiene la dirección de bitcoin asociada a la cartera que debe recibir el pago
La segunda bajo el nombre de “E-mail” contiene una dirección de E-mail, la misma que se muestra en el nuevo nombre de los archivos cifrados.
<img src="https://i.gyazo.com/8e1c07ad603c992e6917d4f43d09bf7c.png" alt="FIG5" /></p>
<h2 id="h2-5ataque-de-bajo-nivel">H2 5. Ataque de bajo nivel</h2>
<p>Este ransomware no actua en modo kernel, como si lo hacen otros como por ejemplo Petya.</p>
<h2 id="h2-6ataque-de-alto--nivel">H2 6. Ataque de alto nivel.</h2>
<p>Una vez el malware ha sido ejecutado simplemente se instala de forma silenciosa en el equipo y espera al reinicio del equipo (sin forzarlo) para mostrar el siguiente mensaje
<img src="https://i.gyazo.com/ab67ea9348b5a6844e43e904de69979f.png" alt="FIG2" />
*Es el mismo contenido que el del fichero ¡satana!.txt</p>
<h2 id="h2-7metodo-de-cifrado-de-archivos">H2 7. Metodo de cifrado de archivos.</h2>
<p>Satana divide los archivos en partes de 32 bytes que cifra de forma independiente para posteriormente juntarlas usando el método RTDSC (Read Time-Stamp Counter).
El algoritmo de cifrado es una modificación de AES 256.</p>
<h2 id="h2-8llave-de-cifrado">H2 8. Llave de cifrado</h2>
<p>Aparentemente la llave de cifrado es siempre la misma. Un mismo archivo siempre devuelve archivos cifrados idénticos.</p>
<h2 id="h2-9medidas-de-proteccin-anti-reversing-en-el-payload">H2 9. Medidas de protección anti reversing en el payload.</h2>
<p>El payload esta empaquetado, pero puede ser fácilmente desempaquetado usando un debugger como OllyDbg, basta con colocar un BP en RtlDecompressBuffer.
Tambien impide la restauración del equipo a un punto anterior a la infección.
<img src="https://i.gyazo.com/581fbf16dc6f24af6f0a170603977f81.png" alt="FIG3" /></p>
<h2 id="h2-10conexin-con-el-cc">H2 10. Conexión con el C&C</h2>
<p>El malware conecta con el C&C y envía la siguiente información en un paquete sin cifrar.
id=7
&code=100
&sdata=5.1.2600 0 1 HOME User 0&name=payload.exe
&md5=59E18B50B822020294A8EA0A4154C7597847B3A6359A08194F4865D804BD7E6
&dlen=66ABDE777F35E50F671B6034FA6453AD
Este proceso muestra un grave fallo en el payload, ya que si en el momento de la infeccion el C&C esta offline la llave se pierde y los archivos no pueden ser recuperados.</p>
<h2 id="h2-12detecion-con-regla-yara">H2 12. Detecion con regla YARA</h2>
<p>rule: Satana_Ransomware
{
meta:
Description = “Deteccion de ransomware Satana”
Author = “SadFud”
Date = “12/07/2016”</p>
<div class="highlighter-rouge"><pre class="highlight"><code>strings:
$satana = { !satana! } nocase
condition:
$satana }
</code></pre>
</div>
<h2 id="h2-11conclusion">H2 11. Conclusion</h2>
<p>Observando el funcionamiento del programa y los fallos que tiene, es posible que esta solo sea una versión de prueba y que la verdadera campaña de infección masiva aun no haya comenzado.</p>
<h2 id="h2-12descarga-de-muestras">H2 12. Descarga de muestras</h2>
<p>http://www27.zippyshare.com/v/WjzkYiPy/file.html
Contraseña:indeseables</p>
Lógica difusa y fuzzy clustering.2016-04-03T00:00:00+00:00http://indeseables.github.io/2016/04/03/logica-difusa-clustering<p>¿Cómo determinamos si una persona es alta o baja? ¿y si la temperatura resulta fria, agradable o caliente? ¿y si algo es barato o caro?. Lo normal en estos casos, cuando no se conoce la lógica difusa que trataremos en esta entrada, es pensar en separarlo mediante rangos perfectamente delimitados. Pero esto no es lo más adecuado y de hecho, no es lo que más se ajusta al mundo real como veremos.</p>
<hr />
<h3 id="lgica-difusa">Lógica difusa</h3>
<p>La lógica difusa (fuzzy logic) nos permite lidiar con cualquier tipo de información imprecisa e.g. datos conocidos aproximadamente, precisión en las medidas, etc</p>
<p>Un caso particular de este tipo de lógica son los conjuntos difusos (Zadeh, 1979).
La diferencia entre los <a href="https://es.wikipedia.org/wiki/Conjunto">conjuntos clásicos</a> y los <a href="https://es.wikipedia.org/wiki/Conjunto_difuso">conjuntos difusos</a> radica en la forma de afirmar si un elemento pertenece a un conjunto. Más concretamente:</p>
<ul>
<li>Conjuntos clásicos: un elemento pertenece, o no pertenece a un conjunto.
<ul>
<li>Predicado de pertenencia <img src="https://latex.codecogs.com/gif.latex?P%28x%29%3AU%5Crightarrow%20%5Cleft%20%5C%7B%200%2C1%20%5Cright%20%5C%7D" alt="" /></li>
</ul>
</li>
<li>Conjuntos difusos: un elemento pertenece en mayor o menor medida a un conjunto (la frontera de los cojuntos es <a href="https://i.gyazo.com/f62cf683e06d573f0e48b2a1cd089e0a.png">difusa</a>).
<ul>
<li>Función de pertenencia <img src="https://latex.codecogs.com/gif.latex?%5Cmu%20_%7BA%7D%3AU%5Crightarrow%20%5B0%2C1%5D" alt="" /></li>
</ul>
</li>
</ul>
<p>Para ver un caso más claro, tomaremos el ejemplo de la estatura mencionado al principio de la entrada. Si lo dividimos de la forma trivial usando rangos bien delimitados, podríamos considerar algo así:</p>
<ul>
<li>[-inf,1.60] : <strong>Baja</strong></li>
<li>[1.61,1.75] : <strong>Media</strong></li>
<li>[1.75,inf] : <strong>Alta</strong></li>
</ul>
<p>Realmente, si una persona que mide 1.75 crece 1cm, ¿pasa a ser alta de repente?. Podemos considerar que dicha persona tiene una posibilidad de pertenencia a <strong>Baja</strong> de 0.25 , a <strong>Media</strong> de 0.9 y a <strong>Alta</strong> de 0.25 (hay que tener presente que se habla de posibilidades de pertenencia no de probabilidades - los conjuntos difusos inducen una distribución de posibilidades -, de ello se encarga el <a href="https://ccc.inaoep.mx/~jagonzalez/AI/Sesion12_RazonamientoProbabilistico.pdf">razonamiento probabilístico</a>).</p>
<p>Estas funciones de pertenencia se suelen especificar mediante funciones <strong>f</strong> que se ajusten a trapecios (no tienen porque ser únicamente trapecios), definiendo cada una de estas funciones un conjunto difuso - partición -. (<a href="https://i.gyazo.com/c961a07b07d23d37079537d282c55a13.png">Ejemplo</a>).
Deben cumplir 2 propiedades:</p>
<ul>
<li>
<p>Completitud: garantizar que ningún valor quede excluido de todas las particiones fuzzy, por lo que dado cualquier elemento del universo <strong>U</strong> empleado, su posibilidad de pertenencia debe ser >0 en alguna de las particiones.</p>
</li>
<li>
<p>Partición fuerte: la suma de las funciones de pertenencia de un elemento de <strong>U</strong> en cada una de las particiones es 1.</p>
</li>
</ul>
<p>También es posible modificar las funciones de pertenencia para tener en cuenta conceptos más complejos modificando los valores difusos e.g. X es extremadamente A, X es más o menos A, etc. Estos modificadores se denominan modificadores linguísticos y existen estándares para su definición. Algunos ejemplos son <a href="https://i.gyazo.com/2059ec8733b503452b6a12323c251203.png">éstos</a>.</p>
<p>Del mismo modo que sobre los conjuntos clásicos hay definidas una serie de operaciones, también las hay en los conjuntos difusos, dados 2 conjuntos difusos A y B, se definen:</p>
<ul>
<li>
<p>Igualdad de conjuntos (A=B): decimos que 2 conjuntos A y B son iguales cuando para todo elemento <strong>x</strong> del universo <strong>U</strong> las funciones de pertenencia de <strong>x</strong> en ambos vale lo mismo <img src="https://i.gyazo.com/2e66f0a8a5a494eaff16e92e2d97c7d2.png" alt="" /></p>
</li>
<li>
<p>Complemento: El complementario de un elemento <strong>x</strong> en un conjunto difuso A es 1 - la función de pertenencia de <strong>x</strong> al conjunto A <img src="https://i.gyazo.com/550c75653c1fb003ab9006f1bed7de6d.png" alt="" /></p>
</li>
<li>
<p>Inclusión (A contenido en B): Decimos que A está contenido en B cuando para todo elemento <strong>x</strong> del universo <strong>U</strong> la función de pertenencia de <strong>x</strong> en A es menor o igual que la función de pertenencia de <strong>x</strong> en B <img src="https://i.gyazo.com/342cae7269a1a990dfe27a1c18fe7ea2.png" alt="" /></p>
</li>
<li>
<p>Intersección (A intersección B): <img src="https://i.gyazo.com/4128013c2e3da71ec1d30e6bcc9c837f.png" alt="" /></p>
</li>
<li>
<p>Unión (A unión B): <img src="https://i.gyazo.com/479b2cb008bbd3c22b314af3e27df039.png" alt="" /></p>
</li>
</ul>
<p>También se definen operaciones aritméticas como: suma, resta, multiplicación y división, que no comentaremos en esta entrada porque no nos serán de utilidad para el caso páctico de <a href="https://es.wikipedia.org/wiki/Fuzzy_clustering">clustering difuso</a>, tampoco veremos otros aspectos clave como el proceso de <a href="https://i.gyazo.com/69ed756e0cd1fa1278d0fdb11ebb3fd2.png">inferencia difusa</a> aplicando reglas y hechos difusos por el mismo motivo, sin embargo, es muy posible que comente en próximas entradas todos esos aspectos.</p>
<p>Por último, destacar que este tipo de métodos está muy extendido y son muchos los <a href="https://i.gyazo.com/a6941f428a90b7fdc918826383f13274.png">dispositivos y aplicaciones</a> que hacen uso de dichos métodos, algunos de ellos son:</p>
<ul>
<li>Control de spam (Mozilla)</li>
<li>Cámara autofoco (Canon)</li>
<li>Control de frenado</li>
<li>Acondicionador de aire doméstico (Mitsubishi)</li>
<li>Aprendizaje automático (e.g. clustering difuso)</li>
</ul>
<hr />
<h3 id="fuzzy-clustering">Fuzzy clustering</h3>
<p>[TODO]</p>
<hr />
<p>Si tienes alguna duda, comentario o sugerencia, notifícanos a través de <a href="mailto:indeseables.git@gmail.com">indeseables.git@gmail.com</a>
y si te ha gustado la entrada puedes <a href="https://twitter.com/intent/tweet?url=http://indeseables.github.io/2016/04/03/logica-difusa-clustering/&text=Lógica difusa y fuzzy clustering.&via=indeseables!" target="_blank"> compartirla!.</a></p>
<hr />
Análisis de malware sirio2016-03-29T00:00:00+00:00http://indeseables.github.io/2016/03/29/analisis-de-malware-sirio<p>Ayer estuve varias horas recolectando samples para hacer mi primera entrada en el blog sobre el análisis de malware pero sólo vi keyloggers y servidores de DarkComet cifrados con crypters en .NET, así que buscando en google encontré <a href="http://syrianmalware.com/">esta página</a> y aunque el último sample es de 2014 me pareció curiosa.</p>
<p>El primer análisis es del primer sample de la web (a8ef5ccebd2e3babdd243a2861673c26). Quiero aclarar que no soy un experto en análisis así que mis procedimientos quizá no sean los más correctos.</p>
<h2 id="a8ef5ccebd2e3babdd243a2861673c26">a8ef5ccebd2e3babdd243a2861673c26</h2>
<ul>
<li>Filename: news.exe</li>
<li>MD5 Hash: a8ef5ccebd2e3babdd243a2861673c26</li>
<li>Filesize: 114K</li>
<li>Analysis or Media:
<ul>
<li><a href="https://docs.google.com/document/d/1LCfzNKtDwBn2BqKo_rvgCxMug8Ow-lZMBT89UFayAhI/edit?usp=sharing">Quick Analysis</a></li>
<li><a href="https://www.virustotal.com/en/file/039058cd0f349c8987a4a61a3de12660b78007235126ee75228933fda2343e4f/analysis/1412724921/">VirusTotal Results</a></li>
<li><a href="https://malwr.com/analysis/YTE3YWRlNjZmMjlhNGNjZmI1YmRjNDQ3YmI1MjAyZjQ/">Malwr Analysis</a></li>
</ul>
</li>
</ul>
<p>Un primer vistazo rápido con CFF Explorer nos muestra que es un ejecutable .NET así que veremos si se puede decompilar. Para ello usaré ILSpy ya que es el que mejor me ha funcionado en la máquina virtual. Al decompilar el ejecutable y abrimos cualquier clase vemos que parece ser que está ofuscado.
<img src="https://i.gyazo.com/f4848a908ff3b9c0e8d80aa21a055a82.png" alt="FIG1" /></p>
<p>Suerte que tenemos <a href="https://github.com/0xd4d/de4dot">de4dot</a>. Normalmente uso un par de veces de4dot por el ejecutable por si las moscas y en este caso parece ser que acerté.
<img src="https://i.gyazo.com/5d56ba4ffb3bcc74e430d09e881b84ae.png" alt="FIG2" /></p>
<p>En una de las clases vemos una variable bastante larga cifrada con Base64, seguramente un ejecutable, pero me entra la curiosidad que empiece por <COMPRESSED> y acabe en </COMPRESSED> así que hay que buscar en el resto de clases alguna referencia a una función de compresión.
<img src="https://i.gyazo.com/8f663ac394688c72cea81f92a0c918a7.png" alt="FIG3" /></p>
<p>Encuentro a la primera la clase encargada de la compresión al ver que hay una llamada ClsCompressedString (¿seré un genio?) y una rápida búsqueda en Google me lleva a <a href="http://www.codeproject.com/Articles/27396/Easy-String-Compression-and-Encryption">esta página</a> donde seguramente el súper hacker habrá sacado el código. Creo un nuevo proyecto en VB.NET y uso la clase para descomprimir y descifrar la string usando la misma función que usa el crypter en una de sus clases. El archivo cifrado se puede descifrar con la clase clsCompressedString comentada antes, luego convirtiendo el resultado (string hexadecimal) a una array de bytes y por último descomprimiendo con GZip. El resultado es (otro) ejecutable .NET, el cual, al decompilarlo (con <a href="https://github.com/0xd4d/dnSpy">dnSpy</a>) parece ser un servidor de NjRAT 0.6.4 que conecta al DNS aliahmahhmod.zapto.org, offline en el momento de escribir el post.</p>
<h2 id="e1d84b350c1465bb4c4c77b1bcec">7263e1d84b350c1465bb4c4c77b1bcec</h2>
<ul>
<li>Filename: برنامج الفيش.exe</li>
<li>MD5 Hash: 7263e1d84b350c1465bb4c4c77b1bcec</li>
<li>Filesize: 8.6M
Analysis or Media:
<ul>
<li><a href="https://docs.google.com/document/d/1DZvHVn1r_EOj53eYKhQ2jO4mHjR5oX1JbLoxqn-8VpM/edit?usp=sharing">Quick Analysis</a></li>
<li><a href="https://www.virustotal.com/en/file/20c52aedfe32d4a71bf5e3353cf3999ea95b3a43c7e40c8820b1b7d24b9e6cb5/analysis/1410845347/">VirusTotal Results</a></li>
</ul>
</li>
</ul>
<p>CFF Explorer nos revela que es un ejecutable .NET (qué sorpresa) así que lo pasamos por de4dot, que indicará que no está ofuscado (unknown ofuscator) por eso lo decompilaré directamente. En ILSpy vemos sólo hay dos clases pero buscando en una de ellas (frmMain) vemos que en el método frmMain_Load (que se ejecutará al abrirse el formulario) hay un código para guardar un archivo en AppData con nombre ‘sql’ (niños malos).
<img src="https://i.gyazo.com/c2797d41f78a506de6c48c1512ab3048.png" alt="FIG4" /></p>
<p>ILSpy no me deja guardar el recurso así que lo abrí desde dnSpy y así lo pude guardar. Al examinar el archivo con CFF Explorer me llevé una enorme sorpresa el ver que era… otro ejecutable .NET. Al decompilar dicho ejecutable veo que sólo tiene una clase llamada Downloader así que tendré que analizarlo durante horas para saber cual es su propósito.
<img src="https://i.gyazo.com/1154346a2b7dc2ab49e3460c3d835fa7.png" alt="FIG5" /></p>
<p>Ambos archivos no están disponibles. Un punto para ellos.</p>
<h2 id="bf01f67db4a5e8e6174b066775eae0">28bf01f67db4a5e8e6174b066775eae0</h2>
<ul>
<li>Filename: psiphon.exe</li>
<li>MD5 Hash: 28bf01f67db4a5e8e6174b066775eae0</li>
<li>Filesize: 1.4M</li>
<li>Analysis or Media:
<ul>
<li><a href="https://citizenlab.org/2014/03/maliciously-repackaged-psiphon/">Maliciously Repackaged Psiphon Found</a></li>
<li><a href="https://www.virustotal.com/en/file/1182ffd81b4ee9bed90ca490ca5bb258e19cce68175d1a69f054030db1075df6/analysis/">VirusTotal Results</a></li>
</ul>
</li>
</ul>
<p>Tercer round. CFF Explorer nos muestra que es, increíblemente, un ejecutable .NET. Ya sabéis la historia: de4dot, ILSpy/dnSpy, buscar, etc. Este ejecutable sólo tiene una clase llamada Windows que no es más que un formulario que droppea dos ejecutables desde recursos llamados server.exe (muy original…) y psiphon3.exe.</p>
<p>El ejecutable psiphon3 me pareció bastante “profesional” así que lo busqué en Google y encontré esta información en la misma <a href="https://s3.amazonaws.com/f58p-mqce-k1yj/en.html">página oficial</a>:</p>
<blockquote>
<p>What is Psiphon 3?</p>
<p>Psiphon 3 is a circumvention tool from Psiphon Inc. that utilizes VPN, SSH and HTTP Proxy technology to provide you with uncensored access to Internet content. Your Psiphon 3 client will automatically learn about new access points to maximize your chances of bypassing censorship.</p>
<p>Psiphon 3 is designed to provide you with open access to online content. Psiphon does not increase your online privacy, and
should not be considered or used as an online security tool.</p>
</blockquote>
<p>Así que supongo que será una herramienta auxiliar para el otro ejecutable (server.exe) que analizaré ahora.</p>
<p>Este ejecutable (.NET como no) está ofuscado con Crypto Ofuscator. Una vez decompilado… sigue estando ofuscado. de4dot no puede limpiar el ejecutable así que al mirar el código fuente no entiendo nada de nada. Tendré que tirar de análisis dinámico. Para ello usaré <a href="https://technet.microsoft.com/en-us/sysinternals/tcpview.aspx">TcpView</a> y <a href="http://www.sandboxie.com/">Sandboxie</a>.</p>
<p>Sandboxie nos revela que el ejecutable se autocopia en AppData con el nombre Explorer.exe y en la carpeta de Autoinicio con nombre chrome.exe. TcpView nos muestra que intenta conectarse a 31.9.48.141:1960.</p>
<h2 id="conclusin">Conclusión</h2>
<p>Durante estos pequeños análisis he sacado tres conclusiones:</p>
<ul>
<li>Los sirios aman .NET.</li>
<li>Los sirios realmente aman .NET.</li>
<li>Sus técnicas son bastante simples. Se sirven de crypters .NET para RATs públicos (NjRAT).</li>
</ul>
<hr />
<p>Si tienes alguna duda, comentario o sugerencia, notifícanos a través de <a href="mailto:indeseables.git@gmail.com">indeseables.git@gmail.com</a>
y si te ha gustado la entrada puedes <a href="https://twitter.com/intent/tweet?url=http://indeseables.github.io/2016/03/29/analisis-de-malware-sirio/&text=Análisis de malware sirio&via=indeseables!" target="_blank"> compartirla!.</a></p>
<hr />
Odisea Funcional (Parte 1)2016-03-28T00:00:00+00:00http://indeseables.github.io/2016/03/28/odisea-funcional-parte1<p>Un título muy acertado pues estudiar el paradigma funcional es cuanto menos una odisea, <strong>preciosa o no dependiendo de a quíen preguntes.</strong></p>
<p>Me apoyaré en apuntes de la universidad en la que estudio, luego el lenguaje utilizado será el mismo, <strong>C#, funcional no puro</strong>, pero trataré de comentar <strong>diferencias con un funcional puro como Haskell</strong> para que se entiendan mejor los conceptos estudiados.</p>
<p><strong>Se pretende</strong> que en esta odisea naveguemos entre los siguientos puntos:<br />
* <strong>Cálculo lambda</strong>
* <strong>Funciones como entidades de primer orden</strong>
* Cláusulas<br />
* Currificación<br />
* Aplicación Parcial<br />
* Continuaciones<br />
* Evaluación Perezosa<br />
* Transparencia Referencial<br />
* Pattern Matching<br />
* Funciones de Orden Superior<br />
* Listas por comprensión</p>
<p>Recalco el <strong><em>“Se pretende”</em></strong> porque no estoy seguro de si podre escribir sobre todo sin que me demoré demasiado, el motivo principal de escribir esta odisea es que me sirva a la vez de estudio.</p>
<p>En esta primera parte se estudiará el contenido <strong><em>más teorico</em></strong> puesto que abarca una introducción a la programación funcional y un breve estudio de la base desde la que empezó todo, el <strong>lambda cálculo</strong></p>
<p><strong>PD</strong>: Intentaré que esta primera parte sea lo menos aburrida posible, pero no hay todo sin nada, y en este caso el contenido que leereis será prácticamente una copia, síntesis o como lo queráis llamar de los apuntes de mi universidad. (Que por cierto, no se si puedo adjuntar en formato PDF al final del post, por lo que por si acaso no se me permite, no lo haré)</p>
<h2 id="qu-es-el-paradigma-funcional">¿Qué es el paradigma funcional?</h2>
<blockquote>
<p>Paradigma declarativo basado en la utilización de funciones que manejan <strong>datos inmutables</strong>
* Los datos nunca se modifican
* En lugar de cambiar un dato, se llama a una función que devuelve el dato modificado sin modificar el original</p>
</blockquote>
<blockquote>
<p>Un programa se define mediante un conjunto de funciones invocándose entre sí<br />
* Las funciones no generan efectos secundarios
* El valor de una expresión depende únicamente de los valores de los parámetros
* (Devolviendo siempre el mismo valor en función de éstos -> Paradigma funcional puro)</p>
</blockquote>
<h2 id="lambda-clculo-o-clculo-lambda">Lambda cálculo o cálculo lambda</h2>
<blockquote>
<p>Es un sistema formal basado en la definición de funciones (abstracción) y su aplicación (invocación), origen de la programación funcional cuando fue definido por <strong>Church y Kleene</strong> alla por los años 30.</p>
</blockquote>
<ul>
<li>Hace uso exhaustivo de la recursión</li>
<li>Se considera el lenguaje más pequeño universal de computación</li>
<li><strong><em>Frecuentemente utilizado por investigadores y diseñadores de lenguajes</em></strong></li>
</ul>
<h3 id="expresiones-lambda">Expresiones lambda</h3>
<p>Se definen como:
* Una abstracción lambda <strong><em>λx.M (M, N, M1, M2, …)</em></strong>
donde <strong>x</strong> es una <strong>variable/parámetro</strong>, y <strong>M</strong> es una <strong>λ-expresión/cuerpo de la función</strong>
* Una aplicación <strong>M N</strong> , donde <strong>M y N son λ-expresiones</strong></p>
<p>Ejemplos de abstracciones:
* Función identidad <strong>f(x) = x</strong> puede representarse como: <strong>λx.x</strong>
* Función doble g(x) = x+x puede representarse como: <strong>λx.x+x</strong></p>
<p><em>PD: No tendría que ser necesario decir que el símbolo “+” podría escribirse en código lambda, pero con fines académicos vamos a considerar las operaciones básicas como “disponibles” en nuestro “intérprete de lambda cálculo”</em></p>
<h3 id="reduccin---aplicacin">Reducción-β ó Aplicación</h3>
<p>La aplicación representa su invocación ( la de la función ) y se define del siguiente modo:
* <code class="highlighter-rouge">(λx.M)N -> M[x:=N] (o M[N/x])</code></p>
<p>Donde X es la variable, M y N son las λ-expresiones y <strong>M[x:=N] (o M[N/x]) representa la expresión lambda M, donde todas las x son sustituidas por la expresión lambda N.</strong></p>
<p><strong>A esta sustitución se le denomina reducción-β</strong>, uds. más que nadie deben saber que el mundo de la ciencia esta rodeado por terminología confusa para de alguna manera espantar al que no se atreva a leer un párrafo entero.</p>
<p>Ejemplos:
* <code class="highlighter-rouge">(λx.x + x)3 -> 3 + 3</code>
* <code class="highlighter-rouge">(λx.x) λy.y*2 -> λy.y*2</code></p>
<h3 id="teorema-de-church-rosser">Teorema de Church-Rosser</h3>
<blockquote>
<p>Establece que el orden en el que se apliquen estas reducciones no afecta al resultado final
* <code class="highlighter-rouge">(λx.x)(λy.y*2)3 -> (λy.y*2)3 -> 3*2</code>
* <code class="highlighter-rouge">(λx.x)(λy.y*2)3 -> (λx.x)(3*2) -> 3*2</code></p>
</blockquote>
<p>De modo que los paréntesis son usados normalmente para <strong>delimitar términos lambda</strong><br />
Por ejemplo: <code class="highlighter-rouge">(λx.x)(λy.y)</code><br />
Al evaluarse <code class="highlighter-rouge">(λy.y)</code> se reduce, esto podría explicarse de manera muy coloquial: el resultado de la función se aplica como parámetro a otra función y realmente queda reducida a una sola.</p>
<p>Sin embargo: <code class="highlighter-rouge">λx.xλy.y</code> es una sola abstracción (función)</p>
<h3 id="variables-libres-y-ligadas">Variables libres y ligadas</h3>
<p>Sea una abstracción <code class="highlighter-rouge">λx.xy</code> se dice que:
* > <code class="highlighter-rouge">x</code> esta <strong>ligada</strong>
* > <code class="highlighter-rouge">y</code> es <strong>libre</strong></p>
<p>En una <strong><em>reducción-β</em></strong> ó <strong><em>sustitución</em></strong> sólo se sustituyen las variables libres<br />
<code class="highlighter-rouge">(λx.x(λx.2+x)y)M</code> <strong>→</strong> <code class="highlighter-rouge">x(λx.2+x)y[x:=M] = M(λx.2+x)y</code></p>
<p>El que sea la primera vez que lea sobre lambda cálculo y pueda leer esta línea sin llorar por dentro, bravo, pero normalmente y por experiencia propia, suele pasar que crees entender como lo hiciste y cuando se te da el problema sin la solución no estas tan seguro de lo que estas haciendo.</p>
<p>[Añadir explicación paso a paso de la reducción]</p>
<p>Para evitar estos conflictos en las reducciones se creó la <strong>conversión-α</strong><br />
* Todas las apariciones de una variable <strong>ligada</strong> en una misma abstracción se pueden renombrar a una <strong>nueva</strong> variable
* Incapié en <strong>nueva</strong>, <code class="highlighter-rouge">funcional = variables inmutables</code></p>
<p><code class="highlighter-rouge">(λx.x(λx.2+x)y)M == (λx.x(λz.2+z)y)M → x(λz.2+z)y[x:=M] = M(λz.2+z)y </code><br />
y si deseas, lo puedes volver a renombrar <code class="highlighter-rouge">== M(λx.2+x)y</code>, ambas son soluciones válidas.</p>
<h3 id="conversin-">conversión-α</h3>
<ul>
<li><code class="highlighter-rouge">λx.xy == λz.zy</code></li>
<li><code class="highlighter-rouge">λx.xy != λy.yy</code></li>
</ul>
<p>Nótese la diferencia, <code class="highlighter-rouge">y</code> es una variable libre, no puede renombrarse:
> Todas las apariciones de una variable <strong>ligada</strong> en una misma abstracción se pueden renombrar a una <strong>nueva</strong> variable</p>
<ul>
<li><code class="highlighter-rouge">λx.2+x == λy.2+y</code></li>
<li><code class="highlighter-rouge">xy(λx.2+x) == xy(λz.2+z)</code></li>
<li>…</li>
</ul>
<h2 id="isomorfismo-curry-howard">Isomorfismo Curry-Howard</h2>
<p>Este apartado lo voy a saltar por alto, lo que si voy a hacer es dejaros el enlace a la Wikipedia y resumir un poco brevemente de lo que trata.</p>
<p>Básicamente debeís saber que al igual que en <strong>Ingeniería civil se fundamentan en el cálculo para hacer sus demostraciones</strong>, en <strong>Ingeniería del Software nos fundamentamos en la lógica</strong> para hacer inferencia y demostraciones.</p>
<p>Por ejemplo:<br />
> <strong>Verificación de programas</strong>: demostrar que un programa es correcto conforme a una especificación:
* Un algoritmo de ordenación será correcto cuando se demuestre:<br />
1. <code class="highlighter-rouge">Tras su invocación, todos los elementos están ordenados.</code>
2. <code class="highlighter-rouge">Para cualquier entrada pasada al algoritmo (Infinitas).</code>
> El isomorfismo o correspondencia de Curry-Howard establece una <strong>relación directa entre programas software y demostraciones matemáticas</strong></p>
<p>Para más información: <a href="https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence">Wikipedia- Isomorfismo - Curry-Howard</a></p>
<h2 id="funciones-entidades-primer-orden">Funciones, Entidades Primer Orden</h2>
<blockquote>
<p>En <strong>paradigma funcional</strong>, las funciones son entidades de primer orden, esto significa que las <strong>funciones son un tipo más</strong>, pudiendose por ejemplo, instanciar variables de tipo función en estructuras de datos, pasarlas como parámetros o retornarlas.</p>
</blockquote>
<h3 id="funciones-de-orden-superior">Funciones de orden superior</h3>
<p>Se dice que una función es de orden superior si:
* recibe alguna función como parámetro
* retorna alguna función como resultado
* Y por conclusión, si recibe y retorna, ¿lógico no?</p>
<p>Por ejemplo, la función <code class="highlighter-rouge">doble</code>: <code class="highlighter-rouge">λf.(λx.f(fx))</code> sería una función de orden superior.</p>
<p>En lenguajes funcionales puros como haskell, esto es nativo, todo son funciones, esto quiere decir que operadores como <code class="highlighter-rouge">+ - / ...</code> son funciones infijas que curiosamente tambien pueden ser invocadas con <a href="https://es.wikipedia.org/wiki/Notaci%C3%B3n_polaca_inversa">notación polaca inversa</a> <code class="highlighter-rouge">+ 1 2</code> = <code class="highlighter-rouge">3</code>.</p>
<p>El problema viene en lenguajes que siguen muy a raja tabla el paradigma orientado a objetos (Veáse Java…), como instancias funciones?, en Java 8 ya se han implementados cosas de funcional pero sigue siendo un completo desastre.</p>
<h3 id="delegados">Delegados</h3>
<p>C# usa algo llamado <strong>delegados</strong>, los delegados son funciones que son <strong>entidades de primer orden</strong>. (al igual que las lambda-expresiones).</p>
<blockquote>
<p>Un delegado constituye un <strong>tipo que representa un método</strong> ya sea de instancia o de clase (<code class="highlighter-rouge">static</code>)</p>
</blockquote>
<p>Un poco de código para entender el concepto:</p>
<pre><code class="language-C#">public delegate int Comparacion(Persona p1, Persona p2);
</code></pre>
<p>Comparación es un método que recibe dos <code class="highlighter-rouge">Persona</code> y devuelve un <code class="highlighter-rouge">ìnt</code></p>
<p>Ahora por ejemplo, podriamos definir un método <code class="highlighter-rouge">OrdenarPersonas</code> independientemente del criterio de ordenación que se escoja.</p>
<pre><code class="language-C#">static public void OrdenarPersonas(Persona[] vector, Comparacion comparacion){
//... Iterando el vector ...
if(comparacion(vector[i], vector[j]) > 0){
Persona aux = vector[i];
vector[i] = vector[j];
vector[j] = aux;
}
//...
}
</code></pre>
<h3 id="tipos-de-delegados-predefinidos">Tipos de delegados predefinidos</h3>
<blockquote>
<p>.Net tiene un conjunto de delegados predefinidos que hacen uso de la potencia de la <strong>genericidad</strong></p>
</blockquote>
<p>Los más utilizados son <code class="highlighter-rouge">Func</code>, <code class="highlighter-rouge">Action</code> y <code class="highlighter-rouge">Predicate</code>.
* > <code class="highlighter-rouge">Func<T></code>: Método sin parámetros que retorna un <code class="highlighter-rouge">T</code>
* > <code class="highlighter-rouge">Func<T1, T2></code>: Método que recibe un parámetro <code class="highlighter-rouge">T1</code> y retorna un <code class="highlighter-rouge">T2</code>
* > <code class="highlighter-rouge">Action</code>: Método sin parámetros ni retorno
* > <code class="highlighter-rouge">Action<T></code>: Método con un parámetro <code class="highlighter-rouge">T</code> sin retorno
* > <code class="highlighter-rouge">Predicate<T></code>: Método que retorna un <code class="highlighter-rouge">bool</code> y recibe un <code class="highlighter-rouge">T</code></p>
<h3 id="delegados-annimos">Delegados Anónimos</h3>
<blockquote>
<p>En la programación funcional es común <strong>poder escribir la función en el momento de pasar ésta como parámetro</strong></p>
</blockquote>
<pre><code class="language-C#">Persona[] personas = ListadoPersonas.CrearPersonasAleatorias();
Persona[] mayoresEdad = Array.FindAll(personas,
delegate(Persona p) { return p.Edad >= 18; }
);
</code></pre>
<p>C# lanzó una buena solución al problema pero la sintaxis seguia siendo dificil de tratar, y por eso decidio implementar <strong>expresiones lambda</strong>, que básicamente mejoraban los <strong>delegados anónimos</strong></p>
<h3 id="expresiones-lambda-1">Expresiones lambda</h3>
<blockquote>
<p>permiten <strong>escribir el cuerpo de funciones completas como expresiones</strong></p>
</blockquote>
<pre><code class="language-C#">//El ejemplo de función de orden superior que veiamos antes
Func<Func<int, int>, int, int> dobleAplicacion = (f, n) => f(f(n));
Console.WriteLine(dobleAplicacion(n => n+n, 3));
//Convertimos el ejemplo de los delegados anónimos
Persona[] personas = ListadoPersonas.CrearPersonasAleatorias();
Persona[] mayoresEdad = Array.FindAll(personas, persona => persona.Edad >= 18);
</code></pre>
<p>En Haskell se utiliza esta sintaxis (Desde el intérprete):
<code class="highlighter-rouge">Haskell
-- DobleAplication de 3 -> 12
Prelude> (\f n -> f (f n)) (\x -> x+x) 3
12
</code>
Haskell tiene inferencia de tipos, pero tiene la característica de poder definir una cabecera con los tipos
<code class="highlighter-rouge">Haskell
factorial :: Integer -> Integer
factorial n = product [1..n]
</code></p>
<h3 id="bucles-y-recursividad">Bucles y Recursividad</h3>
<p>Me imagino que ya os habreís pegado con el concepto de recursividad más veces, y en parte, como <strong>C# es un lenguaje funcional no puro, puede usar estructuras de control de flujo iterativas cuando sea oportuno</strong>, pero los <strong>lenguajes puros como Haskell hacen uso exhaustivo de la recursión</strong></p>
<p>Por ejemplo, imaginate que los operadores: <code class="highlighter-rouge">if</code>-<code class="highlighter-rouge">then</code>-<code class="highlighter-rouge">else</code>, <code class="highlighter-rouge">=</code>, <code class="highlighter-rouge">-</code>, <code class="highlighter-rouge">*</code>los tenemos codificados en lambda cálculo y podemos usarlos.</p>
<blockquote>
<p>Función factorial en lambda cálculo:
<code class="highlighter-rouge">λf.λx. if x=0 or x=1 then 1 else x*f(x-1)</code></p>
</blockquote>
<p>Hay una función de orden superior muy conocida en haskell: <code class="highlighter-rouge">fix</code> ó <strong>combinador de punto fijo</strong> que suele ser muy utilizada y que podremos aplicar tambien en lambda cálculo si estuviera definida previamente.<br />
> <code class="highlighter-rouge">fix f -> f (fix f)</code>
* al aplicar <code class="highlighter-rouge">fix</code> a <code class="highlighter-rouge">f (fix f)</code>
1. Se retorna f<br />
2. Pasando una nueva invocación a <code class="highlighter-rouge">fix f</code> como primer parámetro</p>
<h2 id="ejercicios-para-prcticar-lo-aprendido">Ejercicios para prácticar lo aprendido</h2>
<ul>
<li>Aplica reducción y conversión a: <code class="highlighter-rouge">(λf.λx.f(fx))(λx.x+x)n</code></li>
<li>Usando funciones de orden superior, preferiblemente, usando lambda expresiones, construye tu propia “Calculadora funcional” con las operaciones básicas de : <code class="highlighter-rouge">suma</code>, <code class="highlighter-rouge">resta</code>, <code class="highlighter-rouge">multiplicación</code> y <code class="highlighter-rouge">divisiòn</code></li>
</ul>
<h2 id="soluciones">Soluciones</h2>
<ul>
<li>Lambda cálculo - solución: <a href="https://github.com/Indeseables/indeseables.github.io/blob/master/_codigos/entrada_funcional1/555df00550955abc13e4cec1400672c7.png">Github: Imágen</a></li>
<li>Calculadora funcional: <a href="https://github.com/Indeseables/indeseables.github.io/blob/master/_codigos/entrada_funcional1/calculator.hs">Github: Haskell</a> <a href="https://github.com/Indeseables/indeseables.github.io/blob/master/_codigos/entrada_funcional1/calculator.cs">Github: C#</a></li>
</ul>
<h2 id="siguiente-post">Siguiente post</h2>
<ul>
<li><strong>Cláusulas, Currificación y Aplicación Parcial</strong></li>
</ul>
<h2 id="referencias">Referencias</h2>
<ul>
<li><strong>Códigos de ejemplo y Teoria casi en su totalidad extraidos de las diapositivas de la Escuela de Ingeneria Informática de Uniovi</strong>, <strong>autor</strong> según indícan las diapositivas: <strong>Francisco Ortín Soler</strong></li>
<li>Famoso libro de haskell : <a href="http://aprendehaskell.es">Aprende Haskell</a></li>
</ul>
<hr />
<p>Si tienes alguna duda, comentario o sugerencia, notifícanos a través de <a href="mailto:indeseables.git@gmail.com">indeseables.git@gmail.com</a>
y si te ha gustado la entrada puedes <a href="https://twitter.com/intent/tweet?url=http://indeseables.github.io/2016/03/28/odisea-funcional-parte1/&text=Odisea Funcional (Parte 1)&via=indeseables!" target="_blank"> compartirla!.</a></p>
<hr />
Redes neuronales y autoencoders.2016-03-27T00:00:00+00:00http://indeseables.github.io/2016/03/27/Redes neuronales y Autoencoders<p>Si se ha visto el <a href="http://indeseables.github.io//2016/03/25/implementacion-alternativa-algoritmos-aprendizaje/">post</a> anterior, en el que se comentó la idea general del funcionamiento del Perceptron y se propuso una implementación usando Scipy, la definición de red neuronal como un conjunto de procesadores elementales (neuronas) densamente interconectados no nos resultará difícil de entender, ya que no son más que un conjunto de <a href="https://i.gyazo.com/66efec53d6c5232254e070ba0256c949.png">Perceptron dispuestos en cascada</a>.</p>
<hr />
<h3 id="redes-neuronales">Redes neuronales</h3>
<p>También llamadas redes neuronales artificiales o procesado distribuido y paralelo, se basan en el mismo principio que Perceptron, funciones discriminantes lineales (<a href="https://i.gyazo.com/f895cd0241ad3206721e1444907b4b02.png">FDL</a>) que se determinan mediante el aprendizaje de un vector de parámetros <em>θ</em> resolviendo un problema de optimización en el que se pretende obtener el vector <em>θ</em> que minimice el error cuadrático entre la salida esperada de las muestras de entrenamiento y la salida obtenida por la red neuronal.Este vector de parámetros <em>θ</em>, hace referencia al peso de las conexiones entre las neuronas de la red neuronal (los pesos asociados a cada una de las aristas de la red de la <a href="https://i.gyazo.com/66efec53d6c5232254e070ba0256c949.png">imagen</a>) y es una medida de la complejidad de la red.</p>
<p>Podemos distinguir 2 aspectos de dichas redes:</p>
<ul>
<li>
<p>Topología: hace referencia a la <a href="https://i.gyazo.com/66efec53d6c5232254e070ba0256c949.png">estructura de la red</a>. Definimos una capa como un conjunto de neuronas en un mismo nivel (no hay conexiones entre ellas) y llamaremos a la primera capa (la de las muestras <em>{x1,x2,…,xn}</em>, capa de salida a la última capa y capas ocultas a todas las capas intermedias.</p>
</li>
<li>
<p>Dinámica: describe como fluye la información a través de la red neuronal p.e. <a href="https://i.gyazo.com/3a8b0706c7ce5dfb25cebda4edf6ad11.png">para el caso de la topología anterior</a>. Se puede observar como, para cada neurona <em>i</em> de cada capa (oculta y de salida), se calcula su salida en función de los pesos de las aristas que alcanzan a <em>i</em> y del valor de las neuronas <em>j</em> de la capa anterior que alcanzan a <em>i</em> a través de <em>θij</em> aplicando una función <em>g</em> al resultado obtenido (este tipo de redes neuronales se llaman feed-forward, en las solo hay conexiones hacia delante, en concreto, el tipo de perceptron multicapa que comentamos es un caso particular de red feed-forward donde solo hay conexiones a neuronas de la capa siguiente) algunos ejemplos de funciones de este tipo son <a href="https://i.gyazo.com/24422f154964f9b4f08ecd6ca45181a6.png">estos</a>. Hay toda una historia asociada a éstas funciones, pero sólo considerad que éstas funciones se utilizan para que la red neuronal pueda aprender fronteras de decisión más complejas.</p>
</li>
</ul>
<p>Las redes neuronales pueden aplicarse tanto a regresión (para lo que fueron concebidas) como a clasificación, donde cualquier frontera de decisión basada en trozos de hiperplanos se puede aproximar con un perceptron multicapa de este estilo. Además, pueden ser empleadas tanto en <a href="https://es.wikipedia.org/wiki/Aprendizaje_supervisado">aprendizaje supervisado</a>, como en <a href="https://es.wikipedia.org/wiki/Aprendizaje_no_supervisado">aprendizaje no supervisado</a> (e.g. autoencoders que veremos en el próximo apartado)</p>
<p>Aparte de lo que he comentado, hay muchísimos teoremas relacionados con las redes neuronales, relativos a la convergencia, al factor de aprendizaje, la intratabilidad del aprendizaje (Blum and Rivest, 1992), el tamaño del conjunto de entrenamiento (Ripley, 1993) y muchos otros que no comentaré, además, a los que estén familiarizados con ésto tal vez les resulte raro que no comente el tan conocido algoritmo de aprendizaje <a href="https://es.wikipedia.org/wiki/Propagaci%C3%B3n_hacia_atr%C3%A1s"><em>backpropagation</em></a>, pero me lo ahorraré ya que no deja de ser un método de optimización de descenso por gradiente mediante las funciones de activación <em>g</em>, comentadas antes, que simplifican el cálculo de las derivadas parciales. Si no estáis familiarizados, este proceso vedlo como una caja negra que calcula los pesos óptimos de las conexiones entre las neuronas de la red, para la entrada, lo implementaré con los optimizadores de Scipy (y podemos aplicar métodos no convencionales de optimización para calcular el vector <em>θ</em>, aparte del descenso por gradiente! ). Para implementarlo, basta considerar que el aprendizaje consiste en minimizar el error cuadrático medio de las diferencias entre las salidas esperadas y las obtenidas, e.g. <a href="https://i.gyazo.com/dc04d1204eabedaffd8f62b62fbf1962.png">1 capa oculta</a>, por tanto, podemos considerar dicha función como función objetivo y calcular <em>θ</em> óptimo mediante Scipy, con cualquiera de los algoritmos de optimización implementados.</p>
<p>Con ello, ya sé ha visto una pincelada (muy gruesa) de lo básico para comprender mínimamente la implementación de un ejemplo que permite definir una red neuronal y dado un conjunto de muestras supervisadas (o no supervisadas), aprender el vector de parámetros <em>θ</em> para clasificar o hacer regresión.</p>
<div class="highlighter-rouge"><pre class="highlight"><code><span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf-8 -*-</span>
<span class="c">#</span>
<span class="c"># NeuralNetwork.py</span>
<span class="kn">from</span> <span class="nn">scipy.optimize</span> <span class="kn">import</span> <span class="n">minimize</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">Utils</span> <span class="kn">import</span> <span class="n">MatLoad</span>
<span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">e</span>
<span class="k">def</span> <span class="nf">lineal</span><span class="p">(</span><span class="n">z</span><span class="p">):</span> <span class="k">return</span> <span class="n">z</span>
<span class="k">def</span> <span class="nf">step</span><span class="p">(</span><span class="n">z</span><span class="p">):</span> <span class="k">return</span> <span class="mf">1.0</span> <span class="k">if</span> <span class="n">z</span><span class="o">></span><span class="mi">0</span> <span class="k">else</span> <span class="o">-</span><span class="mf">1.0</span>
<span class="k">def</span> <span class="nf">ramp</span><span class="p">(</span><span class="n">z</span><span class="p">):</span>
<span class="k">if</span> <span class="n">z</span><span class="o">>=</span><span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="mf">1.0</span>
<span class="k">elif</span> <span class="o">-</span><span class="mi">1</span><span class="o"><</span><span class="n">z</span><span class="o"><</span><span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="n">z</span>
<span class="k">elif</span> <span class="n">z</span><span class="o"><=-</span><span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="o">-</span><span class="mf">1.0</span>
<span class="k">def</span> <span class="nf">sigmoid</span><span class="p">(</span><span class="n">z</span><span class="p">):</span> <span class="k">return</span> <span class="mf">1.0</span><span class="o">/</span><span class="p">(</span><span class="mf">1.0</span><span class="o">+</span><span class="p">(</span><span class="n">e</span><span class="o">**</span><span class="p">(</span><span class="o">-</span><span class="n">z</span><span class="p">)))</span>
<span class="k">def</span> <span class="nf">tanh</span><span class="p">(</span><span class="n">z</span><span class="p">):</span> <span class="k">return</span> <span class="p">((</span><span class="n">e</span><span class="o">**</span><span class="n">z</span><span class="p">)</span><span class="o">-</span><span class="p">(</span><span class="n">e</span><span class="o">**-</span><span class="n">z</span><span class="p">))</span><span class="o">/</span><span class="p">((</span><span class="n">e</span><span class="o">**</span><span class="n">z</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="n">e</span><span class="o">**-</span><span class="n">z</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">fast</span><span class="p">(</span><span class="n">z</span><span class="p">):</span> <span class="k">return</span> <span class="n">z</span><span class="o">/</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="nb">abs</span><span class="p">(</span><span class="n">z</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">get_weights_submatrices</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">):</span>
<span class="n">thetas</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">index</span> <span class="o">=</span> <span class="n">units_by_layer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">thetas</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">theta</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">index</span><span class="p">])</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
<span class="n">thetas</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">theta</span><span class="p">[</span><span class="n">index</span><span class="p">:</span><span class="n">index</span><span class="o">+</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">))])</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>
<span class="n">index</span> <span class="o">+=</span> <span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span>
<span class="n">thetas</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">theta</span><span class="p">[</span><span class="n">index</span><span class="p">:])</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span><span class="n">units_by_layer</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">]))</span>
<span class="k">return</span> <span class="n">thetas</span>
<span class="k">def</span> <span class="nf">generate_theta</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">):</span>
<span class="n">n_connections</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span> <span class="n">n_connections</span> <span class="o">+=</span> <span class="p">((</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">units_by_layer</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
<span class="n">n_connections</span> <span class="o">+=</span> <span class="p">(</span><span class="n">units_by_layer</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">units_by_layer</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">])</span>
<span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="n">n_connections</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">forward_propagation</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">):</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">get_weights_submatrices</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">)</span>
<span class="n">vec_factivation</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">vectorize</span><span class="p">(</span><span class="n">factivation</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">vec_factivation</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">X</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">res</span><span class="p">,</span><span class="mi">0</span><span class="p">,[</span><span class="mi">1</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">])],</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">vec_factivation</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">*</span><span class="n">res</span><span class="p">)</span>
<span class="k">return</span> <span class="n">res</span>
<span class="k">def</span> <span class="nf">forward_propagation_transform</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">):</span>
<span class="n">vec_factivation</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">vectorize</span><span class="p">(</span><span class="n">factivation</span><span class="p">)</span>
<span class="k">return</span> <span class="n">vec_factivation</span><span class="p">(</span><span class="n">theta</span><span class="o">*</span><span class="n">X</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">):</span>
<span class="n">res_output_layer</span> <span class="o">=</span> <span class="n">forward_propagation</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">)</span>
<span class="n">mean_squared_error</span> <span class="o">=</span> <span class="p">(</span><span class="n">Y</span><span class="o">-</span><span class="n">res_output_layer</span><span class="p">)</span>
<span class="n">mean_squared_error</span> <span class="o">=</span> <span class="p">(</span><span class="mf">1.0</span><span class="o">/</span><span class="p">(</span><span class="mf">2.0</span><span class="o">*</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span><span class="o">*</span><span class="n">np</span><span class="o">.</span><span class="n">multiply</span><span class="p">(</span><span class="n">mean_squared_error</span><span class="p">,</span><span class="n">mean_squared_error</span><span class="p">)</span><span class="o">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="k">return</span> <span class="n">mean_squared_error</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">,</span><span class="n">max_iter</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span><span class="n">verbose_convergence</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span> <span class="k">return</span> <span class="n">minimize</span><span class="p">(</span><span class="n">fun</span><span class="o">=</span><span class="n">f</span><span class="p">,</span><span class="n">x0</span><span class="o">=</span><span class="n">theta</span><span class="p">,</span><span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">),</span><span class="n">method</span><span class="o">=</span><span class="s">"CG"</span><span class="p">,</span><span class="n">options</span><span class="o">=</span><span class="p">{</span><span class="s">"maxiter"</span><span class="p">:</span><span class="n">max_iter</span><span class="p">,</span><span class="s">"disp"</span><span class="p">:</span><span class="n">verbose_convergence</span><span class="p">})</span>
<span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">):</span> <span class="k">return</span> <span class="n">forward_propagation</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">(</span><span class="n">X</span><span class="p">),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">)</span><span class="o">.</span><span class="n">argmax</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">item</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">):</span> <span class="k">return</span> <span class="n">forward_propagation</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">(</span><span class="n">X</span><span class="p">),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">factivation</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="c">## Testing classifier ##</span>
<span class="n">X</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"X.np"</span><span class="p">);</span> <span class="c"># Vectores por columnas (N=|cols|, M=|rows|) # </span>
<span class="n">Y</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"Y.np"</span><span class="p">);</span> <span class="c"># Vectores por columnas (N=|cols|, M=|rows|) #</span>
<span class="n">XROWS</span><span class="p">,</span><span class="n">XCOLS</span> <span class="o">=</span> <span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="c">#### Test NN aleatoria ####</span>
<span class="n">units_by_layer</span> <span class="o">=</span> <span class="p">[</span><span class="n">XROWS</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c"># Se cuenta la unidad BIAS en la capa oculta (la entrada se asume homogénea) en la de salida NO hay #</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">generate_theta</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">lineal</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"</span><span class="se">\n</span><span class="s"> Detalles de convergencia </span><span class="se">\n</span><span class="s">"</span>
<span class="k">print</span> <span class="n">res</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">res</span><span class="o">.</span><span class="n">x</span>
<span class="k">print</span> <span class="s">"Clase de la muestra [1,-4,-4]: "</span><span class="p">,</span><span class="n">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">([[</span><span class="mi">1</span><span class="p">],[</span><span class="o">-</span><span class="mi">4</span><span class="p">],[</span><span class="o">-</span><span class="mi">4</span><span class="p">]]),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">lineal</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Clase de la muestra [1,4,4]: "</span><span class="p">,</span><span class="n">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">([[</span><span class="mi">1</span><span class="p">],[</span><span class="mi">4</span><span class="p">],[</span><span class="mi">4</span><span class="p">]]),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">lineal</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Regresion con la muestra [1,-4,-4]: "</span><span class="p">,</span><span class="n">predict</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">([[</span><span class="mi">1</span><span class="p">],[</span><span class="o">-</span><span class="mi">4</span><span class="p">],[</span><span class="o">-</span><span class="mi">4</span><span class="p">]]),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">lineal</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Regresion con la muestra [1,-4,-4]: "</span><span class="p">,</span><span class="n">predict</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">([[</span><span class="mi">1</span><span class="p">],[</span><span class="mi">4</span><span class="p">],[</span><span class="mi">4</span><span class="p">]]),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">lineal</span><span class="p">)</span>
</code></pre>
</div>
<p>En el ejemplo se puede ver como se carga el fichero que tiene las muestras <em>X</em> y el que tiene las salidas de dichas muestras <em>Y</em> (la salida de cada muestra, ahora es un conjunto de valores que representan el valor esperado en cada una de las neuronas de salida). Después se define una red neuronal especificando el número de neuronas por capa, en este caso, una red con 4 capas (las de entrada y salida y 2 capas ocultas), donde:</p>
<ul>
<li>
<p>La capa 0, de entrada tiene tantas neuronas como dimensiones tienen las muestras.</p>
</li>
<li>
<p>La capa 1, (1º capa oculta) tiene 4 neuronas (en el código son 5 porque a las capas ocultas hay que sumarle 1, debido a la neurona de bias (<em>+1</em>) que representa el término independiente en la combinación lineal que define la FLD (en la capa de entrada, la unidad de bias viene representada en las muestras al ponerlas en notación homogénea - poniendo un 1 en la primera posición - y en la capa de salida no hay bias).</p>
</li>
<li>
<p>La capa 2 (2º capa oculta) tiene 3 neuronas (en el código 4 por la razón comentada antes).</p>
</li>
<li>
<p>La capa 3, capa de salida tiene tantas neuronas como dimensiones tiene el conjunto de salida esperado (clases en clasificación o nº dimensiones en regresión).</p>
</li>
</ul>
<p>La salida del script con <a href="https://i.gyazo.com/12a7773066427859ab109f9c0b2b9e2b.png">X</a>, <a href="https://i.gyazo.com/f01eb26b0965ea40a8eda8a3250a2b29.png">Y</a> (muestras y salidas dispuestas por columnas) es la siguiente:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
Optimization terminated successfully.
Current function value: 0.075000
Iterations: 41
Function evaluations: 2849
Gradient evaluations: 77
Detalles de convergencia
fun: 0.07500000000044556
jac: array([ -1.40629709e-07, -2.80328095e-07, -2.80328095e-07,
-1.42492354e-07, -2.79396772e-07, -2.79396772e-07,
-1.40629709e-07, -2.80328095e-07, -2.80328095e-07,
-1.41561031e-07, -2.80328095e-07, -2.80328095e-07,
5.92321157e-07, -5.71832061e-07, -5.71832061e-07,
-5.71832061e-07, -5.72763383e-07, 5.92321157e-07,
-5.72763383e-07, -5.72763383e-07, -5.72763383e-07,
-5.71832061e-07, 5.92321157e-07, -5.71832061e-07,
-5.71832061e-07, -5.71832061e-07, -5.72763383e-07,
-4.53554094e-07, -3.41795385e-07, -3.43658030e-07,
-3.41795385e-07, 7.41332769e-07, 5.39235771e-07,
5.38304448e-07, 5.39235771e-07])
message: 'Optimization terminated successfully.'
nfev: 2849
nit: 41
njev: 77
status: 0
success: True
x: array([ 0.29015638, -0.31882134, -0.31882134, 0.29015639, -0.31882129,
-0.31882129, 0.29015638, -0.31882134, -0.31882134, 0.29015634,
-0.31882134, -0.31882134, 0.42504066, -0.07970499, -0.07970504,
-0.07970499, -0.07970508, 0.42504011, -0.07970658, -0.07970659,
-0.07970658, -0.07970657, 0.42504066, -0.07970499, -0.07970504,
-0.07970499, -0.07970508, 1.19071489, -0.49189776, -0.49189632,
-0.49189776, -0.19071479, 0.49189641, 0.49189929, 0.49189641])
Clase de la muestra [1,-4,-4]: 0
Clase de la muestra [1,4,4]: 1
Regresion con la muestra [1,-4,-4]: [[ 1.90000067]
[-0.90000071]]
Regresion con la muestra [1,-4,-4]: [[-0.50000098]
[ 1.50000139]]
</code></pre>
</div>
<p>Se puede ver que para clasificación (índice de la neurona de la capa de salida que maximice su salida) con esas muestras se comporta bien (dibujadlo en un eje cartesiano teniendo en cuenta la salida de cada muestra e intentad separarlas) y mediante regresión podemos conocer el valor de cada una de las neuronas de la capa de salida. En este ejemplo se ha visto como podemos entrenar de forma supervisada una red neuronal, sin embargo pueden ser entrenadas de forma no supervisada de una manera muy peculiar.</p>
<hr />
<h3 id="autoencoders">Autoencoders</h3>
<p>Un <a href="https://en.wikipedia.org/wiki/Autoencoder">autoencoder</a> es una red neuronal utilizada para aprender codificaciones (p.e. <a href="https://es.wikipedia.org/wiki/Reducci%C3%B3n_de_dimensionalidad">reducción de dimensionalidad</a>, compresión, cifrado, etc) eficientes. Comentaré el autoencoder más básico, que es un caso particular de redes feed-forward muy parecido al perceptron multicapa comentado antes, en el que la capa de entrada y la capa de salida tienen el mismo número de neuronas y en lugar de esperar una salida supervisada <em>Y</em>, se espera la misma muestra de entrada <em>X</em>, consiguiendo con ello que la red aprenda a reconstruir la misma entrada que recibe.</p>
<p>La estructura general de un autoencoder es <a href="https://upload.wikimedia.org/wikipedia/commons/2/28/Autoencoder_structure.png">ésta</a>, y el que emplearé en esta entrada es una variación de <a href="https://rubenlopezg.files.wordpress.com/2014/04/sparse-autoencoder2.png">éste</a> en el que se ha modificado el número de neuronas en cada capa (ajustándolo a nuestras muestras de entrenamiento y a lo que se quiera conseguir).</p>
<p>Si nos fijamos, cuando se entrene la red neuronal de la <a href="https://rubenlopezg.files.wordpress.com/2014/04/sparse-autoencoder2.png">figura del autoencoder anterior</a> se calcularán los pesos de todas las conexiones de la red y con ello, podemos separar la red en 2 subredes:</p>
<ul>
<li>
<p>Subred 1: formada por la capa de entrada y la capa oculta, el resultado obtenido de esta subred es el valor de las neuronas que forman la capa oculta, donde se habrá obtenido una transformación de las muestras reales a un espacio alternativo. Por esta razón, a esta subred se le llama <em>encoder</em>.</p>
</li>
<li>
<p>Subred 2: formada por la capa oculta y la capa de salida, en este caso, partiendo del resultado obtenido en las neuronas de la capa oculta en el paso anterior (resultado del encoder) se obtiene el valor de las neuronas de la capa de salida, que recordemos, sus pesos habían sido entrenados (junto con los demás de la red) para reconstruir la entrada de salida. Con ello, podemos obtener a partir del resultado del <em>encoder</em> el resultado original. Por esta razón, a esta subred se le llama <em>decoder</em></p>
</li>
</ul>
<p>Por tanto, como se ha visto, al ser el <em>autoencoder</em> simple un caso particular de red neuronal, podemos hacer uso del script implementado en el apartado anterior, que nos permite diseñar redes neuronales y aprender los pesos que minimicen el error cuadrático medio. Consideraré 2 ejemplos de uso de estos <em>autoencoder</em>, el cifrado de bloques de texto y la compresión.</p>
<hr />
<h3 id="aplicaciones-de-autoencoders-sistema-de-cifrado-por-bloques-1">Aplicaciones de Autoencoders: sistema de cifrado por bloques (1)</h3>
<p>En el primer ejemplo de aplicación, vamos a ver como haciendo uso de un <em>autoencoder</em> como los explicados arriba, podemos construir un <a href="https://es.wikipedia.org/wiki/Cifrado_por_bloques">sistema de cifrado por bloques</a> muy simple, en el que el <em>encoder</em> nos permitirá realizar el proceso de cifrado y el <em>decoder</em> el proceso de descifrado. Supongamos que queremos cifrar el mensaje <em>“holablau”</em>, podemos por ejemplo construir 4 muestras de entrenamiento segmentando el mensaje en conjuntos de 2B en secuencia i.e. <em>S={“ho”,”la”,”bl”,”au”}</em> (tendremos que cifrar y descifrar bloques de 2B) , como hay que obtener una representación en un espacio continuo, podemos obtener para cada elemento de <em>S</em> el valor binario de su código <em>ASCII</em> (concatenar los bits de cada uno de los símbolos de cada elemento), con ello, disponemos cada muestra de entrenamiento en columnas (y en notación homogénea con el 1 delante!) y no necesitamos más (recordad que no se requiere información de la salida, solo se pretende reconstruir la muestra original).</p>
<p>Ahora solo queda fijar el número de neuronas en la capa oculta (<em>M1</em> en el script a 10 , 9 neuronas y la de BIAS) y una vez establecido, ejecutar el script (implementado haciendo uso del script para redes neuronales -MLP- del apartado anterior -NeuralNetwork.py-):</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
<span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf-8 -*-</span>
<span class="c">#</span>
<span class="c"># AutoEncoder.py</span>
<span class="kn">import</span> <span class="nn">NeuralNetwork</span> <span class="kn">as</span> <span class="nn">nn</span>
<span class="kn">from</span> <span class="nn">Utils</span> <span class="kn">import</span> <span class="n">MatLoad</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">X</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"X.np"</span><span class="p">);</span> <span class="c"># Vectores por columnas (N=|cols|, M=|rows|) # </span>
<span class="n">XROWS</span><span class="p">,</span><span class="n">XCOLS</span><span class="p">,</span><span class="n">M1</span> <span class="o">=</span> <span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="mi">10</span> <span class="c"># Se cuenta la unidad BIAS en la capa oculta (la entrada se asume en notación homogénea) #</span>
<span class="n">units_by_layer</span> <span class="o">=</span> <span class="p">[</span><span class="n">XROWS</span><span class="p">,</span><span class="n">M1</span><span class="p">,</span><span class="n">XROWS</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">nn</span><span class="o">.</span><span class="n">generate_theta</span><span class="p">(</span><span class="n">units_by_layer</span><span class="p">)</span>
<span class="n">Y</span> <span class="o">=</span> <span class="n">X</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">nn</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">nn</span><span class="o">.</span><span class="n">lineal</span><span class="p">,</span><span class="n">max_iter</span><span class="o">=</span><span class="mi">1000000</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"</span><span class="se">\n</span><span class="s"> Detalles de convergencia </span><span class="se">\n</span><span class="s">"</span>
<span class="k">print</span> <span class="n">res</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">res</span><span class="o">.</span><span class="n">x</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">nn</span><span class="o">.</span><span class="n">get_weights_submatrices</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Vector original: "</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">([[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],[</span><span class="mi">1</span><span class="p">]])</span>
<span class="n">encoded</span> <span class="o">=</span> <span class="n">nn</span><span class="o">.</span><span class="n">forward_propagation_transform</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">np</span><span class="o">.</span><span class="n">matrix</span><span class="p">([[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],[</span><span class="mi">1</span><span class="p">]]),</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">nn</span><span class="o">.</span><span class="n">lineal</span><span class="p">)</span>
<span class="n">encoded</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">encoded</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Vector cifrado: "</span><span class="p">,</span><span class="n">encoded</span>
<span class="n">decoded</span> <span class="o">=</span> <span class="n">nn</span><span class="o">.</span><span class="n">forward_propagation_transform</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">encoded</span><span class="p">,</span><span class="n">units_by_layer</span><span class="p">,</span><span class="n">nn</span><span class="o">.</span><span class="n">lineal</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Vector descifrado: "</span><span class="p">,</span><span class="n">decoded</span>
</code></pre>
</div>
<p>En el script se ha entrenado primero el <em>autoencoder</em> con el conjunto de muestras que he mencionado antes y después se ha procedido a cifrar el bloque de 2B “ho” (<code class="highlighter-rouge">0110100001101111</code>, y en notación homogénea <code class="highlighter-rouge">10110100001101111</code>), los resultados que se obtienen tras la ejecución del script son:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
Optimization terminated successfully.
Current function value: 0.000000
Iterations: 134
Function evaluations: 105841
Gradient evaluations: 336
Detalles de convergencia
fun: 2.399946487631182e-09
jac: array([ -1.10356042e-06, 0.00000000e+00, -1.10356042e-06,
...
message: 'Optimization terminated successfully.'
nfev: 105841
nit: 134
njev: 336
status: 0
success: True
x: array([ -8.47539898e-01, 1.00000000e+00, -8.47539898e-01,
...
Vector cifrado:
[[ 1. ]
[-5.18020183]
[-5.18122276]
[-5.54306646]
[-4.99780952]
[-4.88438915]
[-5.32546369]
[-4.55401426]
[-5.17546803]
[-6.14500501]]
Vector descifrado:
[[ 2.73595684e-05]
[ 9.99997815e-01]
[ 9.99997815e-01]
[ 2.73595684e-05]
[ 1.00001467e+00]
[ 2.51373514e-05]
[ 1.14924639e-05]
[ 2.63763669e-05]
[ 2.73599263e-05]
[ 9.99997815e-01]
[ 9.99997815e-01]
[ 2.63777148e-05]
[ 1.00000102e+00]
[ 1.00000004e+00]
[ 1.00001689e+00]
[ 1.00001368e+00]]
</code></pre>
</div>
<p>Se puede observar como los 2 bytes <em>“ho”</em>, cifrados mediante el <em>encoder</em>, quedan de la siguiente forma:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
[1.0, -5.1802018305848385, -5.181222759796455, -5.543066463152387, -4.997809523535919, -4.884389148007498, -5.325463692424654, -4.554014256199364, -5.1754680337452275, -6.145005007361612]
</code></pre>
</div>
<p>Podemos transmitir ese cifrado a través de un canal inseguro, habiendo comunicado previamente la clave, que en este caso son los pesos del <em>decoder</em> y el número de neuronas en la capa de salida, mediante un canal seguro p.e. obtenido con criptografía de clave pública.En el destino se descifra mediante el <em>decoder</em> partiendo del cifrado anterior, y se obtiene:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
[2.73595684e-05, 0.999997815, 0.999997815, 2.73595684e-05, 1.00001467, 2.51373514e-05, 1.14924639e-05, 2.63763669e-05, 2.73599263e-05, 0.999997815, 0.999997815,2.63777148e-05, 1.00000102, 1.00000004, 1.00001689, 1.00001368]
</code></pre>
</div>
<p>Si son valores absolutos p.e. 0 y 1, posiblemente no se lleguen a alcanzar nunca debido a que la máxima precisión en coma flotante son 16 dígitos correctos, sin embargo se obtienen valores que prácticamente se corresponden con los valores extremos. En este caso: <code class="highlighter-rouge">[0,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1]</code> que se corresponde con el bloque de 2B original: <code class="highlighter-rouge">0110100001101111</code></p>
<p>De éste modo, podríamos cifrar mensajes completos segmentándolos en bloques de 2B consecutivos e ir cifrando bloque a bloque mediante modos de operación (encadenados o no). Pero si nos fijamos, no solo hemos cifrado, si no que también hemos reducido la información a transmitir (aunque no del todo porque en este ejemplo, al ser la representación discreta y binaria se podría enviar como bloques de bits concatenados y al cifrar de esta manera obtenemos un conjunto de reales que ocupan un mayor número de bytes) pasando de 16 elementos (2B por bloque) a 9 elementos. ¿por qué ha pasado ésto?-</p>
<hr />
<h3 id="aplicaciones-de-autoencoders-compresin-2">Aplicaciones de Autoencoders: compresión (2)</h3>
<p>En el ejemplo anterior se ha visto como se ha reducido el número de elementos a transmitir, ¿por qué?, la respuesta es sencilla, si partimos de una muestra con 16 dimensiones y la usamos para obtener una salida en la primera capa oculta(la última del <em>encoder</em>) de la red neuronal entrenada para la reconstrucción de las muestras, si esta primera capa oculta tiene un menor número de neuronas que dimensiones tienen las muestras, estaremos consiguiendo una reducción del número de elementos. En el ejemplo del cifrado se puede ver como cada muestra tiene 16 dimensiones (1 por cada bit del bloque de 2B) y la primera capa oculta tiene 9 neuronas, por lo que pasaremos de tener un bloque de 16 bits del texto sin cifrar a obtener 9 elementos reales de texto cifrado (se ha obtenido una reducción del número de elementos en el espacio original).</p>
<hr />
<p>Con esto ha quedado una entrada que recoge una pequeña parte de la teoría de redes neuronales y propone una implementación resolviendo el problema de optimización que caracteriza el aprendizaje en este tipo de redes con métodos diferentes a los convencionales (todos los implementados en el módulo <em>optimize</em> de <em>Scipy</em> de ámbito local e.g. gradiente conjugado, L-BFGS, L-BFGS-B, Nelder-Mead etc).</p>
<p>En la siguiente entrada continuaré las entregas de implementaciones alternativas explicando SVM y funciones kernel.</p>
<p>He dejado todo el código nuevo en el <a href="https://github.com/Indeseables/indeseables.github.io/tree/master/_codigos/Entrada_2">repositorio</a>.</p>
<hr />
<p>Si tienes alguna duda, comentario o sugerencia, notifícanos a través de <a href="mailto:indeseables.git@gmail.com">indeseables.git@gmail.com</a>
y si te ha gustado la entrada puedes <a href="https://twitter.com/intent/tweet?url=http://indeseables.github.io/2016/03/27/Redes-neuronales-y-Autoencoders/&text=Redes neuronales y autoencoders.&via=indeseables!" target="_blank"> compartirla!.</a></p>
<hr />
Implementación alternativa de algoritmos de aprendizaje (parte I).2016-03-25T00:00:00+00:00http://indeseables.github.io/2016/03/25/implementacion-alternativa-algoritmos-aprendizaje<p>Estrenaré el blog con el inicio de una serie de entradas que dedicaré a implementar algoritmos de aprendizaje usando la librería <em>Scipy</em> y más concretamente, su módulo <em>optimize</em> (tenía pensado usar <a href="https://www.mcs.anl.gov/petsc/">Petsc</a>, pero no he conseguido convertir matrices a su formato y me ha complicado las cosas).</p>
<p>Dicho módulo nos permite realizar varios métodos de optimización y búsqueda de raices de funciones (nos centraremos en la parte de optimización de momento y tal vez vea optimizaciones alternativas p.e. usando <a href="https://es.wikipedia.org/wiki/M%C3%A9todo_iterativo">métodos iterativos</a> de <a href="https://es.wikipedia.org/wiki/M%C3%A9todo_iterativo#M.C3.A9todos_del_subespacio_de_Krylov">Krylov</a>, que son de mucha utilidad a la hora de resolver sistemas muy grandes de forma eficiente) como pueden ser (no comentaré todos los <a href="http://docs.scipy.org/doc/scipy/reference/optimize.html">métodos</a> de cada tipo de optimización):</p>
<ul>
<li>
<p>Optimización local: permite minimizar (y si cambiamos de signo maximizar) <a href="https://es.wikipedia.org/wiki/Optimizaci%C3%B3n_%28matem%C3%A1tica%29">funciones</a>, variando una serie de parámetros del optimizador como el método de optimización local empleado (Gradiente conjugado, BFGS, Método de Powell …). Al ser un <a href="(https://en.wikipedia.org/wiki/Local_search_%28optimization%29)">método local</a> , puede quedar atascado en mínimos locales por lo que, si la solución obtenida no es suficientemente buena, se pueden obtener nuevas soluciones de varias formas p e. reinicialización aleatoria, son computacionalmente baratos.</p>
</li>
<li>
<p>Optimización de ecuaciones: entre los que se puede encontrar el método de <a href="https://es.wikipedia.org/wiki/M%C3%ADnimos_cuadrados">mínimos cuadrados</a> ampliamente conocidos en <a href="https://es.wikipedia.org/wiki/Regresi%C3%B3n_%28estad%C3%ADstica%29">regresión</a> y optimización</p>
</li>
<li>
<p>Optimizaciones globales: al contrario que los métodos locales, éstos nos proporcionan soluciones óptimas pero son más costosos ya que requieren comprobar el espacio de soluciones completo (a no ser que se disponga de información que lo acote).</p>
</li>
<li>
<p>Ajustes de funciones (<em>fitting</em>): dada una función <em>f</em>, encontrar una función <em>g</em> que la <a href="https://www.google.es/url?sa=t&rct=j&q=&esrc=s&source=web&cd=6&cad=rja&uact=8&ved=0ahUKEwjL37eA69rLAhVCXBoKHUkFBCkQFghDMAU&url=http%3A%2F%2Fwww.ugr.es%2F~lorente%2FAPUNTESMNM%2Fcapitulo5.pdf&usg=AFQjCNEXrijmpFscjHqL7WAoFqF_IXMoJA">aproxime</a>, solo está disponible el método <a href="https://es.wikipedia.org/wiki/M%C3%ADnimos_cuadrados">mínimos cuadrados</a> para aproximación discreta.</p>
</li>
</ul>
<p>En esta primera entrada comentaré la idea general e implementación de 2 algoritmos de aprendizaje básicos (ambos redes neuronales de una única capa): <a href="https://es.wikipedia.org/wiki/Perceptr%C3%B3n"><em>Perceptron</em></a> y <a href="https://es.wikipedia.org/wiki/Adaline"><em>Adaline</em></a> se verán sus versiones <a href="https://es.wikipedia.org/wiki/Clasificador_lineal">lineales</a> (aunque es fácilmente generalizable a funciones no lineales usando <a href="https://es.wikipedia.org/wiki/M%C3%A1quinas_de_vectores_de_soporte#Funci.C3.B3n_Kernel"><em>kernels</em></a> en cierta parte del código que comentaré) haciendo uso de técnicas de optimización sin restricciones (en la siguiente entrada se verá como añadir restricciones y cotas). Por ello, para comprobar los resultados del entrenamiento y del test se utilizará un conjunto de muestras que variará dependiendo del algoritmo que esté comentando.</p>
<hr />
<h2 id="perceptron">Perceptron</h2>
<p>Es un clasificador (en 2 clases {+1,-1} es el que trataremos) basado en funciones discriminantes lineales, el objetivo es encontrar un vector <em>θ</em> que defina un <a href="https://es.wikipedia.org/wiki/Hiperplano">hiperplano</a> separador p.e. una recta en 2D, que separe <a href="http://photos1.blogger.com/blogger/1013/1515/320/SVMSeparacion.jpg">correctamente</a> el conjunto de muestras en 2 clases. Esto equivale a resolver un sistema de <em>N</em> inecuaciones <strong>c</strong>·<strong>θ</strong>·<strong>x</strong>≥<strong>0</strong> (nótese la notación matricial, si se hace vector a vector, equivaldría a resolver <a href="https://i.gyazo.com/e769171d48212f1ccd7d820184b9684d.png">ésto</a>)</p>
<p>Ese sistema de <em>N</em> inecuaciones con |θ| incógnitas es demasiado costoso de resolver mediante métodos directos (aunque podríamos haber empleado otros <a href="https://es.wikipedia.org/wiki/M%C3%A9todo_iterativo">métodos iterativos</a> como los que iba a utilizar en <a href="https://www.mcs.anl.gov/petsc/">Petsc</a>) y por ello, se opta por minimizar ésta <a href="https://i.gyazo.com/5cccecf3f307437adb63609a97f0ed40.png">función</a> ( suma de las <a href="https://i.gyazo.com/2a4626fe88c672270a4f180f6943aa9b.png">distancias</a> de cada muestra mal clasificada al hiperplano separador) equivalente a resolver el sistema original.
Este proceso se suele hacer de forma iterativa, calculando cada vez la suma de las distancias mencionada antes, modificando de acuerdo a esas distancias el valor de <em>θ</em> y repitiendo el proceso hasta que <em>θ</em> converge,varía muy poco, se acaban las iteraciones … .Sin embargo, podemos ahorrarnos la implementación del método iterativo haciendo uso del <a href="http://docs.scipy.org/doc/scipy/reference/optimize.html">módulo</a> comentado antes, configurando correctamente la función objetivo a optimizar, las cotas, las restricciones (en entradas posteriores se verán, cuando se implementen máquinas de vectores de soporte <a href="https://es.wikipedia.org/wiki/M%C3%A1quinas_de_vectores_de_soporte">SVM</a>), la tolerancia etc. Otra cosa a tener en cuenta es que Perceptron siempre converge si las muestras son linealmente separables.</p>
<p>El código es el siguiente:</p>
<div class="highlighter-rouge"><pre class="highlight"><code><span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf-8 -*-</span>
<span class="c">#</span>
<span class="c"># Perceptron.py</span>
<span class="kn">from</span> <span class="nn">scipy.optimize</span> <span class="kn">import</span> <span class="n">minimize</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">Utils</span> <span class="kn">import</span> <span class="n">MatLoad</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">multiply</span><span class="p">(</span><span class="n">theta</span><span class="o">*</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">)</span>
<span class="k">if</span> <span class="n">np</span><span class="o">.</span><span class="n">array_equal</span><span class="p">(</span><span class="n">res</span><span class="o">>=+</span><span class="mi">0</span><span class="p">,</span><span class="n">Y</span><span class="o">>=+</span><span class="mi">0</span><span class="p">):</span> <span class="k">return</span> <span class="o">-</span><span class="mi">9999999</span>
<span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="p">(</span><span class="o">-</span><span class="n">res</span><span class="p">[</span><span class="n">res</span><span class="o"><</span><span class="mi">0</span><span class="p">])</span><span class="o">.</span><span class="nb">sum</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">item</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">):</span>
<span class="n">XROWS</span><span class="p">,</span><span class="n">XCOLS</span><span class="p">,</span><span class="n">YROWS</span> <span class="o">=</span> <span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">Y</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">return</span> <span class="n">minimize</span><span class="p">(</span><span class="n">fun</span><span class="o">=</span><span class="n">f</span><span class="p">,</span><span class="n">x0</span><span class="o">=</span><span class="n">theta</span><span class="p">,</span><span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">),</span><span class="n">method</span><span class="o">=</span><span class="s">"CG"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">np</span><span class="o">.</span><span class="n">inner</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">)</span><span class="o">>=+</span><span class="mi">0</span> <span class="k">else</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">):</span> <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">inner</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">X</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"X.np"</span><span class="p">);</span>
<span class="n">Y</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"Y.np"</span><span class="p">);</span>
<span class="n">XROWS</span><span class="p">,</span><span class="n">XCOLS</span><span class="p">,</span><span class="n">YROWS</span> <span class="o">=</span> <span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">Y</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="o">-</span><span class="mf">0.0003</span><span class="p">,</span><span class="o">-</span><span class="mf">1.54</span><span class="p">,</span><span class="o">-</span><span class="mf">3.78</span><span class="p">])</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"</span><span class="se">\n</span><span class="s"> Detalles de convergencia </span><span class="se">\n</span><span class="s">"</span>
<span class="k">print</span> <span class="n">res</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">res</span><span class="o">.</span><span class="n">x</span>
<span class="k">print</span> <span class="s">"</span><span class="se">\n</span><span class="s">Clase de [1,0,5]:"</span><span class="p">,</span><span class="n">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">]))</span>
</code></pre>
</div>
<p>Para la implementación, se han desarrollado únicamente 4 funciones:</p>
<ul>
<li>
<p><em>f</em>: es la función objetivo que he mencionado <a href="https://gyazo.com/5cccecf3f307437adb63609a97f0ed40">antes</a>, se pasará como parámetro al solver de scipy para minimizar. Se hace un producto vector-matriz para calcular la parte <strong>c</strong>·<strong>θ</strong>·<strong>x</strong> y se comprueba si todas las muestras están bien clasificadas, si lo están queremos que el proceso finalice luego devolvemos una distancia muy pequeña, si no lo están devolvemos la suma de las distancias de las muestras mal clasificadas al hiperplano separador.</p>
</li>
<li>
<p><em>fit</em>: se encarga de llamar al optimizador de <em>Scipy</em> <em>minimize</em>, utilizando el método del <a href="https://es.wikipedia.org/wiki/M%C3%A9todo_del_gradiente_conjugado">gradiente conjugado</a> para entrenar el sistema. Podemos hacer uso de todos los demás <a href="http://docs.scipy.org/doc/scipy/reference/optimize.html">métodos</a> implementados en la librería para el optimizador local <em>minimize</em>.</p>
</li>
<li>
<p><em>classify</em>: una vez entrenado el sistema (obtenido el vector <em>θ</em> adecuado), dada una nueva muestra <em>x</em> permite clasificarla en la clase correcta según <em>θ</em>. Para ello se calcula el producto escalar <em>θ</em>·<em>x</em> y se comprueba si está a un lado del espacio definido por el hiperplano separador o al otro ( <em>θ</em>·<em>x</em>≥0 , <em>θ</em>·<em>x</em>≤0 ).</p>
</li>
<li>
<p><em>predict</em>: dada una muestra <em>x</em> permite hacer regresión una vez el sistema ha sido entrenado. Con ello, se predice el valor de <em>x</em> dado el vector de parámetros <em>θ</em>, en función del resultado del producto escalar <em>θ</em>·<em>x</em>.</p>
</li>
</ul>
<p>Una vez se han definido las funciones necesarias, solo queda ver cómo se comporta el sistema entrenándolo y probándolo. Para ello, se ha hecho uso de un conjunto de muestras de juguete (en notación homogénea) junto con sus salidas, diseñado a mano:</p>
<p>Muestras de entrenamiento: 3 muestras de 3 dimensiones dispuestas por columnas.</p>
<table>
<thead>
<tr>
<th>nº</th>
<th>x1</th>
<th>x2</th>
<th>x3</th>
</tr>
</thead>
<tbody>
<tr>
<td>d1</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>d2</td>
<td>0</td>
<td>2</td>
<td>4</td>
</tr>
<tr>
<td>d3</td>
<td>5</td>
<td>1</td>
<td>2</td>
</tr>
</tbody>
</table>
<p>Salidas: salidas (clases) esperadas para cada una de las 3 muestras (+1 o -1):</p>
<table>
<thead>
<tr>
<th>nº</th>
<th>clase</th>
</tr>
</thead>
<tbody>
<tr>
<td>x1</td>
<td>-1</td>
</tr>
<tr>
<td>x2</td>
<td>+1</td>
</tr>
<tr>
<td>x3</td>
<td>+1</td>
</tr>
</tbody>
</table>
<p>Dibujad las 2 últimas dimensiones de las muestras -d1 y d3 de la primera tabla- en un eje de coordenadas y veréis como encontráis rápidamente infinitas soluciones que separen las muestras en función de la salida (+1 o -1). Eso mismo hará el sistema, dar una posible solución de todas los hiperplanos (rectas en este caso) que sirven para separar las clases (en entradas posteriores, cuando se traten SVM, se podrá elegir entre el mejor de todos aquellos hiperplanos separadores según un criterio de distancia entre clases e hiperplano).</p>
<p>Con ello (en el <em>main</em> del <em>script</em>) partimos de un vector <em>θ</em> aleatorio (se suele inicializar a 0) y lo ajustamos mediante la función <em>fit</em> para que clasifique correctamente las muestras, minimizando la función objetivo mencionada antes. Una vez finalice, obtenemos detalles sobre la finalización del proceso del <em>solver</em> (valor de <em>f</em> final, nº iteraciones realizadas, vector <em>θ</em>, etc) y la salida de una muestra a clasificar [1,0,5], que corresponde al conjunto de muestras de entrenamiento y conocemos su clase (si hemos dibujado las coordenadas como dije) : -1. La salida del script ejemplo es:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>Detalles de convergencia
fun: 0.0
jac: array([ 0., 0., 0.])
message: 'Optimization terminated successfully.'
nfev: 10
nit: 1
njev: 2
status: 0
success: True
x: array([ 1.9997, 4.46 , -0.78 ])
Clase de [1,0,5]: -1
</code></pre>
</div>
<p>Que coincide con lo que esperábamos, observando que el método ha convergido en 2 iteraciones a la solución <em>θ</em> = [ 1.9997, 4.46 , -0.78 ], además ha clasificado la muestra [1,0,5] que efectivamente pertenece a la clase -1.</p>
<hr />
<h2 id="adaline">Adaline</h2>
<p>Adaline es una red neuronal de una capa utilizada en principalmente en regresión lineal. En este caso, el objetivo del aprendizaje es encontrar un vector <em>θ</em> con el que se <a href="https://i.gyazo.com/1b7efc98d8add5937953e722f18a57af.png">minimice la diferencia entre <em>θ</em>·<em>x</em> y la salida esperada <em>y</em> para toda muestra de entrenamiento</a>. Del mismo modo que en Perceptron, se minimizará esta <a href="https://i.gyazo.com/27cfe2a3959b5475029968f5fcc21b12.png">función</a> equivalente a resolver el sistema planteado (ahora no se tienen en cuenta solo las mal clasificadas, si no que se quiere minimizar las diferencias entre la RHS y la LHS del todo el conjunto de ecuaciones).En este caso no he encontrado ningún teorema sobre la convergencia excepto <a href="https://i.gyazo.com/8abc9636f7f210ee276d53e4c043b794.png">éste</a>.</p>
<p>El código es el siguiente:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
<span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf-8 -*-</span>
<span class="c">#</span>
<span class="c"># Adaline.py</span>
<span class="kn">from</span> <span class="nn">scipy.optimize</span> <span class="kn">import</span> <span class="n">minimize</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">Utils</span> <span class="kn">import</span> <span class="n">MatLoad</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="p">((</span><span class="n">theta</span><span class="o">*</span><span class="n">X</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="o">-</span><span class="n">Y</span><span class="p">))</span>
<span class="k">return</span> <span class="p">(</span><span class="mf">1.0</span><span class="o">/</span><span class="mf">2.0</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">multiply</span><span class="p">(</span><span class="n">res</span><span class="p">,</span><span class="n">res</span><span class="p">)</span><span class="o">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">item</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">)))</span>
<span class="k">def</span> <span class="nf">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">):</span>
<span class="n">XROWS</span><span class="p">,</span><span class="n">XCOLS</span><span class="p">,</span><span class="n">YROWS</span> <span class="o">=</span> <span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">Y</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">return</span> <span class="n">minimize</span><span class="p">(</span><span class="n">fun</span><span class="o">=</span><span class="n">f</span><span class="p">,</span><span class="n">x0</span><span class="o">=</span><span class="n">theta</span><span class="p">,</span><span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">):</span> <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">inner</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">np</span><span class="o">.</span><span class="n">inner</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">)</span><span class="o">>=</span><span class="mi">0</span> <span class="k">else</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">X</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"X.np"</span><span class="p">);</span>
<span class="n">Y</span> <span class="o">=</span> <span class="n">MatLoad</span><span class="p">(</span><span class="s">"Y.np"</span><span class="p">);</span>
<span class="n">XROWS</span><span class="p">,</span><span class="n">XCOLS</span><span class="p">,</span><span class="n">YROWS</span> <span class="o">=</span> <span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">X</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">Y</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">XROWS</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">fit</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"</span><span class="se">\n</span><span class="s"> Detalles de convergencia </span><span class="se">\n</span><span class="s">"</span>
<span class="k">print</span> <span class="n">res</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">res</span><span class="o">.</span><span class="n">x</span>
<span class="k">print</span> <span class="s">"</span><span class="se">\n</span><span class="s">Clase de [1,0,5]:"</span><span class="p">,</span><span class="n">classify</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">]))</span>
</code></pre>
</div>
<p>Es idéntico al del Perceptron, cambiando la función objetivo de <a href="https://i.gyazo.com/5cccecf3f307437adb63609a97f0ed40.png"><em>f</em></a> a <a href="https://i.gyazo.com/27cfe2a3959b5475029968f5fcc21b12.png"><em>f’</em></a>. Nótese que la minimización de las diferencias entre la LHS y RHS no garantiza resultados correctos en clasificación donde normalmente el conjunto de salidas posibles <em>Y</em> es discreto.</p>
<p>La salida del script es muy similar a la del caso anterior:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>
Detalles de convergencia
fun: 7.76245804938178e-15
hess_inv: array([[ 4.99999999, -1. , -1. ],
[-1. , 0.26 , 0.18 ],
[-1. , 0.18 , 0.24 ]])
jac: array([ -1.30741529e-10, 3.51008111e-11, 1.42319490e-11])
message: 'Optimization terminated successfully.'
nfev: 40
nit: 5
njev: 8
status: 0
success: True
x: array([ 1.00000026, 0.19999994, -0.40000006])
Clase de [1,0,5]: -1
</code></pre>
</div>
<p>El conjunto de muestras es el mismo que en el caso anterior, sin embargo, se puede observar que el vector <em>θ</em> difiere del obtenido en Perceptron ya que al modificar la función objetivo se resuelve un problema diferente (éste, se podría haber resuelto utilizando la función <a href="http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.leastsq.html#scipy.optimize.leastsq"><em>leastsq</em></a> del módulo <em>optimize</em>).</p>
<hr />
<p>Hasta aquí lo que quería comentar, hay muchísima documentación sobre todo lo que he ido contando y he intentado enlazarlo y simplificarlo en la medida de lo posible para que sea comprensible. En próximas partes comentaré la implementación de máquinas de vectores de soporte <a href="https://es.wikipedia.org/wiki/M%C3%A1quinas_de_vectores_de_soporte">SVM</a>, <a href="https://en.wikipedia.org/wiki/Support_vector_machine#Soft-margin">con</a> y sin márgenes blandos (para ver una introducción la clasificación no lineal) y la aplicación de funciones <a href="https://en.wikipedia.org/wiki/Positive-definite_kernel">kernel</a> para que los clasificadores implementados sepan discriminar de formas más complejas.</p>
<p>Dejo el <a href="https://github.com/Indeseables/indeseables.github.io/tree/master/_codigos/Entrada_1">código disponible hasta el momento</a> en el <a href="https://github.com/Indeseables/indeseables.github.io">repositorio</a>.</p>
<hr />
<p>Si tienes alguna duda, comentario o sugerencia, notifícanos a través de <a href="mailto:indeseables.git@gmail.com">indeseables.git@gmail.com</a>
y si te ha gustado la entrada puedes <a href="https://twitter.com/intent/tweet?url=http://indeseables.github.io/2016/03/25/implementacion-alternativa-algoritmos-aprendizaje/&text=Implementación alternativa de algoritmos de aprendizaje (parte I).&via=indeseables!" target="_blank"> compartirla!.</a></p>
<hr />
What's Indeseables?2016-03-24T00:00:00+00:00http://indeseables.github.io/2016/03/24/WhatsIndeseables<p><a href="http://indeseables.github.io/">Indeseables</a></p>
<div class="highlighter-rouge"><pre class="highlight"><code>fecuoosipqcedaraoanoeolsrmryiaiiedatbuatesnravnreiaeanrceinussnuaeuoribspndolndriaooiiiuudnaesucmipeauaemnsdlnhrnsyrcmdzrljrenarauaeeninitpbgstdgssozius
</code></pre>
</div>
<hr />
<p>Si tienes alguna duda, comentario o sugerencia, notifícanos a través de <a href="mailto:indeseables.git@gmail.com">indeseables.git@gmail.com</a>
y si te ha gustado la entrada puedes <a href="https://twitter.com/intent/tweet?url=http://indeseables.github.io/2016/03/24/WhatsIndeseables/&text=What's Indeseables?&via=indeseables!" target="_blank"> compartirla!.</a></p>
<hr />