Para um computador, um conjunto de palavras não possui nenhum significado intrínseco. Pegar nos resultados do reconhecimento de voz e extrair informação útil sobre a qual o computador pode agir, não é tarefa fácil. Dado que, mesmo as áreas do cérebro humano que processam a linguagem ainda são largamente desconhecidas, os primeiros linguistas aplicados à área da computação tiveram que começar do nada.
A área de Natural Language Understanding (NLU) tem vindo a ser desenvolvida na sua maioria por companhias telefónicas e organizações ligadas à área da internet e redes IP. A razão para tal é sobretudo a fraca qualidade do sinal áudio transmitido nas linhas telefónicas, o que dificulta em muito a aplicação de centrais telefónicas automáticas. É aqui que entra o NLU, usando 75% de reconhecimento de voz certificado para o transformar em 85% ou 90% graças à análise contextual.
Para este propósito, a análise da estrutura gramatical da fala tem pouco interesse. Por isso, e no âmbito deste trabalho, iremos apenas fazer uma análise muito superficial destas técnicas.
Fundamentos
De um ponto de vista mais informático, o NLU funciona ao longo de um processo muito similar com o processo de compilação de um programa, só que ao contrário. Isto é, em vez de adoptar uma metodologia "top-down", em que a compilação pára sempre que detectar a falta de um ponto-e-vírgula (p.e), opta-se por uma abordagem "bottom-up", tipicamente pessimista, que assume de antemão que algo estará errado no seu input e prepara-se para ter o melhor desempenho possível.
Se tivessemos optado por aplicar a abordagem "top-down parsing"
à linguagem natural, muitos problemas iriam surgir, nomeadamente:
Todas as linguagens naturais
coexistem com a ambiguidade, excepções às regras gramaticais,
e inconsistência. A tarefa de definir uma gramática de parsing
para cada linguagem torna-se praticamente impossível.
Não se pode esperar
que todas as pessoas falem de um modo pré-definido, porque isso tornaria
o sistema demasiadamente frágil.
O NLU utiliza uma aproximação mais robusta e objectiva perante
este problema:
Uma aplicação
só deverá definir uma gramática para o menor subconjunto
da linguagem natural apropriada ao seu domínio. Desta forma, muita
da ambiguidade pode ser colocada de parte.
Em vez de usar o "top-down
parsing", o NLU irá utilizar o "bottom-up parsing",
tentando interpretar pequenos fragmentos de palavras soltas e combinando-as
de modo a obter uma "imagem" global do que se está a tentar
dizer.
Por exemplo, na frase "Eu quero ir para Lisboa esta tarde", o NLU
interpreta as palavras "esta tarde" como sendo a descrição
de um instante no tempo, a palavra "Lisboa" como sendo o nome de
uma cidade, e a palavra "para" seguida do nome de uma cidade como
sendo a identificação de um destino. O resto da frase é
descartado porque já temos elementos para extrair o sentido do que
foi pronunciado.
Se algo correr mal, nomeadamente na entrada de voz, e o sistema reconhece
a fala "Elu queru iri para Lisboa espa tarde", os fragmentos mais
importantes "para Lisboa" e "tarde" continuam a ser bem
compreendidos (possivelmente com alguma incerteza, porque não sabemos
a que dia é que o utilizador se está a referir) e 80-100% da
fala é bem interpretada enquanto conteúdo, embora apenas 43%
das palavras tenham sido exactamente identificadas.
Gramáticas
Ambos métodos de parsing requerem um conjunto de regras não ambíguas, completas e objectivas de modo a serem codificadas numa gramática. Algumas décadas atrás, o linguista e político Noam Chomsky definiu diversas classes de linguagem e as gramáticas que as suportam. Por exemplo, as "expressões regulares" podem ser processadas de um modo linear, uma palavra de cada vez, enquanto que as gramáticas "sem contexto" que definem linguagens de programação são recursivas por natureza. Todo o sistema NLU é baseado nestes ideais.
Por exemplo, passemos a considerar uma gramática muito simples, no
âmbito de um sistema de controlo de viagens:
<CIDADE> -> Lisboa
<CIDADE> -> Funchal
<CIDADE> -> Porto
<CHEGADA> -> [indo] para <CIDADE>
<PARTIDA> -> [a partir] de <CIDADE>
<VIAGEM> -> <PARTIDA> <CHEGADA>
<VIAGEM> -> <CHEGADA> <PARTIDA>
Os termos dentro de '<>' são as variáveis. As palavras
dentro de '[]' são opcionais. As regras da '<VIAGEM>' especificam
que uma viagem é definida seja com a cidade de partida seguida pela
de destino seja pelo contrário. A regra '<CHEGADA>' especifica
que podemos definir um destino quer através de 'para' seguido de uma
cidade, quer dizendo 'indo para' seguido de uma cidade.
Desta forma, o sistema é capaz de compreender frases como "Para
Funchal a partir de Lisboa" ou "A partir de Funchal, para Porto".
Mesmo que a entrada de voz fosse destorcida e esta resultasse em "Para bsdvger de Lisboa", o sistema seria capaz de conseguir identificar a cidade de origem e fazer apenas uma pergunta em relação ao destino, evitando assim perguntas tipo "Por favor, repita!".
Entraves à implementação
Quando as pessoas falam, são geralmente bastante descuidadas no que
toca à formulação, produzem erros fonéticos, como
já tivemos oportunidade de ver na secção anterior. Muitas
fontes de erro são introduzidas nos dados de entrada do NLU:
As pessoas não
falam a um ritmo constante, há hesitações, pausas e interjeições
nos momentos mais inoportunos.
Frequentemente, quando
uma pessoa fala, muda de opinião a meio de uma frase. Por exemplo,
um ouvinte humano percebe que "não, desculpe, eu quis dizer sexta-feira"
se refere a uma referência anterior, contudo para um computador já
não é tão perceptível.
Por vezes, as pessoas
duplicam frases ou dizem frases sem nexo sequencial, etc. Um exemplo é
o caso em que ouvimos uma entrevista na TV, apercebemo-nos que a discussão,
diálogo ocorre naturalmente, mas, se tivermos acesso à mesma
entrevista por escrito, depressa nos iremos perceber que há coisas
que não têm nexo.
As pessoas assumem que
o ouvinte é capaz de interligar os pronomes e locuções
como "o", "ele" e "aquele" aos conceitos respectivos.
Mais uma vez, isto é extremamente complicado para um computador.
Como escrever uma gramática
Para tornar o sistema o mais robusto possível, os linguistas que escrevem as suas próprias gramáticas terão que criar regras que aceitem os erros mais comuns de entrada como sendo válidos, num determinado domínio. Também se espera que as regras de mais alto nível (tal como '<viagem>', no exemplo anterior) suportem formulações suficientes para manipular a maioria dos casos (com suporte para aceitar lixo, se for caso disso, como dados válidos).
Quando possível, os linguistas devem tentar limitar os seus domínios em expressões regulares, que podem ser analisadas com complexidade O(n), enquanto gramáticas "sem contexto" requerem O(n³). Infelizmente, isto é raro acontecer excepto em alguns casos.
Contudo, isto não é assim tão óbvio. Cada vez que adicionamos uma regra para cobrir um novo caso, esta corre o risco de introduzir alguma ambiguidade e colocar em risco a funcionalidade do sistema. O crescimento na complexidade é exponencial; uma vez que o sistema atinja uma centena de regras, torna-se quase impossível introduzir o suporte para uma nova frase sem comprometer o já comprovado funcionamento de dez outras.
Como consequência, alguns investigadores estão a começar a experimentar a aprendizagem automática de regras gramaticais. Por exemplo, no nosso caso acima, uma versão inteligente do sistema deveria "descobrir" que o nome de uma cidade a seguir à palavra 'para' é geralmente indicativo de um destino, desde que tenhamos treinado o sistema com vários exemplos onde é explícito que 'para Lisboa' indica que queremos ir para lá. Por enquanto, os resultados são interessantes, mas não atingiram ainda qualidade suficiente para passarem a ser disponibilizados comercialmente.