Spacy primeros pasos y la Fundación de Isaac Asimov
Introducción
Spacy es una libreria libre y open source de python destinada a procesamiento de lenguaje natural, la cual se puede usar en conjunto con otras librerias como gensim, pytorch y tensorflow
Entre las operaciones que puedes realizar se encuentran:
- Tokenización
- Lematización
- NER (Named Entity Recognition)
- Wordvectors preentrenados
- Integración con Deep Learning
- POS (Part of Speech Tagging)
- Labelled Dependency Parsing
- Similaridad de Textos usando representaciones vectoriales
- Segmentacion de sentencias
- Visulizaciones para NER y Dependency parsing
- etc..
En la pagina oficial puedes ver todas las operaciones que puedes llevar a cabo y algunas ventajas con respecto a otras librerias como la velocidad (https://spacy.io/)
Aplicación
La libreria spacy y en general los procesamientos de lenguaje natural se pueden aplicar a analizar textos muy variados y a enriquecer análisis donde se tiene textos (noticias, tweets, encuestas, reseñas, etc), en este caso para mostrar algunas de las operaciones con Spacy, voy a analizar el libro La fundacion de Isaac Asimov libro que forma parte del Ciclo de Trantor y el cual lo tengo en formato epub
La elección de analizar un libro de Isaac Asimov es porque soy fan de varias de sus sagas, y ademas esta inspirado en un post que vi en un blog hace tiempo (ya no encontre el enlace) en el cual analizaban en una linea temporal la serie de game of thrones haciendo uso de tecnicas de ML, por lo cual este solo es un incio pero planeo aplicar mas tecnicas de procesamiento de lenguaje natural al analisis de los libros de Isaac Asimov , y también planeo realizar el análisis de series que me agradan haciendo uso de los subtitulos en formato srt que se puede encontrar en paginas como opensubtitles y subdivx etc…
Para analizar los capitulos del libro primero debemos ser capaces de obtener el texto del archivo epub, El formato epub internamente esta formado por archivos xhmtl, para poder diseccionarlo hare uso de la librería ebooklib, y ya teniendo el codigo xhtml obtendre el texto scrapenadolo con la librería Beautifulsoup
Importamos la librerías
import ebooklib
from ebooklib import epub
import spacy
nlp = spacy.load('es') # spanish
import os
from ebooklib.utils import debug
from bs4 import BeautifulSoup
Cambiamos el directorio de trabajo
os.chdir("/home/adrianrdzv/Descargas")
Leemos el archivo epub correspondiente al libro
book = epub.read_epub("isaac_asimov_fundacion.epub")
Vemos algunos metadatos de libro
print(book.EPUB_VERSION);print("\n")
#print(book.IDENTIFIER_ID);print("\n")
#print(book.items);print("\n")
print(book.title);print("\n")
print(book.version);print("\n")
print(book.get_metadata);print("\n")
None
Fundación
2.0
<bound method EpubBook.get_metadata of <ebooklib.epub.EpubBook object at 0x7f34842802b0»
Vemos la estructura de los documentos del epub
## Ver como esta el epub cuantos documentos xhtml lo forman
for x in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
debug(x.id)
'ciclo_trantor.html'
'x0001.xhtml'
'x0002.xhtml'
'x0003.xhtml'
'x0004.xhtml'
'x0005.xhtml'
'notas.html'
'autor.xhtml'
'cubierta.xhtml'
'dedicatoria.xhtml'
'info.xhtml'
'sinopsis.xhtml'
'titulo.xhtml'
'primera_parte.xhtml'
'segunda_parte.xhtml'
'tercera_parte.xhtml'
'cuarta_parte.xhtml'
'quinta_parte.xhtml'
Vemos la tabla de contenidos del libro
## Ver la tabla de contenidos TOC
for x in book.toc:
print(x.title)
Cubierta
Fundación
El Ciclo de Trántor
Los Psicohistoriadores
Los Enciclopedistas
Los Alcaldes
Los Comerciantes
Los Príncipes Comerciantes
Autor
Guardamos en una lista los xhmtls que analizaremos
## Guardar los documentos del epub en una lista
book_xhtmls = []
for x in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
book_xhtmls.append(x)
book_xhtmls[11].get_content()
b'<?xml version=\'1.0\' encoding=\'utf-8\'?>\n<!DOCTYPE html>\n<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" epub:prefix="z3998: http://www.daisy.org/z3998/2012/vocab/structure/#" lang="en" xml:lang="en">\n <head/>\n <body><div class="sinopsis"> \n <p class="salto10">El hombre se ha dispersado por los planetas de la galaxia. La capital del Imperio es Tr\xc3\xa1ntor, centro de todas las intrigas y s\xc3\xadmbolo de la corrupci\xc3\xb3n imperial. Un psicohistoriador, Hari Seldon, prev\xc3\xa9, gracias a su ciencia fundada en el estudio matem\xc3\xa1tico de los hechos hist\xc3\xb3ricos, el derrumbamiento del Imperio y el retorno a la barbarie por varios milenios. Seldon decide crear dos Fundaciones, situadas en cada extremo de la galaxia, a fin de reducir este periodo de barbarie a mil a\xc3\xb1os.</p> \n </div> \n</body>\n</html>\n'
## Las funciones get_content y get_body_content nos ayudan a extraer
book_xhtmls[0].get_content()[0:300]
b'<?xml version=\'1.0\' encoding=\'utf-8\'?>\n<!DOCTYPE html>\n<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" epub:prefix="z3998: http://www.daisy.org/z3998/2012/vocab/structure/#" lang="en" xml:lang="en">\n <head/>\n <body><h1>El Ciclo de Tr\xc3\xa1ntor</h1> \n \n <p '
## esta solo se trae el body de xhtml
book_xhtmls[0].get_body_content()[0:300]
b' \n <h1>El Ciclo de Tr\xc3\xa1ntor</h1> \n \n <p class="asangre">En 1966, en la 24 Convenci\xc3\xb3n Mundial de Ciencia Ficci\xc3\xb3n, celebrada en Cleveland, se otorg\xc3\xb3 el premio \xc2\xabHugo\xc2\xbb<a href="../Text/notas.html#a4" id="a0"><sup>[1]</sup></a> a la mejor \xc2\xabserie de novelas\xc2\xbb del g\xc3\xa9nero a la Trilog\xc3\xada '
El problema que tenemos aqui es que para analizar el texto lo ocuapamos como texto en formato crudo y todas esas tags xhtml hay que extirparlas, una manera rapida de extraer el texto es simular que escrapeamos ese html con BeautifulSoup4
soup = BeautifulSoup(book_xhtmls[0].get_body_content())
## ahora si podemos trabajar este texto
soup.get_text()[0:300]
'\nEl Ciclo de Trántor\nEn 1966, en la 24 Convención Mundial de Ciencia Ficción, celebrada en Cleveland, se otorgó el premio «Hugo»[1] a la mejor «serie de novelas» del género a la TrilogÃa de las Fundaciones de Isaac Asimov, de la que el presente tÃtulo, Fundación, constituye la primera parte. El cita'
Obtenemos un string con todo el texto del libro concatenado
## El libro como tal son los elementos del 1 al 5 de documentos dentro del epub
libro_texto =""
for i in range(1,6):
soup = BeautifulSoup(book_xhtmls[i].get_body_content())
text_temp = soup.get_text()
libro_texto = libro_texto+text_temp
#Tamanio del texto completo del libro (solo cuerpo)
len(libro_texto)
403112
Aquí comenzamos a realizar operaciones con Spacy (Tokenización, NER, POStagging, Dependency Parsing etc..)
#Analizar los primeros 5k caracteres
res_nlp = nlp(libro_texto[0:5000])
POS tagging
# Ver el POStagging de las primeras 20 palabras
i=0
for token in res_nlp:
if(i==20):
break
print(token.text, token.pos_, token.dep_)
i+=1
SPACE
1 NUM nummod
SPACE
HARI PROPN nsubj
SELDON PROPN ROOT
— PUNCT punct
… PUNCT punct
Nació VERB ccomp
el DET det
año NOUN obl
11988 NUM compound
de ADP case
la DET det
Era PROPN nmod
Galáctica PROPN flat
; PUNCT punct
falleció VERB advcl
en ADP case
12069 NOUN obl
. PUNCT punct
NER (Named Entity Recognition)
## ver las entidades que extrae Spacy
for ent in res_nlp.ents:
if(ent.label_=="PER"):
print(ent.text, ent.start_char, ent.end_char, ent.label_)
Gaal Dornick 872 884 PER
Seldon 917 923 PER
Gaal Dornick 1036 1048 PER
Trántor 1101 1108 PER
Gaal 1765 1769 PER
Hari Seldon 3574 3585 PER
Trántor 3596 3603 PER
Proyecto Seldon 3640 3655 PER
Gaal 3664 3668 PER
Trántor 3728 3735 PER
No 3753 3755 PER
Trántor 4401 4408 PER
Entre las entidades de tipo PER (“persona”) si capta nombres como Gaal Dornick y Hari Seldon, pero capta tambien lugares y otros erroneos
## ver las entidades que extrae Spacy
for ent in res_nlp.ents:
if(ent.label_=="LOC"):
print(ent.text, ent.start_char, ent.end_char, ent.label_)
2 4 LOC
Helicón 227 234 LOC
Arturo 246 252 LOC
1023 1025 LOC
Galaxia 1517 1524 LOC
Galaxia 1620 1627 LOC
Trántor 1687 1694 LOC
Fueron 1696 1702 LOC
Synnax 2027 2033 LOC
Galaxia 3011 3018 LOC
#vamos a correr sobre el libro entero NER POsTagging y las demas cosas
# que nos regresa Spacy
res_nlp = nlp(libro_texto)
## ver las entidades que extrae Spacy
i=0
for ent in res_nlp.ents:
if(i==20):
break
if(ent.label_=="LOC"):
print(ent.text, ent.start_char, ent.end_char, ent.label_)
i=i+1
2 4 LOC
Helicón 227 234 LOC
Arturo 246 252 LOC
1023 1025 LOC
Galaxia 1517 1524 LOC
Galaxia 1620 1627 LOC
Trántor 1687 1694 LOC
Fueron 1696 1702 LOC
Synnax 2027 2033 LOC
Galaxia 3011 3018 LOC
¡Aquello 7324 7332 LOC
Synnax 7426 7432 LOC
ParecÃa 7798 7805 LOC
Dornick 7837 7844 LOC
La 8305 8307 LOC
Llegó 8360 8365 LOC
Trató 8810 8815 LOC
Nómbreme 8923 8931 LOC
Brillaba 9213 9221 LOC
El billete 9338 9348 LOC
Entre los lugares igualmente capta Galaxia, planeta Trantor, planeta Synnax, planeta Hélicon
# Ver el POStagging de las primeras 20 palabras
i=0
for token in res_nlp:
if(i==20):
break
if token.pos_=="PROPN":
print(token.text, token.pos_, token.dep_)
i=i+1
HARI PROPN nsubj
SELDON PROPN ROOT
Era PROPN nmod
Galáctica PROPN flat
Era PROPN nmod
Fundacional PROPN flat
E. PROPN obj
F. PROPN flat
Nacido PROPN flat
Helicón PROPN nmod
Arturo PROPN nmod
Indudablemente PROPN flat
Seldon PROPN nsubj
Gaal PROPN obj
Dornick PROPN flat
Seldon PROPN obj
Enciclopedia PROPN appos
Galáctica[2 PROPN flat
Gaal PROPN nsubj
Dornick PROPN flat
Segmentación de Sentencias
## Sentence splitting (Obtener sentencias)
sents = list(res_nlp.sents)
sents[10]
Por aquel entonces, habÃa cerca de veinticinco millones de planetas habitados en la Galaxia, y absolutamente todos eran leales al imperio, con sede en Trántor.
sents[2]
Las anécdotas sobre su inteligencia son innumerables, y algunas contradictorias.
Visualizaciones de Spacy para NER y Dependency Parsing
from spacy import displacy
soup = BeautifulSoup(book_xhtmls[0].get_body_content())
res_nlp = nlp(soup.get_text())
displacy.render(res_nlp, style='ent', jupyter=True)
soup = BeautifulSoup(book_xhtmls[0].get_body_content())
res_nlp = nlp(soup.get_text()[0:500])
displacy.serve(res_nlp, style='dep')
[93m Serving on port 5000...[0m
Using the 'dep' visualizer
Shutting down server on port 5000.
El resultado grafico hay que verlo en el navegador 127.0.0.1:5000 Abajo esta la imagen de un pedazo del grafo
Referencias
- https://www.crummy.com/software/BeautifulSoup/bs4/doc/
- http://docs.sourcefabric.org/projects/ebooklib/en/latest/tutorial.html
- https://github.com/aerkalov/ebooklib/blob/master/samples/04_markdown_parse/epub2markdown.py
- https://pypi.org/project/EbookLib/
- https://stackoverflow.com/questions/3114786/python-library-to-extract-epub-information
- https://github.com/aerkalov/ebooklib
- https://github.com/ropensci/epubr