Introducción a la Teoría de la información con Python

Esta notebook fue creada originalmente como un blog post por Raúl E. López Briega en Matemáticas, análisis de datos y python. El contenido esta bajo la licencia BSD.

"La información es la resolución de la incertidumbre"

Claude Shannon

"Lo que está en el corazón de cada ser vivo no es un fuego, ni un aliento cálido, ni una chispa de vida. Es información, palabras, instrucciones ... Si quieres entender la vida... piensa en la tecnología de la información"

Richard Dawkins

Introducción

Muchas veces hemos escuchado decir que vivimos en la era de la información. La información parece estar en todo los que nos rodea. Ya sea que consideremos a las computadoras, la evolución, la física, la inteligencia artificial, o nuestro cerebro; podemos llegar a la conclusión de que su comportamiento esta principalmente determinado por la forma en que procesan la información.

La idea de la información nació del antiguo arte de la codificación y decodificación de códigos. Los encargados de esconder los secretos de estado durante la segunda guerra mundial utilizaban, en esencia, métodos para ocultar información y transmitirla de un lugar a otro. Cuando el arte de quebrar estos códigos se combinó con la ciencia de la Termodinámica, la rama de la física encargada del estudio de la interacción entre el calor y otras manifestaciones de la energía; surgió lo que hoy conocemos como Teoría de la información. Esta teoría fue una idea revolucionaria que inmediatamente transformó el campo de las comunicaciones y preparó el camino para la era de las computadoras. Pero las ideas de la Teoría de la información no solo gobiernan las comunicaciones y los bits y bytes de las computadoras modernas; sino que también describen el comportamiento del mundo subatómico, e incluso de toda la vida en la Tierra.

¿Qué es la información?

Hasta no hace no tanto tiempo atrás, nuestro conocimiento de la información era bastante vago y limitado. En 1948, Claude Shannon publicó un artículo titulado "Una teoría matemática de la comunicación", el cual transformó para siempre la forma en que entendemos la información. La Teoría de la información de Shannon proporciona una definición matemática de información y describe con precisión cuánta información se puede comunicar entre los diferentes elementos de un sistema. La teoría de Shannon respalda nuestra comprensión de cómo se relacionan las señales y el ruido, y por qué existen límites definidos para la velocidad a la que se puede comunicar la información dentro de cualquier sistema, ya sea creado por el hombre o biológico. La habilidad de separar la señal del ruido, para extraer la información en los datos, se ha vuelto crucial en las telecomunicaciones modernas.

La Teoría de la información es tan poderosa porque la información es física. La información no es solo un concepto abstracto, y no solo son hechos o figuras, fechas o nombres. Es una propiedad concreta de la materia y la energía que es cuantificable y mensurable. Es tan real como el peso de un trozo de plomo o la energía almacenada en una ojiva atómica, y al igual que la masa y la energía, la información está sujeta a un conjunto de leyes físicas que dictan cómo puede comportarse, cómo la información puede ser manipulada, transferida, duplicada, borrada o destruida. Y todo en el universo debe obedecer las leyes de la información, porque todo en el universo está formado por la información que contiene.

Según la perspectiva de la información de Shannon, el significado no es importante, sino que lo que importa es cuánta información es transmitida por un mensaje. Una de las grandes intuiciones que tuvo Shannon fue darse cuenta que cualquier pregunta que tenga una respuesta finita puede ser respondida por una cadena de preguntas por sí o por no. Así es como surge el concepto de Bit.

Bits

Un Bit es la unidad fundamental en la que podemos medir la información y nos permite decidir entre dos alternativas igualmente probables. La palabra Bit deriva de binary digit, o sea dígito binario, los cuales son representados por 1s y 0s. Pero si bien la palabra Bit deriva de binary digit no debemos confundirlos, ya que representan entidades distintas. Un Bit representa una cantidad de información definitiva. En cambio, un dígito binario es el valor de una variable binaria, el cual, como ya dijimos, puede ser 0 o 1; pero un dígito binario no representa información en sí misma.

