You are here

Segunda parte - aprendizado via "chunking"

 
A segunda parte do tutorial 5 corresponde à alteração do programa "missionaries-and-cannibals" para utilizar planejamento e também maiores detalhes a respeito do processo de chunking, incluindo o uso de valores numéricos para permitir a comparação de estados intermediários entre o SUCESSO e FALHA.
 
 

Utilização do planejamento "look-ahead"

 
A conversão do programa "missionaries-and-cannibals" para utilizar planejamento é bastante simples, seguindo a mesma receita do que foi feito no programa "water-jug":
 
  • Adição da extensão "^problem-space", contendo detalhes para execução da cópia do estado original a ser utilizada no subestado de SELEÇÃO.
  • Modificação da detecção de objetivo final e falha, com a criação das extensões "^success <desired>" e "^failure <desired>" respectivamente.
  • Adição da regra para indicar falha no caso de estado duplicado durante o processo de SELEÇÃO.

 

Foi indicado no tutorial que com o planejamento as regras de armazenamento e seleção dos operadores inversos, no caso de atingir um estado com falha, poderiam ser removidas, uma vez que esses estados já serão evitados durante o planejamento.

Entretanto, sem essas regras e com a remoção do comando "halt" na regra de detecção do estado final, com o aprendizado (CHUNKING) desabilitado o programa simplesmente não parou no estado final, embora o atingisse diversas vezes durante as etapas de SELEÇÃO; não foi feita uma análise aprofundada, mas aparentemente o programa não conseguiu convergir para a solução no estado original, uma vez que permanecia aplicando operadores inversos e desfazendo os passos que haviam sido feitos.

 

Performance com planejamento e aprendizado

 
Sem planejamento, o programa "missionaries-and-cannibals" teve uma performance média de 86,77 ciclos Soar em 13 execuções, com melhor resultado de 24 ciclos e pior resultado de 256 ciclos.
 
Como indicado no tutorial, com o uso de planejamento e aprendizado, a performance da primeira resolução cai para uma média de 148,7 ciclos, com 135 ciclos de melhor resultado e 170 ciclos de pior resultado; entretanto, depois de resolvido o problema uma vez, a solução sempre é obtida em exatos 12 ciclos (11 ciclos da melhor solução mais 1 de inicialização). Durante a resolução do problema, o mecanismo de chunking criou em média 71,7 regras (47 mínimo 106 máximo) que depois são utilizadas para resolver o problema em tempo ótimo.
 
A explicação desse fenômeno é porque com o planejamento availando apenas estados de SUCESSO e FALHA o problema é resolvido várias vezes, em cada processo de SELEÇÃO.
 
 

Avaliação de estados intermediários entre SUCESSO e FALHA - uso de valores numéricos

 
Esta segunda parte do tutorial introduz o uso de valores numéricos para permitir a avaliação de estados intermediários entre o SUCESSO e FALHA. No problema em questão é considerado que é melhor o estado onde mais pessoas estão no lado final desejado do rio. Para isso, é preciso primeiramente indicar a condição para o conjunto de regras padrão "selection.soar", o que é feito com a extensão "^better higher". tal como abaixo:
 
 
É também necessário criar uma regra que calcule a extensão "^numeric-value" para cada estado:
 
 
Com o uso do valor numérico o programa passa a convergir a um resultado, não apresentando o problema descrito acima, mesmo com o aprendizado desabilitado. A performance média foi de 238,3 ciclos, com melhor resultado em 143 ciclos e pior em 341 ciclos.
 
Com o aprendizado habilitado, no entando, a performance caiu, não atingindo mais a resolução ótima a toda execução, com uma performance final (após resolver uma vez o problema) média de 23 ciclos (12 ciclos melhor resultado, 48 ciclos pior resultado). A performance da primeira execução não alterou, ficando com uma média de 149,3 ciclos (145 ciclos melhor resultado, 159 pior resultado).
 
A explicação desse comportamento é o fato de que a aproximação feita pela avaliação númérica implementada na regra "mac*evaluate*state*number" não é exata: de fato, durante a solução do problema em alguns instantes é necessário diminuir o número de pessoas no lado final do rio, como por exemplo no ponto onde se deseja levar todos os canibais para o lado oposto.
 
A seguir está a sequência de resolução ótima do problema com os operadores executados e estados atingidos em negrito:
 
