Blog
Cómo preparar los datos de la revisión de la película para el análisis de los sentimientos
- Publicado por: Rafael Fernandez
- Categoría: Natural Language Processing
La preparación de los datos de texto es diferente para cada problema. La preparación comienza con pasos sencillos, como cargar los datos, pero rápidamente se vuelve difícil con tareas de limpieza que son muy específicas para los datos con los que está trabajando. En esta parte, descubrirás paso a paso cómo preparar los datos de texto de críticas de películas para el análisis de sentimientos. Después de completar esta sección del curso sabrás:
- Cómo cargar datos de texto y limpiarlos para eliminar la puntuación, y otras "no palabras".
- Cómo desarrollar un vocabulario, adaptarlo y guardarlo en un archivo.
- Cómo preparar críticas de películas usando limpieza, un vocabulario predefinido y guardarlas en archivos nuevos listos para modelar.
Resumen del tutorial: Análisis de los sentimientos a partir de los comentarios de una película
- Conjunto de datos de revisión de películas
- Cargar datos de texto
- Limpiar datos de texto
- Desarrollar el vocabulario
- Guardar datos preparados
Conjunto de datos de revisión de películas
Los datos de la revisión de la película son una colección de revisiones de la película recuperadas del Web site de imdb.com en el año 2000. Las revisiones fueron recopiladas y puestas a disposición como parte de su investigación sobre el procesamiento del lenguaje natural. Las revisiones se publicaron originalmente en 2002, pero en 2004 se publicó una versión actualizada y depurada, denominada v2.0. El conjunto de datos está compuesto por 1.000 críticas de películas positivas y 1.000 negativas extraídas de un archivo del grupo de noticias de rec.arts.movies.reviews alojado en IMDB. Los autores se refieren a este conjunto de datos como el conjunto de datos de polaridad.
Los datos se han depurado un poco, por ejemplo:
- El conjunto de datos se compone únicamente de comentarios en inglés.
- Todo el texto ha sido convertido a minúsculas.
- Hay un espacio en blanco alrededor de la puntuación como puntos, comas y corchetes.
- El texto se ha dividido en una frase por línea.
Los datos se han utilizado para algunas tareas relacionadas con el procesamiento del lenguaje natural. Para la clasificación, el rendimiento de los modelos clásicos (como las Support Vector Machines) en los datos está en el rango del 70% al 80% (por ejemplo, del 78% al 82%). Una preparación de datos más sofisticada puede arrojar resultados de hasta un 86% con una validación cruzada 10 veces mayor. Esto nos da una idea aproximada de los años 80, si buscáramos usar este conjunto de datos en experimentos con métodos modernos.
Puede descargar el conjunto de datos desde aquí:
Conjunto de datos de Polaridad de Revisión de Película (revise polarity.tar.gz, 3MB).
http://www.cs.cornell.edu/people/pabo/movie-review-data/review_polarity.tar.gz
Guarda este pequeño fragmento del libro en un archivo llamado txt_sentoken con dos subdirectorios que contienen el texto neg y pos para comentarios negativos y positivos. Las revisiones se almacenan una por archivo con una convención de nomenclatura de cv000 a cv999 para cada uno de los formatos neg y pos. A continuación, veamos cómo cargar los datos de texto.
Cargar datos de texto
En esta sección, veremos cómo cargar archivos de texto individuales y luego procesar los directorios de los archivos. Supondremos que los datos de revisión se descargan y están disponibles en el directorio de trabajo actual en la carpeta txt_sentoken. Podemos cargar un archivo de texto individual abriéndolo, leyendo en el texto ASCII, y cerrando el archivo. Esto es algo estándar de manejo de archivos. Por ejemplo, podemos cargar el primer archivo de revisión negativa cv000_29416.txt como sigue:
# cargar un archivo filename = 'txt_sentoken/neg/cv000_29416.txt' # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el archivo file.close()
Esto carga el documento como ASCII y conserva cualquier espacio en blanco, como nuevas líneas. Podemos convertir esto en una función llamada load_doc() que toma un nombre de archivo del documento para cargar y devuelve el texto.
# cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text
Tenemos dos directorios con 1.000 documentos cada uno. Podemos procesar cada directorio por turno obteniendo primero una lista de archivos en el directorio usando la función listdir(), luego cargando cada archivo por turno. Por ejemplo, podemos cargar cada documento en el directorio negativo usando la función load_doc() para hacer la carga real.
from os import listdir # cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text # especifique el directorio a cargar directory = 'txt_sentoken/neg' # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # cargar documento doc = load_doc(path) print('Cargando %s' % filename)
Al ejecutar este ejemplo, se imprime el nombre de archivo de cada revisión después de cargarla.
... Cargando cv502_10970.txt Cargando cv503_11196.txt Cargando cv504_29120.txt Cargando cv505_12926.txt Cargando cv506_17521.txt Cargando cv507_9509.txt Cargando cv508_17742.txt Cargando cv509_17354.txt Cargando cv510_24758.txt Cargando cv511_10360.txt Cargando cv512_17618.txt
También podemos convertir el procesamiento de los documentos en una función, y usarla como plantilla para desarrollar una función que limpie todos los documentos de una carpeta. Por ejemplo, a continuación definimos una función process_docs() para hacer lo mismo.
from os import listdir # carga doc en la memoria def load_doc(filename): # abrir archivo en modo solo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el archivo file.close() return text # cargar todos los docimentos en el directorio def process_docs(directory): # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # cargar documento doc = load_doc(path) print('Loaded %s' % filename) # especifique el directorio a cargar directory = 'txt_sentoken/neg' process_docs(directory)
Ahora que sabemos cómo cargar los datos de texto de la revisión de la película, veamos cómo limpiarla.
Limpiar datos de texto
En esta sección, veremos qué limpieza de datos podríamos querer hacer con los datos de críticas de la película. Asumiremos que usaremos un modelo de bolsa de palabras o tal vez una incrustación de palabras que no requiera demasiada preparación.
Dividir en tokens
Primero, carguemos un documento y veamos los tokens sin procesar divididos por espacio en blanco. Utilizaremos la función load_doc() desarrollada en la sección anterior. Podemos usar la función split() para dividir el documento cargado en tokens separados por espacios en blanco.
# cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text # cargar el documento filename = 'txt_sentoken/neg/cv000_29416.txt' text = load_doc(filename) # dividido en tokens por espacio en blanco tokens = text.split() print(tokens)
Al ejecutar el ejemplo se obtiene una larga lista de tokens sin procesar del documento.
... ['plot', ':', 'two', 'teen', 'couples', 'go', 'to', 'a', 'church', 'party', ',', 'drink', 'and', 'then', 'drive', '.', 'they', 'get', 'into', 'an', 'accident', '.', 'one', 'of', 'the', 'guys', 'dies', ',', 'but', 'his', 'girlfriend', 'continues', 'to', 'see', 'him', 'in', 'her', 'life', ',', 'and', 'has', 'nightmares', '.', "what's", 'the', 'deal', '?', 'watch', 'the', 'movie', 'and', '"', 'sorta', '"', 'find', 'out', '.', '.', '.', 'critique', ':', 'a', 'mind-fuck', 'movie', 'for', 'the', 'teen', 'generation', 'that', 'touches', 'on', 'a', 'very', 'cool', 'idea', ',', 'but', 'presents', 'it', 'in', 'a', 'very', 'bad', 'package', '.', 'which', 'is', 'what', 'makes', 'this', 'review', 'an', 'even', 'harder', 'one', 'to', 'write', ',', 'since', 'i', 'generally', 'applaud', 'films', 'which', 'attempt', 'to', 'break', 'the', 'mold', ',', 'mess', 'with', 'your', 'head', 'and', 'such', '(', 'lost', 'highway', '&', 'memento', ')', ',', 'but', 'there', 'are', 'good', 'and', 'bad', 'ways', 'of', 'making', 'all', 'types', 'of', 'films', ',', 'and', 'these', 'folks', 'just', "didn't", 'snag', 'this', 'one', 'correctly', '.', 'they', 'seem', 'to', 'have', 'taken', 'this', 'pretty', 'neat', 'concept', ',', 'but', 'executed', 'it', 'terribly', '.', 'so', 'what', 'are', 'the', 'problems', 'with', 'the', 'movie', '?', 'well', ',', 'its', 'main', 'problem', 'is', 'that', "it's", 'simply', 'too', 'jumbled', '.', 'it', 'starts', 'off', '"', 'normal', '"', 'but', 'then', 'downshifts', 'into', 'this', '"', 'fantasy', '"', 'world', 'in', 'which', 'you', ',', 'as', 'an', 'audience', 'member', ',', 'have', 'no', 'idea', "what's", 'going', 'on', '.', 'there', 'are', 'dreams', ',', 'there', 'are', 'characters', 'coming', 'back', 'from', 'the', 'dead', ',', 'there', 'are', 'others', 'who', 'look', 'like', 'the', 'dead', ',', 'there', 'are', 'strange', 'apparitions', ',', 'there', 'are', 'disappearances', ',', 'there', 'are', 'a', 'looooot', 'of', 'chase', 'scenes', ',', 'there', 'are', 'tons', 'of', 'weird', 'things', 'that', 'happen', ',', 'and', 'most', 'of', 'it', 'is', 'simply', 'not', 'explained', '.', 'now', 'i', 'personally', "don't", 'mind', 'trying', 'to', 'unravel', 'a', 'film', 'every', 'now', 'and', 'then', ',', 'but', 'when', 'all', 'it', 'does', 'is', 'give', 'me', 'the', 'same', 'clue', 'over', 'and', 'over', 'again', ',', 'i', 'get', 'kind', 'of', 'fed', 'up', 'after', 'a', 'while', ',', 'which', 'is', 'this', "film's", 'biggest', 'problem', '.', "it's", 'obviously', 'got', 'this', 'big', 'secret', 'to', 'hide', ',', 'but', 'it', 'seems', 'to', 'want', 'to', 'hide', 'it', 'completely', 'until', 'its', 'final', 'five', 'minutes', '.', 'and', 'do', 'they', 'make', 'things', 'entertaining', ',', 'thrilling', 'or', 'even', 'engaging', ',', 'in', 'the', 'meantime', '?', 'not', 'really', '.', 'the', 'sad', 'part', 'is', 'that', 'the', 'arrow', 'and', 'i', 'both', 'dig', 'on', 'flicks', 'like', 'this', ',', 'so', 'we', 'actually', 'figured', 'most', 'of', 'it', 'out', 'by', 'the', 'half-way', 'point', ',', 'so', 'all', 'of', 'the', 'strangeness', 'after', 'that', 'did', 'start', 'to', 'make', 'a', 'little', 'bit', 'of', 'sense', ',', 'but', 'it', 'still', "didn't", 'the', 'make', 'the', 'film', 'all', 'that', 'more', 'entertaining', '.', 'i', 'guess', 'the', 'bottom', 'line', 'with', 'movies', 'like', 'this', 'is', 'that', 'you', 'should', 'always', 'make', 'sure', 'that', 'the', 'audience', 'is', '"', 'into', 'it', '"', 'even', 'before', 'they', 'are', 'given', 'the', 'secret', 'password', 'to', 'enter', 'your', 'world', 'of', 'understanding', '.', 'i', 'mean', ',', 'showing', 'melissa', 'sagemiller', 'running', 'away', 'from', 'visions', 'for', 'about', '20', 'minutes', 'throughout', 'the', 'movie', 'is', 'just', 'plain', 'lazy', '!', '!', 'okay', ',', 'we', 'get', 'it', '.', '.', '.', 'there', 'are', 'people', 'chasing', 'her', 'and', 'we', "don't", 'know', 'who', 'they', 'are', '.', 'do', 'we', 'really', 'need', 'to', 'see', 'it', 'over', 'and', 'over', 'again', '?', 'how', 'about', 'giving', 'us', 'different', 'scenes', 'offering', 'further', 'insight', 'into', 'all', 'of', 'the', 'strangeness', 'going', 'down', 'in', 'the', 'movie', '?', 'apparently', ',', 'the', 'studio', 'took', 'this', 'film', 'away', 'from', 'its', 'director', 'and', 'chopped', 'it', 'up', 'themselves', ',', 'and', 'it', 'shows', '.', 'there', "might've", 'been', 'a', 'pretty', 'decent', 'teen', 'mind-fuck', 'movie', 'in', 'here', 'somewhere', ',', 'but', 'i', 'guess', '"', 'the', 'suits', '"', 'decided', 'that', 'turning', 'it', 'into', 'a', 'music', 'video', 'with', 'little', 'edge', ',', 'would', 'make', 'more', 'sense', '.', 'the', 'actors', 'are', 'pretty', 'good', 'for', 'the', 'most', 'part', ',', 'although', 'wes', 'bentley', 'just', 'seemed', 'to', 'be', 'playing', 'the', 'exact', 'same', 'character', 'that', 'he', 'did', 'in', 'american', 'beauty', ',', 'only', 'in', 'a', 'new', 'neighborhood', '.', 'but', 'my', 'biggest', 'kudos', 'go', 'out', 'to', 'sagemiller', ',', 'who', 'holds', 'her', 'own', 'throughout', 'the', 'entire', 'film', ',', 'and', 'actually', 'has', 'you', 'feeling', 'her', "character's", 'unraveling', '.', 'overall', ',', 'the', 'film', "doesn't", 'stick', 'because', 'it', "doesn't", 'entertain', ',', "it's", 'confusing', ',', 'it', 'rarely', 'excites', 'and', 'it', 'feels', 'pretty', 'redundant', 'for', 'most', 'of', 'its', 'runtime', ',', 'despite', 'a', 'pretty', 'cool', 'ending', 'and', 'explanation', 'to', 'all', 'of', 'the', 'craziness', 'that', 'came', 'before', 'it', '.', 'oh', ',', 'and', 'by', 'the', 'way', ',', 'this', 'is', 'not', 'a', 'horror', 'or', 'teen', 'slasher', 'flick', '.', '.', '.', "it's", 'just', 'packaged', 'to', 'look', 'that', 'way', 'because', 'someone', 'is', 'apparently', 'assuming', 'that', 'the', 'genre', 'is', 'still', 'hot', 'with', 'the', 'kids', '.', 'it', 'also', 'wrapped', 'production', 'two', 'years', 'ago', 'and', 'has', 'been', 'sitting', 'on', 'the', 'shelves', 'ever', 'since', '.', 'whatever', '.', '.', '.', 'skip', 'it', '!', "where's", 'joblo', 'coming', 'from', '?', 'a', 'nightmare', 'of', 'elm', 'street', '3', '(', '7/10', ')', '-', 'blair', 'witch', '2', '(', '7/10', ')', '-', 'the', 'crow', '(', '9/10', ')', '-', 'the', 'crow', ':', 'salvation', '(', '4/10', ')', '-', 'lost', 'highway', '(', '10/10', ')', '-', 'memento', '(', '10/10', ')', '-', 'the', 'others', '(', '9/10', ')', '-', 'stir', 'of', 'echoes', '(', '8/10', ')']
Sólo mirando las fichas en bruto puedes darte cuenta de un montón de ideas de cosas para probar, tales como:
- Eliminar la puntuación de las palabras (p. ej.,’what’s’).
- Eliminar tokens que son sólo signos de puntuación (por ejemplo, ‘-‘).
- Eliminar tokens que contengan números (por ejemplo, ’10/10′).
- Elimina los tokens que tienen un carácter (p. ej.,’a’).
- Elimine los tokens que no tengan mucho significado (por ejemplo,’y’).
Algunas ideas:
- Podemos filtrar la puntuación de los tokens usando expresiones regulares.
- Podemos eliminar tokens que son sólo signos de puntuación o que contienen números utilizando una comprobación isalpha() en cada token.
- Podemos remover palabras en inglés usando la lista cargada usando NLTK.
- Podemos filtrar las fichas cortas comprobando su longitud.
A continuación se muestra una versión actualizada de la limpieza de esta revisión.
from nltk.corpus import stopwords import string import re # cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text # cargar el documento filename = 'txt_sentoken/neg/cv000_29416.txt' text = load_doc(filename) # dividido en tokens por espacio en blanco tokens = text.split() # prepare a regex para el filtrado de caracteres re_punc = re.compile('[%s]' % re.escape(string.punctuation)) # eliminar la puntuación de cada palabra # tokens = [re_punc.sub('', w) for w in tokens] # eliminar los tokens restantes que no estén en orden alfabético tokens = [word for word in tokens if word.isalpha()] # filtrar las palabras de parada stop_words = set(stopwords.words('english')) tokens = [w for w in tokens if not w in stop_words] # filtrar las fichas cortas tokens = [word for word in tokens if len(word) > 1] print(tokens)
Al ejecutar el ejemplo se obtiene una lista de tokens mucho más limpios.
... 'explanation', 'craziness', 'came', 'oh', 'way', 'horror', 'teen', 'slasher', 'flick', 'packaged', 'look', 'way', 'someone', 'apparently', 'assuming', 'genre', 'still', 'hot', 'kids', 'also', 'wrapped', 'production', 'two', 'years', 'ago', 'sitting', 'shelves', 'ever', 'since', 'whatever', 'skip', 'wheres', 'joblo', 'coming', 'nightmare', 'elm', 'street', 'blair', 'witch', 'crow', 'crow', 'salvation', 'lost', 'highway', 'memento', 'others', 'stir', 'echoes']
Podemos poner esto en una función llamada clean_doc() y probarlo en otra revisión, esta vez una revisión positiva.
from nltk.corpus import stopwords import string import re # cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar documento file.close() return text # convertir a doc en tokens limpias def clean_doc(doc): # dividido en tokens por espacio en blanco tokens = doc.split() # prepare a regex para el filtrado de caracteres re_punc = re.compile('[%s]' % re.escape(string.punctuation)) # eliminar la puntuación de cada palabra tokens = [re_punc.sub('', w) for w in tokens] # eliminar los tokens restantes que no estén en orden alfabético tokens = [word for word in tokens if word.isalpha()] # filtrar las palabras de parada stop_words = set(stopwords.words('english')) tokens = [w for w in tokens if not w in stop_words] # filtrar los tokens cortos tokens = [word for word in tokens if len(word) > 1] return tokens # cargar el documento filename = 'txt_sentoken/pos/cv000_29590.txt' text = load_doc(filename) tokens = clean_doc(text) print(tokens)
Una vez más, el procedimiento de limpieza parece producir un buen conjunto de tokens, al menos como un primer corte.
... 'comic', 'oscar', 'winner', 'martin', 'childs', 'shakespeare', 'love', 'production', 'design', 'turns', 'original', 'prague', 'surroundings', 'one', 'creepy', 'place', 'even', 'acting', 'hell', 'solid', 'dreamy', 'depp', 'turning', 'typically', 'strong', 'performance', 'deftly', 'handling', 'british', 'accent', 'ians', 'holm', 'joe', 'goulds', 'secret', 'richardson', 'dalmatians', 'log', 'great', 'supporting', 'roles', 'big', 'surprise', 'graham', 'cringed', 'first', 'time', 'opened', 'mouth', 'imagining', 'attempt', 'irish', 'accent', 'actually', 'wasnt', 'half', 'bad', 'film', 'however', 'good', 'strong', 'violencegore', 'sexuality', 'language', 'drug', 'content']
Hay muchos más pasos de limpieza que podríamos tomar y los dejo a tu imaginación.
A continuación, veamos cómo podemos administrar un vocabulario preferido de tokens.
Desarrollar el vocabulario
Cuando se trabaja con modelos predictivos de texto, como un modelo de bolsa de palabras, hay una presión para reducir el tamaño del vocabulario. Cuanto mayor sea el vocabulario, más dispersa será la representación de cada palabra o documento. Una parte de la preparación del texto para el análisis de sentimientos consiste en definir y adaptar el vocabulario de las palabras apoyadas por el modelo. Podemos hacer esto cargando todos los documentos en el conjunto de datos y construyendo un conjunto de palabras. Podemos decidir apoyar todas estas palabras, o quizás descartar algunas. El vocabulario final elegido puede guardarse en un archivo para su uso posterior, como filtrar palabras en nuevos documentos en el futuro.
Podemos hacer un seguimiento del vocabulario en un Counter, que es un diccionario de palabras y su cuenta con algunas funciones de conveniencia adicionales. Necesitamos desarrollar una nueva función para procesar un documento y añadirlo al vocabulario. La función necesita cargar un documento llamando a la función load_doc() previamente desarrollada. Necesita limpiar el documento cargado usando la función clean_doc() desarrollada previamente, luego necesita agregar todos los tokens al Counter, y actualizar los conteos. Podemos hacer este último paso llamando a la función update() en el objeto contador. Abajo hay una función llamada add_doc_to_vocab() que toma como argumentos un nombre de archivo de documento y un contravocabulario.
# cargar doc y añadirlo al vocaulario def add_doc_to_vocab(filename, vocab): # cargar doc doc = load_doc(filename) # limpiar doc tokens = clean_doc(doc) # actualizar los conteos vocab.update(tokens)
Finalmente, podemos usar nuestra plantilla anterior para procesar todos los documentos en un directorio llamado process_docs() y actualizarla a add_doc_to_vocab().
# cargar todos los documentos en un directorio def process_docs(directory, vocab): # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # añadir doc al vocabulario add_doc_to_vocab(path, vocab)
Podemos juntar todo esto y desarrollar un vocabulario completo de todos los documentos en el conjunto de datos.
import string import re from os import listdir from collections import Counter from nltk.corpus import stopwords # cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text # convertir a un médico en fichas limpias def clean_doc(doc): # dividido en tokens por espacio en blanco tokens = doc.split() # prepare a regex para el filtrado de caracteres re_punc = re.compile('[%s]' % re.escape(string.punctuation)) # eliminar la puntuación de cada palabra tokens = [re_punc.sub('', w) for w in tokens] # eliminar los tokens restantes que no estén en orden alfabético tokens = [word for word in tokens if word.isalpha()] # filtrar las palabras de parada stop_words = set(stopwords.words('english')) tokens = [w for w in tokens if not w in stop_words] # filtrar las fichas cortas tokens = [word for word in tokens if len(word) > 1] return tokens # cargar doc y añadir al vocabulario def add_doc_to_vocab(filename, vocab): # cargar doc doc = load_doc(filename) # limpiar doc tokens = clean_doc(doc) # actualizar los conteos vocab.update(tokens) # cargar todos los documentos en un directorio def process_docs(directory, vocab): # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # añador doc al vocabulario add_doc_to_vocab(path, vocab) # definir vocabulario vocab = Counter() # añadir todos los documentos al vocabulario process_docs('txt_sentoken/neg', vocab) process_docs('txt_sentoken/pos', vocab) # imprimir el tamaño del vocabulario print(len(vocab)) # Imprimir las palabras principales en el vocabulario print(vocab.most_common(50))
Al ejecutar el ejemplo se crea un vocabulario con todos los documentos del conjunto de datos, incluidas las revisiones positivas y negativas. Podemos ver que hay un poco más de 46.000 palabras únicas a través de todas las revisiones y las 3 palabras superiores son film, one, y movie.
46557 [('film', 8860), ('one', 5521), ('movie', 5440), ('like', 3553), ('even', 2555), ('good', 2320), ('time', 2283), ('story', 2118), ('films', 2102), ('would', 2042), ('much', 2024), ('also', 1965), ('characters', 1947), ('get', 1921), ('character', 1906), ('two', 1825), ('first', 1768), ('see', 1730), ('well', 1694), ('way', 1668), ('make', 1590), ('really', 1563), ('little', 1491), ('life', 1472), ('plot', 1451), ('people', 1420), ('movies', 1416), ('could', 1395), ('bad', 1374), ('scene', 1373), ('never', 1364), ('best', 1301), ('new', 1277), ('many', 1268), ('doesnt', 1267), ('man', 1266), ('scenes', 1265), ('dont', 1210), ('know', 1207), ('hes', 1150), ('great', 1141), ('another', 1111), ('love', 1089), ('action', 1078), ('go', 1075), ('us', 1065), ('director', 1056), ('something', 1048), ('end', 1047), ('still', 1038)]
Tal vez las palabras menos comunes, las que aparecen una sola vez en todas las críticas , no son predictivas. Tal vez algunas de las palabras más comunes no son útiles también. Estas son buenas preguntas y realmente deben ser probadas con un modelo predictivo específico. Por lo general, las palabras que sólo aparecen una vez o unas pocas veces en 2.000 críticas probablemente no son predictivas, y se pueden eliminar del vocabulario, lo que reduce en gran medida los tokens que necesitamos modelar. Podemos hacer esto al pasar a través de las palabras y sus cuentas y sólo mantener a aquellos con una cuenta por encima de un umbral elegido. Aquí usaremos 5 ocurrencias.
# mantener tokens con > 5 ocurrencias min_occurrence = 5 tokens = [k for k,c in vocab.items() if c >= min_occurrence] print(len(tokens))
Esto reduce el vocabulario de 46.557 a 14.803 palabras, una gran caída. Quizás un mínimo de 5 ocurrencias es demasiado agresivo; puedes experimentar con diferentes valores. A continuación, podemos guardar el vocabulario de palabras elegido en un nuevo archivo. Me gusta guardar el vocabulario como ASCII con una palabra por línea. A continuación se define una función llamada save_list() para guardar una lista de ítems, en este caso, tokens a fichero, uno por línea.
def save_list(lines, filename): data = '\n'.join(lines) file = open(filename, 'w') file.write(data) file.close()
El ejemplo completo para definir y guardar el vocabulario se muestra a continuación.
import string import re from os import listdir from collections import Counter from nltk.corpus import stopwords # cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text # convertir el documento en fichas limpias def clean_doc(doc): # dividido en tokens por espacio en blanco tokens = doc.split() # prepare a regex para el filtrado de caracteres re_punc = re.compile('[%s]' % re.escape(string.punctuation)) # eliminar la puntuación de cada palabra tokens = [re_punc.sub('', w) for w in tokens] # eliminar los tokens restantes que no estén en orden alfabético tokens = [word for word in tokens if word.isalpha()] # filtrar las palabras de parada stop_words = set(stopwords.words('english')) tokens = [w for w in tokens if not w in stop_words] # filtrar los tokens cortos tokens = [word for word in tokens if len(word) > 1] return tokens # cargar doc y añadir al vocabulario def add_doc_to_vocab(filename, vocab): # cargar doc doc = load_doc(filename) # limpiar doc tokens = clean_doc(doc) # actualizar los contadores vocab.update(tokens) # cargar todos los documentos en un directorio def process_docs(directory, vocab): # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # añadir doc al vocabulario add_doc_to_vocab(path, vocab) # guardar lista en archivo def save_list(lines, filename): data = '\n'.join(lines) file = open(filename, 'w') file.write(data) file.close() # definir vocabulario vocab = Counter() # añadir todos los documentos al vocabulario process_docs('txt_sentoken/neg', vocab) process_docs('txt_sentoken/pos', vocab) # print the size of the vocab print(len(vocab)) # print the top words in the vocab print(vocab.most_common(50)) # mantener tokens con > 5 ocurrencias min_occurrence = 5 tokens = [k for k,c in vocab.items() if c >= min_occurrence] print(len(tokens)) # guardar tokens en un archivo de vocabulario save_list(tokens, 'vocab.txt')
Ejecutar este fragmento final después de crear el vocabulario, guardará las palabras elegidas en un archivo. Es una buena idea echar un vistazo, e incluso estudiar, su vocabulario elegido con el fin de obtener ideas para una mejor preparación de estos datos, o datos de texto en el futuro.
hasnt updating figuratively symphony civilians might fisherman hokum witch buffoons ...
A continuación, podemos utilizar el vocabulario para crear una versión preparada del conjunto de datos de la vista previa de la película.
Guardar datos preparados
Podemos utilizar la limpieza de datos y el vocabulario elegido para preparar cada crítica de película, y guardar las versiones preparadas de las críticas listas para el modelado. Esta es una buena práctica, ya que desvincula la preparación de datos del modelado, lo que le permite centrarse en el modelado y volver a la preparación de datos si tiene nuevas ideas. Podemos empezar cargando el vocabulario de vocab.txt.
# cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el archivo file.close() return text # vocabulario de carga vocab_filename = 'vocab.txt' vocab = load_doc(vocab_filename) vocab = vocab.split() vocab = set(vocab)
A continuación, podemos limpiar las críticas, utilizar el vocabulario cargado para filtrar los tokens no deseados, y guardar las críticas limpias en un nuevo archivo. Un enfoque podría ser guardar todas las críticas positivas en un archivo y todas las negativas en otro archivo, con los tokens filtrados separados por espacio en blanco para cada revisión en líneas separadas. Primero, podemos definir una función para procesar un documento, limpiarlo, filtrarlo y devolverlo como una sola línea que podría guardarse en un archivo. A continuación se define la función doc_to_line() para hacer precisamente eso, tomando un nombre de archivo y vocabulario (como un conjunto) como argumentos. Llama a la función load_doc() previamente definida para cargar el documento y clean_doc() para tokenizar el documento.
# doc de carga, línea de limpieza y retorno de fichas def doc_to_line(filename, vocab): # cargar el documento doc = load_doc(filename) # limpiar doc tokens = clean_doc(doc) # filtrar por vocabulario tokens = [w for w in tokens if w in vocab] return ' '.join(tokens)
A continuación, podemos definir una nueva versión de process_docs() para recorrer todas las revisiones de una carpeta y convertirlas en líneas llamando a doc_to_line() para cada documento. A continuación, se devuelve una lista de líneas.
# cargar todos los documentos en un directorio def process_docs(directory, vocab): lines = list() # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # cargar y limpiar el documentoline = doc_to_line(path, vocab) # añadir a la lista lines.append(line) return lines
Podemos entonces llamar a process_docs() tanto para los directorios de revisiones positivas como negativas, y luego llamar a save_list() de la sección anterior para guardar cada lista de revisiones procesadas en un archivo.
La lista completa de códigos se proporciona a continuación.
import string import re from os import listdir from nltk.corpus import stopwords # cargar doc en memoria def load_doc(filename): # abrir el archivo como de sólo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el documento file.close() return text # convertir a un médico en fichas limpias def clean_doc(doc): # dividido en tokens por espacio en blanco tokens = doc.split() # prepare a regex para el filtrado de caracteres re_punc = re.compile('[%s]' % re.escape(string.punctuation)) # eliminar la puntuación de cada palabra tokens = [re_punc.sub('', w) for w in tokens] # eliminar los tokens restantes que no estén en orden alfabético tokens = [word for word in tokens if word.isalpha()] # filtrar las palabras de parada stop_words = set(stopwords.words('english')) tokens = [w for w in tokens if not w in stop_words] # filtrar los tokens cortos tokens = [word for word in tokens if len(word) > 1] return tokens # guardar lista en archivo def save_list(lines, filename): data = '\n'.join(lines) file = open(filename, 'w') file.write(data) file.close() # doc de carga, línea de limpieza y retorno de fichas def doc_to_line(filename, vocab): # cargar doc doc = load_doc(filename) # limpiar doc tokens = clean_doc(doc) # filtrar por vocabulario tokens = [w for w in tokens if w in vocab] return ' '.join(tokens) # cargar todos los documentos en un directorio def process_docs(directory, vocab): lines = list() # revisar todos los archivos de la carpeta for filename in listdir(directory): # omitir archivos que no tienen la extensión correcta if not filename.endswith(".txt"): next # crear la ruta completa del archivo a abrir path = directory + '/' + filename # Cargar y limpiar doc line = doc_to_line(path, vocab) # añadir a la lista lines.append(line) return lines # cargar vocaulario vocab_filename = 'vocab.txt' vocab = load_doc(vocab_filename) vocab = vocab.split() vocab = set(vocab) # preparar críticas negativas negative_lines = process_docs('txt_sentoken/neg', vocab) save_list(negative_lines, 'negative.txt') # preparar críticas positivas positive_lines = process_docs('txt_sentoken/pos', vocab) save_list(positive_lines, 'positive.txt')
Al ejecutar el ejemplo se guardan dos nuevos archivos, negative.txt y positive.txt, que contienen las revisiones negativas y positivas preparadas respectivamente. Los datos están listos para su uso en una bolsa de palabras o incluso en un modelo de incrustación de palabras.
➡ ¿Quieres aprender mas de Procesamiento de Lenguaje Natural? Accede a nuestro curso:
[…] La preparación del conjunto de datos de críticas de películas se describió por primera vez en el post anterior. […]