Commit 1d486c406f1d88238e01ec77ad61b83bc7d5d7e2

Authored by Erickson Silva
1 parent ada59211
Exists in master and in 1 other branch devel

Altera alexp para um classe

src/ClassificaSentencas.py 0 → 100644
@@ -0,0 +1,165 @@ @@ -0,0 +1,165 @@
  1 +#! /usr/bin/env python2.6
  2 +# -*- coding: utf-8 -*-
  3 +
  4 +#---------------------------------
  5 +
  6 +# Editado:
  7 +
  8 +#Autor: Erickson Silva
  9 +#Email: <erickson.silva@lavid.ufpb.br> <ericksonsilva@live.com>
  10 +
  11 +#LAViD - Laboratório de Aplicações de Vídeo Digital
  12 +
  13 +#---------------------------------
  14 +
  15 +
  16 +# Donatus Brazilian Portuguese Parser
  17 +#
  18 +# Copyright (C) 2010-2013 Leonel F. de Alencar
  19 +#
  20 +# Author: Leonel F. de Alencar <leonel.de.alencar@ufc.br>
  21 +# Homepage: <http://www.leonel.profusehost.net/>
  22 +#
  23 +# Project's URL: <http://sourceforge.net/projects/donatus/>
  24 +# For license information, see LICENSE.TXT
  25 +#
  26 +# $Id: alexp.py $
  27 +
  28 +"""Este módulo contém funções que permitem utilizar o Aelius para etiquetar uma sentença, construindo entradas lexicais com base nas etiquetas atribuídas às palavras da sentença. Essas entradas lexicais são integradas em uma gramática CFG dada, que é transformada em um parser, utilizado para gerar uma árvore de estrutura sintagmática da sentença.
  29 +"""
  30 +import re,nltk, time, random
  31 +from os.path import expanduser
  32 +from os import environ, path
  33 +from Aelius.Extras import carrega
  34 +from Aelius import AnotaCorpus, Toqueniza
  35 +from unicodedata import normalize
  36 +
  37 +
  38 +class ClassificaSentencas(object):
  39 +
  40 + def __init__(self):
  41 + self.sentenca_anotada = ""
  42 + self.sleep_times = [0.1,0.2]
  43 +
  44 + def toqueniza(self, s):
  45 + """Decodifica string utilizando utf-8, retornando uma lista de tokens em unicode.
  46 + """
  47 + regex = re.compile('[%s]' % re.escape('“”'))
  48 + regex2 = re.compile('[%s]' % re.escape('«»'))
  49 + try:
  50 + decodificada = regex2.sub('',regex.sub('"',s.replace("–", "-").replace("—", "-"))).decode("utf-8")
  51 + except:
  52 + decodificada = s.decode("utf-8")
  53 + return Toqueniza.TOK_PORT.tokenize(decodificada)
  54 +
  55 + def obter_classificacao_morfologica(self):
  56 + return self.sentenca_anotada
  57 +
  58 + def etiqueta_sentenca(self, s):
  59 + """Aplica um dos etiquetadores do Aelius na etiquetagem da sentença dada como lista de tokens.
  60 + """
  61 + etiquetador = carrega("AeliusHunPos")
  62 + anotada = AnotaCorpus.anota_sentencas([s],etiquetador,"hunpos")[0]
  63 + while (anotada[0][1] is None):
  64 + time.sleep(random.choice(sleep_times))
  65 + anotada = AnotaCorpus.anota_sentencas([s],etiquetador,"hunpos")[0]
  66 + regex = re.compile('[%s]' % re.escape('!"#&\'()*+,-./:;<=>?@[\\]^_`{|}~'))
  67 + tag_punctuation = [".",",","QT","("]
  68 + anotada_corrigida = []
  69 + for x in anotada:
  70 + if x[1] not in tag_punctuation:
  71 + if x[1] == "NUM":
  72 + try:
  73 + float(x[0].replace(',', '.'))
  74 + anotada_corrigida.append(x)
  75 + continue
  76 + except:
  77 + pass
  78 +
  79 + tupla = [regex.sub('',x[0]).lower(),x[1]]
  80 + if tupla[0] != "": anotada_corrigida.append(tupla)
  81 + else:
  82 + if x[0] == ".":
  83 + anotada_corrigida.append(["[ponto]".decode("utf-8"),"SPT"])
  84 + elif x[0] == "?":
  85 + anotada_corrigida.append(["[interrogacao]".decode("utf-8"),"SPT"])
  86 + elif x[0] == "!":
  87 + anotada_corrigida.append(["[exclamacao]".decode("utf-8"),"SPT"])
  88 + return anotada_corrigida
  89 +
  90 + def gera_entradas_lexicais(self, lista):
  91 + """Gera entradas lexicais no formato CFG do NLTK a partir de lista de pares constituídos de tokens e suas etiquetas.
  92 + """
  93 + entradas=[]
  94 + for e in lista:
  95 + # é necessário substituir símbolos como "-" e "+" do CHPTB
  96 + # que não são aceitos pelo NLTK como símbolos não terminais
  97 + c=re.sub(r"[-+]","_",e[1])
  98 + c=re.sub(r"\$","_S",c)
  99 + entradas.append("%s -> '%s'" % (c, self.remove_acento(e[0])))
  100 + return entradas
  101 +
  102 + def corrige_anotacao(self, lista):
  103 + """Esta função deverá corrigir alguns dos erros de anotação mais comuns do Aelius. No momento, apenas é corrigida VB-AN depois de TR.
  104 + """
  105 + i=1
  106 + while i < len(lista):
  107 + if lista[i][1] == "VB-AN" and lista[i-1][1].startswith("TR"):
  108 + lista[i]=(lista[i][0],"VB-PP")
  109 + i+=1
  110 +
  111 + def encontra_arquivo(self):
  112 + """Encontra arquivo na pasta vlibras-translate.
  113 + """
  114 + if "TRANSLATE_DATA" in environ:
  115 + return path.join(environ.get("TRANSLATE_DATA"), "cfg.syn.nltk")
  116 + return expanduser("~") + "/vlibras-translate/data/cfg.syn.nltk"
  117 +
  118 + def extrai_sintaxe(self):
  119 + """Extrai gramática armazenada em arquivo cujo caminho é definido relativamente ao diretório nltk_data.
  120 + """
  121 + arquivo = self.encontra_arquivo()
  122 + if arquivo:
  123 + f=open(arquivo,"rU")
  124 + sintaxe=f.read()
  125 + f.close()
  126 + return sintaxe
  127 + else:
  128 + print "Arquivo %s não encontrado em nenhum dos diretórios de dados do NLTK:\n%s" % (caminho,"\n".join(nltk.data.path))
  129 +
  130 + def analisa_sentenca(self, sentenca):
  131 + """Retorna lista de árvores de estrutura sintagmática para a sentença dada sob a forma de uma lista de tokens, com base na gramática CFG cujo caminho é especificado como segundo argumento da função. Esse caminho é relativo à pasta nltk_data da instalação local do NLTK. A partir da etiquetagem morfossintática da sentença são geradas entradas lexicais que passam a integrar a gramática CFG. O caminho da gramática e o parser gerado são armazenados como tupla na variável ANALISADORES.
  132 + """
  133 + parser = self.constroi_analisador(sentenca)
  134 + codificada=[]
  135 + for t in self.sentenca_anotada:
  136 + if t[1] != "SPT":
  137 + codificada.append(self.remove_acento(t[0]).encode("utf-8"))
  138 + trees=parser.parse_one(codificada)
  139 + return trees
  140 +
  141 + def constroi_analisador(self, s):
  142 + """Constrói analisador a partir de uma única sentença não anotada, dada como lista de tokens, e uma lista de regras sintáticas no formato CFG, armazenadas em arquivo. Esta função tem um bug, causado pela maneira como o Aelius etiqueta sentenças usando o módulo ProcessaNomesProprios: quando a sentença se inicia por paravra com inicial minúscula, essa palavra não é incorporada ao léxico, mas a versão com inicial maiúscula.
  143 + """
  144 + self.sentenca_anotada = self.etiqueta_sentenca(s)
  145 + self.corrige_anotacao(self.sentenca_anotada)
  146 + entradas = self.gera_entradas_lexicais(self.sentenca_anotada)
  147 + lexico="\n".join(entradas)
  148 + gramatica="%s\n%s" % (self.extrai_sintaxe().strip(),lexico)
  149 + cfg=nltk.CFG.fromstring(gramatica)
  150 + return nltk.ChartParser(cfg)
  151 +
  152 + def remove_acento(self, texto):
  153 + try:
  154 + return normalize('NFKD', texto.encode('utf-8').decode('utf-8')).encode('ASCII', 'ignore')
  155 + except:
  156 + return normalize('NFKD', texto.encode('iso-8859-1').decode('iso-8859-1')).encode('ASCII','ignore')
  157 +
  158 + def exibe_arvores(self, arvores):
  159 + """Função 'wrapper' para a função de exibição de árvores do NLTK"""
  160 + nltk.draw.draw_trees(*arvores)
  161 +
  162 + def iniciar_classificacao(self, sentenca):
  163 + tokens = self.toqueniza(sentenca)
  164 + tree = self.analisa_sentenca(tokens)
  165 + return tree
