Cómo fragmentar datos de texto un análisis comparativo

Fragmentación de datos de texto para análisis comparativo

Explorando enfoques distintos para la segmentación de texto.

Imagen compilada por el autor. Imagen de piña de Canva.

Introducción

El proceso de ‘segmentación de texto’ en Procesamiento del Lenguaje Natural (PLN) implica la conversión de datos de texto no estructurado en unidades significativas. Esta tarea aparentemente simple oculta la complejidad de los diversos métodos empleados para lograrlo, cada uno con sus fortalezas y debilidades.

A grandes rasgos, estos métodos suelen caer en una de dos categorías. El primer grupo, los métodos basados en reglas, se basan en el uso de separadores explícitos como la puntuación o los espacios, o la aplicación de sistemas sofisticados como las expresiones regulares, para dividir el texto en segmentos. La segunda categoría, los métodos de agrupación semántica, aprovecha el significado inherente contenido en el texto para guiar el proceso de segmentación. Estos pueden utilizar algoritmos de aprendizaje automático para discernir el contexto e inferir divisiones naturales dentro del texto.

En este artículo, exploraremos y compararemos estos dos enfoques distintos para la segmentación de texto. Representaremos los métodos basados en reglas con NLTK, Spacy y Langchain, y contrastaremos esto con dos técnicas de agrupación semántica diferentes: KMeans y una técnica personalizada para la Agrupación de Oraciones Adyacentes.

El objetivo es proporcionar a los profesionales una comprensión clara de las ventajas, desventajas y casos de uso ideales de cada método para facilitar la toma de decisiones en sus proyectos de PLN.

En el argot brasileño, “abacaxi”, que se traduce como “piña”, significa “algo que no da un buen resultado, un lío enredado o algo que no sirve”.

Casos de uso para la segmentación de texto

La segmentación de texto puede ser utilizada por varias aplicaciones diferentes:

  1. Resumen de texto: Al descomponer grandes cuerpos de texto en segmentos manejables, podemos resumir cada sección de forma individual, lo que conduce a un resumen general más preciso.
  2. Análisis de sentimientos: Analizar el sentimiento de segmentos más cortos y coherentes a menudo puede arrojar resultados más precisos que analizar un documento completo.
  3. Extracción de información: La segmentación ayuda a localizar entidades o frases específicas dentro del texto, mejorando el proceso de recuperación de información.
  4. Clasificación de texto: Descomponer el texto en segmentos permite a los clasificadores centrarse en unidades más pequeñas y contextualmente significativas en lugar de en documentos completos, lo que puede mejorar el rendimiento.
  5. Traducción automática: Los sistemas de traducción a menudo operan en segmentos de texto en lugar de en palabras individuales o documentos completos. La segmentación puede ayudar a mantener la coherencia del texto traducido.

Comprender estos casos de uso puede ayudar a elegir la técnica de segmentación más adecuada para su proyecto específico.

Comparación de diferentes métodos para la segmentación semántica

En esta parte del artículo, compararemos métodos populares para la segmentación semántica de texto no estructurado: el Tokenizador de Oraciones de NLTK, el Separador de Texto de Langchain, la Agrupación KMeans y la Agrupación de Oraciones Adyacentes basada en similitud.

En el siguiente ejemplo, evaluaremos esta técnica utilizando un texto extraído de un PDF, procesándolo en oraciones y sus grupos.

Los datos que utilizamos fueron extraídos de un PDF exportado de la página de Wikipedia de Brasil.

Para extraer texto de un PDF y dividirlo en oraciones con NLTK, utilizamos las siguientes funciones:

from PyPDF2 import PdfReaderimport nltknltk.download('punkt')# Extracción de texto de un PDFdef extract_text_from_pdf(file_path):    with open(file_path, 'rb') as file:        pdf = PdfReader(file)        text = " ".join(page.extract_text() for page in pdf.pages)    return text# Extraer texto del PDF y dividirlo en oracionestext = extract_text_from_pdf(file_path)

