Sumário
Nesse exercício foi construída uma simples aplicação para entender os conceitos básicos que envolvem simulações na arquitetura CLARION. O primeiro programa usado é o SimpleHelloWorld que foi escrito usando um unico método. Esse programa usa somente o mecanismo de ACS (Action Centered Sybsystem), que é responsável por controlar as ações do agente no mundo virtual, tanto aquelas relacionadas a movimentos físicos externos ou a operações mentais.
O objetivo do programa SimpleHelloWorld é dizer "Hello", quando alguém diz "hello" para ele, ou "goodbye
", quando alguém diz "goodbye" para ele. Seu funcionamento básico se dá pelo uso da mecanismo ACS com reforço do treinamento da rede backpropagation no bottom level e nas regras extraídas e refinadas (RER - rule extraction and refinement) com o intuito de extrair regras baseadas no reforço. No final da tarefa do agente, seu objetivo deve ser representado como regras no top level do ACS. Outro ponto importante é que o agente não precisa ter nenhum conhecimento a priori sobre a dinâmica da tarefa a ser realizada. Esse é um dos pontos em que a arquitetura CLARION se diferencia da arquitetura Soar, onde é necessário ter conhecimento a priori.
O primeiro passo para poder trabalhar com o programa SimpleHelloWorld foi adicioná-lo a um projeto da linguagem C#. Foi usada essa linguagem porque a bilioteca da arquitetura CLARION foi programada nela. Para poder trabalhar com essa linguagem foi usado o ambiente de desenvolvimento MonoDevelop que é open-source e pode ser encontrado no seguinte endereço web: http://monodevelop.com/.
Para criar um projeto nesse ambiente de desenvolvimento, depois de abri-lo clica-se em Ficheiro -> New -> Soluction. Depois disso, na Janela que for aberta, deve-se selecionar a opção C# no painel Modelo e depois clicar na opção Projeto Modo Texto.
Depois que foi criado o projeto, adicionou-se o arquivo SimpleHelloWorld. Para isso, clicou-se com o botão direito do mouse no nome do projeto e depois em Add -> Add Files. Então, seleciou-se o arquivo HelloWorld - Simple.cs que se encontra no diretório samples/Beginner/ que fica na raiz da pasta biblioteca CLARION. Também foi necessário adicionar a biblioteca CLARION ao projeto. Para isso, clicou-se com o botão direito do mouse na pasta Referências no Solution Explorer do MonoDevelop. Depois disso clicou-se na opção Edit References e então, na Aba .Net Assembly, selecionou-se o arquivo CLARIONLibrary.dll que fica localizado na pasta raiz da biblioteca CLARION. Por fim, para finalizar o projeto, como o próprio arquivo HelloWorld - Simple.cs já é o método principal, então excluiu-se o método Main que foi adicionado automaticamente na criação do projeto. Veja na Figura 1.1 a aparência do projeto após seguir todas as etapas mencionadas.
Figura 1.1: Projeto Hello World.
Apesar de já ter adicionado a biblioteca CLARION ao projeto, para usá-la deve-se importá-la por meio do comando using da linguagem C#. Então, no cabeçalho do arquivo HelloWorld - Simple.cs foi adicionado o código apresentado abaixo:
using Clarion;
using Clarion.Framework;
Os namespaces Clarion e Clarion.Framework contêm a maioria das classes necessárias para a implementação de agentes que usem a arquitetura CLARION.
A classe principal do programa SimpleHelloWorld contém o código de inicialização do mundo, do agente e os mecanismos que permitem a interação do agente com o mundo. Essa classe foi declarada da segunte forma:
public class SimpleHelloWorld;
Veja na Figura 1.2 o cabeçalho, a declaração da classe SimpleHelloWorld e seus códigos iniciais.
Figura 1.2: Código inicial da classe SimpleHelloWorld.
Nos códigos iniciais da classe SimpleHelloWorld são feitas declarações de variáveis e configurações que serão usadas durante a simulação. Por exemplo, na linha 27 do código apresentado na Figura 1.2, está sendo definido que o resultado da simulação será gravado em um arquivo chamado HelloWorldSimple.txt.
Para representar o ambiente de simulação devem ser usados pares dimensão-valor (dimension-value pairs). No programa SimpleHelloWorld a dimensão é representada pelo nome Salutation e o valor que ele pode ter é Hello ou Goodbye. Essa relação foi criada usando o seguinte código:
DimensionValuePair hi = World.NewDimensionValuePair("Salutation", "Hello");
DimensionValuePair bye = World.NewDimensionValuePair("Salutation", "Goodbye");
Depois de definir os pares dimensão-valor, é necessário definir quais ações estão relacionadas a esses pares. No programa SimpleHelloWorld, ao receber uma saudação salutation, a ação a ser tomada pelo agente é responder em>Hello ou Goodbye. Para definir as ações deve ser usado o método NewExternalActionChunk, conforme mostrado abaixo:
ExternalActionChunk sayHi = World.NewExternalActionChunk("Hello");
ExternalActionChunk sayBye = World.NewExternalActionChunk("Goodbye");
Após definir o ambiente da simulação e as ações para cada par dimensão-valor, é necessário inicializar o agente. Para isso, foi usado o código apresentado abaixo:
Agent John = World.NewAgent("John");
O agente do exemplo usado nessa seção foi chamado de John. Porém, até esse ponto esse é um agente vazio, pois não possui nenhum conhecimento sobre os pares dimensão-valor, nem sobre as ações definidas anteriormente, além de não possuir nenhum mecanismo funcional para que ele interaja com o mundo. No exemplo dessa seção, o único mecanismo funcional que precisa ser inicializado é uma rede de decisão implícita (implicit decision network - IDN) que fica no bottom level do ACS e precisa ser configurada por meio de uma rede neural backpropagation. Para isso, foi usado o código apresentado abaixo:
SimplifiedQBPNetwork net = AgentInitializer.
InitializeImplicitDecisionNetwork(John, SimplifiedQBPNetwork.Factory);
Observe que a rede neural utilizada foi SimplifiedQBPNetwork, que é uma rede neural backpropagation que está localizada no namespace Clarion.Framework da biblioteca CLARION. Outro ponto importante é que para a inicialização da IDN foi passado o nome do agente e um gerador (factory) que é usado para contruir a rede desejada. Com isso, a função AgentInitializer.InitializeImplicitDecisionNetwork retornará um componente implícito do tipo que é especificado pelo gerador. Esse compontente é armazenado na variável net, que pode ser considerada parte do agente.
Depois de inicializar o agente, é necessário fornecer ao mesmo a habilidade de escolher qual ação deve ser tomada baseado nas informações sensoriais que ele recebe do mundo. No exemplo dessa seção, existem dois pares dimensão-valor. O método Input.Add deve ser usado para definir as possíves dimensões (saudação) e o método Output.Add deve ser usado para definir o valor associado a cada dimensão (resposta para cada saudação). Então, foi usado o código apresentado abaixo:
net.Input.Add(hi);
net.Input.Add(bye);
net.Output.Add(sayHi);
net.Output.Add(sayBye);
Depois de inicializar a variável net, deve-se avisar o agente que ele pode começar a usar a rede neural por meio do seguinte código:
Outro ponto importante é o ajuste dos parâmetros. Esse ajuste pode variar dependendo da aplicação. No programa SimpleHelloWorld esse ajuste foi feito pelo seguinte código:
net.Parameters.LEARNING_RATE = 1;
John.ACS.Parameters.PERFORM_RER_REFINEMENT = false;
Com as etapas apresentadas até o momento, o agente e o mundo virtual foram configurados. Então, agora é preciso definir como o agente irá interagir com o mundo. O código inicial dessa etapa é apresentado na Figura 2.3.
Figura 1.2: Código inicial da etapa de interação com o mundo virtual.
No código da figura acima, são configuradas algumas variáveis úteis para a simulação. Uma delas é a rand que recebe o valor de um gerador de números aleatórios, com o objetivo de escolher a configuração da informação sensorial. Outra variável é a si que é um ponteiro de informação sensorial para o ciclo atual de percepção e ação. Por fim, a variável chosen captura a ação escolhida pelo agente.
A parte principal da etapa de iteração do agente com o mundo virtual ocorre no laço de repetição apresentado na Figura 1.4. Esse laço representa o fluxo da tarefa e dentro dele, a informação sensorial é configura para cada ciclo de percepção e ação. Depois a ação escolhida é capturada, um feedback é retornado e a performance do agente é armazenada.
Figura 1.4: Fluxo da tarefa realizada pelo agente.
Na Figura 1.4, do lado direito do código, são apresentados os números das linhas. Para comentar o código, serão mencionados esses números.
No código apresentado na Figura 1.4, cada iteração corresponde a ciclo de percepção e ação. A primeira coisa desse ciclo é a obtenção da informação sensorial obtida pela interação com o mundo. Para isso é usado o método NewSensoryInformation que recebe como parâmetro o nome do agente que deve receber as informações sensoriais, conforme pode ser visto na linha 62 do código apresentado na Figura 1.4.
As estruturas de controle apresentadas entre as linhas 64 e 76 do código mostrado na Figura 1.4 são usadas para decidir se o agente irá receber como saudação um "hi" ou "bye". Essa decisão é feita aleatoriamente, pois cada saudação tem uma chance de 50% de ser escolhida.
Depois que a saudação que o agente irá receber é escolhida, essa informação sensorial é percebida pelo agente, por meio do código apresentado na linha 79. Após isso, a ação que será tomada pelo agente é retornada pelo método GetChosenExternalAction, como pode ser visto na linha 82.
Após a escolha da ação que será tomada pelo agente, a estrutura de controle que está entre as linhas 84 e 124 determina as consequências da ação, punindo ou recompensado o agente. Observe também que, para dar um feedback para o agente, é usado o método ReceiveFeedback. Quando esse método é chamado, inicia-se automaticamente uma rodade de aprendizado internamento no agente.
Os linhas finais do código aprensentado na Figura 1.4, estão apenas atualizando o progresso da simulação e imprimindo os resultados.
A última etapa do programa SimpleHelloWorld é interromper o agente. Para isso, foi utilizado código mostrado abaixo, que encerra todos os processos do agente.
Executando o programa SimpleHelloWorld que foi apresentado nessa seção, obtém-se como resultado o que está sendo apresentado na Figura 1.5.
Figura 1.4: Execução do programa SimpleHelloWorld.
Nessa seção será discutido como usar e configurar a estrutura de objetivos (goal strutucture). Essa é uma estrutura interna do agente que contém seus objetivos na forma de objetos do mundo do tipo GoalChunk. Essa estrutura fica dentro do top level do MS (motivacional subsystem), mas sua interação com o mundo ocorre por meio da classe Agent. Isso pode ser comprovado, pois para visualizar o conteúdo da estrutura de objetivos deve-se usar o método GetInternals. Veja um exemplo de como isso pode ser feito com o agente do programa SimpleHelloWorld:
//Gets all of the items in the goal structure
IEnumerable gsContents =
(IEnumerable)John.GetInternals
(Agent.InternalWorldObjectContainers.GOAL_STRUCTURE);
//Gets the current goal
GoalChunk currentGoal = John.CurrentGoal;
Como mencionado anteriormente, os objetivos são representados como chunks usando a classe GoalChunk. Além disso, eles são inicializados através de um objeto World, conforme mostrado abaixo:
GoalChunk g = World.NewGoalChunk();
Existem também dois parâmetros que podem ser usados para ajustar a estrutura de objetivos. Esses parâmetros estão localizados na classes parameters do MotivationalSubsystem e podem ser configurados da seguinte forma:
John.MS.Parameters.CURRENT_GOAL_ACTIVATION_OPTION =
MotivationalSubsystem.CurrentGoalActivationOptions.FULL;
John.MS.Parameters.GOAL_STRUCTURE_BEHAVIOR_OPTION =
MotivationalSubsystem.GoalStructureBehaviorOptions.STACK;
Para inicializar um objetivo deve-se usar o método World.NewGoalChunk. Depois de inicializá-lo pode-se usá-lo como parte da entrada para um componente, tal como mostrado no código abaixo:
... //Elided code initializing other world objects GoalChunk
g = World.NewGoalChunk();
Agent John = World.NewAgent("John");
SimplifiedQBPNetwork net = AgentInitializer.
InitializeImplicitDecisionNetwork(John, SimplifiedQBPNetwork.Factory);
net.Input.Add(g);
... //Elided code performing additional initialization for the network
Para ativar os objetivos podem ser usados dois métodos diferentes. O primeiro deles é setando os objetivos manualmente na estrutura de objetivos. O segundo é setando os objetivos por meio de "goal actions".
Para ativar ou adicionar manualmente um objetivo na estrutura de objetivos, pode-se utilizar o método SetGoal. Veja um exemplo de como fazer isso para o agente do programa SimpleHelloWord:
Observe que no código acima são setados o objetivo (g) e seu nível de ativação (1). Para fazer o processo contrário, ou seja, desativar ou remover o objetivo da estrutura de objetivos, pode-se usar o método ResetGoal, conforme mostrado a seguir:
Como foi mencionado, a segunda maneira de ativar objetivos na estrutura de objetivos é por meio de "goal actions". Os "goal actions" pode ser usados no ACS ou MSC, porém será apresentado apenas como usá-los no ACS.
Na biblioteca CLARION ações que são usadas para atualizar a estrutura de objetivos são definidas por meio da classe GoalStructureUpdateActionChunk. O conteúdo dessas ações actions chunks possuem informações sobre os tipos de atualizações que devem ser executadas. Então, supondo que deseja-se uma ação que seta o objetivo g na estrutura de objetivos, pode-se usar o seguinte código:
GoalStructureUpdateActionChunk gAct = World.NewGoalStructureUpdateActionChunk();
gAct.Add(GoalStructure.RecognizedActions.SET, g);
No código apresentado acima, é usado um enumerador chamado RecognizedActions. Esse enumerador, que é suado por várias classes da biblioteca CLARION, contém uma lista de comandos que podem ser executados por uma ação em uma instância da classe, tais como o SET. que adiciona um objetivo para a estrutura de objetivos, o RESET. que remove um objetivo de uma estrutura de objetivos, o RESET_ALL. que remove todos os objetivos e o SET_RESET. que combina as ações SET. e RESET_ALL..
Para que um componente do ACS possa usar uma dessas ações, ela deve ser especificada na camada de saída do componente, tal como apresentado no exemplo abaixo:
... //Elided code performing additional initialization for the network
net.Output.Add(gAct);
Otimizando o desempenho de tarefas por meio de ajustes nos parâmetros
A biblioteca CLARION fornece vários parâmetros para diversos mecanismos. Porém, muitas vezes, usar parâmetros padrões não satisfaz as necessidades de uma determinada tarefa e precisam ser ajustados. Os parâmetros são implementados na biblioteca CLARION como parâmetro global (static) ou como parâmetro local (instance).
Uma classe importante da biblioteca CLARION é a (RefineableActionRule) que implementa uma regra é aprimorável e contém métodos para a generalização e especialização. Cada um desses tipos de métodos possui um limite que pode ser aprimorado para otimizar a frequência com que um ou outro processo ocorre. Veja abaixo um exemplo de como alterar estes limites durante a inicialização de uma tarefa.
RefineableActionRule.GlobalParameters.SPECIALIZATION_THRESHOLD_1 = -.6;
RefineableActionRule.GlobalParameters.GENERALIZATION_THRESHOLD_1 = -.2;
Além dos parâmetros padrões da biblioteca CLARION, também existem parâmetros extras que podem ajudar na execução de uma tarefa. Veja, no código abaixo, alguns desses parâmetros que receberam valor false como forma de desativar algumas formas de aprendizagem que ocorrem no ACS:
ActionCenteredSubsystem.GlobalParameters.PERFORM_RER_REFINEMENT = false;
ActionCenteredSubsystem.GlobalParameters.PERFORM_RULE_EXTRACTION = false;
ActionCenteredSubsystem.GlobalParameters.PERFORM_TOP_DOWN_LEARNING = false;
ActionCenteredSubsystem.GlobalParameters.PERFORM_BL_LEARNING = false;
Para acessar os parâmetros globais deve-se fazer uma chamada estática, ou seja, usando-se o nome da classe, seguido pelo parâmetro associado. Por exemplo, para acessar o parâmetro global associado a especialização ou generalização foi usado o código RefineableActionRule.GlobalParameters. Mas, para pode ajustar os parâmetros deve-se entender como eles são implementados e quais as consequências das alterações feitas neles.
É importante saber que alterações em parâmetros globais só fazem efeito para uma instância de uma classe somente se forem feitas antes que ela seja inicializada. Seja, por exemplo, o seguinte código:
Agent John = World.NewAgent("John");
ActionCenteredSubsystem.GlobalParameters.PERFORM_RER_REFINEMENT = false;
Observe que no código acima, tentou-se alterar o parâmetro de aprimoramento (PERFORM_RER_REFINEMENT) do ACS do agente John. Porém como o agente já havia sido inicializado, essa alteração irá falhar e o parâmetro continuará com o mesmo valor (true). Para que esse parâmetro global possa ser alterado, deve-se trocar a ordem das linhas de código acima. Logo, a linha de código que altera o parâmetro, deve ser colocada antes da linha de código que inicializa o agente.
Outra maneira de alterar um parâmetro da biblioteca CLARION é através de uma base por instância, o que caracteriza uma alteração local do parâmetro. Como é uma alteração local, então ela terá afeito apenas para as instâncias de uma classe e, para isso, deve-se usar a propriedade Parameters dessa classe. Então, se por exemplo, deseja-se alterar o parâmetro PERFORM_RER_REFINEMENT apenas para o ACS do agente John, pode-se usar o seguinte código:
John.ACS.Parameters.PERFORM_RER_REFINEMENT = false;
Diferente do que pode ser feito com parâmetro globais, um parâmetro local pode ser alterado a qualquer momento, inclusive depois que a instância local de uma classe já foi inicializada. Outra diferençã entre os dois tipos de parâmetros é que o global não pode ter seu valor alterado novamente depois de um ajuste, enquanto que o local pode ser alterado quantas vezes necessário e em qualquer ponto da tarefa que está sendo simulada.
Configurando a memória de trabalho
A memória de trabalho pode ser definida como um container, localizado dentro do ACS do agente, que guarda informações sobre o mundo. Toda a interação com a memória deve ser feita por meio da classe Agent. Por exemplo, para visualizar o conteúdo da memória de trabalho, deve-se usar o método GetInternals conforme é mostrado a seguir:
IEnumerable wmContents =
(IEnumerable)John.GetInternals
(Agent.InternalWorldObjectContainers.WORKING_MEMORY);
A memória de trabalho pode conter qualquer tipo de chunk. Para adicionar um chunk na memória de trabalho podem ser usadas duas formas diferentes: manualmente ou por meio de working memory update action chunk.
Para adicinoar um chunk manualmente na memória de trabalho, deve-se chamar o método SetWMChunk, de maneira semelhante foi mencionado em relação a estrutura de objetivos. Veja, a seguir, um exemplo de como fazer isso:
Como pode ser observado, na adição de um chunk deve ser informado dois parâmetros: o chunk(ch) e seu nível de ativação (1). Para fazer o processo contrário, ou seja, para remover o chunk da memória de trabalho, pode-se usar o método ResetWMChunk, conforme mostrado a seguir:
Como foi mencionado anteriormente, a segunda maneira de adicionar um chunk na memória de trabalho é por meio de working memory update action chunk. Esse mecanismo é implementado na biblioteca CLARION pela classe WorkingMemoryUpdateActionChunk. Essa classe é usada para executar ações que atualizam a memória de trabalho. O conteúdo dessas ações (action chunks) possuem informações sobre os tipos de atualizações que devem ser executadas. Então, supondo que deseja-se uma ação que adiciona o chunk ch na memória de trabalho, pode-se executá-la usando o seguinte código:
WorkingMemoryUpdateActionChunk wmAct = World.NewWorkingMemoryUpdateActionChunk();
wmAct.Add(WorkingMemory.RecognizedActions.SET, ch);
No código mostrado acima, é usado um enumerador chamado RecognizedActions que é suado por várias classes da biblioteca CLARION e contém uma lista de comandos que podem ser executados por meio de ações executadas em uma instância da classe, tais como o SET que adiciona um chunk na memória de trabalho, o RESET que remove um chunk da memória de trabalho, o RESET_ALL que remove todos os chunks e o SET_RESET que combina as ações SET e RESET_ALL.
Para que um componente do ACS possa usar uma dessas ações, deve-se especificá-la na camada de saída do componente, assim como é apresentado a seguir:
... //Elided code performing additional initialization for the network
net.Output.Add(wmAct);
Com as etapas mostradas acima, todas as vezes que o ACS selecionar esta WorkingMemoryUpdateActionChunk, o sistema irá executar os comandos especificados por essa ação.
Nessa seção será apresentado formas de configurar o MS (motivacional subsystem) e o MCS (meta-cognitive subsystem). A tarefa do subsistema MS é fornecer motivações subjacentes para a percepção, ação e cognição, em termos de fornecer impulso e feedback. Ele se preocupa com drives e suas operações, o que significa que ele se preocupa em porque o agente escolhe determinadas ações que ele executa. Já, a tarefa do MCS é monitorar, direcionar e regular os processos cognitivos com o objetivo de melhorar sua performance. Essas operações do MCS pode ser fetas na forma configuração de objetivos para o ACS, interrupção e alteração dinâmica (durante a execução de um processo) do ACS e do NACS (non-action-centered subsystem), por meio de alteração de alguns de seus parâmetros essenciais. O controle e regulação também podem ser realizados pelo MCS por meio da configuração de funções de reforço para o ACS baseado em drive states.
Configurando e usando drives e módulos metacognitivos
Os drives e os módulos metacognitivos possuem uma forte ligação na arquitetura CLARION. Os drives usam fatores (factors) das informações do estado interno e externo para transformá-los em drives fortes (strength). Porém, os drives só têm maiores efeitos sobre o funcionamento do agente se tiver mecanismos para processar esses drives fortes e tomar decisões com base neles. Esses mecanismos que foram citados são os módulos metacognitivos, que além de tomar decisões com base nos drives forte, iniciam uma série de ações metacognitivas internamente dirigidas.
Os drives na biblioteca CLARION são definidos por sua própria classe e usam a seguinte convenção: PrimaryDriveNameDrive. Então, por exemplo, para o drive food usa-se FoodDrive e para o drive dominance e power usa-se DominancePowerDrive. Para iniciar esses drives usa-se o método InitializeDrive na classe AgentInitializer. Por exemplo, para inicializar o FoodDrive no agente John, pode-se usar o seguinte código:
FoodDrive food = AgentInitializer.InitializeDrive(John, FoodDrive.Factory, .5););
Os drives podem pertencer a um dos seguintes grupos no interior do MS: Approach drives (BAS drives), Avoidance drives (BIS drives), a união dos dois anteriores (Both drives) ou de um tipo não especificado Unspecified drives). Se for necessário alterar o grupo de um drive é necessário especificar o grupo como um poarâmetro adicinal durante a inicialização. Por exemplo, para mudar o grupo do FoodDrive pode-se usar o seguinte código:
FoodDrive foodDrive = AgentInitializer.InitializeDrive
(John, FoodDrive.Factory, .5, MotivationalSubsystem.
DriveGroupSpecifications.BOTH);
Quando se trabalha com drives na arquitetura CLARION, em geral, deseja-se usar uma rede neural backpropagation pré-treinada nos mesmos. Porém, como drives são apenas invólucros para um ImplicitComponent, então, o pré-treinamento deve ser feito em um componente implícito, o que é difícil. Diante disso, pode-se usar um componente fácil e rápido, o DriveEquation. Esse componente deve ser usado quando a configuração do agente está sendo testada. Depois que o agente estiver configurado corretamente, deve-se substituir o componente DriveEquation por um componente implícito mais distribuído, como o BPNetwork. Veja abaixo um exemplo de como configurar o componente DriveEquation com o FoodDrive do agente John.
DriveEquation foodEq = AgentInitializer.
InitializeDriveComponent(foodDrive, DriveEquation.Factory);
A classe DriveEquation, apresentada na equação acima, usa a segunte equação para calcular a força do drive:
DSd,t = Gainuniversal × Gainsystem × Gaindrive × Stimulusd,t × Deficitd + Baselined
Depois que a inicialização do ImplicitComponent, do drive é feita, deve-se enviá-la para o drive e depois enviar o drive para o agente, tal como mostrado no exemplo abaixo:
foodDrive.Commit(foodEq);
John.Commit(foodDrive);
Se forem usadas somente entradas típicas (typical inputs) para o DriveEquation, é necessário somente especificar a ativação da entrada STIMULUS. Por exemplo, para fazer isso para o FoodDrive do agente John, pode-se usar o seguinte código:
si = World.NewSensoryInformation(John);
si[typeof(FoodDrive), FoodDrive.MetaInfoReservations.STIMULUS] = 1;
... //Elided initialization of other aspects of the sensory information
John.Perceive(si); ...
//Elided code for running the rest of the task
Como pode ser observado no código acima não foi necessário adicionar a variável STIMULUS para o objeto de informação sensorial, pois o método NewSensoryInformation automaticamente popula o objeto de informação sensorial com todas as meta informações do agente, que incluem, entradas de drives, saídas de drives e outros objetos.
Agora que já foram apresentados os conceitos básicos de drives, deve-se conhecer alguns aspectos do módulo meta-cognitivo.
O módulo meta-cognitivo age como um mini ACS, com a diferença que suas ações manipulam aspectos internos do agente. Esse módulo pode ser constituído por qualquer combinação de instâncias ImplicitComponent com instâncias RefineableActionRule no top level.
O processo de configuração do módulo metacognitivo é semelhante a configuração de um drive. Para exemplificar como fazer essa configuração, nessa seção será usado o módulo GoalSelectionModule. Veja, a seguir, um exemplo de como inicializar esse módulo.
GoalSelectionModule gsm = AgentInitializer.
InitializeMetaCognitiveModule (John, GoalSelectionModule.Factory);
Depois de inicializar o módulo, pode-se usar componentes implícitos e regras para populá-lo. Para iniciar esses componentes pode-se usar os métodos InitializeMetaCognitiveDecisionNetwork ou InitializeMetaCognitiveActionRule localizados no AgentInitializer. Veja um exemplo abaixo:
GoalSelectionEquation gse = AgentInitializer.
InitializeMetaCognitiveDecisionNetwork (gsm, GoalSelectionEquation.Factory);
Como foi mencionado anteriormente, o bottom level do MS é quem determina os drives fortes baseado na combinação de estímulos da informação sensorial e de determinadas diferenças individuais, como ganho e déficit. Por exemplo, o módulo GoalSelectionEquation combina os drives fortes para fazer recmendações para a estrutura objetivo com base na seguinte equação:
GSg,t = ∑d dsd,t × Relevanced,g + ∑i dvi,t × Relevancedv,g
A primeira parte da equação acima refere-se aos drives fortes. Para cada drive forte é aplicado um fator de peso que especifica a relevância de cada drive para o objetivo para o qual a força objetivo está sendo calculada. Por outro lado, a segunda parte da equação acima é semelhante ao primeira parte, mas aplica o processo para outro objeto, como chunk e pares de dimensão-valor.
Para configurar o GoalSelectionEquation para atualizar a estrutura de objetivos (GoalStructure), deve-se usar GoalStructureUpdateActionChunk que é responsável por reinicializar a estrutura de objetivos e adicionar um determinado objetivo. Veja, a seguir, um exemplo de como fazer essa operação com a estrutura de objetivos do agente John.
gse.Input.Add(foodDrive.GetDriveStrength());
No código apresentado acima o par dimensão-valor da força do drive do FoodDrive foi adicionado para a camada de entrada do GoalSelectionEquation. Agora, no código mostrado abaixo, o GoalStructureUpdateActionChunk é inicializado e ele adicionar o objetivo g na estrutura de objetivos. Depois, o GoalSelectionEquation é adicionado para a camada de saída.
GoalStructureUpdateActionChunk gAct = World.NewGoalStructureUpdateActionChunk();
gAct.Add(GoalStructure.RecognizedActions.SET_RESET, g);
gse.Output.Add(gAct);
Agora é necessário especificar a relevância que cada entrada tem para cada saída, usando a seguinte convenção:
SomeGoalSelectionEquation.SetRelevance(SomeGoalStructureUpdateActionChunk,
SomeDrive or SomeWorldObject, SomeRelevanceValue);
Aplicando a convenção apresentada acima para correlacionar o FoodDrive com o GoalStructureUpdateActionChunk, obtém-se o seguinte código:
gse.SetRelevance(gAct, foodDrive, 1);
Para finalizar a criação do componente para o módulo metacognitivo é preciso enviá-lo para o módulo e depois enviá-lo para o agente, conforme mostra o exemplo abaixo:
gsm.Commit(gse);
John.Commit(gsm);
Como os componentes e módulos foram enviados (comitted) o sistema automaticamente irá integrá-los aos processos internos do agente.
Nessa seção será mostrado como configurar o mecanismo de raciocínio NACS. A tarefa do NACS é manter o conhecimento geral do agente, tanto implícito quanto explícito. Mais especificamente, o NACS é usado para representar o conhecimento não centrado em ações com o objetivo de fazer inferências a respeito do mundo. O conhecimento explícito é representado na forma de regras associativas explícitas no top level, enquanto que o conhecimento implícito é representado na forma de memórias associativas implítas no bottom level.
Para o entendimento sobre o NACS, será usado o programa Simple Reasoner que está contido no arquivo Reasoner - Simple.cs que se encontra no diretório samples/Advanced/ que fica na raiz da pasta biblioteca CLARION.
Assim como no programa SimpleHelloWord, o cabeçalho do programa Simple Reasoner usa o comendo using para impoortar a biblioteca CLARION, conforme pode ser visto na Figura 5.1.
Figura 5.1: Cabeçalho do programa Simple Reasoner.
A classe principal do programa Simple Reasoner contém o código de inicialização do mundo, do agente e os mecanismos que permitem a interação do agente com o mundo. Veja o código dessa classe na Figura 5.2.
Figura 5.2: Classe principal do programa Simple Reasoner.
Na Figura 5.2, do lado direito do código, são apresentados os números das linhas. Para comentar o código, serão mencionados esses números.
Como pode ser visto na Figura 5.2, a primeira coisa que foi feita foi chamar uma função, na linha 97, para inicializar o mundo. Essa função inicializa o mundo com 30 pares de dimensão-valor e 5 chunks, manualmente criadas, conforme mostrado na Figura 5.2.
Figura 5.3: Criação manual dos chunks.
Observe que a matriz mostrada acima possui duas dimensões. Cada submatriz da segunda dimensão especifica um padrão de ativação para os 30 pares de dimensão-valor. Veja, na Figura 5.4 a função usa a matriz apresentada acima para incializar o mundo.
Figura 5.4: Função que inicializa o mundo.
Pode ser visto na função apresentada na Figura 5.4 que foi criado um DeclarativeChunk para cada um dos padrões (linha 154). Além disso, as coleções de dvs e de chunks são usadas apenas para acompanhar os pares de dimensão-valor e os chunks declarativos entre as diferentes fases da tarefa.
Na função principal, apresentada na Figura 5.2, depois que o mundo é incializado e o agente também, o método AddKnowledge é chamado (linhas 102 e 103). Esse método é usado para adicionar os chunks declarativos para dentro do armazém de conhecimento geral (GKS - general knowledge store) do agente. Na arquitetura CLARION, todos os chunks que são usados como parte do raciocínio do agente devem ser adicionados para o GKS e nunca podem ser alterados depois que forem adicionados.
A próxima etapa do método principal (Figura 5.2) é incializar a rede neural de Hopfield no bottom level do NACS do agente, usando o código entre as linhas 106 e 113. Pode ser observado que a inicialização da rede neural de Hopfield é diferente da rede feed-forward padrão, pois a rede de Hopfield não possui uma camada de entrada e saída e, consequentemente, os objetos IWorldObjec são adicionados a uma coleção geral de nós da rede, em vez de serem especificados como parte que alguma entrada ou saída.
A rede neural de Hopfield é uma rede auto-associativa, cuja função é tentar reconstruir o conhecimento que está codificado em uma determinada entrada com distorção ou ruído. Como foi mencionado anteriormente, no programa Simple Reasoner deseja-se codificar os cinco padrões representados por chunks declarativos. O método para fazer essa codificação é o Encode que fica no ImplicitComponentInitializer. A Figura 5.5. apresenta esse método.
Figura 5.5: Método Encode.
Como pode ser visto na Figura 5.5, a única informação que deve ser passada para o método Encode é a base de dados que será codificada. Depois de codificar o conhecimento, a próxima etapa é gerar e adicionar as regras associativas para o top level.
Para iniciar as regras associativas no top level do NACS, pode-se usar a seguinte convenção:
If pattern X, then conclude pattern X + 1
Então, se na entrada do top level o DeclarativeChunk representa o padrão 1, logo, o top level deve concluir que o DeclarativeChunk representa o padrão 2. Veja, na Figura 5.6 como criar essas regras no top level do NACS.
Figura 5.5: Método SetupRules.
O próximo passo é a definição dos parâmetros de raciocínio do agente. No programa Simple Reasoner precisam ser definidos dois parâmetros. O primeiro deles especifica que o NACS deve realizar duas iterações de raciocínio antes de retornar suas conclusões, enquanto o segundo especifica que essas conclusões devem ser retornadas totalmente ativas. Veja abaixo como esses parâmetros foram definidos:
reasoner.NACS.Parameters.REASONING_ITERATION_COUNT = 2;
reasoner.NACS.Parameters.CONCLUSION_THRESHOLD = 1;
Agora que os parâmetros foram definidos deve-se chamar o método PerformReasoning passando como parâmetro a entrada (input) que foi usada para inicializar o raciocínio:
var o = reasoner.NACS.PerformReasoning(si);
O método PerformReasoning usado acima irá retornar as conclusões na forma de objetos ChunkTuple. Então, o próximo passo é processar as conclusões. Todo o processo de entrada dos padrões, inicialização do raciocínio e do processamento das conclusões foram codificados no método apresentado na Figura 5.7.
Figura 5.7: Método DoReasoning.
Diante do que foi apresentado, será observada a seguinte característica: na primeira iteração o bottom level completará o padrão de entrada parcial, na segunda iteração o top level irá receber a conclusão associada ao padrão que foi reconstruído e concluirá o próximo padrão e na conclusão será ontido os chunks de cada iteração de raciocínio.
Para finalizar o programa Simple Reasoner, como pode ser visto na linha 131 do código mostrado na Figura 5.2, o agente é interrompido.
Parte do resultado da execução do programa Simple Reasoner, que foi apresentado nessa seção, é mostrado na Figura 5.8.
Figura 5.8: Execução do programa Simple Reasoner.