Ahora bien, volviendo a las preguntas por sí o por no que mencionamos antes; responder cada una de estas preguntas requiere un Bit de información. Sólo necesitamos un Bit para responder una pregunta como ¿sos un hombre o una mujer?; el 0 puede significar hombre y el 1 mujer. Con simplemente transmitir ese dígito en el mensaje, estamos transmitiendo la respuesta. Pero aquí viene otra de las grandes intuiciones de Shannon; tampoco importa la forma que tome el mensaje, puede ser una luz roja versus una luz verde; o una bandera blanca y otra roja; realmente no importa el medio que se utilice, el mensaje siempre contiene un Bit de información.

Y ¿qué pasa con otro tipo de preguntas? preguntas como adivinar un número entero entre 1 y 1000, o como, ¿cuál es la capital de Islandia? Estas preguntas también pueden ser respondidas con una cadena de Bits. El lenguaje no es más que una cadena de símbolos y cualquier símbolo puede ser representado con una cadena de Bits. Por lo tanto, cualquier respuesta que pueda ser escrita en un lenguaje puede ser representada con una cadena de Bits, de 1s y 0s. Los Bits son el medio fundamental de la información.

Esta realización, que cualquier información, cualquier respuesta, puede ser codificada en una cadena de Bits, nos abre la puerta para pensar que entonces debe existir una forma de medir cuánta información hay en un mensaje.¿Cuál es la mínima cantidad de Bits para codificar un mensaje? Por ejemplo, para responder la pregunta planteada anteriormente de adivinar un número entero entre 1 y 1000, no se necesitan más que 10 Bits!. Shannon encontró que una pregunta con $N$ posibles resultados puede ser respondida con una cadena de Bits de $log_2 N$ Bits; es decir que solo necesitamos $log_2 N$ Bits de información para distinguir entre $N$ posibilidades. Si no me creen, más abajo les dejo un botón para jugar a adivinar el número. (Si les consume más de 10 bits llegar a la respuesta correcta, no están utilizando la estrategia correcta!).

Todo esto último relacionado a cómo medir cuánta información contiene un mensaje nos lleva a otro de los conceptos fundamentales de la Teoría de la información, el concepto de Entropía.

Entropía

La idea central de la Teoría de la información de Shannon es la Entropía. La información y la Entropía están intimimamente relacionadas, ya que esta última es en sí misma una medida de información. Cuando Shannon comenzó a desarrollar su teoría, encontró una formula que le permitía analizar la información en un mensaje en términos de Bits. Esta formula que encontró mide, a grandes rasgos, cuan poco predecible es una cadena de Bits. Mientras menos predecible, existen menos probabilidades de poder generar el mensaje completo desde una cadena más pequeña de Bits. Es decir, que al intentar medir cuan poco predecible es una cadena de Bits, Shannon esperaba poder encontrar cuánta información contenía el mensaje.

Veamos algunos ejemplos en Python:


In [7]:
# <!-- collapse=True -->
import matplotlib.pyplot as plt
import numpy as np
import warnings

# ingnorar mensajes de advertencias en el notebook
warnings.filterwarnings('ignore')

# graficos en el notebook
%matplotlib inline


def entropia(X):
    """Devuelve el valor de entropia de una muestra de datos""" 
    probs = [np.mean(X == valor) for valor in set(X)]
    return round(np.sum(-p * np.log2(p) for p in probs), 3)


def entropia_prob_pq(x):
    """Devuelve la entropia de una probabilidad de dos posibilidades"""
    return round((-x * np.log2(x)) + (-(1 - x ) * np.log2((1 - x))), 3)


def entropia_posibilidades(x):
    """Devuelve la entropía para la cantidad de posibilidades independientes x"""
    return round(np.log2(x), 3)

In [21]:
# Graficando la información como sorpresa
# Mientras menos probable, más sorpres y más información contiene.
vent = np.vectorize(entropia_posibilidades)

X = np.linspace(0, 1, 11)

plt.plot(X, vent(X)*-1)
plt.title("Información como sorpresa")
plt.grid(color='b', linestyle='-', linewidth=.3)
plt.xlabel(r'Probabilidades $p(x)$')
plt.ylabel(r'sorpresa $H(x) = log_2 1/p(x)$')
plt.show()



In [2]:
# Graficando la entropia en el caso de 2 posibilidades con
# probabilidad p y (1- p)

# vectorizar la función para poder pasarle un vector de parámetro
vent = np.vectorize(entropia_prob_pq)

X = np.linspace(0, 1, 11)