De esta forma, obtenemos una cadena text con una longitud de 210964 caracteres.

Aquí hay un ejemplo del texto de Wikipedia:

muestra = text[1015:3037]print(muestra)"""=======Salida:=======Brasil es el quinto país más grande del mundo por área y el séptimo más poblado. Su capital es Brasilia, y su ciudad más poblada es São Paulo. La federación está compuesta por la unión de los 26 estados y el Distrito Federal. Es el único país de las Américas que tiene el portugués como lengua oficial.[11][12] Es una de las naciones más multiculturales y étnicamente diversas, debido a más de un siglo de inmigración masiva de todo el mundo,[13] y es el país mayoritariamente católico romano más poblado. Limitado por el Océano Atlántico al este, Brasil tiene una costa de 7,491 kilómetros (4,655 mi).[14] Limita con todos los demás países y territorios de América del Sur excepto Ecuador y Chile y abarca aproximadamente la mitad del área terrestre del continente.[15] Su cuenca amazónica incluye una vasta selva tropical, hogar de una fauna diversa, una variedad de sistemas ecológicos y extensos recursos naturales que abarcan numerosos hábitats protegidos.[14] Este patrimonio ambiental único sitúa a Brasil en el primer lugar de 17 países megadiversos y es objeto de un interés global significativo, ya que la degradación ambiental a través de procesos como la deforestación tiene impactos directos en cuestiones globales como el cambio climático y la pérdida de biodiversidad.El territorio que se conocería como Brasil estaba habitado por numerosas naciones tribales antes del desembarco en 1500 del explorador Pedro Álvares Cabral, quien reclamó la tierra descubierta para el Imperio Portugués. Brasil permaneció como colonia portuguesa hasta 1808, cuando la capital del imperio fue trasladada de Lisboa a Río de Janeiro. En 1815, la colonia fue elevada a la categoría de reino con la formación del Reino Unido de Portugal, Brasil y Algarve. La independencia se logró en 1822 con la creación del Imperio de Brasil, un estado unitario gobernado bajo una monarquía constitucional y un sistema parlamentario. La ratificación de la primera constitución en 1824 condujo a la formación de una legislatura bicameral, ahora llamada Congreso Nacional."""

Tokenizador de Oraciones de NLTK

El Natural Language Toolkit (NLTK) proporciona una función útil para dividir el texto en oraciones. Este tokenizador de oraciones divide un bloque de texto dado en sus oraciones componentes, las cuales luego se pueden utilizar para un procesamiento adicional.

Implementación

A continuación se muestra un ejemplo de cómo utilizar el tokenizador de oraciones de NLTK:

import nltknltk.download('punkt')# Dividir el Texto en Oracionesdef dividir_texto_en_oraciones(texto):    oraciones = nltk.sent_tokenize(texto)    return oracionesoraciones = dividir_texto_en_oraciones(texto)

Esto devuelve una lista de 2670 oraciones extraídas del texto de entrada, con una media de 78 caracteres por oración.

Evaluación del Tokenizador de Oraciones de NLTK

Aunque el Tokenizador de Oraciones de NLTK es una forma directa y eficiente de dividir un texto extenso en oraciones individuales, tiene ciertas limitaciones:

  1. Dependencia del Idioma: El Tokenizador de Oraciones de NLTK depende mucho del idioma del texto. Funciona bien con el inglés, pero puede no proporcionar resultados precisos con otros idiomas sin una configuración adicional.
  2. Abreviaturas y Puntuación: El tokenizador ocasionalmente puede interpretar erróneamente abreviaturas u otra puntuación al final de una oración. Esto puede hacer que fragmentos de oraciones se traten como oraciones independientes.
  3. Falta de Comprensión Semántica: Al igual que la mayoría de los tokenizadores, el Tokenizador de Oraciones de NLTK no considera la relación semántica entre las oraciones. Por lo tanto, se puede perder un contexto que abarque múltiples oraciones en el proceso de tokenización.

