Commit 1d486c406f1d88238e01ec77ad61b83bc7d5d7e2
1 parent
ada59211
Exists in
master
and in
1 other branch
Altera alexp para um classe
Showing
3 changed files
with
169 additions
and
161 deletions
Show diff stats
... | ... | @@ -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 | 166 | \ No newline at end of file | ... | ... |
src/TraduzSentencas.py
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | |
7 | 7 | #LAViD - Laboratório de Aplicações de Vídeo Digital |
8 | 8 | |
9 | -import alexp | |
9 | +from ClassificaSentencas import * | |
10 | 10 | from AplicaRegras import * |
11 | 11 | from AplicaSinonimos import * |
12 | 12 | import logging |
... | ... | @@ -24,6 +24,7 @@ class TraduzSentencas(object): |
24 | 24 | def __init__(self): |
25 | 25 | '''Instancia os aplicadores de regras e sinônimos. |
26 | 26 | ''' |
27 | + self.classificador = ClassificaSentencas() | |
27 | 28 | self.aplic_regras = AplicaRegras() |
28 | 29 | self.aplic_sin = AplicaSinonimos() |
29 | 30 | self.check_level() |
... | ... | @@ -33,13 +34,13 @@ class TraduzSentencas(object): |
33 | 34 | ''' |
34 | 35 | try: |
35 | 36 | has_sintatica = True |
36 | - analise_sintatica = alexp.run(sentenca) | |
37 | + analise_sintatica = self.classificador.iniciar_classificacao(sentenca) | |
37 | 38 | except Exception as ex: |
38 | 39 | self.salvar_log(str(traceback.format_exc())) |
39 | 40 | analise_sintatica = None |
40 | 41 | has_sintatica = False |
41 | 42 | |
42 | - analise_morfologica = alexp.getAnaliseMorfologica() | |
43 | + analise_morfologica = self.classificador.obter_classificacao_morfologica() | |
43 | 44 | |
44 | 45 | if (isinstance(analise_sintatica,type(None))): |
45 | 46 | regras_aplicadas = self.aplic_regras.aplicar_regras_morfo(analise_morfologica) | ... | ... |
src/alexp.py
... | ... | @@ -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 | 0 | \ No newline at end of file |