run
     1: O: O1 (initialize-missionaries-and-cannibals)
 
 Proposing move 1 cannibals from left
 Proposing move 1 missionaries from left
 Proposing move 2 cannibals(s) from left
 Proposing move 2 missionaries(s) from left
 Proposing move 1 missionary and 1 cannibal from left
 
     2: O: O6 (move)
 Move 1 cannibals(s) to right
 Move 1 missionaries(s) to right
 M:2, C:2 ~~~~~ B M:1, C:1
 
 Proposing move 1 missionary and 1 cannibal from right
 Proposing move 1 missionaries from right
 Proposing move 1 cannibals from right
 
     3: O: O8 (move)
 Move 1 missionaries(s) to left
 M:3, C:2 B ~~~~~ M:0, C:1
 
 Proposing move 1 missionary and 1 cannibal from left
 Proposing move 1 missionaries from left
 Proposing move 2 missionaries(s) from left
 Proposing move 1 cannibals from left
 Proposing move 2 cannibals(s) from left
 
     4: O: O14 (move)
 Move 2 cannibals(s) to right
 M:3, C:0 ~~~~~ B M:0, C:3
 
 Proposing move 1 cannibals from right
 Proposing move 2 cannibals(s) from right
 
     5: O: O15 (move)
 Move 1 cannibals(s) to left
 M:3, C:1 B ~~~~~ M:0, C:2
 
 Proposing move 1 missionaries from left
 Proposing move 1 cannibals from left
 Proposing move 2 missionaries(s) from left
 Proposing move 1 missionary and 1 cannibal from left
 
     6: O: O19 (move)
 Move 2 missionaries(s) to right
 M:1, C:1 ~~~~~ B M:2, C:2
 
 Proposing move 1 cannibals from right
 Proposing move 1 missionaries from right
 Proposing move 2 cannibals(s) from right
 Proposing move 2 missionaries(s) from right
 Proposing move 1 missionary and 1 cannibal from right
 
     7: O: O25 (move)
 Move 1 cannibals(s) to left
 Move 1 missionaries(s) to left
 M:2, C:2 B ~~~~~ M:1, C:1
 
 Proposing move 1 cannibals from left
 Proposing move 1 missionaries from left
 Proposing move 2 cannibals(s) from left
 Proposing move 2 missionaries(s) from left
 Proposing move 1 missionary and 1 cannibal from left
 
     8: O: O29 (move)
 Move 2 missionaries(s) to right
 M:0, C:2 ~~~~~ B M:3, C:1
 
 Proposing move 1 cannibals from right
 Proposing move 1 missionaries from right
 Proposing move 2 missionaries(s) from right
 Proposing move 1 missionary and 1 cannibal from right
 
     9: O: O31 (move)
 Move 1 cannibals(s) to left
 M:0, C:3 B ~~~~~ M:3, C:0
 
 Proposing move 1 cannibals from left
 Proposing move 2 cannibals(s) from left
 
    10: O: O36 (move)
 Move 2 cannibals(s) to right
 M:0, C:1 ~~~~~ B M:3, C:2
 
 Proposing move 1 missionaries from right
 Proposing move 1 cannibals from right
 Proposing move 2 missionaries(s) from right
 Proposing move 2 cannibals(s) from right
 Proposing move 1 missionary and 1 cannibal from right
 
    11: O: O37 (move)
 Move 1 missionaries(s) to left
 M:1, C:1 B ~~~~~ M:2, C:2
 
 Proposing move 1 cannibals from left
 Proposing move 1 missionaries from left
 Proposing move 1 missionary and 1 cannibal from left
 
    12: O: O44 (move)
 Move 1 cannibals(s) to right
 Move 1 missionaries(s) to right
 M:0, C:0 ~~~~~ B M:3, C:3
 
 Proposing move 1 cannibals from right
 Proposing move 1 missionaries from right
 Proposing move 2 cannibals(s) from right
 Proposing move 2 missionaries(s) from right
 Proposing move 1 missionary and 1 cannibal from right
 
 Problem solved. right has 3 missionaries, 3 cannibals and 1 boat.
 
> Success state reached. S1
Interrupt received.
This Agent halted.
 
 
 

Análise das regras criadas a partir do CHUNKING

 
Nesta seção será feita uma breve análise das regras de CHUNKING que são disparadas na resolução do problema, depois que ele já foi resolvido uma vez e Soar aprendeu as regras de resolução. Estão analisados os primeiros 2 passos.
 
PRIMEIRO PASSO
 
Estado inicial:  
 
M:3, C:3 B ~~~~~ M:0, C:0
 
São propostas os seguntes operadores:
 
 Proposing move 1 cannibals from left
 Proposing move 1 missionaries from left
 Proposing move 2 cannibals(s) from left
 Proposing move 2 missionaries(s) from left
 Proposing move 1 missionary and 1 cannibal from left
 
Esses operadores disparam os seguintes CHUNKS:
 
print chunk-96*d5*tie*2
sp {chunk-96*d5*tie*2
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 3 ^cannibals 3 ^other <o1>)
    (<o1> ^missionaries 0)
    (<o2> ^from <l1> ^types 1 ^missionaries 2 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> -)
}
 
print chunk-183*d122*tie*2
sp {chunk-183*d122*tie*2
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^boat 1 ^missionaries 3 ^cannibals 3 ^other <o1>)
    (<o1> ^boat 0 ^other <l1> ^missionaries 0 ^cannibals 0)
    (<o2> ^boat 1 ^from <l1> ^types 2 ^missionaries 1 ^cannibals 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    (<d1> ^right <r1>)
    (<r1> ^missionaries 3 ^cannibals 3)
    -->
    (<s1> ^operator <o2> >)
}
 