Separador de Oraciones de Spacy

Spacy, otra poderosa biblioteca de procesamiento de lenguaje natural, proporciona una función de tokenización de oraciones que se basa en gran medida en reglas lingüísticas. Es un enfoque similar al de NLTK.

Implementación

Implementar el separador de oraciones de Spacy es bastante sencillo. Así es cómo hacerlo en Python:

import spacynlp = spacy.load('en_core_web_sm')doc = nlp(text)sentences = list(doc.sents)

Esto devuelve una lista de 2336 oraciones extraídas del texto de entrada, con una media de 89 caracteres por oración.

Evaluación del Separador de Oraciones de Spacy

El separador de oraciones de Spacy tiende a crear fragmentos más pequeños en comparación con el Separador de Texto de Caracteres de Langchain, ya que se adhiere estrictamente a los límites de las oraciones. Esto puede ser ventajoso cuando se necesitan unidades de texto más pequeñas para el análisis.

Al igual que NLTK, sin embargo, el rendimiento de Spacy depende de la calidad del texto de entrada. Para textos mal puntuados o estructurados, los límites de las oraciones identificadas pueden no ser siempre precisos.

A continuación, veremos cómo Langchain proporciona un marco para dividir datos de texto en fragmentos y lo compararemos con NLTK y Spacy.

Separador de Texto de Caracteres de Langchain

El Separador de Texto de Caracteres de Langchain funciona dividiendo recursivamente el texto en caracteres específicos. Es especialmente útil para texto genérico.

El separador se define mediante una lista de caracteres. Intenta dividir el texto en función de estos caracteres hasta que los fragmentos generados cumplan con el criterio de tamaño deseado. La lista predeterminada es [“\n\n”, “\n”, ” “,”“], con el objetivo de mantener juntos los párrafos, las oraciones y las palabras tanto como sea posible para mantener la coherencia semántica.

Implementación

Considera el siguiente ejemplo, donde dividimos el texto de muestra extraído de nuestro PDF utilizando este método.

# Inicializar el separador de texto con parámetros personalizadoscustom_text_splitter = RecursiveCharacterTextSplitter(    # Establecer el tamaño de fragmento personalizado    chunk_size = 100,    chunk_overlap  = 20,    # Utilizar la longitud del texto como medida de tamaño    length_function = len,)# Crear los fragmentostextos = custom_text_splitter.create_documents([muestra])# Imprimir los dos primeros fragmentosprint(f'### Fragmento 1: \n\n{textos[0].page_content}\n\n=====\n')print(f'### Fragmento 2: \n\n{textos[1].page_content}\n\n=====')"""=======Salida:=======### Fragmento 1: Brasil es el quinto país más grande del mundo en superficie y el séptimo más poblado. Su capital=====### Fragmento 2: es Brasilia, y su ciudad más poblada es São Paulo. La federación está compuesta por la unión de====="""

Finalmente, terminamos con 3205 fragmentos de texto, representados por la lista texts. Aquí, la media para cada fragmento es de 65.8 caracteres, un poco menos que la media de NLTK (79 caracteres).

Cambiando los parámetros y usando el separador ‘\n’:

Para un enfoque más personalizado en el Separador de Langchain, podemos modificar los parámetros chunk_size y chunk_overlap según nuestras necesidades. Además, podemos especificar solo un carácter (o conjunto de caracteres) para la operación de separación, como \n. Esto guiará al separador a dividir el texto en fragmentos solo en los caracteres de nueva línea.

Consideremos un ejemplo donde establecemos chunk_size en 300, chunk_overlap en 30 y solo usamos \n como el separador.

# Inicializar el separador de texto personalizado con parámetros personalizados
custom_text_splitter = RecursiveCharacterTextSplitter(
    # Establecer tamaño de fragmento personalizado
    chunk_size = 300,
    chunk_overlap = 30,
    # Usar longitud del texto como medida de tamaño
    length_function = len,
    # Usar solo "\n\n" como separador
    separators = ['\n'])

