extrayendo n gramos de un gran texto

Por ejemplo tenemos el siguiente texto:

“Spark es un marco para escribir progtwigs rápidos y distribuidos. Spark resuelve problemas similares a los que hace Hadoop MapReduce, pero con un enfoque rápido en memoria y una API de estilo funcional limpio. …”

Necesito todas las secciones posibles de este texto, respectivamente, para una palabra por una palabra, luego dos por dos, tres por tres y cinco a cinco. Me gusta esto:

unos: [‘Chispa’, ‘es’, ‘a’, ‘marco’, ‘para’, ‘escribir,’ rápido ‘,’ distribuido ‘,’ progtwigs ‘, …]

twos: [‘Spark is’, ‘is a’, ‘a framework’, ‘framework for’, ‘for writing’ …]

threes: [‘Spark es un’, ‘es un framework’, ‘un framework para’, ‘framework para escribir’, ‘para escribir rápido’, …]

. . .

fives: [‘Spark es un marco para’, ‘es un marco para escribir’, ‘un marco para escribir rápido’, ‘marco para escribir rápidamente distribuido’, …]

Tenga en cuenta que el texto que se va a procesar es enorme (alrededor de 100 GB). Necesito la mejor solución para este proceso. Puede ser procesado multi hilo en paralelo.

No necesito la lista completa a la vez, puede ser streaming.

En primer lugar, asegúrese de tener líneas en su archivo y, sin preocupaciones, puede leerlo línea por línea (se explica aquí ):

 with open('my100GBfile.txt') as corpus: for line in corpus: sequence = preprocess(line) extract_n_grams(sequence) 

Supongamos que su cuerpo no necesita ningún tratamiento especial. Supongo que puedes encontrar un tratamiento adecuado para tu texto, solo quiero que se coloque en tokens deseables:

 def preprocess(string): # do what ever preprocessing that it needs to be done # eg convert to lowercase: string = string.lower() # return the sequence of tokens return string.split() 

No sé qué quieres hacer con n-grams. Supongamos que desea contarlos como un modelo de lenguaje que cabe en su memoria (por lo general lo hace, pero no estoy seguro acerca de 4 y 5 gramos). La forma más fácil es usar la biblioteca nltk :

 from nltk.util import ngrams lm = {n:dict() for n in range(1,6)} def extract_n_grams(sequence): for n in range(1,6): ngram = ngrams(sentence, n) # now you have an n-gram you can do what ever you want # yield ngram # you can count them for your language model? for item in ngram: lm[n][item] = lm[n].get(item, 0) + 1 

Si no necesita la lista completa a la vez, entonces la mejor opción debería ser utilizar iteradores. Por lo tanto, mi solución se ve así:

 import re text = "Spark is a framework for writing fast, distributed programs. Spark solves similar problems as Hadoop MapReduce does but with a fast in-memory approach and a clean functional style API." word_re = re.compile(r"\w+") words = [text[word.start():word.end()] for word in word_re.finditer(text)] ngrams = ((words[k] for k in xrange(j, j + i + 1)) for i in xrange(len(words)) for j in xrange(len(words) - i)) for ngram in ngrams: for word in ngram: print word, print 

Esto le da todos los ngrams necesarios en el orden deseado. Tenga en cuenta que los iteradores son inevitables incluso para los ngrams mismos, ya que su texto es tan grande como 500G, y la mayoría de sus “todas las secciones posibles” no cabrán en su memoria.

Tenga en cuenta también que en su caso deberá contar la longitud de su texto y obtener las palabras de él por separado, ya que no podrá mantenerlo en la memoria como en mi ejemplo de código.

Esto debería hacerlo idealmente. Puede personalizar los parámetros min_len y max_len para adaptarlos a sus necesidades. La aplicación de una función de clasificación también puede darle una buena idea de qué n-gtwigs se destacan de los demás.

 import nltk from nltk.util import * from nltk.collocations import * content = "Spark is a framework for writing fast, distributed programs. Spark solves similar problems as Hadoop MapReduce does but with a fast in-memory approach and a clean functional style API. ..." tokens = nltk.word_tokenize(content) bgs = everygrams(tokens, min_len=users_minimium, max_len=users_maximum) fdist_bg = nltk.FreqDist(bgs) for k,v in fdist_bg.items(): print k,v 

Y como menciona la ejecución en paralelo, puede consultar el siguiente fragmento de código utilizando Spark MLib y Python.

 from pyspark.ml.feature import NGram wordDataFrame = sqlContext.createDataFrame([ (0, ["Hi", "I", "heard", "about", "Spark"]), (1, ["I", "wish", "Java", "could", "use", "case", "classes"]), (2, ["Logistic", "regression", "models", "are", "neat"])], ["label","words"]) ngram = NGram(inputCol="words", outputCol="ngrams") ngramDataFrame = ngram.transform(wordDataFrame) for ngrams_label in ngramDataFrame.select("ngrams", "label").take(3): print(ngrams_label) 

El enlace a la solución y otras técnicas de Extracción de Características en Spark está aquí: Extracción de Características de Spark MLib

Espero eso ayude. Aclamaciones. 🙂

He escrito una biblioteca de C que hace esto: https://github.com/adriaan-pelzer/corpusToNgrams

Esencialmente, la manera más efectiva que se me ocurrió fue la siguiente:

  • Analiza a través del corpus, personaje por personaje.
  • Si encuentra un carácter de final de palabra (espacio, coma, punto, etc.), agregue la palabra a una matriz de 1 gramo.
  • Repita para n = 2 a maxN:
  • Si la longitud de la matriz de 1 gramo es mayor que n, concatene las últimas n palabras de la matriz de 1 gramo y agréguela a la matriz de n-gramo.

Lo anterior puede implementarse en una función recursiva, y requiere que solo analice el corpus una vez.

¿Has escrito algún código? Intente googlear N-gram generation o busque aquí: Cálculo de N gramos usando Python

Parece que quieres generar 1 gramos (una lista de las palabras), hasta 5 gramos.

Haría cada una de ellas en una pasada, luego pasaría a n + 1-gramo.