plt.plot(X, vent(X))
plt.title("Entropia para 2 posibilidades con probabilidad p")
plt.grid(color='b', linestyle='-', linewidth=.3)
plt.xlabel('Probabilidades p')
plt.ylabel('Bits')
plt.show()



In [14]:
# La entropia de una muestra de 2 posibilidades completamente
# aleatorias, en la que cualquiera de los 2 valores tiene la 
# misma probabilidad (p=0.5) de ser seleccionada es de 1 bit

# Muestra de 10000 valores aleatorios entre 0 y 1
X = np.random.randint(0, 2, size=10000)

entropia(X), entropia_posibilidades(2)


Out[14]:
(1.0, 1.0)

In [13]:
# La entropia de una muestra de 8 posibilidades completamente
# aleatorias es igual a 3 bits.

# Muestra de 10000 valores aleatorios entre 0 y 7
X = np.random.randint(0, 8, size=10000)

entropia(X), entropia_posibilidades(8)


Out[13]:
(2.999, 3.0)

In [18]:
entropia_posibilidades(.1)


Out[18]:
-3.322

Redundancia

Otro de los conceptos fundamentales de la Teoría de la información es el de Redundancia. La Redundancia son esas pistas adicionales en una sentencia o mensaje que nos permiten entender su significado incluso si el mensaje esta incompleto o distorsionado; son esos caracteres extra en una cadena de símbolos, la parte predecible que nos permite completar la información faltante. Cualquier sentencia de cualquier lenguaje es altamente redundante. Todo sentencia nos proporciona información adicional para que podemos descifrarla. Esta Redundancia es fácil de ver, simplemente tr-t- d- l--r -st- m-ns-j-. A pesar de que quitemos todas la vocales, igualmente se podemos entender la sentencia.

Para nosotros, la redundancia del lenguaje es algo bueno, porque hace que un mensaje sea más fácil de comprender incluso cuando el mensaje está parcialmente modificado por el entorno. Podemos entender a un amigo hablando en un restaurante abarrotado de gente o hablando con un teléfono celular con mucha estática gracias a la Redundancia . La Redundancia es un mecanismo de seguridad; nos asegura que el mensaje se transmita incluso si se daña levemente en el camino. Todos los idiomas tienen estas redes de seguridad integradas compuestas de patrones, estructuras y un conjunto de reglas que los hacen redundantes. Usualmente no estamos al tanto de esas reglas, pero nuestro cerebro las usa inconscientemente mientras leemos, hablamos, escuchamos y escribimos.

Cuando eliminamos toda la redundancia en una cadena de símbolos, lo que queda es su núcleo concreto y cuantificable. Eso es la información, ese algo central e irreductible que se encuentra en la esencia de toda sentencia.

Para explorar en carne propia como la información es una medida de sorpresa y como la mayoría de los mensajes contienen bastantes Bits redundantes, les dejo otro juego; la idea es adivinar nombres que empiezan con "R" de Raúl a medida que se van descubriendo nuevas letras. Les garantizo que podrán descubrir los nombres sin tener que llegar que se descubra la última letra!

Teorema de la Capacidad del canal

La mayoría de las señales naturales, como sonidos e imágenes, transmiten información en una forma relativamente diluida, de modo que una gran cantidad de datos contiene una pequeña cantidad de información. Hay dos razones por las que la información es tan diluida en las señales naturales. En primer lugar, los valores que están cerca uno del otro tienden a tener valores similares (por ejemplo, en imágenes), o a estar relacionados entre sí (por ejemplo, en el idioma), de modo que diferentes valores de señales duplican parcialmente la información que llevan. Una segunda razón, más sutil, esta relaciona con el canal de comunicación. Un canal de comunicación es un medio que se utiliza para transmitir información de un emisor a un receptor. La distribución óptima para un canal de comunicación determinado depende de las restricciones que se aplican. Por ejemplo, si un canal tiene límites inferiores y superiores, la recodificación de una señal de modo que todos sus valores ocurran con la misma frecuencia (es decir, una distribución uniforme) garantiza que cada dígito binario transporta tanta información como sea posible (es decir, un bit). Por lo tanto, para un canal con límites fijos, la distribución óptima es uniforme. En conjunto, estas consideraciones sugieren que una señal puede transmitirse a través de un canal de comunicación de la manera más eficiente si (1) primero se transforma en una señal con valores independientes, y (2) los valores de esta señal transformada tienen una distribución optimizada para ese canal en particular.