# Crear los fragmentos
custom_texts = custom_text_splitter.create_documents([sample])

# Imprimir los primeros dos fragmentos
print(f'### Fragmento 1: \n\n{custom_texts[0].page_content}\n\n=====\n')
print(f'### Fragmento 2: \n\n{custom_texts[1].page_content}\n\n=====')

Ahora, comparemos algunas salidas de los parámetros estándar con los parámetros personalizados:

# Imprimir los fragmentos de muestra
print("==== Fragmentos de muestra de 'Parámetros estándar': ====\n\n")
for i, fragmento in enumerate(texts):
  if i < 4:
    print(f"### Fragmento {i+1}: \n{fragmento.page_content}\n")
    
print("==== Fragmentos de muestra de 'Parámetros personalizados': ====\n\n")
for i, fragmento in enumerate(custom_texts):
  if i < 4:
    print(f"### Fragmento {i+1}: \n{fragmento.page_content}\n")
    
"""=======Resultado:=======
Fragmentos de muestra de 'Parámetros estándar':
### Fragmento 1: Brasil es el quinto país más grande del mundo por área y el séptimo más poblado. Su capital
### Fragmento 2: es Brasilia, y su ciudad más poblada es Sao Paulo. La federación está compuesta por la unión de
### Fragmento 3: de la unión de los 26
### Fragmento 4: estados y el Distrito Federal. Es el único país de las Américas que tiene el portugués como

Fragmentos de muestra de 'Parámetros personalizados':
### Fragmento 1: Brasil es el quinto país más grande del mundo por área y el séptimo más poblado. Su capital es Brasilia, y su ciudad más poblada es Sao Paulo. La federación está compuesta por la unión de los 26
### Fragmento 2: estados y el Distrito Federal. Es el único país de las Américas que tiene el portugués como idioma oficial.[11][12] Es una de las naciones más multiculturales y étnicamente diversas, debido a más de un siglo de
### Fragmento 3: inmigración masiva de todo el mundo,[13] y el país mayoritariamente católico romano más poblado. Limitado por el Océano Atlántico al este, Brasil tiene una costa de 7.491 kilómetros (4.655 millas).[14] Cubre
### Fragmento 4: todas las demás países y territorios de América del Sur excepto Ecuador y Chile y cubre aproximadamente la mitad del área terrestre del continente.[15] Su cuenca amazónica incluye una vasta selva tropical, hogar de diversas"""

Ya podemos ver que estos parámetros personalizados generan fragmentos mucho más grandes y, por lo tanto, conservan más contenido que el conjunto de parámetros predeterminado.

Evaluación del Separador de Texto de Caracteres de Langchain

Después de dividir el texto en fragmentos utilizando diferentes parámetros, obtenemos dos listas de fragmentos: texts y custom_texts, que contienen 3205 y 1404 fragmentos de texto, respectivamente. Ahora, grafiquemos la distribución de las longitudes de los fragmentos para estos dos escenarios para comprender mejor el impacto de cambiar los parámetros.

Figura 1: Gráfico de distribución de las longitudes de los fragmentos para el separador de Langchain con diferentes parámetros (Imagen del autor)

En este histograma, el eje x representa las longitudes de los fragmentos, mientras que el eje y representa la frecuencia de cada longitud. Las barras azules representan la distribución de las longitudes de los fragmentos para los parámetros originales, y las barras naranjas representan la distribución de los parámetros personalizados. Al comparar estas dos distribuciones, podemos ver cómo los cambios en los parámetros afectaron las longitudes de los fragmentos resultantes.

Recuerda, la distribución ideal depende de los requisitos específicos de tu tarea de procesamiento de texto. Es posible que desees fragmentos más pequeños y numerosos si estás tratando con un análisis detallado o fragmentos más grandes y menos numerosos para un análisis semántico más amplio.