0 \ No newline at end of file 166 \ No newline at end of file
src/TraduzSentencas.py
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 6
7 #LAViD - Laboratório de Aplicações de Vídeo Digital 7 #LAViD - Laboratório de Aplicações de Vídeo Digital
8 8
9 -import alexp 9 +from ClassificaSentencas import *
10 from AplicaRegras import * 10 from AplicaRegras import *
11 from AplicaSinonimos import * 11 from AplicaSinonimos import *
12 import logging 12 import logging
@@ -24,6 +24,7 @@ class TraduzSentencas(object): @@ -24,6 +24,7 @@ class TraduzSentencas(object):
24 def __init__(self): 24 def __init__(self):
25 '''Instancia os aplicadores de regras e sinônimos. 25 '''Instancia os aplicadores de regras e sinônimos.
26 ''' 26 '''
  27 + self.classificador = ClassificaSentencas()
27 self.aplic_regras = AplicaRegras() 28 self.aplic_regras = AplicaRegras()
28 self.aplic_sin = AplicaSinonimos() 29 self.aplic_sin = AplicaSinonimos()
29 self.check_level() 30 self.check_level()
@@ -33,13 +34,13 @@ class TraduzSentencas(object): @@ -33,13 +34,13 @@ class TraduzSentencas(object):
33 ''' 34 '''
34 try: 35 try:
35 has_sintatica = True 36 has_sintatica = True
36 - analise_sintatica = alexp.run(sentenca) 37 + analise_sintatica = self.classificador.iniciar_classificacao(sentenca)
37 except Exception as ex: 38 except Exception as ex:
38 self.salvar_log(str(traceback.format_exc())) 39 self.salvar_log(str(traceback.format_exc()))
39 analise_sintatica = None 40 analise_sintatica = None
40 has_sintatica = False 41 has_sintatica = False
41 42
42 - analise_morfologica = alexp.getAnaliseMorfologica() 43 + analise_morfologica = self.classificador.obter_classificacao_morfologica()
43 44
44 if (isinstance(analise_sintatica,type(None))): 45 if (isinstance(analise_sintatica,type(None))):
45 regras_aplicadas = self.aplic_regras.aplicar_regras_morfo(analise_morfologica) 46 regras_aplicadas = self.aplic_regras.aplicar_regras_morfo(analise_morfologica)
src/alexp.py
@@ -1,158 +0,0 @@ @@ -1,158 +0,0 @@
1 -#! /usr/bin/env python2.6  
2 -# -*- coding: utf-8 -*-  
3 -  
4 -#---------------------------------  
5 -  
6 -# Editado:  
7 -  
8 -#Autor: Erickson Silva  
9 -#Email: <erickson.silva@lavid.ufpb.br> <ericksonsilva@live.com>  
10 -  
11 -#LAViD - Laboratório de Aplicações de Vídeo Digital  
12 -  
13 -#---------------------------------  
14 -  
15 -  
16 -# Donatus Brazilian Portuguese Parser  
17 -#  
18 -# Copyright (C) 2010-2013 Leonel F. de Alencar  
19 -#  
20 -# Author: Leonel F. de Alencar <leonel.de.alencar@ufc.br>  
21 -# Homepage: <http://www.leonel.profusehost.net/>  
22 -#  
23 -# Project's URL: <http://sourceforge.net/projects/donatus/>  
24 -# For license information, see LICENSE.TXT  
25 -#  
26 -# $Id: alexp.py $  
27 -  
28 -"""Este módulo contém funções que permitem utilizar o Aelius para etiquetar uma sentença, construindo entradas lexicais com base nas etiquetas atribuídas às palavras da sentença. Essas entradas lexicais são integradas em uma gramática CFG dada, que é transformada em um parser, utilizado para gerar uma árvore de estrutura sintagmática da sentença.  
29 -"""  
30 -import re,nltk, time, random  
31 -from os.path import expanduser  
32 -from os import environ, path  
33 -from Aelius.Extras import carrega  
34 -from Aelius import AnotaCorpus, Toqueniza  
35 -from unicodedata import normalize  
36 -  
37 -sentenca_anotada=""  
38 -sleep_times=[0.1,0.2]  
39 -  
40 -def toqueniza(s):  
41 - """Decodifica string utilizando utf-8, retornando uma lista de tokens em unicode.  
42 - """  
43 - regex = re.compile('[%s]' % re.escape('“”'))  
44 - decodificada=regex.sub('"',s.replace("–", "-").replace("—", "-")).decode("utf-8")  
45 - return Toqueniza.TOK_PORT.tokenize(decodificada)  
46 -  
47 -def getAnaliseMorfologica():  
48 - return sentenca_anotada  
49 -  
50 -def etiquetaSentenca(s):  
51 - """Aplica um dos etiquetadores do Aelius na etiquetagem da sentença dada como lista de tokens.  
52 - """  
53 - etiquetador = carrega("AeliusHunPos")  
54 - anotada = AnotaCorpus.anota_sentencas([s],etiquetador,"hunpos")[0]  
55 - while (anotada[0][1] is None):  
56 - time.sleep(random.choice(sleep_times))  
57 - anotada = AnotaCorpus.anota_sentencas([s],etiquetador,"hunpos")[0]  
58 - regex = re.compile('[%s]' % re.escape('!"#&\'()*+,-./:;<=>?@[\\]^_`{|}~'))  
59 - tag_punctuation = [".",",","QT","("]  
60 - anotada_corrigida = []  
61 - for x in anotada:  
62 - if x[1] not in tag_punctuation:  
63 - if x[1] == "NUM":  
64 - try:  
65 - float(x[0].replace(',', '.'))  
66 - anotada_corrigida.append(x)  
67 - continue  
68 - except:  
69 - pass  
70 -  
71 - tupla = [regex.sub('',x[0]).lower(),x[1]]  
72 - if tupla[0] != "": anotada_corrigida.append(tupla)  
73 - else:  
74 - if x[0] == ".":  
75 - anotada_corrigida.append(["[ponto]".decode("utf-8"),"SPT"])  
76 - elif x[0] == "?":  
77 - anotada_corrigida.append(["[interrogacao]".decode("utf-8"),"SPT"])  
78 - elif x[0] == "!":  
79 - anotada_corrigida.append(["[exclamacao]".decode("utf-8"),"SPT"])  
80 - return anotada_corrigida  
81 -  
82 -def geraEntradasLexicais(lista):  
83 - """Gera entradas lexicais no formato CFG do NLTK a partir de lista de pares constituídos de tokens e suas etiquetas.  
84 - """  
85 - entradas=[]  
86 - for e in lista:  
87 - # é necessário substituir símbolos como "-" e "+" do CHPTB  
88 - # que não são aceitos pelo NLTK como símbolos não terminais  
89 - c=re.sub(r"[-+]","_",e[1])  
90 - c=re.sub(r"\$","_S",c)  
91 - entradas.append("%s -> '%s'" % (c, removeAcento(e[0])))  
92 - return entradas  
93 -  
94 -def corrigeAnotacao(lista):  
95 - """Esta função deverá corrigir alguns dos erros de anotação mais comuns do Aelius. No momento, apenas é corrigida VB-AN depois de TR.  
96 - """  
97 - i=1  
98 - while i < len(lista):  
99 - if lista[i][1] == "VB-AN" and lista[i-1][1].startswith("TR"):  
100 - lista[i]=(lista[i][0],"VB-PP")  
101 - i+=1  
102 -  
103 -def encontraArquivo():  
104 - """Encontra arquivo na pasta vlibras-translate.  
105 - """  
106 - if "TRANSLATE_DATA" in environ:  
107 - return path.join(environ.get("TRANSLATE_DATA"), "cfg.syn.nltk")  
108 - return expanduser("~") + "/vlibras-translate/data/cfg.syn.nltk"  
109 -  
110 -def extraiSintaxe():  
111 - """Extrai gramática armazenada em arquivo cujo caminho é definido relativamente ao diretório nltk_data.  
112 - """  
113 - arquivo=encontraArquivo()  
114 - if arquivo:  
115 - f=open(arquivo,"rU")  
116 - sintaxe=f.read()  
117 - f.close()  
118 - return sintaxe  
119 - else:  
120 - print "Arquivo %s não encontrado em nenhum dos diretórios de dados do NLTK:\n%s" % (caminho,"\n".join(nltk.data.path))  
121 -  
122 -def analisaSentenca(sentenca):  
123 - """Retorna lista de árvores de estrutura sintagmática para a sentença dada sob a forma de uma lista de tokens, com base na gramática CFG cujo caminho é especificado como segundo argumento da função. Esse caminho é relativo à pasta nltk_data da instalação local do NLTK. A partir da etiquetagem morfossintática da sentença são geradas entradas lexicais que passam a integrar a gramática CFG. O caminho da gramática e o parser gerado são armazenados como tupla na variável ANALISADORES.  
124 - """  
125 - parser=constroiAnalisador(sentenca)  
126 - codificada=[]  
127 - for t in sentenca_anotada:  
128 - if t[1] != "SPT":  
129 - codificada.append(removeAcento(t[0]).encode("utf-8"))  
130 - trees=parser.parse_one(codificada)  
131 - return trees  
132 -  
133 -def constroiAnalisador(s):  
134 - """Constrói analisador a partir de uma única sentença não anotada, dada como lista de tokens, e uma lista de regras sintáticas no formato CFG, armazenadas em arquivo. Esta função tem um bug, causado pela maneira como o Aelius etiqueta sentenças usando o módulo ProcessaNomesProprios: quando a sentença se inicia por paravra com inicial minúscula, essa palavra não é incorporada ao léxico, mas a versão com inicial maiúscula.  
135 - """  
136 - global sentenca_anotada  
137 - sentenca_anotada=etiquetaSentenca(s)  
138 - corrigeAnotacao(sentenca_anotada)  
139 - entradas=geraEntradasLexicais(sentenca_anotada)  
140 - lexico="\n".join(entradas)  
141 - gramatica="%s\n%s" % (extraiSintaxe().strip(),lexico)  
142 - cfg=nltk.CFG.fromstring(gramatica)  
143 - return nltk.ChartParser(cfg)  
144 -  
145 -def removeAcento(texto):  
146 - try:  
147 - return normalize('NFKD', texto.encode('utf-8').decode('utf-8')).encode('ASCII', 'ignore')  
148 - except:  
149 - return normalize('NFKD', texto.encode('iso-8859-1').decode('iso-8859-1')).encode('ASCII','ignore')  
150 -  
151 -def exibeArvores(arvores):  
152 - """Função 'wrapper' para a função de exibição de árvores do NLTK"""  
153 - nltk.draw.draw_trees(*arvores)  
154 -  
155 -def run(sentenca):  
156 - tokens=toqueniza(sentenca)  
157 - tree=analisaSentenca(tokens)  
158 - return tree  
159 \ No newline at end of file 0 \ No newline at end of file