Processamento de Linguagem Natural
Notas: Estas são anotações pessoais de aula — não necessariamente coesas, não necessariamente estruturadas e, o mais importante, não necessariamente precisas! Por favor, verifique as fontes e sinta-se à vontade para apontar eventuais erros por e-mail!
1. O estudo formal da língua natural
1.1 O estudo da língua natural
- Gramática := pode ser enunciada como: descrições explícitas das regras implícitas que os falantes dominam sem saber enunciar, existe em várias camadas: fonologia, morfologia: formação de palavras, sintaxe: combinação em sentenças, semântica: significado das expressões. Vamos tratar principalmente de sintaxe e semântica
- A linguística é o estudo científico da língua humana. Chomsky (1957) propôs identificar uma língua com o conjunto de todas sentenças gramaticais dessa língua, separando a competência: capacidade de reconhecer sentenças válidas do desempenho: fazer o uso efetivo.
Uma boa gramática deve: 1) gerar expressões bem formadas da língua 2) determinar a estrutura interna das expressÕes 3) permitir a atribuição de significado as estruturas
1.2 Sintaxe, semântica e pragmática
Outras definições (ou reorganizando palavras):
- Sintaxe: estudo das cadeias e da estrutura imposta pela gramática
- Semântica: estudo da relação entre cadeias e seus significados
- Pragmática: estudo do uso comunicativo das cadeias significativas
“Tipos de Linguagem” -> Uma linguagem natural é essencialmente julgada pelos seus falantes nativos, enquanto uma linguagem formal possui suas definições de certo e errado.
Trazendo outros termos ao nosso entendimento, saber “que” e “como”:
- Significado operacional := saber como executar/seguir instruções
- Significado denotacional := saber que expressão descreve o mundo
1.3 Os propósitos da comunicação
A língua desempenha muitos papéis na realidade, o foco deste curso será descrição de estados de coisas e de raciocínio
1.4 Línguas naturais e línguas formais
Três traços de design cruciais das línguas humanas que as diferenciam de sistemas animais, como a dança das abelhas:
- Dupla articulação: dois níveis estruturais — unidades significativas (morfemas/palavras) feitas de unidades não significativas (fonemas), que apenas diferenciam significados
- Recursão: padrões estruturais podem se repetir indefinidamente
- Contextualidade: o significado depende em parte do contexto de uso
Essas propriedades permitem construir/entender infinitas sentenças a partir de recursos finitos (sons e regras) — condição para a aprendizagem rápida da língua pelas crianças.
- Composicionalidade (princípio atribuído a Frege): o significado de uma expressão complexa depende dos significados das partes e de como são combinadas sintaticamente. É tratada como questão metodológica, não como traço definidor das línguas naturais, a pergunta é se queremos projetar a semântica de forma composicional. Exige sistematicidade
- Dificuldades recorrentes à composicionalidade: pronomes e pressuposições, que dependem de contexto
- Linguagens formais compartilham algumas propriedades das línguas naturais, mas carecem da dimensão pragmática (engano, ironia, metáfora) e da flexibilidade ligada à vagueza e ao conhecimento de fundo, por isso línguas naturais são melhores para comunicação humana, e linguagens formais para matemática/computação
- Em termos computacionais: a interpretação é uma função recursiva sobre a estrutura sintática — cada construção gramatical tem um caso, combinando os resultados dos “filhos”
1.5 O que a semântica formal não é
Existe uma dificuldade, explicar coisas como o que é uma bicicleta a desenhando, estamos associando dois símbolos um ao outro, a palavra ao desenho, dificuldade de explicar significado sem referência de símbolos
1.6 Semântica computacional e programação funcional
Baseada no cálculo lambda tipado, mais em breve
- Programação funcional é útil a linguistas porque força precisão total nas regras propostas e dá retorno imediato sobre teorias linguísticas.
- Dois usos principais de máquinas em semântica computacional: 1) automatizar a construção de representações de significado; 2) operar sobre os resultados (verificação/construção de modelos, inferência), essenciais para PLN
- O livro usa Lean (em vez da tradição em Prolog), pois Prolog precisa de operadores de controle (assert, retract, cut) que comprometem a pureza lógica e dificultam entender/depurar programas. Já a programação funcional permite implementações fiéis às definições formais, com base lógica sólida que facilita verificar a correção da teoria semântica
2. Programação Funcional no Lean
2.1 Termos e Tipos
- Termo := expressão sintaticamente válida que representa um objeto e possui um tipo
- Tipos funcionam como conjuntos mas não são conjuntos, servem pra classificar termos e impor disciplina (
1 ∈ 2no papel vs erro de tipagem em Lean) - Tipos básicos:
ℕ,ℤ,ℚ,Bool.σ → τ:= tipo das funções de σ em τ - Ordem superior := tipo com
→aninhada à esquerda de outra→, ex(ℤ → ℤ) → ℚ #evalcalcula,#checksó confirma/pergunta o tipo sem computardef nome : Tipo := valorintroduz nome no ambiente, tipo às vezes inferido- Tipos também são termos e têm tipo (
Bool : Type,Type : Type 1…), hierarquia de universos existe pra evitar paradoxo de “tipo de todos os tipos”
2.2 Funções
fun x => x * x/λ x ↦ x * xsão a mesma coisa, unicode é aceito- Nome do parâmetro não importa pra identidade da função (provável com
rfl) opaque:= declara tipo/constante/função sem dar implementaçãoexamplesem nome,theoremcom nome pra poder referenciar depois.rflfecha igualdade quando os dois lados computam o mesmo valor+,*são açúcar sintático pra funções via classes (Nat.addetc), isso é o que dá o polimorfismo- Tipo não resolvido vira metavariável (
?m.7), um buraco esperando contexto ou anotação pra decidir - Funções são de primeira classe: podem ser passadas como argumento (ordem superior) e também retornadas (currying/avaliação parcial)
2.3 Expressões
- Toda expressão é termo. Termo canônico := já está na forma final, não reduz mais
letnomeia valor dentro de uma expressão, expressão toda tem valorif-then-elseé expressão, não comando — por isso os dois ramos precisam ter mesmo tipo
2.4 Estruturas
structureagrupa valores nomeados, cria tipo novo + namespace, gera construtor (Point.mk) e projeção por campo (Point.x)⟨_, _⟩:= construtor anônimo, usado quando o tipo esperado já deixa claro qual construtor usar, serve também pra desmontar argumento{ p with x := ... }:= cópia da estrutura alterando só alguns campos, útil quando tem muito campo
2.5 Tipos Indutivos
inductivelista as formas que os valores do tipo podem ter — construção mais importante do curso, uma BNF é literalmente uminductive- Sem argumento → enumeração (
Day). Com argumento → registro variante. Forma referindo o próprio tipo → recursivo/árvore (Nat) deriving Repr, DecidableEqgera exibição e igualdade automaticamente
2.6 Recursão
- Definição recursiva precisa de caso base + garantia (verificada pelo compilador) de que chega nele
- Em
Nat, casar0/n + 1já dá as duas coisas de graça (chamada recursiva sobre algo estritamente menor) factorial(casando padrão) vsfactorial'(comif,x - 1como decréscimo que precisa ser verificado, não vem de graça da forma)- Casamento de padrão é açúcar pra
matchpor baixo IO.println <| exprpra interpretare afins,#evalsozinho não interpreta
2.7 Listas e Polimorfismo
List αé indutivo:[]oux :: xs— por isso recursão sobre lista tem a mesma forma que recursão sobreNatαé parâmetro de tipo,List Nat ≠ List Stringmesmo sendo gerados pelo mesmoList{α : Type}entre chaves := parâmetro implícito, Lean infere sozinho
2.8 O tipo Option
List α → αpromete elemento sempre, impossível pra lista vaziaList α → Option αcorrige no tipo:some xounone, obriga quem chama a tratar os dois casos- Alternativa: função devolve valor default do tipo (ex
String.backde string vazia)
2.9 Processamento de Listas
List.mapaplica função a cada elementoList.filtermantém só quem satisfaz condiçãoList.foldl/List.foldrreduzem lista a valor finalList.all/List.any:= todos / algum satisfaz condição, devolveBool
2.10 Composição de Funções
f ∘ g:= aplica g depois f,(f ∘ g) x = f (g x)
2.11 Classes de Tipos
- Função genérica sobre α pode exigir capacidade via classe entre colchetes, ex
[BEq α]pra poder usar== deriving instance BEq for Daygera automático, mas igualdade não trivial (exAngle, −90° = 270°) precisa de instância manualBEq αdevolveBool(==) vsDecidableEq αdevolve prova de igualdade/desigualdade (usável emife em demonstração)Repr:= como representar texto de um tipo, produzStd.Format(nãoStringdireto), formato intermediário que permite pretty printing (indentação/quebra configurável, ideia de Wadler). Separa estrutura do dado da apresentação — dá pra customizar sem mexer na definição do tipo
2.12 Cadeias de Texto
Stringé UTF-8 empacotada, não lista de char — não dá pra casar padrão direto (c :: csnão funciona)- Solução: converter
String ↔ List Char(.toList/String.ofList) pra processar recursivamente
2.13 Lean e o Cálculo Lambda
- LC tem 3 formas (BNF): variável, aplicação
(E E), abstração(λ v ↦ E)— essa gramática vira tipo indutivo direto (Lam), cada cláusula BNF = um construtor. Isso é o motor do curso: gramática = tipo indutivo, “bem formado” = “bem tipado” - Lean é baseado no CiC (Cálculo de Construtores Indutivos), extensão tipada do LC
- β-redução := aplicar abstração a argumento substitui parâmetro pelo valor no corpo.
#evalexecuta,rflverifica - Substituição não é trivial (risco de captura de variável), mas Lean trata certo por baixo dos panos
- Nem toda redução termina: ω =
(λx ↦ x x) (λx ↦ x x)entra em loop — e nem dá pra escrever em Lean, porque exigiria x com tipo?m → ?ne?mao mesmo tempo, impossível de tipar - No cálculo lambda tipado (base de Lean): todo termo bem tipado tem forma normal, toda redução termina — por isso Lean é linguagem de programação E lógica consistente ao mesmo tempo (terminação garantida pelos tipos). Às vezes Lean não prova sozinho que uma função termina, aí a prova de terminação é trabalho meu
- Além de tipos simples/indutivos: tipos dependentes (tipo depende de valor, ex
Vector), proposições como tipos (Prop), universos de tipos (Type,Type 1…) pra evitar paradoxo
3. Lógica
3.1 Provas em Lean
- Lean é linguagem de programação E assistente de prova: a mesma linguagem escreve funções e provas sobre elas
Prop:= tipo das proposições (enunciados verdadeiros ou falsos). Proposição é um tipo e prova é um termo desse tipo, então provar é construir um valor- Declarar uma proposição não é decidi-la:
#checkpergunta o tipo, não avalia se é verdade (1 = 2é umPropperfeitamente válido) - Prova como termo:
theorem OneEqSelf : 1 = 1 := Eq.refl 1. Declarar teorema é parecido com declarar função byabre o modo de táticas: descreve-se como montar a prova e Lean constrói o termo (#printmostra qual foi). Útil quando o termo escrito à mão fica granderflfecha igualdade quando os dois lados reduzem à mesma expressão por computação (consequência do CIC). Leis algébricas comoNat.mul_commnão são por definição, precisam ser aplicadas comexact- Sistema dedutivo := conjunto de regras de dedução.
Propimplementa a dedução natural de Gentzen (1930):Γ ⊢ α:= α deriva de Γ,⊢ α:= α é válida (tautologia) - Cada conectivo tem regra de introdução (como construir uma prova que o usa) e de eliminação (como usar uma prova que o tem)
| Conectivo | Introdução | Eliminação |
→ | intro (supõe o antecedente) | modus ponens: h hP. apply raciocina de trás para frente, exact fecha com hipótese de mesmo tipo, assumption procura a hipótese sozinha |
∧ | constructor (dois objetivos), ⟨_, _⟩, And.intro | projeções .1/.2 (.left/.right), obtain ⟨a, b⟩ := h |
∨ | basta um lado: left/right (Or.inl/Or.inr) | prova por casos (Or.elim): cases h with, concluir nos dois casos |
¬ | ¬P é P → False: intro hP e derivar False | absurd; ex falso quodlibet: de False sai qualquer coisa (False.elim) |
↔ | constructor (uma prova para cada →), Iff.intro | h.mp, h.mpr |
∀ | intro x com x arbitrário (entra no contexto como variável) | aplicação: h d prova P d |
∃ | precisa de testemunha: use d, Exists.intro | obtain ⟨d, hd⟩ := h dá nome à testemunha sem saber qual é |
- Lógica construtiva (intuicionista) := provar
P ∨ Qexige saber qual lado vale. Não assume que toda proposição é verdadeira ou falsa - Lógica clássica acrescenta o terceiro excluído
Classical.em : P ∨ ¬P, de onde vêmby_cases(divide em P e ¬P) eby_contra(redução ao absurdo: supõe¬Pe derivaFalse) - Em Lean um tipo pode ser vazio, ao contrário do domínio não vazio da FOL clássica. Por isso
(∀ x, P x) → ∃ x, P xsó prova com umu : Uexplícito (ou a classeNonempty) - Indução:
induction n with | zero | succ a ih. Caso base emNat.zero, passo assume a propriedade ema(hipóteseih) e prova paraa + 1 - Extensionalidade:
defdefine funções no sentido intensional (o algoritmo), mas duas definições distintas podem ser a mesma função extensional (mesma tabela entrada → saída).funextreduzf = ga mostrar que coincidem em todo ponto decidecomputa a decisão dentro do kernel (simples, pode ser lenta);native_decidecompila para código nativo antes de executar.omegaesimpaparecem quando forem necessárias
3.2 Lógica Proposicional
- Em
Propvale a irrelevância da prova: Lean não distingue provas diferentes de uma proposição, então não dá para casar padrão numa prova dep ∧ q.Propnão é dado, não é computável - Para manipular fórmulas como dado e decidir consequência, usa-se um tipo indutivo
Formula(átomos com nomeString,top,bot,neg,conj,disj). Implicação e bicondicional são definidas:impl f g := disj (neg f) g,iff:= conjunção das duas implicações - Sem operadores infixos não há ambiguidade nem precedência. “Maria é jovem e bonita ou triste” vira
conj j (disj b t)oudisj (conj j b) t, leituras diferentes - Linguagem proposicional := todas as fórmulas construíveis sobre um vocabulário de átomos.
namesextrai o vocabulário (ordenado e sem repetição).j,b,tsão identificadores da metalinguagem (Lean), as strings"MJ","MB","MT"são o vocabulário da fórmula conjsedisjsrecebem lista. Base da recursão: conjunção vazia étope disjunção vazia ébot(elementos neutros). A lista fica fora do construtor, pois indutivo aninhado é mais difícil de manipular- Como
Formulaé dado, dá para contar operadores, medir profundidade etc., coisa impossível comProp
Semântica
Formula.eval f valuecalcula o valor de verdade por recursão, comvalue : String → Bool(valoração). Só o casoatomconsulta a valoração, os demais combinam subfórmulas- Tautologia (válida,
⊨ α) := verdadeira em toda valoração. Contradição (insatisfatível) := falsa em toda valoração. Satisfatível := alguma valoração a torna verdadeira. Contingente := verdadeira em alguma e falsa em outra - Toda tautologia é satisfatível, mas nem toda satisfatível é tautologia. Se α é contradição, então
⊨ ¬α allValsgera todas as valorações (2ⁿ para n átomos), o que dá a tabela verdade.tautologypercorre comall,satisfiablecomany,contradiction := !satisfiable. Funciona porque os átomos são finitos- Consequência lógica:
P ⊨ Qsse⊨ P → QsseP ∧ ¬Qé contradição (é assim queimpliesé implementado). Com várias premissas,{P₁, …, Pₙ} ⊨ αsse⊨ (P₁ ∧ … ∧ Pₙ) → α - Equivalência
α ≡ β:= mesmo valor em toda valoração, ou seja,impliesnos dois sentidos. Todas as tautologias são equivalentes entre si, o mesmo vale para as contradições - Semântica por atualização: parte-se do conjunto de todas as valorações e
updatemantém só as que satisfazem a fórmula. Contradição esvazia o conjunto, tautologia não tira nada
Meta-teoremas e ponte com Prop
- Meta-teoremas são teoremas sobre PL escritos em Lean, não fórmulas de PL. Mostram que as definições são consistentes (ex: toda tautologia é equivalente a
top, a negação de tautologia é contradição, princípio da contraposiçãoF₁ ⊨ F₂sse¬F₂ ⊨ ¬F₁) Formula.denote f v : Proptroca cada construtor deFormulapelo conectivo deProp(conjvira∧,negvira¬)eval_iff_denote:eval = true ↔ denote, provado por indução em f. As duas leituras concordam: calcular o valor de verdade e provar a proposição correspondente
3.3 Lógica de Primeira Ordem
- Em PL, “Toda maçã é vermelha” seria um átomo indivisível, sem capturar o quantificador nem a relação entre maçã e cor. FOL (lógica de predicados) acrescenta: termos (variáveis ou funções aplicadas a termos), predicados n-ários sobre termos,
∀e∃ - “Primeira ordem” := quantifica apenas sobre indivíduos do domínio
Sintaxe
Variable:= nome + índice (lista de naturais para gerar variáveis novas).Term:=var voustruct nome args(símbolo funcional aplicado). Constante := função de aridade zero (struct "c" [])Formula αé parametrizada pelo tipo dos termos:Formula Variablesó tem variáveis,Formula Termé a sintaxe completa. Construtores:atom,eq,top,bot,neg,impl,equi,conj,disj,forall_,exists_- Linguagem de primeira ordem := fórmulas construíveis a partir de um vocabulário de símbolos predicativos e funcionais
- Em
∀x F(ou∃x F) o quantificador liga as ocorrências de x em F que não estejam ligadas por um quantificador interno. Fórmula aberta := tem ocorrência livre de variável. Fechada (sentença) := não tem.freeVarscoleta as livres e filtra a variável de cada quantificador - Termo t é livre para v em F quando substituir v por t não faz nenhuma variável de t ser capturada por um quantificador. Se não for, renomeia-se as variáveis ligadas de F (variante alfabética) e a substituição passa a ter o significado certo
Semântica
- Em PL basta uma valoração. Em FOL a interpretação é uma estrutura: um domínio D mais a interpretação dos símbolos (predicados viram relações em D, símbolos funcionais viram funções em D)
- Como termos têm variáveis, também é preciso uma atribuição
Assign D := Variable → D.g[v := d](Assign.update) muda só o valor de v FInterpinterpreta símbolos funcionais,liftAssigninterpreta termos por recursão,Interpinterpreta predicados emBoolFormula.eval f dom I g tvalavalia por recursão. Os quantificadores percorremdom(allpara∀,anypara∃), então o domínio precisa ser finito e enumerável. Em domínios infinitos não existe processo de decisão- Exemplo: um predicado binário
Esobre quatro vértices é um grafo direcionado, eE x yé uma aresta de x para y. Símbolos fora do vocabulário ou com aridade errada avaliam silenciosamente parafalse Fin n:= naturais menores que n, útil para domínios finitos.Fin.mkexige prova de que o número é menor que o limite- Para sentenças a verdade não depende de g, mas a definição passa por subfórmulas abertas (ex: “Todo mestre tem um aprendiz” e depois “Ele tem um aprendiz”, como os pronomes da língua natural)
- Válida (
⊨ α) := verdadeira em toda interpretação.P ⊨ C:= todo modelo de P é modelo de C. Não há equivalente deallValsem FOL, então não se decideP ⊨ C: só dá para refutar achando um contraexemplo (I ⊨ PeI ⊭ C)
Ponte com Prop
Denot D := String → List D → Propinterpreta predicados emProp.denotetroca cada construtor pelo conectivo de Lean, eforall_vira o próprio∀eval_iff_denoteagora precisa da hipótesehdom : ∀ d, d ∈ dom(a lista contém todos os elementos), contrapartida formal da limitação de só calcular quantificadores em domínios finitosdenotenão depende de lista: traduz a fórmula paraPrope permite provar fatos em domínios infinitos (ex:∀x ∃y R[x,y]nos naturais com R como<, testemunhaa + 1)- Em Prop:
Valid:= verdadeira em todo modelo,Satisfiable:= algum modelo a torna verdadeira,Implies f g := Valid (impl f g) - Com símbolos funcionais a estrutura vira a tripla
(D, I, F), eValidTquantifica também sobreFInterpe usaliftAssign
4. Conjuntos e Relações
4.1 Conjuntos e notação de conjuntos
- Conjunto := função característica de um elemento, duas formas de responder pertinência:
α → Bool(calcula, roda) ouα → Prop(enuncia, prova) — é essa segunda que Lean usa:Set α := α → Prop x ∈ Aé só açúcar praA x, e o desdobramento entre os dois érfl(não pede justificativa) — útil pra destravar prova empacadadecidenão desdobradef, então em conjunto definido viadefprecisaunfoldantes dedecideconseguir calcular∅ := fun _ ↦ False,Set.univ := fun _ ↦ True- União/interseção são disjunção/conjunção elemento a elemento
Even n(enuncia, ∃r, n = r+r) vsisEven n(calcula,n % 2 == 0) := mesma dicotomia calcular/enunciar de conjuntos. Mathlib prova que coincidem (Nat.even_iff), e uma vez provado issoEven ntambém fica calculável via#eval— mecanismo por trás disso é a classeDecidable(mesma família deBEq/DecidableEq)
4.2 Relações
Rel α β := α → β → Prop— mesma ideia de conjunto, mas pra pares- Inversa (
flip R) troca a ordem dos argumentos — em língua é o que a voz passiva faz - Composição (
Relation.Comp R S) encadeia por elemento intermediário:xrelaciona azse existeycomx R yey S z— provar composição é exibir essey. É o que dá “avô” = “pai” ∘ “pai” - Reflexividade/simetria/transitividade são afirmações sobre a relação inteira (quantificador + conectivo), as três juntas dão
Equivalence - Divisibilidade (
∣) é outro exemplo enuncia-mas-calcula: existe instânciaDecidablepor trás
4.3 Funções
- Função é caso particular de relação: pra cada
a, no máximo umbrelacionado — é essa unicidade que permite escreverf xem vez de “algum b tal que…” - Toda relação (como conjunto de pares) tem função característica —
likesRjá É a função característica da relação de gostar