Divisor de texto de caracteres de Langchain vs. NLTK y Spacy

Anteriormente, generamos 3205 fragmentos utilizando el divisor de Langchain con sus parámetros predeterminados. Por otro lado, el tokenizador de oraciones de NLTK dividió el mismo texto en un total de 2670 oraciones.

Para tener una comprensión más intuitiva de la diferencia entre estos métodos, podemos visualizar la distribución de las longitudes de los fragmentos. La siguiente gráfica muestra las densidades de las longitudes de los fragmentos para cada método, lo que nos permite ver cómo se distribuyen las longitudes y dónde se encuentran la mayoría de las longitudes.

Figura 2: Gráfica de distribución de las longitudes de los fragmentos resultantes del divisor de Langchain con parámetros personalizados vs. NLTK y Spacy (Imagen del Autor)

A partir de la Figura 1, podemos ver que el divisor de Langchain produce una densidad de longitudes de grupo mucho más concisa y tiende a tener más grupos más largos, mientras que NLTK y Spacy parecen producir salidas muy similares en términos de longitud de grupo, prefiriendo oraciones más pequeñas pero con muchos valores atípicos con longitudes que pueden llegar hasta 1400 caracteres, y una tendencia a disminuir la longitud.

Agrupamiento de KMeans

El agrupamiento de oraciones es una técnica que implica agrupar oraciones según su similitud semántica. Al utilizar incrustaciones de oraciones y un algoritmo de agrupamiento como K-means, podemos implementar el agrupamiento de oraciones.

Implementación

Aquí hay un ejemplo simple de código utilizando la biblioteca de Python sentence-transformers para generar incrustaciones de oraciones y scikit-learn para el agrupamiento de K-means:

from sentence_transformers import SentenceTransformerfrom sklearn.cluster import KMeans# Cargar el modelo de Sentence Transformermodel = SentenceTransformer('all-MiniLM-L6-v2')# Definir una lista de oraciones (datos de texto)oraciones = ["Esta es una oración de ejemplo.", "Otra oración va aquí.", "..."]# Generar las incrustaciones para las oracionesincrustaciones = model.encode(oraciones)# Elegir un número apropiado de grupos (aquí elegimos 5 como ejemplo)num_grupos = 3# Realizar el agrupamiento de K-meanskmeans = KMeans(n_clusters=num_grupos)grupos = kmeans.fit_predict(incrustaciones)

Aquí puedes ver que los pasos para agrupar una lista de oraciones son:

  1. Cargar un modelo de transformación de oraciones. En este caso, estamos utilizando all-MiniLM-L6-v2 de sentence-transformers/all-MiniLM-L6-v2 en HuggingFace.
  2. Definir tus oraciones y generar sus incrustaciones con el método encode() del modelo.
  3. Luego defines tu técnica de agrupamiento y el número de grupos (aquí estamos utilizando KMeans con 3 grupos) y finalmente lo ajustas al conjunto de datos.

Evaluación del agrupamiento de KMeans

Y finalmente graficamos una nube de palabras para cada grupo.

from wordcloud import WordCloudimport matplotlib.pyplot as pltfrom nltk.corpus import stopwordsfrom nltk.tokenize import word_tokenizeimport stringnltk.download('stopwords')# Definir una lista de palabras de detenidastop_words = set(stopwords.words('english'))# Definir una función para limpiar las oracionesdef clean_sentence(oracion):    # Tokenizar la oración    tokens = word_tokenize(oracion)    # Convertir a minúsculas    tokens = [w.lower() for w in tokens]    # Eliminar puntuación    table = str.maketrans('', '', string.punctuation)    stripped = [w.translate(table) for w in tokens]    # Eliminar tokens no alfabéticos    palabras = [palabra for palabra in stripped if palabra.isalpha()]    # Filtrar palabras de detención    palabras = [w for w in palabras if not w in stop_words]    return palabras# Calcular e imprimir nubes de palabras para cada grupofor i in range(num_grupos):    oraciones_grupo = [oraciones[j] for j in range(len(oraciones)) if grupos[j] == i]    oraciones_limpias = [' '.join(clean_sentence(s)) for s in oraciones_grupo]    texto = ' '.join(oraciones_limpias)    nube_palabras = WordCloud(max_font_size=50, max_words=100, background_color="white").generate(texto)    plt.figure()    plt.imshow(nube_palabras, interpolation="bilinear")    plt.axis("off")    plt.title(f"Grupo {i}")    plt.show()

