“Can machines think?”

Alan Turing

22 - Inteligência Artificial

22.1 - Linguagens Simbólicas.

Linguagens simbólicas podem ser explicadas em comparação com a área mais famosa dentro da IA atualmente: aprendizado de máquina (machine learning). Com aprendizado queremos dizer que um programa que recebe um conjunto de dados (de treinamento a princípio), e sem instruções específicas pode tirar conclusões através de padrões estatísticos e/ou relações vetoriais. Isso fica mais claro quando falamos de reconhecimento de imagem. Enviamos ao algoritmo uma série de imagens de um certo tipo (gatos) e, uma vez que o sistema foi treinado, da próxima vez esperamos que ele reconheça imagens de gatos com pequenas ou grandes variações (posição, escala, rotação, qualidade, etc…). A parte importante é que não foi desenvolvido um algoritmo específico para isso, nem intervenção humana, somente determinados padrões identificados em dados. Isso não é verdade quando falamos em programação simbólica. Neste caso, devemos informar através de símbolos reconhecidos por humanos, além de lógica computacional, o que o programa deve fazer. A complexidade é determinada por nossa capacidade de entender um problema e colocá-lo na forma de algoritmo, com todas as ramificações que queremos investigar. A grande vantagem é conseguir extrair completamente do código o que o programa está executando, ao contrário do modelo de aprendizado de máquina, onde a saída não nos diz muito do que foi feito no meio do caminho (black box).

Linguagens simbólicas permitem que descrevamos certos aspectos do problema a ser modelado de forma ideal para a inteligência humana, ou seja, enquanto seres possuidores de linguagem, com suas palavras, contexto, semântica e regras gramaticais, podemos explicar e trocar conhecimentos com outros humanos, não dependendo de relacionamentos obscuros encontrados no aprendizado de máquina puro. Obviamente essa abordagem tem vários problemas e por um tempo foi completamente ignorada para financiamento na área.

A representação do conhecimento é o processo de traduzir conhecimento em uma forma que possa ser processado por um computador. Os símbolos usados em linguagens simbólicas podem representar conceitos, fatos e relacionamentos entre eles.

O raciocínio simbólico envolve o uso de operações lógicas, como dedução e indução, para manipular símbolos e obter novos conhecimentos. Uma das aplicações mais comuns do raciocínio simbólico é em sistemas especialistas, que são sistemas de IA projetados para fornecer conselhos ou tomar decisões em um domínio específico. Os sistemas especialistas normalmente usam uma base de conhecimento de regras e fatos representados em uma linguagem simbólica e um mecanismo de raciocínio que aplica operações lógicas para obter novos conhecimentos ou tomar decisões com base no conhecimento existente.

As linguagens simbólicas também são usadas no processamento de linguagem natural (NLP = Natural Language Processing), que é o campo da IA que se concentra na compreensão e geração da linguagem humana. As linguagens simbólicas são usadas para representar o significado de palavras e frases de uma forma estruturada que pode ser processada por um computador.

Um exemplo clássico de raciocínio simbólico em NLP é o uso de gramáticas e analisadores para analisar sentenças de linguagem natural em estruturas sintáticas. As estruturas sintáticas podem então ser usadas para extrair significado e executar tarefas como análise de sentimento, resposta a perguntas e classificação de texto.

Aqui está um exemplo de uso de raciocínio simbólico para NLP em Python:


    import nltk

    # Define a grammar for parsing simple sentences
    grammar = nltk.CFG.fromstring("""
    S -> NP VP
    NP -> Det N
    VP -> V NP
    Det -> 'the' | 'a'
    N -> 'cat' | 'dog'
    V -> 'chased' | 'ate'
    """)

    # Create a parser for the grammar
    parser = nltk.ChartParser(grammar)

    # Parse a sentence
    sentence = "the cat chased a dog"
    tokens = nltk.word_tokenize(sentence)
    parses = parser.parse(tokens)

    # Print the parses
    for parse in parses:
        print(parse)

Neste exemplo, definimos uma gramática livre de contexto para analisar frases simples com uma estrutura sujeito-verbo-objeto. Em seguida, criamos um objeto analisador usando a gramática e analisamos uma frase ("o gato perseguiu um cachorro") usando o método ChartParser da biblioteca Natural Language Toolkit (NLTK).

