LR-POR / cl-conllu

tool for working with conllu files in CL
Apache License 2.0
13 stars 5 forks source link

Função projective #85

Closed Cristiananc closed 4 years ago

Cristiananc commented 4 years ago

No arquivo projective.lisp temos uma "tradução" para lisp da função validate_projective_punctuation feita em python que está no repositório tools do UD.

https://github.com/UniversalDependencies/tools/blob/master/validate.py

A ideia é usar a função para verificar se a não projetividade de uma sentença é causada por uma pontuação. O problema é que mesmo para as sentenças que foram dadas válidas pelo código em python.

Os reports podem ser encontrados aqui:

https://github.com/UniversalDependencies/UD_Portuguese-Bosque/tree/workbench/reports/validation

a função em lisp retorna que há não projetividade causada pela pontuação.

arademaker commented 4 years ago

Em 4afaa405891a29c9430bb479ec8dff0fe10ac7b7 temos o predicado por token e por sentença para indicar projetividade. Agora precisamos adaptar e/ou construir alguma função que use eles para validar os casos de erro para UD, isto é, quanto um token de punct:

Cristiananc commented 4 years ago

Dado um arco não-projetivo a podemos observar as palavras que estão entre o token e seu head nesse dado arco. Se houver alguma pontuação, observamos se o head da pontuação está compreendido no intervalo dos ids do arco a, senão podemos concluir que o punct é uma causa da não-projetividade nesse arco. Há um cruzamento entre o arco da pontuação e o arco observado, ou explicando de outra forma eu não consigo chegar ao punct saindo do head do token (arco a). Aqui ao invés de olhar pontuação por pontuação, como é o caso do algoritmo em python, eu só verifico para pontuações que possam de fato causar algum problema, e também podemos reportar se há uma projetividade que é causada somente pela própria sintaxe da sentença. Com isso não precisamos de funções para excluir ancestrais entre outras funções auxiliares que eram necessárias antes.

Nesse segundo caso ele retorna quais arcos se tornam não projetivos por causa dessa pontuação específica.

Exemplo: No arquivo CP0946.conllu eu tenho o seguinte erro: ​[Line 84 Sent CP946-4 Node 11]: [L3 Syntax punct-causes-nonproj] Punctuation must not cause non-projectivity of nodes [13] Syntax errors: 1 FAILED with 1 errors

Esse é um erro do tipo do segundo item acima. Na sentença 4 eu tenho todos os arcos projetivos, exceto um que é o referente ao token 13. A causa da não-projetividade é dada por uma pontuação cujo arco cruza o arco do token 13. Esse erro também é importante porque se não fosse observado estariamos trabalhando com uma sentença não projetiva que pode ser perfeitamente projetiva corrigindo essa relação do token 11.

arademaker commented 4 years ago

Preciso de uma explicação mais objetiva de como iremos retornar a mensagem de erro mais próxima da mensagem do validate.py. Nas mensagens de erro do python, ambas apresentam uma lista de nós como argumento, exemplo:

[Line 21 Sent CP218-1 Node 15]: [L3 Syntax punct-causes-nonproj] Punctuation must not cause non-projectivity of nodes [16]

[Line 48 Sent CP175-2 Node 27]: [L3 Syntax punct-causes-nonproj] Punctuation must not cause non-projectivity of nodes [28, 29]

[Line 117 Sent CP218-3 Node 11]: [L3 Syntax punct-is-nonproj] Punctuation must not be attached non-projectively over nodes [8, 9]

No primeiro caso, a lista é de apenas 1 nó. No segundo e terceiro casos a lista é de dois nós. Como poderemos obter estas listas?

Cristiananc commented 4 years ago

[Line 21 Sent CP218-1 Node 15]: [L3 Syntax punct-causes-nonproj] Punctuation must not cause non-projectivity of nodes [16]

No nosso caso ao verificar o nó 16 vamos encontrar que o nó 15 causa uma não projetividade nele. A mensagem poderia ser dada como: [Linha .. Sent .. Nó 16] Há uma não-projetividade causada pelo nó 15.

O nó 27 é uma pontuação que cruza com o nó 28 e 29, por isso uma lista com dois items. [Line 48 Sent CP175-2 Node 27]: [L3 Syntax punct-causes-nonproj] Punctuation must not cause non-projectivity of nodes [28, 29] Aqui seriam dois erros, [Linha .. Sent .. Nó 28] Há uma não-projetividade causada pelo nó 27. [Linha .. Sent .. Nó 29] Há uma não-projetividade causada pelo nó 27.