A continuación tenemos los gráficos de WordCloud para los clusters generados:

Figura 3: Gráfico de Word Cloud para la agrupación KMeans - cluster 0 (Imagen del Autor)
Figura 4: Gráfico de Word Cloud para la agrupación KMeans - cluster 1 (Imagen del Autor)
Figura 5: Gráfico de Word Cloud para la agrupación KMeans - cluster 2 (Imagen del Autor)

En nuestro análisis de la nube de palabras para la agrupación KMeans, es evidente que cada cluster se diferencia de manera distintiva en función de la semántica de sus palabras más frecuentes. Esto demuestra una fuerte diferenciación semántica entre los clusters. Además, se observa una variación notable en los tamaños de los clusters, lo que indica una disparidad significativa en el número de secuencias que cada cluster contiene.

Limitaciones de la agrupación KMeans

La agrupación de oraciones, aunque beneficiosa, tiene algunas limitaciones destacables. Las principales limitaciones incluyen:

  1. Pérdida del orden de las oraciones: La agrupación de oraciones no conserva la secuencia original de las oraciones, lo que podría distorsionar el flujo natural de la narrativa. **Esto es muy importante**
  2. Eficiencia computacional: KMeans puede ser intensivo en términos de recursos computacionales y lento, especialmente con corpus de texto grandes o al trabajar con un mayor número de clusters. Esto puede ser una desventaja significativa para aplicaciones en tiempo real o al manejar big data.

Agrupación de oraciones adyacentes

Para superar algunas de las limitaciones de la agrupación KMeans, especialmente la pérdida del orden de las oraciones, un enfoque alternativo podría ser agrupar las oraciones adyacentes en función de su similitud semántica. La premisa fundamental de este enfoque es que dos oraciones que aparecen consecutivamente en un texto son más propensas a estar relacionadas semánticamente que dos oraciones que están más separadas.

Implementación

A continuación se muestra una implementación ampliada de esta heurística utilizando oraciones de Spacy como entradas:

import numpy as npimport spacy# Carga el modelo de Spacynlp = spacy.load('en_core_web_sm')def process(text):    doc = nlp(text)    sents = list(doc.sents)    vecs = np.stack([sent.vector / sent.vector_norm for sent in sents])    return sents, vecsdef cluster_text(sents, vecs, threshold):    clusters = [[0]]    for i in range(1, len(sents)):        if np.dot(vecs[i], vecs[i-1]) < threshold:            clusters.append([])        clusters[-1].append(i)        return clustersdef clean_text(text):    # Agrega aquí tu proceso de limpieza de texto    return text# Inicializa la lista de longitudes de los clusters y la lista de textos finalesclusters_lens = []final_texts = []# Procesa el fragmentothreshold = 0.3sents, vecs = process(text)# Agrupa las oracionesclusters = cluster_text(sents, vecs, threshold)for cluster in clusters:    cluster_txt = clean_text(' '.join([sents[i].text for i in cluster]))    cluster_len = len(cluster_txt)        # Verifica si el cluster es demasiado corto    if cluster_len < 60:        continue        # Verifica si el cluster es demasiado largo    elif cluster_len > 3000:        threshold = 0.6        sents_div, vecs_div = process(cluster_txt)        reclusters = cluster_text(sents_div, vecs_div, threshold)                for subcluster in reclusters:            div_txt = clean_text(' '.join([sents_div[i].text for i in subcluster]))            div_len = len(div_txt)                        if div_len < 60 or div_len > 3000:                continue                        clusters_lens.append(div_len)            final_texts.append(div_txt)                else:        clusters_lens.append(cluster_len)        final_texts.append(cluster_txt)