A saída do programa é a(s) estrutura(s) sintática(s) que o analisador sintático gera para a sentença. Nesse caso, há apenas uma análise, que corresponde à frase analisada como "o gato perseguiu um cachorro".

Além de sistemas especialistas e processamento de linguagem natural, as linguagens simbólicas são usadas em muitas outras áreas da IA, como planejamento e programação, robótica e raciocínio automatizado. O raciocínio simbólico pode ser usado para resolver problemas que exigem raciocínio lógico e decisões complexas baseadas em regras. No entanto, uma das limitações da IA simbólica é sua incapacidade de lidar com incertezas e informações incompletas, que é uma característica comum de muitos problemas do mundo real.

Uma maneira pela qual o raciocínio simbólico é usado na robótica é por meio do uso de técnicas de representação e raciocínio do conhecimento (KRR em inglês). O KRR permite que os robôs representem conhecimento sobre o mundo, incluindo fatos, conceitos e relacionamentos, usando estruturas simbólicas como frames, regras e ontologias. Esse conhecimento pode então ser usado para raciocinar sobre o mundo, tomar decisões e planejar ações.

Por exemplo, um robô em um depósito pode usar técnicas de KRR para representar o conhecimento sobre o layout do depósito, a localização de vários produtos e a ordem em que precisam ser recuperados. O robô pode então raciocinar sobre esse conhecimento para planejar a rota mais eficiente pelo armazém e recuperar os produtos na ordem correta.

O raciocínio simbólico também pode ser usado para planejamento e execução de tarefas em robótica. Nesse caso, as tarefas são representadas como uma sequência de ações de alto nível que precisam ser executadas para atingir um objetivo. O robô usa o raciocínio simbólico para planejar e executar a sequência de ações necessárias para atingir o objetivo, levando em consideração restrições como limitações físicas e requisitos de segurança.

Por exemplo, um robô pode usar o raciocínio simbólico para planejar e executar uma sequência de ações para montar um produto em uma linha de montagem. O robô primeiro representa o produto como um conjunto de peças e o processo de montagem como uma sequência de etapas. Em seguida, raciocina sobre a ordem em que as etapas precisam ser executadas, os recursos e ferramentas necessários para cada etapa e os requisitos de segurança para cada etapa. Por fim, executa a sequência de ações para montar o produto.