La Capacidad de canal es la cantidad máxima de información que se puede comunicar desde el emisor al receptor. La capacidad se puede medir en términos de la cantidad de información por símbolo, y si un canal comunica $n$ símbolos por segundo, entonces su capacidad se puede expresar en términos de información por segundo (por ejemplo, bits/s).

Información e incertidumbre

Nuestra experiencia del mundo nos lleva a concluir que muchos eventos son impredecibles y algunas veces bastante inesperados. Estos pueden variar desde el resultado de simples juegos de azar como arrojar una moneda e intentar adivinar si será cara o cruz, al colapso repentino de los gobiernos, o la caída dramática de los precios de las acciones en el mercado bursátil. Cuando tratamos de interpretar tales eventos, es probable que tomemos uno de dos enfoques: nos encogeremos de hombros y diremos que fue por casualidad o argumentaremos que podríamos haber sido más capaces de predecir, por ejemplo, el colapso del gobierno si hubiéramos tenido más información sobre las acciones de ciertos ministros. En cierto sentido, podemos decir que estos dos conceptos de información e incertidumbre están más estrechamente relacionados de lo que podríamos pensar. De hecho, cuando nos enfrentamos a la incertidumbre, nuestra tendencia natural es buscar información que nos ayude a reducir esa incertidumbre en nuestras mentes. Las herramientas que nos proporciona la Teoría de la información están en las bases de todos los modelos que desarrollamos para intentar predecir y lidiar con la incertidumbre del futuro.

Aquí concluye esta introducción, el trabajo de Shannon habrió un campo enorme del conocimiento científico. Por años, criptógrafos habían intentado esconder información y reducir la redundancia sin siquiera saber como medirlas; o los ingenieros trataron de diseñar maneras eficientes de transmitir mensajes sin conocer los límites que la Naturaleza ponía a su eficiencia. La Teoría de la información de Shannon revolucionó la criptografía, el procesamiento de señales, las ciencias de la computación, la física, y un gran número de otros campos.

Para cerrar, les dejo la implementación de los juegos utilizados en el artículo utilizando Python! :)

Saludos!


In [5]:
# <!-- collapse=True -->
import random

random.seed(1982)


def adivinar_numero():
    mi_numero = random.randint(1, 1000)
    bits = 1
    tu_numero = int(input("Adivine un número entero entre 1 y 1000\nIngrese un número entre 1 y 1000: "))
    
    while tu_numero != mi_numero:
        if tu_numero < mi_numero:
            tu_numero = int(input("Su número es muy bajo!\nIngrese otro número entre 1 y 1000:"))
        else:
            tu_numero = int(input("Su número es muy alto!\nIngrese otro número entre 1 y 1000:"))
        bits += 1
    
    print("Felicidades el número es {0} y ha utilizado {1} bits!".format(mi_numero, bits))

In [6]:
# <!-- collapse=True -->
def adivinar_nombre():
    nombres = [
    "ramses", "rodolfo", "regina", "ruth", "ramiro",
    "ramon", "roxana", "rebeca", "raquel", "ruben",
    "rosario", "renata", "raul", "romina", "roberto",
    "ricardo", "rafael", "rosa", "rodrigo", "rocio"
    ]
    index = random.randint(0, 19)
    mi_nombre = nombres[index]
    tu_nombre = input("Adivina el nombre! Empieza con R y tiene {} letras: ".format(len(mi_nombre)))
    letras = 2
    bits = 1
    
    while tu_nombre.lower() != mi_nombre:
        mi_nombre_parcial = mi_nombre[:letras]
        if mi_nombre_parcial == mi_nombre:
            break
        
        tu_nombre = input("Inténtalo otra vez! Empieza con {0} y tiene {1} letras:".format(mi_nombre_parcial,
                                                                                           letras))
        bits += 1
        letras += 1
    
    print("El nombre es {0} y has utilizado {1} bits! Los restantes {2} son redundantes!".format(mi_nombre.upper(),
                                                                                                bits, 
                                                                                                 len(mi_nombre) - bits))

In [ ]:
adivinar_numero()

In [ ]:
adivinar_nombre()

Este post fue escrito utilizando Jupyter notebook. Pueden descargar este notebook o ver su version estática en nbviewer.