Principales conclusiones de este código:

  1. Procesamiento de texto: Cada fragmento de texto se pasa a la función process. Esta función utiliza la biblioteca SpaCy para crear embeddings de oraciones, que se utilizan para representar el significado semántico de cada oración en el fragmento de texto.
  2. Creación de clústeres: La función cluster_text forma clústeres de oraciones en función de la similitud coseno de sus embeddings. Si la similitud coseno es menor que un umbral especificado, comienza un nuevo clúster.
  3. Verificación de longitud: Luego, el código verifica la longitud de cada clúster. Si un clúster es demasiado corto (menos de 60 caracteres) o demasiado largo (más de 3000 caracteres), se ajusta el umbral y el proceso se repite para ese clúster en particular hasta alcanzar una longitud aceptable.

Echemos un vistazo a algunos de los fragmentos de salida de este enfoque y comparemoslos con el Separador Langchain:

====   Fragmentos de muestra de 'Separador Langchain con parámetros personalizados':   ====### Fragmento 1: Brasil es el quinto país más grande del mundo por área y el séptimo más poblado. Su capital es Brasilia, y su ciudad más poblada es São Paulo. La federación está compuesta por la unión de los 26### Fragmento 2: estados y el Distrito Federal. Es el único país en las Américas que tiene el portugués como idioma oficial.[11][12] Es una de las naciones más multiculturales y étnicamente diversas, debido a más de un siglo de====   Fragmentos de muestra del 'Agrupamiento de Oraciones Adyacentes':   ====### Fragmento 1: Brasil es el quinto país más grande del mundo por área y el séptimo más poblado. Su capital es Brasilia, y su ciudad más poblada es São Paulo.### Fragmento 2: La federación está compuesta por la unión de los 26 estados y el Distrito Federal. Es el único país en las Américas que tiene el portugués como idioma oficial.[11][12]

Genial, ahora comparemos la distribución de las longitudes de los fragmentos de texto final_texts (del enfoque de agrupamiento de secuencias adyacentes) con las distribuciones del Separador de Texto de Caracteres de Langchain y el Tokenizador de Oraciones de NLTK. Para hacer esto, primero debemos calcular las longitudes de los fragmentos en final_texts:

final_texts_lengths = [len(chunk) for chunk in final_texts]

Ahora podemos graficar las distribuciones de los tres métodos:

Figura 3: Gráfico de distribución de las longitudes de los fragmentos resultantes de los diferentes métodos probados (Imagen del autor)

A partir de la Figura 6, podemos deducir que el separador de Langchain, utilizando su tamaño de fragmento predefinido, crea una distribución uniforme, lo que implica longitudes de fragmento consistentes.

Por otro lado, el Separador de Oraciones Spacy y el Tokenizador de Oraciones NLTK parecen preferir oraciones más pequeñas, aunque con muchos valores atípicos más grandes, lo que indica su dependencia de señales lingüísticas para determinar divisiones y potencialmente producir fragmentos de tamaño irregular.

Por último, el enfoque de Agrupamiento de Secuencias Adyacentes personalizado, que agrupa en función de la similitud semántica, exhibe una distribución más variada. Esto podría ser indicativo de un enfoque más sensible al contexto, manteniendo la coherencia del contenido dentro de los fragmentos al tiempo que permite una mayor flexibilidad en tamaño.

Evaluando el enfoque de Agrupamiento de Secuencias Adyacentes