Aqui o erro na verdade é a diferença entre o get-projection e a lista de nós que estão entre o head do arco e o token. Pode ser uma lista com uma quantidade qualquer de nós. [Line 117 Sent CP218-3 Node 11]: [L3 Syntax punct-is-nonproj] Punctuation must not be attached non-projectively over nodes [8, 9]

arademaker commented 4 years ago

hum... tentando pensar um pouco, mudei a função is-token-projective para ao invés de retornar apenas t (true) ou nil (false), retornar uma lista de nós. A condição de ser projetivo para um token era todos os tokens entre o token-id e seu token-head serem parte da projeção do token projeção, isto equivale à (set-difference arange prj :test #'equal) porque se arange ⊆ prj então arange - prj é nulo, certo? Eu agora retorno o resultado desta diferença.

CL-CONLLU> (let ((se (car (read-conllu "/Users/ar/work/ud-bosque/documents/CP0218.conllu"))))
         (mapcar (lambda (tk) (multiple-value-list (is-token-projective tk se))) 
             (sentence-tokens se)))
((T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL)
 (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (NIL (15)) (T NIL) (T NIL)
 (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL)
 (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL)
 (T NIL) (NIL (34)) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL) (T NIL)
 (T NIL) (T NIL) (T NIL) (T NIL) (T NIL))
arademaker commented 4 years ago

Mas como isso pode ajudar na sua lógica acima @Cristiananc ?

CL-CONLLU> (mapcar #'is-sentence-projective 
           (read-conllu "/Users/ar/work/ud-bosque/documents/CP0218.conllu"))
(NIL T NIL NIL)
arademaker commented 4 years ago

Temos agora

CL-CONLLU> (validate-punct (car (read-conllu "/Users/ar/work/ud-bosque/documents/CF0883.conllu")))
((#<TOKEN "," PUNCT #22-punct-1> PUNCT-CAUSES-NONPROJ-OF
  (#<TOKEN ")" PUNCT #24-punct-12> #<TOKEN "respectivamente" ADV #23-advmod-4>))
 (#<TOKEN "(" PUNCT #11-punct-12> PUNCT-CAUSES-NONPROJ-OF
  (#<TOKEN "," PUNCT #22-punct-1>))
 (#<TOKEN ")" PUNCT #24-punct-12> PUNCT-IS-NONPROJ-OVER
  (#<TOKEN "respectivamente" ADV #23-advmod-4> #<TOKEN "," PUNCT #22-punct-1>))
 (#<TOKEN "," PUNCT #22-punct-1> PUNCT-IS-NONPROJ-OVER
  (#<TOKEN "EUA" PROPN #21-conj-18> #<TOKEN "e" CCONJ #20-cc-21>
   #<TOKEN "Unido" PROPN #19-flat:name-18> #<TOKEN "Reino" PROPN #18-nmod-12>
   #<TOKEN "o" DET #17-det-18> #<TOKEN "de" ADP #16-case-18>
   #<TOKEN "exterior" NOUN #15-nmod-12> #<TOKEN "o" DET #14-det-15>
   #<TOKEN "de" ADP #13-case-15> #<TOKEN "ex-ministros" NOUN #12-appos-4>
   #<TOKEN "(" PUNCT #11-punct-12> #<TOKEN "Kissinger" PROPN #10-flat:name-9>
   #<TOKEN "Henry" PROPN #9-conj-7> #<TOKEN "e" CCONJ #8-cc-9>
   #<TOKEN "Carrington" PROPN #7-appos-6> #<TOKEN "lorde" NOUN #6-nmod-4>
   #<TOKEN "internacionais" ADJ #5-amod-4>
   #<TOKEN "mediadores" NOUN #4-nsubj-26> #<TOKEN "os" DET #3-det-4>)))

Mas a mensagem de erro é:

% cat ../reports/validation/CF0883.report
[Line 28 Sent CF883-1 Node 22]: [L3 Syntax punct-causes-nonproj] Punctuation must not cause non-projectivity of nodes [4, 23, 24]
[Line 28 Sent CF883-1 Node 22]: [L3 Syntax punct-is-nonproj] Punctuation must not be attached non-projectively over nodes [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[Line 30 Sent CF883-1 Node 24]: [L3 Syntax punct-is-nonproj] Punctuation must not be attached non-projectively over nodes [22]
Syntax errors: 3
*** FAILED *** with 3 errors
arademaker commented 4 years ago

@Cristiananc de uma olhada em https://github.com/own-pt/cl-conllu/blob/projective/projective.lisp#L68-L85. Dá para explicar quem está certo? Nós ou o validate.py?

arademaker commented 4 years ago

Em 1c99551ecb9fda0d60b632b519ef89fa7e2ef84e, ajustei o código submetido pela @Cristiananc

Ainda temos diferenças em relação ao validate.py que precisamos explicar. Até porque, seria interessante criarmos um issue sugerindo correção do validate.py no repositório http://github.com/universaldependencies/tools

Para a sentença CF0883-1 temos:

((#<TOKEN ")" PUNCT #24-punct-12> PUNCT-IS-NONPROJ-OVER (22 23))
 (#<TOKEN "," PUNCT #22-punct-1> PUNCT-CAUSES-NONPROJ-OF 23)
 (#<TOKEN "," PUNCT #22-punct-1> PUNCT-IS-NONPROJ-OVER
   (3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21)))

E o report do validate.py:

[Line 30 Sent CF883-1 Node 24]: [L3 Syntax punct-is-nonproj] 
 Punctuation must not be attached non-projectively over nodes 
 [22]

ok, para este caso concordamos que colocar o 23 está mais certo!

[Line 28 Sent CF883-1 Node 22]: [L3 Syntax punct-causes-nonproj] 
 Punctuation must not cause non-projectivity of nodes 
 [4, 23, 24]

aqui temos que explicar. Vc não disse nada sobre este caso na mensagem anterior. Listamos apenas o 23.

O 23 é um token que tem seu arco para o head cruzando o arco do 22 e seu head está entre o 22 e o head do 22.

Mas o 24 também tem, embora seja uma pontuação. Pergunta 1: se não fosse pontuação ele seria listado? Se a resposta for sim, blz, podemos argumentar que o 24 sendo pontuação já seria listado como acima como atachado de forma não projetiva e nosso report está evitando uma redundância.

E o caso do token 4? Ele cruza o arco do 22 para chegar no head dele mas ele é que esta entre o 22 e o head do 22. O head do 4 está FORA do range 22 e head-22. Seria isso a razão de não listarmos o 4? O 4 aparece na lista abaixo, do token 22. Pergunta 2: isto vai sempre ocorrer? Quando um nó que tiver pai fora do range TK e HEAD-TK, ele irá aparecer na lista punct-is-nonproj do TK e assim seria redundante coloca-lo também na lista punct-causes-nonproj?

[Line 28 Sent CF883-1 Node 22]: [L3 Syntax punct-is-nonproj] 
 Punctuation must not be attached non-projectively over nodes 
 [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

aqui concordamos que colocar o 21 está mais certo!

Cristiananc commented 4 years ago

Sim, a projeção retorna os tokens que são descendentes do head do token e estão entre o head do token e ele mesmo. A saída da função anterior estava divergindo disso, então eu escrevi a que subi no repositório.

Pergunta 1:

Sobre a ausência do nó 24 ele seria listado sim em (#<TOKEN "," PUNCT #22-punct-1> PUNCT-CAUSES-NONPROJ-OF 23) caso não fosse uma pontuação. O que acontece é que como ele é não projetivo e é pontuação entra só no then da condição if. Esse é um caso em que uma pontuação é um arco não projetivo, token 24, e nessa lista de nós que não são descendentes do head dele existe uma pontuação, o token 22. Pode ser interessante adicionar uma condição pra que esse tipo de situação seja reportada porque talvez facilite numa futura correção humana.

Pergunta 2:

Não, porque como no caso do token 4, o pai do nó está fora do range TK e HEAD-TK mas TK é um descendente do pai do nó. Então TK não pode causar não-projetividade do nó pois eu consigo chegar nele a partir do pai do nó. Mas o nó não é descendente do HEAD-TK logo ele aparece na lista punct-is-nonproj doTK.

No nosso caso, o erro do punct-causes-nonproj é sobre tokens que são pontuações que estão no intervalo de um certo arco e ele não é um descendente do head do token. Acho que não é muito importante pra gente observar esses cruzamentos e se pensarmos bem eles podem até levar a uma conclusão equivocada. Por exemplo, o token 4 é projetivo. Como uma pontuação pode causar não projetividade de um nó que é projetivo por definição? No que eu percebo temos um report mais confiável e que segue a definição de projetividade observando cada arco individualmente.

arademaker commented 4 years ago

@Cristiananc para encerrarmos este assunto só quero entender a aparente inconsistência no seu texto. Vc diz:

Pode ser interessante adicionar uma condição pra que esse tipo de situação seja reportada porque talvez facilite numa futura correção humana.

e depois

No que eu percebo temos um report mais confiável e que segue a definição de projetividade observando cada arco individualmente.

Afinal, vc está sugerindo deixarmos nosso sistema como está agora ou ainda fazer alguma mudança? Se vai ficar como está, então estamos prontos para reportar os problemas que achamos no validate.py?

Cristiananc commented 4 years ago

@Cristiananc para encerrarmos este assunto só quero entender a aparente inconsistência no seu texto. Vc diz:

Pode ser interessante adicionar uma condição pra que esse tipo de situação seja reportada porque talvez facilite numa futura correção humana.

e depois

No que eu percebo temos um report mais confiável e que segue a definição de projetividade observando cada arco individualmente.

Afinal, vc está sugerindo deixarmos nosso sistema como está agora ou ainda fazer alguma mudança? Se vai ficar como está, então estamos prontos para reportar os problemas que achamos no validate.py?

Pensei um pouco melhor e concordo com a observação de redundância. Podemos deixar como está sim e apontar os problemas que achamos. Dito isso, acho que posso dar o issue como encerrado.

arademaker commented 4 years ago

Quase lá... Mas vc disse:

Por exemplo, o token 4 é projetivo. Como uma pontuação pode causar não projetividade de um nó que é projetivo por definição?

O token 4 na CF883-1 é mediadores com head encontraram, o arco é encontraram -> mediadores.

Pela definição, todos os nós entre o mediadores e o verbo encontraram deveriam ser descendentes de encontraram, certo? Como o encontraram é o root, todos vão ser... mas curiosamente, https://urd2.let.rug.nl/~kleiweg/conllu/, desenha o arco do 22 cruzando o arco do 4. Isto quer dizer que seria possível este cruzamento ser evitado no desenho?

arademaker commented 4 years ago

O que eu to tentando entender é se o 4 é projetivo ou não... vc disse que é, certo? e se for então realmente o 22 não pode causar não projetividade no 4 se ele é projetivo. E isto quer dizer que o 4 realmente só pode ser listado na lista dos tokens que estão entre o 22 e seu head e não são descendentes do head do 22. Se vc confirmar isso, agora estou super confiante que a validate.py tem um bug. Também é interessante como a implementação lisp me parece muito mais clara do que a implementação Python.

Cristiananc commented 4 years ago

Quase lá... Mas vc disse:

Por exemplo, o token 4 é projetivo. Como uma pontuação pode causar não projetividade de um nó que é projetivo por definição?

O token 4 na CF883-1 é mediadores com head encontraram, o arco é encontraram -> mediadores.

Pela definição, todos os nós entre o mediadores e o verbo encontraram deveriam ser descendentes de encontraram, certo? Como o encontraram é o root, todos vão ser... mas curiosamente, https://urd2.let.rug.nl/~kleiweg/conllu/, desenha o arco do 22 cruzando o arco do 4. Isto quer dizer que seria possível este cruzamento ser evitado no desenho?

Interessante. Acho que esse seria um problema geométrico também. Não tem como desenhar sem que o arco 22 cruze com o arco 4. Mas esse não é um critério para definir projeção de um arco, visto que o arco 4 é projetivo e faz um cruzamento.

arademaker commented 4 years ago

No bosque temos várias sentenças ainda que foram reportadas pelo validate.py como não projetivas, com arcos (tokens) não projetivos que são pontuação ou causados por pontuação.

Usando a nova validate-punct temos 1406 sentenças que identificamos como pontuação causando ou estando non-projective:

WORKING> (multiple-value-bind (invalid valid)
         (serapeum:partition #'cl-conllu::validate-punct (read-conllu "documents/"))
       (mapcar #'length (list invalid valid)))
(1406 7958)

Mas o report do validate.py diz apenas 1244 sentenças:

% cat reports/validation/*.report | grep Line | awk '{print $4}' | sort | uniq | wc -l
    1244

Um caso particular é a segunda sentença do CF2-2. Nós dizemos que o token 11 e 7 causam não projetividade do 4. Está correto?

WORKING> (cl-conllu::validate-punct (cadr (read-conllu "documents/CF0002.conllu")))
((#<TOKEN "»" PUNCT #11-punct-8> CL-CONLLU::PUNCT-CAUSES-NONPROJ-OF 4)
 (#<TOKEN "«" PUNCT #7-punct-8> CL-CONLLU::PUNCT-CAUSES-NONPROJ-OF 4))

O validate.py diz que este arquivo não tem problema nenhum, vide report.

Quem está certo? Se estivemos certos, estamos pegando 162 casos a mais de erros não reportados pelo validate.py.

arademaker commented 4 years ago

commit f5ffdbb solves this issue and:

  1. the function is-sentence-projective returns two values. The boolean and the possible list of non-projective tokens.
  2. the function validate-punct is now working as expected. See https://github.com/UniversalDependencies/tools/issues/66#issuecomment-636035528

We need now a better organization of validation tests, to add in the lib all validations from validate.py. But this issue is now solved.