Em uma conversa recente com Joscha Bach , um dos pensadores mais originais desta geração, ele fez a afirmação surpreendente de que a simultaneidade móvel está em desacordo com a inteligência artificial geral . Neste contexto, a simultaneidade móvel significa o tipo de simultaneidade que se encontra quando os agentes (também conhecidos como processos computacionais) podem descobrir uns aos outros, que é a topologia de comunicação (quem sabe quem e quem está falando com quem) está evoluindo. Este modelo é muito diferente de um modelo onde os elementos computacionais são soldados juntos como componentes de uma placa-mãe. A simultaneidade móvel é mais como a Internet ou as redes de telefonia onde as pessoas que acabaram de se conhecer pela primeira vez ficam sabendo dos sites, endereços de e-mail e números de telefone umas das outras. O argumento de Joscha é que os cérebros são apenas plásticos, ou seja, as conexões entre os neurônios mudam apenas durante o aprendizado, mas não durante a computação geral.
Joscha Bach , um dos pensadores mais originais de sua geração, fez a afirmação surpreendente de que a simultaneidade móvel está em desacordo com a inteligência artificial geral
Minha resposta a essa proposição é que ela assume que a mente não está hospedada em um modelo lógico de computação executado no hardware do cérebro. Afinal, a máquina virtual Java (JVM) é um modelo de computação muito diferente do hardware em que é executada. O modelo de computação de Haskell é uma variação ainda mais dramática da ideia de computação do que aquela incorporada no hardware em que o glorioso compilador Haskell ( GHC ) normalmente é hospedado. Por que a mente não seria organizada assim? Para usar a metáfora gráfica de Joscha, por que a mente não surgiria como um modelo computacional colonizador que se hospeda no hardware do cérebro? Se for, bem, rholang, uma implementação do rho-calculus, está hospedado em chips Intel e AMD para começar, e seus modelos computacionais são muito diferentes daquele capturado pelo rho-calculus.
Em particular, o cálculo ro fornece suporte direto para computação concorrente móvel. A topologia de comunicação entre uma sociedade de processos executando no ro-cálculo é dinâmica. Quem conhece quem e pode falar com quem nesta sociedade muda ao longo da computação. Essa maneira de pensar sobre o cálculo-ro antecipa meu argumento de que há boas razões para supor que um modelo como o cálculo-ro pode ter se hospedado e colonizado o hardware do cérebro humano, na verdade, qualquer cérebro que suporte um teoria da mente.
Para fazer esse argumento, quero fazer algumas distinções que nem todo cientista da computação, muito menos todo desenvolvedor, faz. Eu faço uma distinção entre código, dados e computação. O código arbitrário pode ser tratado como alguns dados, que são uma instância de algum tipo de estrutura de dados na qual o modelo de computação é expresso ou hospedado. Por exemplo, você pode hospedar um modelo computacional Turing-completo , como Haskell , em uma linguagem expressa por meio de uma gramática livre de contexto, por exemplo, a gramática para programas Haskell bem formados. No entanto, sabemos que as gramáticas livres de contexto não são Turing-completas. Como isso pode ser? Como pode algo comprovadamente menos expressivo do que os modelos Turing-completos representar a computação Turing-completa?
Ela atinge o cerne da distinção entre sintaxe e semântica. A gramática do termo linguagem expressa a sintaxe dos programas, não a dinâmica da computação , ou seja, a semântica do código. Em vez disso, a dinâmica da computação surge pela interação de regras (que operam sobre a sintaxe) com um determinado pedaço de sintaxe, ou seja, algum código, representando a computação que se deseja efetuar. No cálculo lambda (o modelo de computação no qual Haskell se baseia), o cavalo de batalha da computação é uma regra chamada redução beta. Esta regra representa a operação de uma função em dados através do ato de substituir os dados por variáveis que ocorrem no código. Os dados sobre os quais opera são uma representação sintática da aplicação de uma função aos dados, mas não é a computação que corresponde à aplicação da função aos dados. Essa computação acontece quando a redução beta opera na sintaxe, transformando-a em um novo pedaço de sintaxe. Essa distinção é como modelos que são menos expressivos que o Turing-completo (por exemplo, gramáticas livres de contexto) podem hospedar a computação Turing-completa.
Como pode algo comprovadamente menos expressivo do que os modelos Turing-completos representar a computação Turing-completa? Ela atinge o cerne da distinção entre sintaxe e semântica. A gramática do termo linguagem expressa a sintaxe dos programas, não a dinâmica da computação, ou seja, a semântica do código.
Sem insistir no ponto, mas a mesma distinção acontece em Java e JVM. A dinâmica de computação na JVM acontece através de regras que operam sobre uma combinação de registradores na máquina virtual juntamente com uma representação do código. Um programador Java olhando para um pedaço de código Java não está olhando para a computação. Longe disso. A sintaxe de um programa Java é uma janela para toda uma gama de computações possivelmente diferentes que ocorrem dependendo do estado dos registradores da JVM no momento em que o código é executado. A diferença entre essas duas formas de avaliação, redução beta no cálculo lambda versus as transições da JVM, é muito importante, e voltaremos a ela.
Por enquanto, porém, uma maneira de pensar sobre essa distinção entre código e computação é por meio de uma analogia com a física. Tradicionalmente, as leis da física são expressas por meio de três coisas: uma representação de estados físicos (pense nisso como a sintaxe dos programas), leis de movimento que dizem como os estados mudam ao longo do tempo (pense nisso como as regras que operam na sintaxe) ; e condições iniciais (pense nisso como uma parte específica do código que você deseja executar). Sob essa ótica, a física é vista como uma linguagem de programação de propósito especial cuja execução corresponde de uma maneira particular à maneira como o mundo físico evolui com base em nossas observações. A física é testável porque nos permite executar um programa e ver se a evolução de algum estado inicial para um estado alcançado por meio das leis do movimento corresponde às nossas observações. Em particular, quando vemos o mundo físico em uma configuração que corresponde ao nosso estado inicial, ele passa por um processo de evolução que corresponde ao que nossas leis de movimento dizem que deveria e aterrissa em um estado que nossas leis de movimento dizem deveria? O fato de a física ter essa forma é o motivo pelo qual podemos representá-la efetivamente no código.
A analogia da física coloca os modelos de computação como o cálculo lambda, o cálculo π ou o cálculo ro em contraste com modelos como as transições da JVM. Quando você está olhando para um trecho de código Java, não tem todas as informações necessárias para entender como ele se comportará. Dependendo do estado da JVM, o mesmo trecho de código Java pode se comportar de maneira muito diferente. Em contraste, os cálculos computacionais mencionados acima casam a forma com a função. O que você vê em uma expressão é o que você obtém. Do ponto de vista da física, como cientista, a única coisa que você precisa para fazer engenharia reversa são as leis do movimento, também conhecidas como regras da computação. Você não precisa adivinhar um monte de estado oculto.
Uma vez que vemos a distinção entre código e computação, a distinção entre código e dados é relativamente intuitiva, embora um tanto sutil. Os dados em um programa de computador também são apenas sintaxe. Nesse sentido, não é diferente do código, que também é apenas sintaxe. Todo programador Lisp entende essa ideia de que, de alguma forma, código é dado e dados são código. Mesmo Java suporta um tipo de metaprogramação em que o código Java pode ser manipulado como objetos Java. A questão é: existe alguma linha divisória real entre código e dados?
A resposta é um sim definitivo. Dados são códigos que possuem propriedades muito específicas; por exemplo, o código sempre é comprovadamente executado até o término. Nem todo código faz isso. De fato, a famosa resolução de Turing do Entscheidungsproblem nos mostra que não podemos, em geral, saber quando um programa será interrompido para uma linguagem que desfruta de uma certa qualidade de expressividade, isto é, Turing-completude. Porém, existem linguagens menos expressivas, e as linguagens Turing-completas desfrutam de sublinguagens ou fragmentos adequados que são menos expressivos do que a linguagem inteira. Os dados residem na sintaxe que permite provar que a computação associada a uma parte da sintaxe será interrompida. Da mesma forma, os dados residem em uma sintaxe que permite provar que a computação terá apenas ramificações finitas.
Os programadores não pensam em dados assim, eles apenas reconhecem os dados quando os veem. Mas em modelos de computação como o cálculo lambda que não vêm equipados com tipos de dados embutidos, tudo, até mesmo coisas como os números de contagem ou os valores booleanos, verdadeiro e falso, são representados como código. Escolher qual código constitui dados e qual constitui programas de uso geral tem a ver com a capacidade de detectar quando o código possui os tipos de propriedades que discutimos acima. Em geral, existem tipos de sistemas que podem detectar propriedades como essa. Novamente, é uma questão sutil, mas, felizmente, não precisamos entender todas as sutilezas, nem precisamos entender exatamente onde está a linha divisória entre dados e código, apenas que existe uma.
Em resumo, código e dados são apenas sintaxe que representa um estado no qual uma regra, ou muitas regras, irão operar. Os dados são expressos em um fragmento menos expressivo da sintaxe do que o código, dando a eles um caráter definido ou finitário que o código nem sempre possui. A computação é o processo de evolução que surge quando algumas regras interagem com uma representação de um estado. Agora, o que tudo isso tem a ver com a IA ou a mente ou mesmo o ro-cálculo?
O ro-cálculo tem uma representação sintática da distinção entre computação e código. Possui uma operação que expressa o empacotamento de uma computação como um pedaço de código para que possa ser operada, transformando-a em um novo código. Ele também tem uma operação para transformar um pedaço de código de volta em uma computação. Uau, você pode dizer, isso é uma merda de próximo nível. Mas, como mencionamos, os programadores Lisp e os programadores Java fazem esse tipo de metaprogramação há muito tempo. Eles tem que. A razão tem a ver com a escala. É impossível para equipes humanas gerenciar bases de código envolvendo milhões e milhões de linhas de código sem suporte automatizado. Eles usam programas de computador para escrever programas de computador. Eles usam programas de computador para criar implantações de programas de computador. Metaprogramação é uma necessidade no mundo de hoje.
O argumento de Smith é que a introspecção, a capacidade da mente de olhar para o próprio processo da mente, é uma característica fundamental da inteligência. Para alguns, essa é até a característica definidora da inteligência.
Mas nos anos 80, ainda nos primórdios da IA, um pesquisador chamado Brian Cantwell Smith fez uma observação que ressoou em mim e em muitas outras pessoas em IA e áreas adjacentes à IA. O argumento de Smith é que a introspecção, a capacidade da mente de olhar para o próprio processo da mente, é uma característica fundamental da inteligência. Para alguns, essa é até a característica definidora da inteligência. Para concretizar essa ideia de introspecção, que ele chamou de reflexão computacional, Smith projetou uma linguagem de programação chamada 3-Lisp que possui os mesmos operadores do cálculo ro. Especificamente, 3-Lisp tem sintaxe para representar a reificação de uma computação em código e sintaxe para refletir o código de volta na execução da computação.
Agora, há uma boa razão para suspeitar que existe uma conexão entre o problema de escala que os desenvolvedores de hoje enfrentam e o problema de modelar nossa capacidade reflexiva e introspectiva como seres racionais. Em particular, gerenciar a complexidade de representar nosso próprio raciocínio torna-se tratável na presença de reflexão computacional. Podemos aplicar todos os nossos truques algorítmicos a representações de nosso próprio raciocínio para obter um raciocínio melhor. Essa observação é ampliada no contexto do que os biólogos evolutivos chamam de teoria da mente .
Especificamente, a introspecção surge da vantagem evolutiva obtida por ser capaz de modelar computacionalmente o comportamento de outras pessoas, em particular, membros de sua própria espécie. Se Alice desenvolver a capacidade de modelar o comportamento de Bárbara, e Bárbara for notavelmente semelhante a Alice (como na mesma espécie, mesma tribo, até mesmo na mesma estrutura familiar extensa), então Alice está muito perto de ser capaz de modelar o comportamento de Alice. E quando Alice precisa modelar o comportamento de Barbara quando Barbara está interagindo com Alice, então Alice está diretamente envolvida na modelagem do comportamento de Alice. Levando isso a uma escala onde Alice pode modelar sua unidade familiar ou o comportamento de sua tribo é onde as coisas ficam realmente interessantes. Mais sobre isso em breve, mas por enquanto, podemos ver que algo sobre reflexão computacional tem a ver com melhorar o raciocínio em escala em dois sentidos da palavra: (a escala de complexidade) melhorar o raciocínio aplicando o raciocínio a si mesmo e (a escala social ) melhorando o raciocínio sobre um grande número de agentes de raciocínio.
De fato, as ideias de Smith sobre reflexão computacional e seu papel na inteligência e no projeto de linguagens de programação foram uma inspiração para o projeto do ro-cálculo, que considera a reificação e a reflexão como operadores computacionais primitivos. No entanto, onde o 3-Lisp e o rho-calculus partem juntos, o 3-Lisp é decididamente seqüencial. Não tem como representar razoavelmente uma sociedade de processos computacionais autônomos rodando independentemente enquanto interagem e coordenam. Mas no contexto de uma teoria da mente, isso é exatamente o que um raciocinador precisa fazer. Eles precisam de um modelo explícito de seu contexto social, que é composto por agentes autônomos que agem de forma independente, ao mesmo tempo em que se comunicam e coordenam.
Mais ou menos na mesma época em que Smith desenvolvia suas ideias de reflexão computacional, Marvin Minsky desenvolvia sua famosa tese sobre a Sociedade da Mente. Minha opinião sobre a proposta de Minsky é que a mente é algo como o Congresso dos Estados Unidos ou qualquer outro corpo deliberativo. Consiste em um grupo de agentes independentes que estão disputando diferentes recursos (como financiamento da base tributária). O que pensamos como uma decisão consciente é mais como o resultado de um longo processo deliberativo entre um bando de agentes independentes e autônomos que muitas vezes ocorre bem abaixo da experiência consciente. Mas, o processo deliberativo resulta em um voto vinculativo, e esse voto vinculante é o que é experimentado como uma decisão consciente.
Como essa visão, que coloca a maior parte da computação fora do raciocínio consciente, pode ser reconciliada com uma visão da mente como essencialmente, de fato, reflexiva por definição? O cálculo de ro foi projetado com uma resposta para esta pergunta em mente.
O cálculo ro diz que os agentes computacionais vêm em apenas seis formas:
Observe como três dessas construções usam o símbolo x. Dois deles usam x como se fosse um canal de comunicação entre agentes, e um deles usa x como se fosse uma referência a um trecho de código. O único truque de mágica que o ro-cálculo tem na manga é que os canais são referências a um trecho de código. Demora um pouco para se acostumar, mas vem com o tempo.
Como observadores externos do contexto social de Alice, podemos anotar seu comportamento como uma composição paralela do comportamento de cada indivíduo. Em símbolos é P1 | P2 | … | Pn onde Pi é o modelo do i-ésimo indivíduo no contexto social de Alice. Agora, um modelo do comportamento de Alice precisa de uma representação dessa composição paralela para que seu próprio comportamento represente o raciocínio sobre isso. Em símbolos, isso é @( P1 | P2 | … | Pn ).
Armados apenas com essa quantidade de informações sobre o ro-cálculo, podemos retornar à nossa narrativa sobre Alice e encontrar representações parcimoniosas de todos os desafios enfrentados pelo desenvolvimento de sua inteligência social e introspectiva. Como observadores externos do contexto social de Alice, podemos anotar seu comportamento como uma composição paralela do comportamento de cada indivíduo. Em símbolos, é P1 | P2 | … | Pn, onde Pi é o modelo do i-ésimo indivíduo no contexto social de Alice. Agora, um modelo do comportamento de Alice precisa de uma representação dessa composição paralela para que seu próprio comportamento represente o raciocínio sobre isso. Em símbolos, isso é @( P1 | P2 | … | Pn ). Para Alice ter esses dados localizados em algum lugar que ela tenha acesso, ela coloca o modelo em um canal x!( P1 | P2 | … | Pn ), e quando ela precisa recuperá-lo, ela executa
for( y <- x )AlicePensandoNosColegas( y ) | x!( P1 | P2 | … | Pn )
A regra laboriosa da computação no cálculo ro, que é muito semelhante em espírito à redução beta do cálculo lambda, é que uma expressão como essa evolui para
AlicePensandoNosColegas( @( P1 | P2 | … | Pn ) )
Então, agora os pensamentos de Alice sobre seus colegas têm uma representação explícita de seu comportamento disponível para Alice. Com ele, ela pode simular o comportamento de seus colegas, simulando o comportamento de P1 | P2 | ... | Pn através de operações em @( P1 | P2 | ... | Pn ). Podemos modelar Alice observando o comportamento real de seu colega com uma expressão como Alice | P1 | P2 | ... | Pn. Alice pode comparar sua simulação com suas observações. Na verdade, tudo o que podemos modelar também está disponível para Alice tanto para executar quanto para reificar em dados e comparar o código e suas simulações com o que ela observa do comportamento real de seu contexto social. Isso inclui o próprio comportamento de Alice.
Isso pode ter passado um pouco rápido, mas pense nisso. Este é o menor conjunto de operações necessário para Alice modelar simultaneamente seu contexto social e a si mesma nele. Em particular, os encadeamentos estão 'disponíveis conscientemente' para Alice justamente quando seu próprio comportamento reifica esses encadeamentos em dados e seu processamento interage com esses dados. Este argumento é parte do que entrou nas deliberações de projeto do ro-cálculo. É o menor modelo de computação que reconcilia os argumentos de Smith para a reflexão computacional com os argumentos de Minsky para uma Sociedade da Mente que se encaixa na explicação da biologia evolutiva dos organismos com uma teoria da mente. Qualquer coisa menor perde um componente-chave da situação.
Esse argumento é o motivo pelo qual é plausível que um modelo de computação como o ro-cálculo encontre apoio no hardware do cérebro de Alice. Ela precisa de todos os elementos desse modelo para competir com outros membros de sua espécie que também estão correndo para modelar o comportamento de seu contexto social. É por isso que, muito ao contrário da posição de Joscha, eu argumentaria que a simultaneidade móvel está no cerne da inteligência artificial geral.
Muita gratidão a Ralph Benko por seus comentários editoriais infalivelmente astutos!