El enfoque de Agrupamiento de Secuencias Adyacentes ofrece beneficios únicos:

  1. Coherencia contextual: Genera fragmentos temáticamente consistentes al considerar coherencia semántica y contextual.
  2. Flexibilidad: Equilibra la preservación del contexto y la eficiencia computacional, proporcionando tamaños de fragmento ajustables.
  3. Ajuste de umbral: Permite a los usuarios ajustar el proceso de fragmentación según sus necesidades, ajustando el umbral de similitud.
  4. Preservación de secuencia: Conserva el orden original de las oraciones en el texto, esencial para modelos de lenguaje secuenciales y tareas donde el orden del texto es importante.

Comparando Métodos de Fragmentación de Texto: Resumen de Insights

Separador de Texto de Caracteres de Langchain

Este método proporciona longitudes de fragmento consistentes, lo que da como resultado una distribución uniforme. Esto puede ser beneficioso cuando se necesita un tamaño estándar para el procesamiento o análisis posterior. El enfoque es menos sensible a la estructura lingüística específica del texto, centrándose más en producir fragmentos de una longitud de caracteres predefinida.

Tokenizador de oraciones NLTK y separador de oraciones Spacy

Estos enfoques muestran una preferencia por oraciones más pequeñas pero incluyen muchos valores atípicos más grandes. Si bien esto puede resultar en fragmentos lingüísticamente coherentes, también puede generar una alta variabilidad en el tamaño de los fragmentos.

Estos métodos pueden ofrecer buenos resultados que también pueden ser utilizados como entradas para tareas posteriores.

Agrupación de secuencia adyacente

Este método genera una distribución más variada, lo que indica su enfoque sensible al contexto. Al agrupar en función de la similitud semántica, garantiza que el contenido dentro de cada fragmento sea coherente al tiempo que permite flexibilidad en el tamaño de los fragmentos. Este método puede ser ventajoso cuando es importante preservar la continuidad semántica de los datos de texto.

Para una representación más visual y abstracta (o tonta), veamos la Figura 7 a continuación e intentemos descubrir qué tipo de “corte” de piña representaría mejor los enfoques discutidos:

Figura 7: Diferentes métodos de fragmentación de texto mostrados como cortes de piña (Imagen compilada por el autor. Imagen de piña de Canva)

Listándolos en orden:

  1. El corte número 1 representaría un enfoque basado en reglas, en el que simplemente puedes “pelar” el texto “basura” que deseas en función de filtros o expresiones regulares. Mucho trabajo para hacer toda la piña, ya que también retiene muchos valores atípicos con un tamaño de contexto mucho más grande.
  2. Langchain sería como el corte número 2. Piezas muy similares en tamaño pero que no contienen todo el contexto deseado (es un triángulo, por lo que también podría ser una sandía).
  3. El corte número 3 es definitivamente KMeans. Incluso puedes agrupar solo lo que tiene sentido para ti, la parte más jugosa, pero no obtendrás su núcleo. Sin él, los fragmentos pierden toda la estructura y el significado. Creo que también lleva mucho trabajo hacer eso… especialmente para piñas más grandes.
  4. Por último, el corte número 4 ilustra el método de Agrupación de Secuencias Adyacentes. El tamaño de los fragmentos puede variar, pero a menudo mantienen información contextual, similar a las piezas desiguales de piña que aún indican la estructura general de la fruta.

TL;DR: En este artículo, hemos comparado tres métodos de fragmentación de texto y sus beneficios únicos. Langchain ofrece tamaños de fragmento consistentes, pero la estructura lingüística pasa a un segundo plano. NLTK y Spacy proporcionan fragmentos lingüísticamente coherentes, pero el tamaño varía considerablemente. La Agrupación de Secuencia Adyacente agrupa en función de la similitud semántica, proporcionando coherencia de contenido con tamaños de fragmento flexibles. En última instancia, la elección óptima depende de sus necesidades específicas, incluida la coherencia lingüística, la uniformidad en el tamaño de los fragmentos y la potencia computacional disponible.

¡Gracias por leer!

  • ¡Sígueme en Linkedin!