Abaixo há um exemplo de raciocínio simbólico usando Lisp:


    ; Define a simple rule-based system that determines the color of a fruit based on its name
    (defun color-of-fruit (fruit)
    (cond ((member fruit '(apple cherry strawberry)) 'red)
            ((member fruit '(banana lemon pineapple)) 'yellow)
            ((member fruit '(grape blueberry blackberry)) 'purple)
            (t 'unknown-color)))

    ; Test the system by asking for the color of an apple
    (color-of-fruit 'apple) ; returns 'red

Neste exemplo, definimos uma função chamada color-of-fruit que recebe um parâmetro de fruta e usa um conjunto de regras para determinar sua cor. O código usa a função $\text{cond}$, que é uma construção Lisp para ramificação condicional, para testar cada nome de fruta possível e retornar a cor correspondente. Se o nome da fruta não for reconhecido, a função retorna um símbolo de 'cor desconhecida'.

Podemos testar o sistema chamando a função cor da fruta com diferentes nomes de frutas, como 'maçã', 'banana' ou 'uva'. Nesse caso, chamar (cor da fruta 'maçã) retornaria o símbolo 'vermelho', indicando que a cor de uma maçã é vermelha de acordo com nosso sistema baseado em regras.

Este é um exemplo muito simples, mas demonstra o poder de linguagens simbólicas como Lisp para representar e manipular conhecimento em IA. Lisp é particularmente adequado para raciocínio simbólico, pois fornece construções poderosas para manipular listas e símbolos e permite a criação fácil de sistemas baseados em regras como o mostrado aqui.

Uma forma de alcançar a integração neural-simbólica, através do uso de redes neurais, seria aprender os parâmetros dos modelos simbólicos. Por exemplo, uma rede neural pode ser usada para aprender os pesos de um conjunto de regras lógicas usadas para raciocinar sobre um determinado domínio. A rede neural seria treinada em exemplos de entradas e saídas e aprenderia a ajustar os pesos das regras para produzir as saídas corretas.

Outra abordagem é usar redes neurais para codificar informações simbólicas em representações distribuídas que podem ser processadas por redes neurais. Nesta abordagem, a informação simbólica é transformada em um conjunto de vetores contínuos que podem ser processados por redes neurais. A rede neural pode então ser usada para raciocinar sobre as informações simbólicas codificadas nos vetores.

Uma vantagem de usar redes neurais no raciocínio simbólico é sua capacidade de lidar com dados ruidosos e incertos. As redes neurais são capazes de aprender padrões em dados mesmo quando os dados estão incompletos ou contêm erros. Isso os torna adequados para tarefas de raciocínio que envolvem informações incompletas ou incertas. Outra vantagem é a capacidade de aprender com grandes conjuntos de dados. As redes neurais são capazes de aprender padrões complexos em dados treinando em grandes conjuntos de dados, o que pode ser difícil ou impossível de fazer com sistemas de raciocínio simbólico.

22.2 - Programação em Lógica.

Utiliza lógica formal para programação de programas de inteligência artificial. O padrão comumente utilizado é de sentenças que expressão regras sobre o problema investigado. Nas linguagens de programação que utilizam esse tipo de abordagem, as regras seguem o formato de cláusulas, declarações tem a implicações lógicas: $H\; se \; B_1\; e\; ...\; B_n$ , onde $H$ é a cabeça da regra e os $B_n$ formam o corpo. Fatos são regras que não têm corpo, ou seja, escritas na forma $H$ . Quando não há dependência de fórmulas complexas na declaração, ou seja, fórmulas atômicas, as cláusulas são chamadas de cláusulas definidas ou de Horn. Quando este não é o caso, a linguagem de programação pode representar conhecimento de forma não monotônica.

22.3 - Resolução de Problemas como Busca.

Alguns problemas em IA podem ser resolvidos quando utilizamos técnicas de busca, mas não somente, sendo que representação de conhecimento e computação evolucionária são alternativas. Aqui definimos busca como encontrar uma informação desejada em uma estrutura de dados. Normalmente utilizamos os seguintes passos para executar esse método:

  1. Definir o problema;
  2. Analisar o problema;
  3. Identificar as soluções possíveis;
  4. Escolher uma solução ótima;
  5. Implementar.

As principais propriedades dos algoritmos de busca são:

  • Completude: Fornece uma solução para qualquer entrada;
  • Otimização: Se dentre as soluções, retorna a melhor delas;
  • Complexidade de tempo: Quão eficiente em tempo de execução;
  • Complexidade de espaço: Armazenamento necessário no pior caso;

Os tipos de busca podem ser informada ou não informada. No primeiro caso não temos informação sobre o domínio do problema, isto é, deve-se proceder através de força-bruta ou busca cega. São algoritmos desse tipo: Busca em largura, busca em profundidade (com limitação ou iteração), busca bidirecional e busca por custo uniforme. No segundo, também chamado de busca heurística ou busca direta, há detalhes conhecidos sobre como encontrar o objetivo, quantos passos, o custo dos caminhos, etc… Isso obviamente torna o processo mais eficiente. Pode-se implementar funções heurísticas. Nesse modelo temos os tipos: Algoritmo guloso best-first e $A^*$.

22.4 - Estratégias de Busca, Busca Cega e Busca Heurística.

Busca cega ou busca não uniforme é um tipo de pesquisa quando não há informações sobre o domínio do problema, onde a única diferença a se considerar é se um estado é o procurado ou não. Ao transformarmos um determinado problema em um grafo, por exemplo, no primeiro nível de busca, os nós ligados ao elemento inicial, não há preferência de qual será selecionado para exploração e estratégias devem ser definidas para melhorar sua eficiência. Alguns exemplos de estratégia:

  • Busca em largura
  • Busca de custo uniforme
  • Busca em profundidade
  • Busca em profundidade com limite
  • Busca iterativa em profundidade (combina busca em largura e em profundidade)

Ao contrário dos sistemas especialistas em conhecimento, onde sempre é melhor ter mais dados sobre determinado domínio para melhorar o comportamento do sistema e seus resultados, a busca heurística segue outro caminho, utilizando funções que criam pesos em cada ramificação para procurar a melhor solução rapidamente. Uma resposta aproximada pode ser encontrada de forma muito mais rápida do que utilizando um algoritmo otimizado e preciso. O objetivo da heurística é encontrar uma solução boa o suficiente, segundo algum critério pré-definido, ao invés de buscar a melhor solução. Busca utilizando heurística pode ser combinada com outros modelos para prover maior eficiência de tempo e qualidade.

22.5 - Hill climbing, best first, simulated annealing e Algoritmo A*.

A técnica chamada Hill Climbing é utilizada em processos de análise numérica para busca local. É um tipo de algoritmo iterativa que utiliza uma solução qualquer para o problema e vai refinando-a através de mudanças incrementais até uma dada solução, ou seja, até não encontrar um valor melhor do que o anterior, dados os critérios de busca. A melhor solução é chamada de ótimo global, enquanto que uma solução encontrada em uma vizinhança é chamada de ótimo local. O algoritmo simplex utiliza essa técnica.

Best first é uma técnica de busca que expande um determinado nó em um grafo com base em regra pré-definida. Essa regra pode ser uma heurística, que define uma função $f(n)$ dado que $n$ é o nó em análise e também utiliza informações sobre o objetivo, os nós anteriores e qualquer outro dado sobre o problema. Desta forma, o algoritmo implementando pode avaliar se em um dado nó a solução parece estar próxima ou não e continuar explorando o caminho mais provável. Os algoritmos $A^*$ e $B^*$.

A técnica simulated annealing (SA) é utilizada para aproximações probabilísticas para encontrar um ótimo global de uma dada função. É comum de ser usada quando o espaço de busca é discreto. É mais eficiente quando o foco está em encontrar o ótimo global, ao invés de outros parâmetros, porém, soluções piores podem ser aceitas.

O algoritmo $A^*$ (A-star) é do tipo pesquisa em grafo que tem complexidade em espaço do tipo $O(b^d)$, mas é eficiente em encontrar boas soluções dentre as opções. É considerado com um extensão ao algoritmo de Dijkstra para busca. Utiliza heurísticas para melhorar a pesquisa. Foi desenvolvido originalmente para encontrar rotas com menor soma de caminhos.

22.6 - Busca como Maximização de Função.

Otimização de funções é uma área de intensa pesquisa em matemática, principalmente quando falamos de métodos numéricos e computacionais. O objetivo é encontrar soluções ótimas de uma função dadas determinadas restrições. No caso de um busca, podemos modelar o problema como uma pesquisa por uma determinada otimização em um conjunto de dados. Ao encontrar um dado $\vec{x}$ onde $f(\vec{x})$ é máximo ou mínimo, estamos otimizando essa função. Há três elementos envolvidos: a entrada, a função e o valor de saída (ou custo).

Algumas restrições que devemos avaliar são: quantidade de variáveis de entrada, tipo das variáveis e intervalo aceito de valores. As soluções candidatas podem ser refinadas com base em maiores informações do problema, reduzindo o conjunto de possíveis variáveis. O espaço de busca é o universo de todas as soluções candidatas, definidas por tamanho, tipo e intervalo. Podemos ordenar essas candidatas com base em alguma função objetivo, descartando aquelas em pior posição.

A função objetivo recebe uma entrada e devolve um custo, porém, é específica para o domínio do problema avaliado. Pode ser custosa para avaliar certas entradas, ou seja, não é simples otimizá-la. A superfície de resposta é uma propriedade geométrica do custo com relação à função objetivo na medida que mudamos as soluções candidatas. Podemos avaliar essas relações com gráficos, procurando por picos ou vales, dependendo do interesse. É também através dessas estruturas que podemos determinar a dificuldade em navegar pelo espaço de busca.

Ao analisarmos determinados custos, devemos separar entre ótimos locais e globais, levando em conta questões de eficiência do algoritmo e intervalo de aproximação.

22.7 - Grafos And/Or.

Um grafo (ou árvore) tipo AND-OR é uma representação útil para solução de problemas que podem resolvidos por um processo de decomposição em problemas menores. Essa decomposição, ou redução, gera arcos que são chamados de arcos AND e podem apontar para qualquer número de nós sucessores que devem ser resolvidos. Analogamente, em um grafo OR, múltiplos arcos podem emergir de um nó único, indicando diversas formas em que um problema pode ser resolvido.

22.8 - Esquemas para Representação do Conhecimento: Lógicos, em Rede, Estruturados, Procedurais.

Ao desenvolver sistemas inteligentes precisamos criar modelos de como representar o conhecimento de seu ambiente. Agentes que possuem conhecimento podem tomar melhores decisões, mas precisam de estruturas para armazenar e operar sobre informações. O sistema deve ser desenhado de tal forma que o agente possa receber uma entrada por sensores, consultar a base de conhecimento, atualizar dados se necessário e inferir uma ação, por exemplo. A base de conhecimento pode ter uma estrutura de sentenças, na linguagem de representação de conhecimento.

O processo de inferência deriva novas sentenças das já existente e permite que o agente adquira novos fatos sobre o mundo e possa atualizar seu conhecimento sobre eventos desconhecidos.

As ações principais em uma base de conhecimento são: Tell, Ask, Perform. O agente recebe uma entrada e consulta por uma ação como resultado. Há várias camadas em um agente com base em conhecimento:

  • Camada conhecimento: primeiro nível, onde especificamos o que o agente sabe e seus objetivos;
  • Camada lógica: indica como o conhecimento é armazenado e codificado em sentenças;
  • Camada implementação: possui a representação física de conhecimento e lógica.

São dois modos principais de construção do agente com base em conhecimento: declarativo e procedural. No primeiro modo, o agente é inicializado sem sentenças e então recebe informações externas do que fazer. No último, o comportamento está codificado diretamente no agente.

Os elementos que podem ser representados são: objetos, eventos, performance, metaconhecimento e fatos.

22.9 - Sistemas de Produção com Encadeamento para a Frente e Encadeamento para trás (forward and backward chaining).

Em construção.

22.10 - Raciocínio Não-Monotônico.

Raciocínio monotônico é um processo que não muda de direção, ou seja, mantém de forma contínua na mesma direção, aumentando ou diminuindo de magnitude. O caso não monotônico é o inverso, muda de direção se o conhecimento sobre o problema aumenta. É uma forma de modelar como as conclusões são modificadas na medida que recebemos mais dados sobre o problema. Ela lida com modelos incompletos ou incertos.

22.11 - Formalismos para a Representação de Conhecimento Incerto.

Em construção.

22.12 - A Regra de Bayes.

Em construção.

22.13 - Conjuntos e Lógica Fuzzy.

Em construção.

22.14 - Aprendizado de Máquina.

Um mapa de auto-organizável é uma técnica sem supervisão que é usada para representação em baixas dimensões de um conjunto de dados de altas dimensões sem destruir a estrutura topológica dos dados. Os dados podem ser representados em grupos (clusters) para observação de valor similares que são visualizados em mapas bi-dimensionais, facilitando a análise e observação. É um tipo de rede neural artificial, mas é treinanda para aprendizado competitivo ao invés de aprendizado por correção de erro. Esse tipo de técnica foi desenvolvida por Teuvo Kohonen na década de 1980. Algumas terminologias utilizadas:

  • Agrupamento: Uma forma de organização de classes na camada de saída em um Mapa de Kohonen. Os nós podem ser normalmente organizados em forma de grade;
  • Aprendizado competitivo: Somente um neurônio da rede fornece a resposta para uma determinada entrada;
  • Neurônio vencedor: Modelo que deixa um único neurônio ativo de saída (ou de um grupo) executando a saída para um determinado subconjunto de dados;
  • Redes recorrentes: Nós de saída podem se conectar aos nós de entrada.
  • Vizinhança: Define a área de influência em um neurônio, ajustando como seus pesos são ajustados.
22.15 - Aprendizado Indutivo.

Em construção.

22.16 - Árvores de Decisão, Redes Neurais e Algoritmos Genéticos.

Em construção.

22.17 - Sistemas Especialistas.

Em construção.

22.18 - Processamento de Linguagem Natural.

Em construção.

22.19 - Agentes Inteligentes.

Em construção.

22.20 - Robótica.

Em construção.