ScikitLearn
Tutorial ScikitLearn: Preprocesamiento de textos para NLP
Scikit-Learn: Preprocesamiento de textos para NLP en Python con NLTK. Aprende tokenización, normalización y eliminación de stop words para preparar textos en modelos de aprendizaje automático.
Aprende ScikitLearn GRATIS y certifícateTokenización y segmentación de texto
La tokenización es un paso fundamental en el preprocesamiento de textos para aplicaciones de Procesamiento del Lenguaje Natural (NLP). Consiste en dividir un texto en unidades más pequeñas llamadas tokens, que pueden ser palabras, subpalabras o caracteres. Esta segmentación permite transformar los datos textuales en formatos manejables para los algoritmos de aprendizaje automático.
En Scikit Learn, la tokenización se realiza principalmente a través de las clases CountVectorizer
y TfidfVectorizer
. Estas clases convierten textos en representaciones numéricas y ofrecen mecanismos integrados para tokenizar y normalizar el texto. Por defecto, utilizan un analizador basado en expresiones regulares que divide el texto en tokens basándose en patrones predefinidos.
El parámetro clave para controlar el proceso de tokenización es token_pattern
. Este parámetro acepta una expresión regular que define cómo se segmentará el texto. Por ejemplo, el patrón por defecto es r'(?u)\\b\\w\\w+\\b'
, que captura secuencias de caracteres alfanuméricos de al menos dos caracteres de longitud.
Si se requiere un control más preciso sobre la tokenización, es posible proporcionar una función personalizada al parámetro tokenizer
. Esta función debe tomar una cadena de texto y devolver una lista de tokens. A continuación se muestra un ejemplo de cómo utilizar una función personalizada para tokenizar el texto:
from sklearn.feature_extraction.text import CountVectorizer
def mi_tokenizador(texto):
# Convertir a minúsculas
texto = texto.lower()
# Dividir por espacios en blanco
tokens = texto.split()
# Filtrar tokens no alfanuméricos
tokens = [token for token in tokens if token.isalnum()]
return tokens
corpus = [
"El rápido zorro marrón salta sobre el perro perezoso.",
"Un vaso de agua con hielo, por favor.",
"¡Bienvenidos al mundo del procesamiento de lenguaje!"
]
vectorizador = CountVectorizer(tokenizer=mi_tokenizador, token_pattern=None)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este ejemplo, la función mi_tokenizador
convierte el texto a minúsculas y lo divide en tokens basándose en espacios en blanco. Luego filtra los tokens para mantener solo aquellos que son alfanuméricos. Al pasar esta función personalizada al CountVectorizer
, se controla exactamente cómo se realiza la tokenización.
Además de tokenizer
, Scikit Learn permite modificar el proceso de preprocesamiento a través del parámetro preprocessor
. Este acepta una función que toma el texto original y devuelve el texto modificado antes de la tokenización. Por ejemplo, para eliminar signos de puntuación o normalizar acentos:
import unicodedata
import re
from sklearn.feature_extraction.text import CountVectorizer
def mi_preprocesador(texto):
# Eliminar acentos
texto = unicodedata.normalize('NFD', texto).encode('ascii', 'ignore').decode('utf-8')
# Eliminar signos de puntuación
texto = re.sub(r'[^\w\s]', '', texto)
return texto
vectorizador = CountVectorizer(tokenizer=mi_tokenizador, preprocessor=mi_preprocesador)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
Aquí, mi_preprocesador
elimina los acentos y los signos de puntuación antes de la tokenización. Esto es útil para normalizar el texto y reducir la variabilidad causada por diferentes formas de escribir las mismas palabras.
Para tareas más avanzadas, se puede utilizar un analizador personalizado a través del parámetro analyzer
. Un analizador es una función que combina el preprocesamiento, la tokenización y el filtrado de tokens en un solo paso. Al definir un analizador personalizado, se obtiene un control total sobre el proceso de transformación del texto:
def mi_analizador(texto):
# Preprocesamiento: eliminar acentos y signos de puntuación
texto = mi_preprocesador(texto)
# Tokenización: dividir por espacios en blanco
tokens = texto.split()
# Filtrado: eliminar tokens cortos
tokens = [token for token in tokens if len(token) > 2]
return tokens
vectorizador = CountVectorizer(analyzer=mi_analizador)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este código, mi_analizador
incorpora el preprocesamiento, la tokenización y el filtrado de tokens. Esto permite crear un proceso de segmentación de texto totalmente adaptado a las necesidades específicas del problema.
Es importante considerar el equilibrio entre la complejidad del proceso de tokenización y el rendimiento del modelo. Una tokenización más detallada puede capturar matices del lenguaje, pero también puede aumentar significativamente el tamaño del vocabulario y los recursos computacionales necesarios.
En ciertos casos, puede ser beneficioso utilizar técnicas como la tokenización mediante n-gramas, que considera secuencias de n tokens consecutivos. Esto se configura en Scikit Learn usando los parámetros ngram_range
y analyzer
. Por ejemplo, para incluir unigramas, bigramas y trigramas:
vectorizador = CountVectorizer(ngram_range=(1, 3))
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
Al ajustar ngram_range
a (1, 3)
, el CountVectorizer
incluye unigramas, bigramas y trigramas en la representación del texto. Esto permite capturar dependencias entre palabras que un enfoque de unigramas podría pasar por alto.
Alternativamente, para realizar una tokenización a nivel de caracteres, se puede establecer el parámetro analyzer
a 'char'
. Esta técnica es útil para ciertos tipos de análisis lingüísticos, como la detección de errores ortográficos o el análisis morfológico:
vectorizador = CountVectorizer(analyzer='char', ngram_range=(2, 4))
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
En este ejemplo, se generan secuencias de caracteres (bigramas hasta tetragramas). Esto crea una representación basada en patrones de caracteres que puede ser útil para modelos que analizan estructuras sublexicales.
Al trabajar con idiomas que no utilizan espacios para separar palabras, como el chino o el japonés, la segmentación de texto requiere herramientas especializadas. Aunque Scikit Learn no proporciona soporte directo para estos idiomas, es posible integrar su pipeline con bibliotecas externas y luego incorporar los resultados al proceso de vectorización.
Finalmente, es fundamental asegurar que el proceso de tokenización sea consistente entre el conjunto de entrenamiento y el de prueba. Las mismas funciones de preprocesamiento y tokenización deben aplicarse a todos los datos para evitar discrepancias que puedan afectar negativamente el rendimiento del modelo.
Normalización: minúsculas, lematización y stemming
La normalización es un paso esencial en el preprocesamiento de textos para Procesamiento del Lenguaje Natural (NLP). Su propósito es transformar las palabras a una forma común para reducir la variabilidad léxica y facilitar el análisis. Las técnicas más habituales incluyen la conversión a minúsculas, la lematización y el stemming.
La conversión a minúsculas es una forma sencilla de normalizar el texto. Al transformar todas las letras a minúsculas, se evita que las diferencias de capitalización afecten al análisis. En Scikit-Learn, los vectorizadores como CountVectorizer
y TfidfVectorizer
aplican esta transformación por defecto mediante el parámetro lowercase=True
.
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
"El rápido Zorro marrón salta sobre el Perro perezoso.",
"Un Vaso de Agua con Hielo, por favor.",
"¡Bienvenidos al Mundo del Procesamiento de Lenguaje!"
]
vectorizador = CountVectorizer()
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
En este ejemplo, las palabras "Zorro" y "zorro" se consideran iguales gracias a la conversión a minúsculas. Sin embargo, a veces puede ser necesario desactivar esta opción. Para mantener la capitalización original, se puede establecer lowercase=False
:
vectorizador = CountVectorizer(lowercase=False)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
La lematización es una técnica más sofisticada que reduce las palabras a su forma base o lema. Por ejemplo, las palabras "corriendo", "corrí" y "corre" se reducen al lema "correr". Esto ayuda a agrupar diferentes formas de la misma palabra y mejorar la consistencia del análisis. Para realizar lematización en Scikit-Learn, se requiere integrar una herramienta externa como NLTK o spaCy y crear un preprocesador o tokenizador personalizado.
A continuación se muestra cómo utilizar spaCy para lematizar el texto antes de vectorizarlo:
import spacy
from sklearn.feature_extraction.text import CountVectorizer
# Cargar el modelo de spaCy para español
nlp = spacy.load('es_core_news_sm')
def lematizador(texto):
doc = nlp(texto)
tokens_lematizados = [token.lemma_ for token in doc if not token.is_punct]
return tokens_lematizados
vectorizador = CountVectorizer(tokenizer=lematizador)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este código, la función lematizador
procesa el texto con spaCy y extrae los lemas de cada token, excluyendo los signos de puntuación. Al pasar esta función al parámetro tokenizer
de CountVectorizer
, se asegura que la lematización se aplique durante el proceso de vectorización.
El stemming es otra técnica de normalización que reduce las palabras a su raíz o stem, que puede no ser una palabra válida en el idioma. Por ejemplo, "corriendo", "corrí" y "corre" podrían reducirse a "corr". Aunque el stemming es menos preciso que la lematización, es más sencillo y ligero computacionalmente. Para aplicar stemming en Scikit-Learn, se puede integrar NLTK y crear un tokenizador personalizado.
Aquí se muestra cómo utilizar el SnowballStemmer de NLTK para realizar stemming en español:
from nltk.stem.snowball import SnowballStemmer
from sklearn.feature_extraction.text import CountVectorizer
import nltk
# Descargar recursos necesarios de NLTK si no se han descargado previamente
nltk.download('punkt')
stemmer = SnowballStemmer('spanish')
def stemmizador(texto):
tokens = nltk.word_tokenize(texto, language='spanish')
stems = [stemmer.stem(token) for token in tokens if token.isalnum()]
return stems
vectorizador = CountVectorizer(tokenizer=stemmizador)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
En este ejemplo, la función stemmizador
tokeniza el texto utilizando word_tokenize
de NLTK y aplica el stemmer a cada token alfanumérico. Al proporcionar esta función al CountVectorizer
, se incorpora el stemming al proceso de vectorización.
Es importante destacar que la elección entre lematización y stemming depende de las necesidades específicas del proyecto. La lematización preserva mejor el significado semántico, mientras que el stemming es más rápido pero menos preciso. Ambas técnicas ayudan a reducir la dimensionalidad del vocabulario y mejorar la generalización de los modelos.
Además, es posible combinar estas técnicas con otros pasos de preprocesamiento. Por ejemplo, se puede crear un preprocesador personalizado que convierta el texto a minúsculas, elimine acentos y luego aplique lematización:
import unicodedata
def preprocesador_personalizado(texto):
# Convertir a minúsculas
texto = texto.lower()
# Eliminar acentos
texto = unicodedata.normalize('NFD', texto).encode('ascii', 'ignore').decode('utf-8')
return texto
def lematizador(texto):
texto = preprocesador_personalizado(texto)
doc = nlp(texto)
tokens_lematizados = [token.lemma_ for token in doc if not token.is_punct]
return tokens_lematizados
vectorizador = CountVectorizer(tokenizer=lematizador)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este código, el preprocesador_personalizado
realiza la conversión a minúsculas y elimina los acentos para normalizar el texto. Luego, el lematizador
aplica la lematización sobre el texto preprocesado.
Al utilizar estos enfoques, es fundamental recordar que las funciones personalizadas pueden afectar al rendimiento del vectorizador. El parámetro n_jobs
de CountVectorizer
puede ajustarse para realizar el procesamiento en paralelo y mejorar la eficiencia.
vectorizador = CountVectorizer(tokenizer=lematizador, n_jobs=-1)
La normalización del texto mediante minúsculas, lematización y stemming es esencial para crear modelos de NLP robustos y efectivos. Al reducir la variabilidad léxica, se mejora la calidad de las características extraídas y, por ende, el desempeño de los algoritmos de aprendizaje automático.
Eliminación de stop words y caracteres especiales
La eliminación de stop words y caracteres especiales es un paso crucial en el preprocesamiento de textos para Procesamiento del Lenguaje Natural (NLP). Las stop words son palabras muy frecuentes que suelen aportar poco valor semántico al análisis, como artículos, preposiciones y conjunciones. Los caracteres especiales, por otro lado, pueden introducir ruido en los datos y afectar negativamente al rendimiento de los algoritmos.
En Scikit-Learn, es posible eliminar las stop words de forma eficiente utilizando los parámetros integrados en las clases CountVectorizer
y TfidfVectorizer
. Estas clases disponen del parámetro stop_words
, que permite especificar una lista de palabras a omitir durante el proceso de vectorización.
Por defecto, Scikit-Learn ofrece una lista de stop words para el idioma inglés. Sin embargo, al trabajar con textos en español, es necesario proporcionar una lista personalizada. A continuación, se muestra cómo eliminar las stop words en español utilizando una lista predefinida:
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
"El rápido zorro marrón salta sobre el perro perezoso.",
"Un vaso de agua con hielo, por favor.",
"Bienvenidos al mundo del procesamiento de lenguaje natural."
]
# Lista de stop words en español
stop_words_es = [
"el", "la", "los", "las", "un", "una", "unos", "unas", "de", "del",
"con", "y", "o", "a", "por", "para", "en", "al", "lo", "le", "les",
"su", "sus", "es", "son", "que"
]
vectorizador = CountVectorizer(stop_words=stop_words_es)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este ejemplo, el parámetro stop_words
recibe una lista de palabras comunes en español que serán excluidas durante la vectorización. Esto ayuda a reducir el vocabulario y a centrar el análisis en términos más relevantes.
También es posible utilizar listas de stop words proporcionadas por bibliotecas como NLTK, que ofrece colecciones estándar para varios idiomas. Para integrarlas, se puede proceder de la siguiente manera:
import nltk
from sklearn.feature_extraction.text import CountVectorizer
# Descargar las stop words en español de NLTK
nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words_es = stopwords.words('spanish')
vectorizador = CountVectorizer(stop_words=stop_words_es)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
Aquí se utiliza la lista de stop words en español de NLTK, que es más completa y está actualizada. Esto garantiza una mejor eliminación de palabras irrelevantes.
Además de las stop words, los caracteres especiales pueden interferir en el análisis textual. Para eliminarlos, se puede crear un preprocesador personalizado que limpie el texto antes de la tokenización. Un ejemplo de preprocesador que elimina caracteres especiales utilizando expresiones regulares es el siguiente:
import re
from sklearn.feature_extraction.text import CountVectorizer
def preprocesador(texto):
# Eliminar caracteres especiales y dígitos
texto_limpio = re.sub(r'[^a-zA-ZñÑáéíóúÁÉÍÓÚüÜ\s]', '', texto)
# Convertir a minúsculas
texto_limpio = texto_limpio.lower()
return texto_limpio
vectorizador = CountVectorizer(preprocessor=preprocesador, stop_words=stop_words_es)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
La función preprocesador
elimina todos los caracteres que no sean letras o espacios, incluyendo tildes y diéresis. Al pasar esta función al parámetro preprocessor
, se asegura que la limpieza se aplique antes de la tokenización.
Para un control más granular, se puede definir un tokenizador personalizado que, además de eliminar caracteres especiales, filtre tokens específicos. Por ejemplo:
def tokenizador(texto):
# Aplicar el preprocesador personalizado
texto_limpio = preprocesador(texto)
# Dividir en tokens
tokens = texto_limpio.split()
# Filtrar tokens que no sean alfabéticos
tokens = [token for token in tokens if token.isalpha()]
return tokens
vectorizador = CountVectorizer(tokenizer=tokenizador, stop_words=stop_words_es, preprocessor=None)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Al establecer preprocessor=None
, se evita el preprocesamiento interno de CountVectorizer
, permitiendo utilizar exclusivamente las funciones personalizadas.
Es posible que ciertos proyectos requieran preservar algunos caracteres especiales o stop words por su relevancia en el contexto. En tales casos, se pueden ajustar las expresiones regulares o modificar las listas de stop words adecuadamente.
Otra opción para manejar los caracteres especiales es utilizar el parámetro token_pattern
de CountVectorizer
. Este parámetro define una expresión regular para identificar los tokens durante la tokenización. Por ejemplo:
# Definir un patrón para tokens que solo incluyan letras y tildes
patron_token = r"(?u)\b[a-zA-ZáéíóúñüÁÉÍÓÚÑÜ]+\b"
vectorizador = CountVectorizer(token_pattern=patron_token, stop_words=stop_words_es)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
El patrón patron_token
captura palabras que consisten únicamente en letras, incluyendo caracteres con tildes y diéresis, y excluye otros símbolos o dígitos.
Para integrar técnicas más avanzadas, se puede utilizar spaCy para filtrar tokens según propiedades lingüísticas, como su categoría gramatical. Por ejemplo, para eliminar stop words y caracteres especiales utilizando spaCy:
import spacy
from sklearn.feature_extraction.text import CountVectorizer
# Cargar el modelo de spaCy para español
nlp = spacy.load('es_core_news_sm')
def tokenizador_spacy(texto):
doc = nlp(texto)
tokens = [
token.lemma_ for token in doc
if not token.is_punct and not token.is_stop and token.is_alpha
]
return tokens
vectorizador = CountVectorizer(tokenizer=tokenizador_spacy)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
La función tokenizador_spacy
procesa el texto con spaCy, eliminando signos de puntuación (is_punct
), stop words (is_stop
) y manteniendo solo tokens alfabéticos (is_alpha
). Además, utiliza los lemas de las palabras para una mejor normalización.
En algunos casos, puede ser útil eliminar o reemplazar ciertos caracteres especiales específicos, como emojis o símbolos técnicos. Esto se puede lograr ampliando la lógica del preprocesador. Por ejemplo:
def preprocesador_avanzado(texto):
# Eliminar emojis y símbolos técnicos
texto = re.sub(r'[\U00010000-\U0010ffff]', '', texto)
# Eliminar otros caracteres especiales
texto = re.sub(r'[^a-zA-ZñÑáéíóúÁÉÍÓÚüÜ\s]', '', texto)
# Convertir a minúsculas
texto = texto.lower()
return texto
vectorizador = CountVectorizer(preprocessor=preprocesador_avanzado, stop_words=stop_words_es)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Aquí, se utiliza una expresión regular para eliminar emojis y otros símbolos fuera del plano básico Unicode, asegurando una limpieza más exhaustiva del texto.
Es importante señalar que la eliminación de stop words y caracteres especiales debe adaptarse al contexto del análisis. En ciertos dominios, algunas stop words pueden ser significativas, o los caracteres especiales pueden contener información relevante. Por ello, es recomendable ajustar las listas y los patrones de acuerdo a las necesidades específicas del proyecto.
Además, al trabajar con textos que incluyen contracciones o expresiones idiomáticas, se debe tener cuidado para no eliminar información valiosa. Técnicas adicionales de normalización y expansión de contracciones pueden ser necesarias.
Por último, es conveniente integrar estos procesos en un pipeline de Scikit-Learn para mantener un flujo de trabajo ordenado y reproducible. Esto facilita la experimentación y la implementación de modelos en producción.
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('vectorizacion', CountVectorizer(
preprocessor=preprocesador,
stop_words=stop_words_es
)),
# Aquí se pueden agregar más pasos al pipeline, como un modelo de clasificación
], memory = None)
X = pipeline.fit_transform(corpus)
print(pipeline.named_steps['vectorizacion'].get_feature_names_out())
La utilización de pipelines permite encadenar múltiples transformaciones y modelos, asegurando que el preprocesamiento se aplique de manera consistente a todos los datos.
Manejo de signos de puntuación y espacios en blanco
En el preprocesamiento de textos para Procesamiento del Lenguaje Natural (NLP), el tratamiento adecuado de los signos de puntuación y los espacios en blanco es fundamental para mejorar la calidad de las características extraídas y, por ende, el rendimiento de los modelos. Los signos de puntuación pueden influir en el significado de las frases y su presencia o ausencia puede afectar el proceso de tokenización y análisis.
En Scikit-Learn, el CountVectorizer
y el TfidfVectorizer
manejan los signos de puntuación y los espacios en blanco de acuerdo con el patrón de tokens definido en el parámetro token_pattern
. Por defecto, este patrón es r"(?u)\b\w\w+\b"
, el cual captura secuencias de caracteres alfanuméricos de al menos dos caracteres, separadas por límites de palabra. Esto implica que los signos de puntuación y ciertos caracteres especiales son ignorados durante la tokenización.
Para personalizar cómo se manejan los signos de puntuación, es posible modificar el token_pattern
o proporcionar una función personalizada al parámetro tokenizer
. Por ejemplo, si se desea incluir palabras de un solo carácter o tokens que contengan signos de puntuación internos (como "e-mail" o "vis-à-vis"), se puede ajustar el patrón de la siguiente manera:
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
"¡Hola! ¿Cómo estás?",
"En el e-mail, se mencionó el proyecto vis-à-vis.",
"La temperatura es de 20°C en Nueva York."
]
patron_personalizado = r"(?u)\b\w[\w\-]+\b"
vectorizador = CountVectorizer(token_pattern=patron_personalizado)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este ejemplo, el patrón patron_personalizado
permite capturar tokens que incluyen guiones internos, lo cual es útil para palabras compuestas o términos técnicos. Al ajustar el token_pattern
, se controla cómo se segmenta el texto y cómo se manejan los signos de puntuación específicos.
Si se requiere un control más preciso sobre los signos de puntuación, se puede utilizar un preprocesador personalizado para eliminar o transformar los caracteres deseados antes de la tokenización. Por ejemplo, para eliminar todos los signos de puntuación excepto los símbolos de interrogación y exclamación, se puede hacer lo siguiente:
import re
def preprocesador_puntuacion(texto):
# Mantener símbolos de interrogación y exclamación
texto = re.sub(r"[^\w\s!?¡¿]", "", texto)
return texto
vectorizador = CountVectorizer(preprocessor=preprocesador_puntuacion)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
La función preprocesador_puntuacion
utiliza una expresión regular para eliminar todos los caracteres que no sean alfanuméricos, espacios en blanco o signos de interrogación y exclamación. De esta manera, se preservan elementos que pueden ser importantes para el análisis semántico o la detección de emociones.
El manejo de los espacios en blanco también puede afectar la tokenización. Por defecto, los espacios múltiples son tratados como separadores simples, pero en algunos casos, los espacios adicionales pueden tener significado (como en formatos de texto preformateado). Si se necesita preservar los espacios en blanco o normalizarlos, se puede utilizar un preprocesador para ajustar el texto antes de la tokenización.
Por ejemplo, para reemplazar múltiples espacios consecutivos por un único espacio, se puede implementar la siguiente función:
def preprocesador_espacios(texto):
# Reemplazar múltiples espacios por uno solo
texto = re.sub(r"\s+", " ", texto)
return texto.strip()
vectorizador = CountVectorizer(preprocessor=preprocesador_espacios)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
Esta función preprocesador_espacios
normaliza los espacios en blanco, lo cual puede ser útil cuando el texto original contiene espacios irregulares debido a errores de formato o extracción de datos.
En casos donde los signos de puntuación tienen un valor analítico, como en el análisis de sentimientos donde las exclamaciones o interrogaciones pueden indicar énfasis o emociones, es conveniente convertir estos signos en tokens separados. A continuación, se muestra cómo tokenizar los signos de puntuación específicos:
def tokenizador_con_puntuacion(texto):
# Dividir incluyendo signos de exclamación e interrogación como tokens
tokens = re.findall(r"\w+|[!?¡¿]", texto)
return tokens
vectorizador = CountVectorizer(tokenizer=tokenizador_con_puntuacion)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
El tokenizador_con_puntuacion
utiliza una expresión regular para separar palabras y signos de exclamación e interrogación, convirtiendo estos últimos en tokens individuales. Esto permite que el modelo aprenda patrones asociados con la presencia de dichos signos.
Además, es posible que se desee normalizar ciertos signos de puntuación, reemplazándolos por equivalentes o eliminándolos. Por ejemplo, convertir comillas tipográficas en comillas simples o dobles estándar:
def normalizar_puntuacion(texto):
# Reemplazar comillas tipográficas por comillas estándar
texto = texto.replace("“", '"').replace("”", '"').replace("‘", "'").replace("’", "'")
return texto
vectorizador = CountVectorizer(preprocessor=normalizar_puntuacion)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
La función normalizar_puntuacion
unifica el uso de comillas, lo cual puede ser importante para mantener la coherencia en el análisis de textos provenientes de diferentes fuentes.
En cuanto al manejo de los espacios en blanco en contextos multilingües o con sistemas de escritura sin separación clara entre palabras (como el chino o el japonés), se requiere una segmentación más avanzada. Aunque Scikit-Learn no proporciona directamente herramientas para estos idiomas, se puede integrar con bibliotecas externas y adaptar el preprocesamiento.
Cuando se trabaja con textos que incluyen saltos de línea, tabulaciones u otros caracteres de espacio en blanco, es posible que se necesite consolidarlos o eliminarlos según el caso. Por ejemplo, para eliminar todos los espacios en blanco excepto los espacios simples:
def limpiar_espacios(texto):
# Reemplazar tabulaciones y saltos de línea por espacios
texto = texto.replace("\n", " ").replace("\t", " ")
# Reemplazar múltiples espacios por uno solo
texto = re.sub(r"\s+", " ", texto)
return texto.strip()
vectorizador = CountVectorizer(preprocessor=limpiar_espacios)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
La función limpiar_espacios
limpia el texto de espacios en blanco extras, lo cual es útil para textos que han sido extraídos de fuentes con formatos variados.
En algunos contextos avanzados, es necesario conservar el contexto proporcionado por los signos de puntuación. Por ejemplo, al analizar entidades nombradas en un texto, los puntos y comas pueden indicar límites de frases o cláusulas que son relevantes. En tales casos, se puede optar por tokenizadores más sofisticados que preserven esta información, como los proporcionados por bibliotecas especializadas como spaCy o NLTK.
Sin embargo, si se desea continuar utilizando Scikit-Learn y aprovechar funcionalidades avanzadas, se puede crear un análisis personalizado que incorpore estas bibliotecas. A continuación, se muestra un ejemplo utilizando spaCy para conservar los signos de puntuación como tokens:
import spacy
# Cargar el modelo de spaCy para español
nlp = spacy.load('es_core_news_sm')
def analizador_spacy(texto):
doc = nlp(texto)
tokens = [token.text for token in doc]
return tokens
vectorizador = CountVectorizer(tokenizer=analizador_spacy, token_pattern=None)
X = vectorizador.fit_transform(corpus)
print(vectorizador.get_feature_names_out())
Salida:
En este caso, al establecer token_pattern=None
, se desactiva el tokenizador interno de CountVectorizer, permitiendo que el analizador_spacy
maneje completamente la tokenización, incluyendo los signos de puntuación como tokens separados. Esto proporciona un mayor control y precisión en el procesamiento del texto.
Es importante considerar que el manejo de los signos de puntuación y los espacios en blanco debe adaptarse a los objetivos específicos del análisis. En algunos modelos, puede ser beneficioso eliminar completamente la puntuación para reducir el ruido, mientras que en otros, preservarla puede aportar información valiosa.
Finalmente, al diseñar el pipeline de preprocesamiento, es recomendable probar diferentes configuraciones y evaluar el impacto en el rendimiento del modelo. La flexibilidad que ofrece Scikit-Learn permite ajustar el manejo de signos de puntuación y espacios en blanco para optimizar los resultados en proyectos de Procesamiento del Lenguaje Natural.
Construcción de pipelines de preprocesamiento de textos
La construcción de pipelines en Scikit-Learn permite organizar y automatizar el proceso de preprocesamiento de textos para aplicaciones de Procesamiento del Lenguaje Natural (NLP). Utilizando la clase Pipeline
, es posible encadenar múltiples transformaciones y modelos, asegurando que el flujo de datos sea consistente y reproducible.
Dado que el preprocesamiento de textos a menudo implica una serie de pasos, como la normalización, la vectorización y la selección de características, los pipelines simplifican este flujo al integrarlos en una estructura coherente. A continuación, se muestra cómo construir un pipeline que incorpore varias etapas de preprocesamiento y un modelo de clasificación.
Supongamos que se dispone de un conjunto de datos de mensajes de correos electrónicos etiquetados como "spam" o "no spam". El objetivo es construir un modelo que sea capaz de clasificar nuevos correos electrónicos basándose en su contenido. Para ello, se puede crear un pipeline que realice los siguientes pasos:
- Preprocesamiento del texto: Limpieza y normalización.
- Vectorización: Conversión del texto en una representación numérica.
- Clasificación: Entrenamiento de un modelo predictivo.
Primero, se importan las clases necesarias:
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
Se define un preprocesador personalizado para limpiar el texto, eliminando números y convirtiendo todo a minúsculas:
import re
def preprocesar_texto(texto):
texto = texto.lower()
texto = re.sub(r'\d+', '', texto)
texto = re.sub(r'[^\w\s]', '', texto)
return texto
A continuación, se crea el pipeline utilizando la clase Pipeline
, especificando los pasos y sus respectivos parámetros:
pipeline = Pipeline([
('vectorizador', CountVectorizer(preprocessor=preprocesar_texto, stop_words='spanish')),
('clasificador', LogisticRegression(solver='liblinear'))
], memory = None)
En este pipeline, el vectorizador utiliza CountVectorizer
con el preprocesador personalizado y elimina las stop words en español. El clasificador es una regresión logística que utilizará la representación numérica generada por el vectorizador.
Es posible ajustar los hiperparámetros de cada paso utilizando un diccionario de parámetros. Por ejemplo, para realizar una búsqueda de los mejores hiperparámetros:
from sklearn.model_selection import GridSearchCV
parametros = {
'vectorizador__ngram_range': [(1, 1), (1, 2)],
'clasificador__C': [0.1, 1, 10]
}
grid_search = GridSearchCV(pipeline, parametros, cv=5)
Aquí, se está buscando el mejor rango de n-gramas para el vectorizador y el valor de regularización C para el clasificador.
Una vez configurado el pipeline y los parámetros, se puede entrenar el modelo con los datos de entrenamiento:
# Supongamos que X_train contiene los textos y y_train las etiquetas
grid_search.fit(X_train, y_train)
Tras el entrenamiento, es posible predecir las etiquetas de nuevos textos:
# Predicción sobre datos de prueba
predicciones = grid_search.predict(X_test)
Para mejorar el proceso de preprocesamiento, se puede incorporar un tokenizador personalizado, como una función de lematización con spaCy:
import spacy
nlp = spacy.load('es_core_news_sm')
def tokenizar_lematizar(texto):
documento = nlp(texto)
tokens = [token.lemma_ for token in documento if not token.is_stop and not token.is_punct]
return tokens
Se actualiza el pipeline para utilizar este tokenizador:
pipeline = Pipeline([
('vectorizador', CountVectorizer(tokenizer=tokenizar_lematizar)),
('clasificador', LogisticRegression(solver='liblinear'))
], memory = None)
Al utilizar el CountVectorizer con el tokenizador personalizado, se aplicará la lematización a cada texto antes de la vectorización.
Es posible combinar múltiples transformaciones utilizando la clase FeatureUnion
o ColumnTransformer
cuando se tienen diferentes tipos de datos. Por ejemplo, si se dispone de datos numéricos adicionales:
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler
transformador = ColumnTransformer([
('texto', CountVectorizer(tokenizer=tokenizar_lematizar), 'texto'),
('numerico', StandardScaler(), ['longitud', 'palabras_clave'])
])
El pipeline se ajusta para incluir el transformador de columnas:
pipeline = Pipeline([
('preprocesamiento', transformador),
('clasificador', LogisticRegression(solver='liblinear'))
], memory = None)
En este ejemplo, el ColumnTransformer aplica el CountVectorizer
a la columna de texto y el StandardScaler
a las características numéricas, combinando ambas para el entrenamiento del clasificador.
Es importante destacar que los nombres asignados a cada paso en el pipeline permiten acceder y ajustar sus parámetros fácilmente. Además, utilizar pipelines facilita la reproducibilidad y el mantenimiento del código, ya que todas las transformaciones y el modelo están encapsulados en una estructura coherente.
Para evaluar el rendimiento del modelo, se pueden utilizar métricas como la matriz de confusión, la precisión o el F1-score:
from sklearn.metrics import classification_report, confusion_matrix
# Evaluación del modelo
print(classification_report(y_test, predicciones))
print(confusion_matrix(y_test, predicciones))
Al utilizar pipelines, también es sencillo implementar técnicas de validación cruzada, lo que permite obtener estimaciones más fiables del rendimiento del modelo.
Para preservar el modelo entrenado y reutilizarlo en el futuro, se puede guardar el pipeline completo utilizando joblib
:
import joblib
# Guardar el pipeline entrenado
joblib.dump(grid_search.best_estimator_, 'modelo_spam_pipeline.joblib')
# Cargar el pipeline entrenado
modelo_cargado = joblib.load('modelo_spam_pipeline.joblib')
De esta manera, el pipeline conserva tanto los pasos de preprocesamiento como el modelo entrenado, listo para hacer predicciones sobre nuevos datos.
Si se requiere implementar el modelo en un entorno de producción, el uso de pipelines simplifica la integración, ya que se necesita llamar únicamente al método predict
del pipeline cargado:
# Predicción de un nuevo correo electrónico
nuevo_correo = ["Gana dinero rápido con este truco increíble"]
prediccion = modelo_cargado.predict(nuevo_correo)
print(prediccion)
La construcción de pipelines también permite experimentar con diferentes modelos y técnicas de preprocesamiento sin modificar significativamente el código. Por ejemplo, se puede sustituir el clasificador por un árbol de decisión:
from sklearn.tree import DecisionTreeClassifier
pipeline = Pipeline([
('vectorizador', CountVectorizer(tokenizer=tokenizar_lematizar)),
('clasificador', DecisionTreeClassifier())
], memory = None)
O incorporar el uso de TfidfVectorizer
en lugar de CountVectorizer
:
from sklearn.feature_extraction.text import TfidfVectorizer
pipeline = Pipeline([
('vectorizador', TfidfVectorizer(tokenizer=tokenizar_lematizar)),
('clasificador', LogisticRegression(solver='liblinear'))
], memory = None)
Los pipelines ofrecen flexibilidad y eficiencia en el desarrollo de modelos de NLP al permitir un diseño modular y escalable. Además, al integrar todos los pasos en un solo objeto, se reduce la posibilidad de errores y se mejora la consistencia en el manejo de los datos.
Es recomendable seguir buenas prácticas al construir pipelines, como:
- Nombrar claramente cada paso para facilitar su identificación.
- Validar los datos después de cada transformación si es necesario.
- Utilizar funciones personalizadas para etapas específicas del preprocesamiento.
- Documentar las transformaciones aplicadas para mantener la transparencia del modelo.
En conclusión, la utilización de pipelines en Scikit-Learn es fundamental para construir flujos de preprocesamiento de textos eficientes y robustos, lo que resulta esencial en proyectos avanzados de Procesamiento del Lenguaje Natural.
Ejercicios de esta lección Preprocesamiento de textos para NLP
Evalúa tus conocimientos de esta lección Preprocesamiento de textos para NLP con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Todas las lecciones de ScikitLearn
Accede a todas las lecciones de ScikitLearn y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Aprendizaje Automático
Introducción Y Entorno
Introducción E Instalación
Introducción Y Entorno
Introducción Al Preprocesamiento De Datos
Preprocesamiento De Datos
Identificación Y Tratamiento De Valores Faltantes
Preprocesamiento De Datos
Escalado De Datos
Preprocesamiento De Datos
Normalización De Datos
Preprocesamiento De Datos
Codificación De Variables Categóricas
Preprocesamiento De Datos
Ingeniería De Características
Preprocesamiento De Datos
Selección De Características
Preprocesamiento De Datos
Extracción De Características
Preprocesamiento De Datos
Particionamiento De Datos
Preprocesamiento De Datos
Preprocesamiento De Datos Desbalanceados
Preprocesamiento De Datos
Introducción A La Regresión
Regresión
Regresión Lineal
Regresión
Regresión Knn Kneighborsregressor
Regresión
Regresión Svm Con Svr
Regresión
Regresión Con Árboles Decisiontreeregressor
Regresión
Regresión Con Algoritmos De Conjunto
Regresión
Introducción A La Clasificación
Clasificación
Clasificación Con Regresión Logística
Clasificación
Clasificación Knn Kneighborsclassifier
Clasificación
Clasificación Svm Con Svc
Clasificación
Clasificación Con Árboles Decisiontreeclassifier
Clasificación
Clasificación Con Algoritmos De Conjunto
Clasificación
Reducción De La Dimensionalidad Con Pca
Aprendizaje No Supervisado
Clustering Con Kmeans
Aprendizaje No Supervisado
Clustering Jerárquico
Aprendizaje No Supervisado
Clustering De Densidad Con Dbscan
Aprendizaje No Supervisado
Preprocesamiento De Textos Para Nlp
Nlp
Representación De Texto Y Extracción De Características
Nlp
Clasificación De Texto Con Scikit Learn
Nlp
Análisis De Sentimiento
Nlp
Técnicas Avanzadas De Extracción De Características
Nlp
Introducción Al Análisis De Series Temporales
Series Temporales
Preprocesamiento De Datos De Series Temporales
Series Temporales
Ingeniería De Características Para Series Temporales
Series Temporales
Transformación Y Escalado De Series Temporales
Series Temporales
Validación Y Evaluación De Modelos En Series Temporales
Series Temporales
Validación Y Evaluación De Modelos
Validación De Modelos
Técnicas De Validación Cruzada
Validación De Modelos
Métricas De Regresión
Validación De Modelos
Métricas De Clasificación
Validación De Modelos
Ajuste De Hiperparámetros
Validación De Modelos
Introducción A Pipelines
Pipelines Y Despliegue
Creación De Pipelines Básicos
Pipelines Y Despliegue
Preprocesamiento De Datos Con Pipelines
Pipelines Y Despliegue
Pipelines Y Validación Cruzada
Pipelines Y Despliegue
Pipelines Con Columntransformer
Pipelines Y Despliegue
Exportar E Importar Pipelines
Pipelines Y Despliegue
Objetivos de aprendizaje de esta lección
- Comprender la tokenización y segmentación de texto utilizando Scikit-Learn.
- Aplicar técnicas de normalización como conversión a minúsculas, lematización y stemming.
- Implementar la eliminación de stop words y caracteres especiales en textos.
- Manejar signos de puntuación y espacios en blanco durante el preprocesamiento.
- Construir pipelines de preprocesamiento de textos eficientes con Scikit-Learn.