print chunk-150*d72*tie*2
sp {chunk-150*d72*tie*2
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^operator <o3> + ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 2 ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^cannibals 2 ^name move)
    (<o3> ^from <l1> ^types 1 ^missionaries 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> = <o3>)
}
 
print chunk-151*d72*tie*3
sp {chunk-151*d72*tie*3
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^operator <o3> + ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 2 ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^missionaries 1 ^name move)
    (<o3> ^from <l1> ^types 1 ^cannibals 2 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> = <o3>)
}
 
print chunk-162*d78*tie*3
sp {chunk-162*d78*tie*3
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^operator <o3> + ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 2 ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^missionaries 1 ^name move)
    (<o3> ^from <l1> ^types 1 ^cannibals 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> = <o3>)
}
 
print chunk-165*d78*tie*6
sp {chunk-165*d78*tie*6
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^operator <o3> + ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 2 ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^cannibals 1 ^name move)
    (<o3> ^from <l1> ^types 1 ^missionaries 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> = <o3>)
}
 
print chunk-152*d72*tie*4
sp {chunk-152*d72*tie*4
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 2 ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1)
    (<o2> ^from <l1> ^types 1 ^missionaries 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> -)
}
 
print chunk-148*d69*tie*2
sp {chunk-148*d69*tie*2
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^cannibals 2 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> -)
}
 
print chunk-161*d78*tie*2
sp {chunk-161*d78*tie*2
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^operator <o3> + ^problem-space <p1> ^desired <d1>)
    (<l1> ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^cannibals 2 ^name move)
    (<o3> ^from <l1> ^types 1 ^cannibals 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> = <o3>)
}
 
print chunk-166*d78*tie*7
sp {chunk-166*d78*tie*7
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^operator <o3> + ^problem-space <p1> ^desired <d1>)
    (<l1> ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^cannibals 1 ^name move)
    (<o3> ^from <l1> ^types 1 ^cannibals 2 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> = <o3>)
}
 
print chunk-167*d78*tie*8
sp {chunk-167*d78*tie*8
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^cannibals 2 ^other <o1>)
    (<o1> ^missionaries 1 ^cannibals 1)
    (<o2> ^from <l1> ^types 1 ^cannibals 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o2> -)
}
 
 
Cada uma dessas regras de chunk captura o estado atual e um dos operadores propostos, definindo uma preferência para a sua aplicação. Dessa maneira, Soar executa o operador que foi definido como o melhor pelo "chunk-183*d122*tie*2", que corresponde justamente ao primeiro movimento da solução ótima:
 
     2: O: O6 (move)
 Move 1 cannibals(s) to right
 Move 1 missionaries(s) to right
 
 
SEGUNDO PASSO
 
Agora, o estado resultante é:
 
[VALID STATE] M:2, C:2 ~~~~~ B M:1, C:1
 
Neste estado são propostos os seguintes operadores:
 
 Proposing move 1 missionary and 1 cannibal from right
 Proposing move 1 missionaries from right
 Proposing move 1 cannibals from right
 
 
Eles, por sua vez, disparam as seguintes regras de CHUNKING:
 
print chunk-187*d148*tie*2
sp {chunk-187*d148*tie*2
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <o1> ^operator <o2> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^boat 0 ^missionaries 2 ^cannibals 2 ^other <o1>)
    (<o1> ^boat 1 ^other <l1> ^missionaries 1 ^cannibals 1)
    (<o2> ^boat 1 ^from <o1> ^types 1 ^missionaries 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    (<d1> ^right <r1>)
    (<r1> ^missionaries 3 ^cannibals 3)
    -->
    (<s1> ^operator <o2> >)
}
 
print chunk-103*d15*tie*4
sp {chunk-103*d15*tie*4
    :chunk
    (state <s1> ^name mac ^left <l1> ^right <r1> ^operator <o1> +
          ^problem-space <p1> ^desired <d1>)
    (<l1> ^missionaries 2 ^cannibals 2)
    (<r1> ^other <l1> ^cannibals 1)
    (<o1> ^from <r1> ^types 1 ^cannibals 1 ^name move)
    (<p1> ^name missionaries-and-cannibals)
    -->
    (<s1> ^operator <o1> -)
}
 
 
Mais uma vez Soar executa operador que foi definido como o melhor, neste passo pelo "chunk-187*d148*tie*2":
 
    3: O: O8 (move)
 Move 1 missionaries(s) to left
 
 M:3, C:2 B ~~~~~ M:0, C:1
 
 
 

Comentários

 
 
  • O comando "watch --chunk --print"  não foi encontrado, sendo apenas possível usar "watch --chunk", o qual foi utilizado em conjunto com o comando "watch --learning print" para visualizar os chunks à medida que era criados e utilizados.
  • Por erro de codificação, durante o processo de modificação do programa para a utilização do planejamento não foi retirado o operador de preferência INDIFERENTE de algum dos operadores; com essa indicação de indiferença o problema não era resolvido, pois o mecanismo de SELEÇÃO não era utilizado em todos os momentos, o que não permitia a exploração de todos os caminhos.
 

 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer