Entendimiento == aplicado a una matriz NumPy

Soy nuevo en Python, y estoy aprendiendo TensorFlow . En un tutorial que utiliza el conjunto de datos notMNIST , proporcionan un código de ejemplo para transformar la matriz de tags en una matriz codificada de uno en uno.

El objective es tomar una matriz que consta de enteros de tags 0 … 9 y devolver una matriz donde cada entero se haya transformado en una matriz codificada de uno de n como esta:

0 -> [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1 -> [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] 2 -> [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] ... 

El código que dan para hacer esto es:

 # Map 0 to [1.0, 0.0, 0.0 ...], 1 to [0.0, 1.0, 0.0 ...] labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32) 

Sin embargo, no entiendo cómo este código hace eso en absoluto. Parece que solo genera una matriz de enteros en el rango de 0 a 9, y luego compara eso con la matriz de tags y convierte el resultado en un flotador. ¿Cómo un operador == resultado una matriz codificada en uno de n ?

Hay algunas cosas que están sucediendo aquí: las operaciones vectoriales de Numpy, la adición de un eje singleton y la transmisión.

Primero, deberías poder ver cómo el == hace la magia.

Digamos que comenzamos con una matriz de tags simple. == comporta de una manera vectorizada, lo que significa que podemos comparar la matriz completa con un escalar y obtener una matriz que consiste en los valores de cada comparación elemental. Por ejemplo:

 >>> labels = np.array([1,2,0,0,2]) >>> labels == 0 array([False, False, True, True, False], dtype=bool) >>> (labels == 0).astype(np.float32) array([ 0., 0., 1., 1., 0.], dtype=float32) 

Primero obtenemos una matriz booleana, y luego coaccionamos para flotar: False == 0 en Python, y True == 1. Así que terminamos con una matriz que es 0 donde las labels no es igual a 0 y 1 donde está.

Pero no hay nada especial en comparación con 0, podríamos comparar con 1 o 2 o 3 para obtener resultados similares:

 >>> (labels == 2).astype(np.float32) array([ 0., 1., 0., 0., 1.], dtype=float32) 

De hecho, podríamos recorrer cada etiqueta posible y generar esta matriz. Podríamos usar un listcomp:

 >>> np.array([(labels == i).astype(np.float32) for i in np.arange(3)]) array([[ 0., 0., 1., 1., 0.], [ 1., 0., 0., 0., 0.], [ 0., 1., 0., 0., 1.]], dtype=float32) 

Pero esto realmente no se aprovecha de numpy. Lo que queremos hacer es tener cada etiqueta posible comparada con cada elemento, IOW para comparar

 >>> np.arange(3) array([0, 1, 2]) 

con

 >>> labels array([1, 2, 0, 0, 2]) 

Y aquí es donde entra la magia de la difusión numpy. En este momento, las labels son un objeto de forma unidimensional (5,). Si lo convertimos en un objeto de forma bidimensional (5,1), entonces la operación “emitirá” sobre el último eje y obtendremos una salida de forma (5,3) con los resultados de comparar cada entrada en El rango con cada elemento de las tags.

Primero podemos agregar un eje “extra” a las labels usando None (o np.newaxis ), cambiando su forma:

 >>> labels[:,None] array([[1], [2], [0], [0], [2]]) >>> labels[:,None].shape (5, 1) 

Y luego podemos hacer la comparación (esta es la transposición del acuerdo que vimos anteriormente, pero eso realmente no importa).

 >>> np.arange(3) == labels[:,None] array([[False, True, False], [False, False, True], [ True, False, False], [ True, False, False], [False, False, True]], dtype=bool) >>> (np.arange(3) == labels[:,None]).astype(np.float32) array([[ 0., 1., 0.], [ 0., 0., 1.], [ 1., 0., 0.], [ 1., 0., 0.], [ 0., 0., 1.]], dtype=float32) 

La transmisión en número es muy poderosa y vale la pena leerla.

En resumen, == aplicado a una matriz numpy significa aplicar el elemento == a la matriz. El resultado es una matriz de booleanos. Aquí hay un ejemplo:

 >>> b = np.array([1,0,0,1,1,0]) >>> b == 1 array([ True, False, False, True, True, False], dtype=bool) 

Para contar, diga cuántos 1s hay en b , no necesita convertir la matriz a flotar, es decir, el .astype(np.float32) se puede guardar, porque en python boolean es una subclase de int y en Python 3 you tiene True == 1 False == 0 . Así que aquí es cómo se cuentan cuántos hay en b :

 >>> np.sum((b == 1)) 3 

O:

 >>> np.count_nonzero(b == 1) 3