Blog
Cómo Preparar Datos de Texto con Scikit-Learn
- Publicado por: Rafael Fernandez
- Categoría: Natural Language Processing
Los datos de texto requieren una preparación especial antes de que puedas empezar a utilizarlos para el modelado predictivo. El texto debe ser analizado para eliminar palabras, lo que se denomina tokenización. A continuación, las palabras deben codificarse como números enteros o valores de coma flotante para su uso como entrada a un algoritmo de aprendizaje de máquina, llamado extracción de características (o vectorización). La biblioteca scikit-learn ofrece herramientas fáciles de usar para realizar tanto la tokenización como la extracción de características de sus datos de texto. En este tutorial, descubrirás exactamente cómo puedes preparar tus datos de texto para el modelado predictivo en Python con scikit-learn. Después de completar este tutorial, usted sabrá:
- Cómo convertir texto a vectores de recuento de palabras con CountVectorizer.
- Cómo convertir texto a vectores de frecuencia de palabra con TfidfVectorizer.
- Cómo convertir texto a enteros únicos con HashingVectorizer.
Vamos a empezar con la lección.
El modelo de la bolsa de palabras
No podemos trabajar directamente con texto cuando usamos algoritmos de aprendizaje automático. En su lugar, necesitamos convertir el texto a números. Es posible que queramos realizar la clasificación de documentos, por lo que cada documento es una entrada y una etiqueta de clase, es la salida de nuestro algoritmo predictivo. Los algoritmos toman vectores de números como entrada, por lo tanto necesitamos convertir documentos a vectores de números de una longitud fija.
Un modelo simple y efectivo para pensar acerca de los documentos de texto en el aprendizaje automático se llama el Modelo de la Bolsa de Palabras, o BoW (Bag-of-Words Model). Note, que cubrimos el modelo BoW en gran detalle en la siguiente parte, comenzando con la lección número 3 Bolsa de palabras. El modelo es sencillo, ya que elimina toda la información de orden de las palabras y se centra en la aparición de palabras en un documento. Esto se puede hacer asignando a cada palabra un número único. Entonces cualquier documento que veamos puede ser codificado como un vector de longitud fija con la longitud del vocabulario de palabras conocidas. El valor en cada posición en el vector podría ser llenado con una cuenta o frecuencia de cada palabra en el documento codificado.
Este es el modelo de la bolsa de palabras, donde sólo nos preocupan los esquemas de codificación que representan qué palabras están presentes o hasta qué punto están presentes en los documentos codificados sin ninguna información sobre el orden. Hay muchas maneras de extender este sencillo método, tanto aclarando mejor lo que es una palabra, como definiendo qué codificar acerca de cada palabra en el vector. La biblioteca de scikit-learn proporciona tres esquemas diferentes que podemos usar, y veremos brevemente cada uno de ellos.
Contar palabras con CountVectorizer
El CountVectorizer proporciona una manera simple de tokenizar una colección de documentos de texto y construir un vocabulario de palabras conocidas. Pero también se puede codificar nuevos documentos usando este vocabulario. Puede utilizarlo como se indica a continuación:
- Cree una instancia de la clase CountVectorizer.
- Llame a la función fit() para aprender un vocabulario de uno o más documentos.
- Llame a la función transform() en uno o más documentos según sea necesario para codificar cada uno como un vector.
Se devuelve un vector codificado con una longitud de todo el vocabulario y un número entero para el número de veces que cada palabra apareció en el documento. Debido a que estos vectores contendrán muchos ceros, los llamamos dispersos. Python proporciona una forma eficiente de manejar vectores dispersos en el paquete scipy.sparse. Los vectores devueltos de una llamada a transform() serán vectores dispersos, y tu puedes transformarlos de nuevo a arrays NumPy para ver y entender mejor lo que está sucediendo al llamar a la función toarray(). A continuación se muestra un ejemplo de cómo utilizar el CountVectorizer para tokenizar, crear un vocabulario y, a continuación, codificar un documento.
from sklearn.feature_extraction.text import CountVectorizer # lista de documentos de texto text = ["The quick brown fox jumped over the lazy dog."] # crear la transformación vectorizer = CountVectorizer() # tokenizar y construir el vocabulario vectorizer.fit(text) # resumen print(vectorizer.vocabulary_) # codificador de documentos vector = vectorizer.transform(text) # resumir vector codificado print(vector.shape) print(type(vector)) print(vector.toarray())
Arriba, puedes ver que accedemos al vocabulario para ver exactamente lo que fue tokenizado por llamadas en ciclo:
print(vectorizer.vocabulary_)
Podemos ver que todas las palabras se hicieron en minúsculas por defecto y que la puntuación fue ignorada. Estos y otros aspectos del tokenizing se pueden configurar y le animo a que revise todas las opciones de la documentación de la API. Ejecutando el ejemplo, primero se va a imprimir el vocabulario, luego la forma del documento codificado. Podemos ver que hay 8 palabras en el vocabulario, y por lo tanto los vectores codificados tienen una longitud de 8. Podemos entonces ver que el vector codificado es una matriz dispersa. Finalmente, podemos ver una versión de array del vector codificado que muestra un conteo de 1 ocurrencia para cada palabra excepto el (índice e id 7) que tiene una ocurrencia de 2.
{'dog': 1, 'fox': 2, 'over': 5, 'brown': 0, 'quick': 6, 'the': 7, 'lazy': 4, 'jumped': 3} (1, 8) <class 'scipy.sparse.csr.csr_matrix'> [[1 1 1 1 1 1 1 2]]
Es importante destacar que el mismo vectorizador se puede utilizar en documentos que contienen palabras no incluidas en el vocabulario. Estas palabras son ignoradas y no se da ningún conteo en el vector resultante. Por ejemplo, abajo hay un ejemplo de cómo usar el vectorizador de arriba para codificar un documento con una palabra en el vocabulario y una palabra que no lo es.
# codificar otro documento text2 = ["the puppy"] vector = vectorizer.transform(text2) print(vector.toarray())
Ejecutando este ejemplo se imprime la versión array del vector disperso codificado mostrando una ocurrencia de una palabra en el vocabulario y la otra palabra no se imprime, ya que en este vocabulario es completamente ignorada.
[[0 0 0 0 0 0 0 1]]
Los vectores codificados pueden ser utilizados directamente con un algoritmo de aprendizaje de máquina.
Frecuencias de palabras con TfidfVectorizer
El recuento de palabras es un buen punto de partida, pero es muy básico. Un problema con los recuentos simples es que algunas palabras como “testamento” aparecerán muchas veces y sus recuentos grandes no serán muy significativos en los vectores codificados. Una alternativa es calcular las frecuencias de las palabras, y el método más popular es el llamado TF-IDF. Este es un acrónimo que significa Frecuencia de Término – Frecuencia Inversa de Documento que son los componentes de las puntuaciones resultantes asignadas a cada palabra.
- Término Frecuencia: Esto resume la frecuencia con la que una palabra dada aparece dentro de un documento.
- Frecuencia inversa de documentos: Esto reduce la escala de las palabras que aparecen mucho en los documentos.
Sin entrar en la matemática, TF-IDF son puntuaciones de frecuencia de palabras que tratan de resaltar las palabras que son más interesantes, por ejemplo, frecuentes en un documento pero no en todos los documentos. El TfidfVectorizer tokenizará documentos, aprenderá el vocabulario y las ponderaciones inversas de frecuencia de documentos, y te permitirá codificar nuevos documentos. Alternativamente, si ya tiene un CountVectorizer aprendido, puede usarlo con un TfidfTransformer para calcular las frecuencias inversas de los documentos y comenzar a codificarlos. Se utiliza el mismo proceso de crear, ajustar y transformar que con el CountVectorizer. A continuación se muestra un ejemplo del uso del TfidfVectorizer para aprender vocabulario y frecuencias inversas de documentos a través de tres documentos pequeños y luego codificar uno de esos documentos.
from sklearn.feature_extraction.text import TfidfVectorizer # lista de documentos de texto text = ["The quick brown fox jumped over the lazy dog.", "The dog.", "The fox"] # crear la transformación vectorizer = TfidfVectorizer() # tokenizar y construir vocabulario vectorizer.fit(text) # resumir print(vectorizer.vocabulary_) print(vectorizer.idf_) # documento codificado vector = vectorizer.transform([text[0]]) # resumir vector codificado print(vector.shape) print(vector.toarray())
Un vocabulario de 8 palabras se aprende de los documentos y a cada palabra se le asigna un índice entero único en el vector de salida. Las frecuencias inversas de documentos se calculan para cada palabra del vocabulario, asignando la puntuación más baja de 1,0 a la palabra más frecuentemente observada: el índice 7. Finalmente, el primer documento se codifica como una matriz dispersa de 8 elementos y podemos revisar las puntuaciones finales de cada palabra con valores diferentes para el zorro y el perro a partir de las otras palabras del vocabulario.
{'fox': 2, 'lazy': 4, 'dog': 1, 'quick': 6, 'the': 7, 'over': 5, 'brown': 0, 'jumped': 3} [ 1.69314718 1.28768207 1.28768207 1.69314718 1.69314718 1.69314718 1.69314718 1. ] (1, 8) [[ 0.36388646 0.27674503 0.27674503 0.36388646 0.36388646 0.36388646 0.36388646 0.42983441]]
Las puntuaciones se normalizan a valores entre 0 y 1 y los vectores de documento codificados se pueden utilizar directamente con la mayoría de los algoritmos de aprendizaje automático.
Hashing con HashingVectorizer
Los recuentos y las frecuencias pueden ser muy útiles, pero una limitación de estos métodos es que el vocabulario puede llegar a ser muy amplio. Esto, a su vez, requerirá grandes vectores para codificar los documentos e impondrá grandes requisitos a la memoria y a los algoritmos de ralentización. Un trabajo inteligente es usar un hash de palabras para convertirlas en números enteros. La parte inteligente es que no se requiere vocabulario y se puede elegir un vector de longitud fija arbitraria. Una desventaja es que el hash es una función unidireccional, por lo que no hay forma de volver a convertir la codificación en una palabra (lo que puede no ser importante para muchas tareas de aprendizaje supervisado).
La clase HashingVectorizer implementa este enfoque que se puede utilizar para convertir palabras en hash de forma coherente y, a continuación, convertir en token y codificar documentos según sea necesario. El siguiente ejemplo muestra el HashingVectorizer para codificar un solo documento. Se eligió un tamaño de vector arbitrario de longitud fija de 20. Esto corresponde al rango de la función hash, donde valores pequeños (como 20) pueden resultar en colisiones de hash. Volviendo a las clases de Ciencias de la Computación, creo que hay heurísticas que puedes usar para escoger la longitud del hash y la probabilidad de colisión basada en el tamaño estimado del vocabulario (por ejemplo, un factor de carga del 75%). Vea cualquier buen libro de texto sobre el tema. Tenga en cuenta que este vectorizador no requiere una llamada para encajar en los documentos de datos de entrenamiento. En cambio, después de la instanciación, se puede utilizar directamente para empezar a codificar documentos.
from sklearn.feature_extraction.text import HashingVectorizer # lista de documentos de texto text = ["The quick brown fox jumped over the lazy dog."] # crear la transformación vectorizer = HashingVectorizer(n_features=20) # documento codificado vector = vectorizer.transform(text) # resumir vector codificado print(vector.shape) print(vector.toarray())
Al ejecutar el ejemplo se codifica el documento de muestra como una matriz dispersa de 20 elementos. Los valores del documento codificado corresponden a recuentos de palabras normalizados por defecto en el rango de -1 a 1, pero se pueden hacer recuentos enteros simples cambiando la configuración por defecto.
(1, 20) [[ 0. 0. 0. 0. 0. 0.33333333 0. -0.33333333 0.33333333 0. 0. 0.33333333 0. 0. 0. -0.33333333 0. 0. -0.66666667 0. ]]
➡ Continúa aprendiendo con nosotros en nuestro curso:
Estimados:
Agradezco mucho esta valiosa información, he replicado los códigos y han corrido bien. Estoy incursionado en el manejo de Python aplicado a NLP, en particular intento medir la distancia y similitud de una base de datos de topónimos o direcciones geo referencial y fue de mucha ayuda.