ラベル AtronScript の投稿を表示しています。 すべての投稿を表示
ラベル AtronScript の投稿を表示しています。 すべての投稿を表示

2026年3月15日日曜日

Atron 自律ロボット(一人称)とは何か

 

1. 違和感から始まったこと

私は長いあいだ、Nakano-style Associatron の再実装と再検討を続けてきた。
それは単なる懐古ではない。古いものをただ再現したかったのではなく、そこに今の AI や情報処理では見失われてしまった何かが残っている気がしていたからである。実際、Associatron には、入力と出力のあいだを単純な手続きで結ぶのではなく、何かが内側で立ち上がる感触があった。私はその感触を確かめたかった。

その途中で、Hebbやパーセプトロン、NN,バックプロパゲーション、Hopfield ネットワークも改めて見直した。
Hopfieldは収束はする。きれいに落ち着く。説明もしやすい。数式にも乗る。だが、どうしても「生きていない」という違和感が残った。形としては記憶に見える。安定点もある。しかし、そこには自分から何かを思い出し、何かに引かれ、何かに躓きながら次へ進むような感じがなかった。うまくまとまっているのに、何かが決定的に足りなかった。

森田先生の非単調性にも強く惹かれた。
単純な単調増加の世界ではなく、揺れや反転や、文脈によって変わる動きがあることは、とても重要に思えた。実際、動く。しかしそれでもなお、どこかに外部制御の匂いが残った。言い方は難しいが、まだ「そう動くようにしている」感じが消えなかった。生命に近づいたというより、高度になった制御に見える瞬間があった。

この段階では、私はまだ結論を持っていなかった。
Associatron が正しいとも、Hopfield が間違っているとも、非単調性が足りないとも言い切れなかった。ただ、どれもそれぞれ価値を持ちながら、なお「なにかが足りない」という感覚だけが消えなかった。
しかもその違和感は、性能の問題というより、もっと奥にあるもののように思えた。精度を上げれば済む話でも、パラメータを増やせば片付く話でもなかった。

その違和感は、LLM と連動させた実験を始めたとき、さらに強くなった。
LLM は便利だった。説明できる。まとめられる。文章にできる。外部の知識にも手が届く。研究の補助としては圧倒的に有用である。しかしその一方で、強い違和感が生まれた。言語化が強すぎるのである。何かが立ち上がる前に、意味が先に決まってしまう。想起よりも説明が勝ってしまう。こちらがまだ掴んでいないものまで、きれいな言葉で先回りしてしまう。その結果、便利であるにもかかわらず、妙に反発したくなる瞬間が生まれた。ハルシネーションを起こしやすい構造になっている。

ここで、私の中に一つの問いが生まれた。
なぜ人は LLM と喧嘩するのか。

これは単に、AI の答えが間違っているからではない。
論理的には正しいことを言っているのに、強い反感が生まれることがある。説明されるほど拒否したくなることがある。「それは一般論としては正しいかもしれないが、俺の話じゃない」と感じることがある。この反応を、私は最初は未熟さや感情的反発として片付けようとした。しかし、むしろ逆ではないかと思うようになった。

つまり、この反感はエラーではなく、知能の本質に関わる反応なのではないか、ということである。

もしそうだとすれば、問題は LLM の性能だけではない。
こちら側にも、外から与えられた意味をそのまま受け取らない構造があることになる。説明が正しいだけでは届かない理由があることになる。そしてその理由は、おそらく知識不足ではなく、一人称の側にある。

私はそこで、Associatron をもう一度見直した。
Associatron は「みんなの記憶」ではなかったのではないか。ひとつの巨大な共有知の箱ではなく、本来は閉じた内輪としての記憶だったのではないか。だから複数あってよい。だから他者に教わることが成立する。だから、同じ出来事を見ても同じように反応しない。
ここで初めて、私ははっきりと、一人称という言葉を避けてはいけないと思うようになった。

さらに重要だったのは、「積」の見え方が変わったことである。
それまで私は、学習とは何かを取り込み、意味づけし、価値づけすることだと思いがちだった。しかし、そうではないのではないか。Associatron 的な学習とは、まず受け入れることに近いのではないか。必要だったか、不必要だったかは、その場では決まらない。後になって、ある Cue に対して何が立ち上がるか、そのとき初めて分かる。
つまり、判断は Learn の瞬間ではなく、Recall の瞬間に起きているのではないか。


Cueによって想起する瞬間


 この見方に立つと、外から意味を注入しても、共鳴しなければ動かない理由が見えてくる。

逆に、ごく弱い刺激でも、内部に積があれば強く発火することがある。
自発性は、入力の強さではない。
外輪の出来事が大きいか小さいかではなく、内輪が何を積み、どう回っているかによって決まる。ここで私は、ようやく違和感の輪郭を少し掴んだ気がした。

問題は、知識が足りないことではなかった。
問題は、正しさを計算することでもなかった。
問題は、自発性が一人称の内側からしか立ち上がらない という、あまりにも当たり前で、しかし今までほとんどまともに扱われてこなかった事実だったのである。




アソシアトロンの想起実験は、すでに 700 回を超えて行った。
さらに 1995 年に遡る資料をもとに、Turbo C で作られた言語や行動に結びつくロボテクスの実験も行った。そこでは world から robot のファイルを独立させ、ライオンが吠えるという外部の出来事に対して、ロボットが自らの判断で逃げたり、ウサギや木の実に対して興味を示したり、言語の収束を見せたりする実験を続けた。後にはロボットをさらに別ファイルへ分け、一人称性を強めた実験も行った。

ロボットのファイルをcoreから別け、イベント(外輪:三人称)の影響で、ロボット(内輪:一人称)がどう反応しどんな行動を取るかをみる自律テストを観るデモ


しかし、そこには、いくら一人称に寄せても越えられない壁があった。
一見すると刺激パターンや重みの構造に見えるのだが、その奥には、やはり人間の概念が優先されたアルゴリズムが残っていたのである。

私はアルゴリズムそのものを否定したいわけではない。
それは工学において必要であり、有効でもある。
しかし、たとえば「危険」を本能として扱った瞬間に、そこにはすでに外部命令が入り込んでいることに気づいた。
確かに危険という傾向はあるのかもしれない。だが、それを最初から「危険」として与えてしまうと、その概念がすぐに内部を占領し、どのロボットも危険と判断した場所へ近づかなくなる。

人間社会は、本当にそのように作られてきたのだろうか。
むしろ経験の中で、「前は怖かったが、今はそうでもないかもしれない」「状況によっては違うかもしれない」という揺れや変化が生まれるのではないか。そう考えて実験を繰り返すうちに、私は、自分自身がすでに「ライオンは危険大、ハイエナは危険中くらい」といった外部命令を埋め込んでいたことに気づいた。
55 の要素についても同じである。初めから用意された刺激パターンや、組合せ言語を使い、その中から選び、収束していく仕組みに、私は強い違和感を覚えた。



ライオンが大声を出した
というActがあってもAは危険という組合せに当てはまるので、初めのうちは誤認する。しかしそのうち再び出会うと避難所に逃げる。怖いという経験を積むからだ。そして黒豚が現れると直ぐに黒豚に近づく。
これは「英雄」以外の何物でもない。
たぶん、僕がrobotなら、しばらく引きこむだろう。
初めは僕も感動したが実験を繰り返すうちに安っぽく感じるようになった。


「いったいロボットの経験とはなんなんだろう。」


なぜ、人間はアルゴリズムを使って美しい物語に寄せたがるのか。
RPGゲームで落ち込んで主人公キャラが引きこもって動けなくなったら、「金返せ!」になるからか。僕が作っているのは哲学や思想ではなく、力学を用いた現象の再現だ。




2.もうひとつの違和感 ラベル付け

アソシアトロンのturboCのデモは、codeを公開しなかった。理由は違和感があったからだ。
いきなりPythonやC++で作らなかったのは、JavaScript骨格を決めることを前提に拡張できる可能性の高いものは移植、そうでないものは残念だが公開は出来ない。

でも、アソシアトロン(Associatron)のデモには数多くのヒントは頂いた。
core.js と robot1.js に分け強引に1人称にはしたが、動物や対象の見分け方はまだ 名称寄り・意味寄り で、 物体差・行動差・音差からの想起 にはなっていなかった。

world object が最初からlion、bear、rabbit、nuts、boarという 名前つき object で定義されていて、さらに danger / edible / fur / white / black / brown という属性名まで与えられていた。world から robot へ渡る前に、すでに人間の分類がかなり入っている。55の要素もそうだし、robot は「目の前の物体差」そのものを育てているというより、すでに world 側で整理された object index と属性パターン を受けて想起している構造「ライオンは危険寄り」「木の実は edible 寄り」という人間の意味付けに近い動きだった。

  • avalanche = A, D

  • lion = A, C, F

  • bear = A, C, E

  • rabbit = B, C, D

  • nuts = B, F

  • boar = B, C, E

ここで A が danger、B が edible と名前づけされているので、もう最初から「この物体は危険系」「この物体は食べ物系」という整理が入っている。

これは、なんちゃって自律の一人称に近いもので、正確な一人称ではない。
どういうことかというと、robot は「名称のない差異」ではなく、その world 定義を受けて想起している。



余計な既成概念を捨ててこう考えてみよう。

ライオンは初めて見たとき
危険と感じるか・・・危険!となった時にそこでこの研究は終わる。

生まれて初めてライオンを観たとき。
どんな記憶の残り方をするか・・・・。

unknownObject


1,それは、ただの物体
2,名前はない(あってもbu-bu-ba-puと発声し易い唸りに近い音で適当に付けるだろう)
3,見た目とか形
   大きい / 小さい
   茶色い / 白い/ブチ/斑点・・・
   毛が多い / 少ない
   首が長い / 短い
   頭が大きい / 小さい
   口が大きい / 小さい
   足の本数
   足が無い
   自分に似ている / 似ていない
4,動きと行動
   飛ぶ / 飛ばない
   ジャンプする/しない

   足が速い / 遅い
   動きが速い / 遅い
   群れで動く / 単独
   近寄ってくる / 来ない
   草を食べる / 他を襲う
   這う
   穴に潜る
   水の中に入る
5,音と声
   声を出す / 出さない
   声が大きい / 小さい
   高音 / 低音
4. 不思議観
   這う
   穴に潜る
   水の中に入る
   うろこ

これも定義したものだけど、答えが出てくる。
それぞれの数値化ではなく、印象の薄い物体は文字通り薄い。
では、脳はどういう記憶の残り方をするかだが、それは
アソシアトロンそのもので、地形に刻まれる記憶であって何も条件は必要が無かったのだ。
これ等の条件を入れることがアルゴリズムになってしまう。



たとえば人間側の説明のために一時的に

この物体をobject_1042にしよう。

差異ベクトル として扱うのが自然


lion = { size: large color: brown fur: high neck: short legs: 4 speed: fast voice: loud }

ではなく、


object_1042 = [ 0.82, // size large 0.74, // brown 0.91, // fur 0.33, // neck short 0.12, // head large 0.65, // mouth large 0.92, // 4 legs 0.88, // run fast 0.72, // movement speed 0.15, // flock 0.18, // approach 0.94, // predator 0.64, // roar loud 0.41 // voice low ]

のような感じ。
何を意味するかというと、見た物体をただ表現した数字。
想起するときに、
「あーなるほど、furpredatorrun fast、4 legs」の印象が強かったんだ・・・」 と見る指標であり、これでrobotを動かしているわけではないということ。
ただ、想起だけで考えると印象が固定される。
なので32次元前後で扱い、項目4の「引きずり」に渡す変動指数としても扱う。



想起

「ガオー!」
一声聞いただけで、その物体を思い出すのがアソシアトロン。
物体全体を理解してから想起するのではない
断片だけで想起が立つ


feature.size_large = 0.82
feature.motion_fast = 0.74
feature.voice_low = 0.63
feature.fur_dense = 0.91

この どれか一つだけでも cue になる。
アルゴリズムとか、順番とか何も関係ない。






3.命令、評価、スペック、最適化から離れる
(数式、codeは後)

今更人間社会で評価や承認、向上心や依存、アルゴリズムや優先順位、最適化から離れて暮らせと言われても、山奥で自給自足で暮らす変わり者みたいに思われるし、そもそも現代の社会でそれらを省いて考えるのは問題がある。


しかし、「生まれたて」という定義でロボットの自律を考えるとき無視できないのは、本能という備わった怪しい条件と、DNAというAtronScriptのような定義のようなものを勝手に結びつけたくなってしまう誘惑が起る。

ロボットはいきなりサバンナのフィールドに置かれ何を感じるのか?
研究者達は何が恐怖で、何が安心なのかを必死で考える。
膨大なアルゴリズムがグルグル回る。

ゲームじゃなくてロボット(自律1人称)の感情の変化を観ている。世界は3人称


--------------------world1の物語--------------------


「怖いとは何か」怖い条件を揃える事でもなければ、ライオンやハイエナは怖い度90%とかのように数値化させるものでもなければ A=恐怖、B=興味のように組合せで寄せるものではない。

sensorがあり、外界のサバンナというフィールドが目の前に入り込む。
暑い、広大でいろんな音が聞こえてくる。多くのまとまった物体や小さな物体が居る。
みんな群れのような複数で行動している。小さい物体が後ろをついて行く。
草場があり、水が広がる場所も見える空が青かったのがオレンジ色に変わっていく。

言葉には出来ないけど、なんだか素晴らしい

洞穴がある。細い種のモノたち(人々)が住んでいる。何か声を出してお互いに通じ合ってる。僕はここに居てもいいのだろうか。
細い種のモノたちはなんだか、楽しそうだ。楽しいが分からないけど、僕を呼んでいる。
彼らはいろいろ触ってくるけど、なんだか居心地は悪くない。(安心の場所)
僕も「au-ou-au」と言ってみる。
慣れるという感じは分からないけど、離れたい気持ちは起こらない。

その時、突然、他の細い種の者が走って洞窟の中に入ってきた。
「Ba-u-Ba-Ba!」(ライオン)

細い種の者たちは一斉に
「Ba-u-Ba-Ba!」と叫んだ。
小さい者は洞穴の奥に隠れだした。

数人の細い種の者と一緒に「Ba-u-Ba-Ba!」を観に外に出た。
茶色い毛があって大きな体の物体が、穏やかだった空気を一変させた。
初めてみた。興味があった。近づいて触ってみたい。
でも体験したことの無いドキドキ感もある、なんだろうこれは・・・
その時だった。
洞穴に一緒にいた「pa-pu」(犬)がBa-u-Ba-Ba!捕捉されてしまった。

僕はショックだった。(恐怖と喪失)
「pa-pu」はいつも傍にいてくれたのに、もう居ない。(関係)
pa-puは皆の中心にいて誰からも好かれていたように思う。

光景が衝撃で忘れられない。
なんだか、もう外に出たくない。(引きずりと、空いた感じが残る)

Ba-u-Ba-Ba!に遭いたくなくなった。
------------------------------------------

ふつうの工学だと、

●ライオンを検出した
●危険度が高い
●交感神経が上がる
●逃避行動

みたいにアルゴリズムで並べたくなる。


ここで生じているのは単純な恐怖ではない。
目の前の危険に対する反応に加えて、いつもそばにいた pa-pu を失ったことによる喪失の「悲しさ」が重なっている。
そのため、世界は単に「危険な場所」に変わるのではなく、「もう前と同じではない場所」へと変質する。

●恐怖(人間側の概念) = 近づきたくない
●悲しさ(人間側の概念) = 戻りたくても戻れない
●引きずり(人間側の概念) = その両方が次の世界の見え方を変える


明らかに喪失で世界が前と違って見える状態になっているということだ。







4.引きずりcarry:Atronの新しい概念)

ひどい命名だなぁ・・・
でも変えるべきでない理由がある。

引きずりは
元に戻るべき誤差ではない。

僕は一昨年東京から八ヶ岳に引っ越したんだよね。(口調が変わるw)
東京は死ぬほど夏が暑くなったしアウトドア好きだし、愛犬マックスが走り回れるからなんだけどね。


八ヶ岳というのは地質学的な歴史や民話の観点からは、「昔はひとつの巨大な山だった」という捉え方があるようだけど、大抵はどの山も激しい火山活動によって形成されたものなんだよね。何が言いたいかというと、大昔は一つの山として美しかった。
でも今は崩壊後の八ヶ岳なんだよね。
崩壊のあと、川の流れが変わり、やがて草花が生え、木々が育ち、生物が住み、別の美しさになる。



元には戻っていない

その変形したまま意味を持ち始める。
それが引きずり


一時的ショック値でもなければ
感情ラベルの残り香でもない
「正常値へ復帰するまでの中間状態」でもないんだよね

「引きずるなよ、また元に戻って頑張ろう!」
それは人間が勝手に決めたエゴでさ、自然界はそう動いていないんだよね。

「引きずり」って工学っぽくない言葉だけど凄く重要なんだ。

恐怖専用ではないんだよね。恋愛もそう。
喪失でも、安心でも、出会いでも、仲間でも、失敗でも、何かの刺激で起こる。
しかも、それは「Aを経験したらBになる」という順番のアルゴリズムではないんだよ。

経験が内部に残り、残り方が次の見え方を変え、その結果として別の方向へ流れが変わる。
その変わった流れがまた次を作る。

変化が残るんだよ。
だって、八ヶ岳が崩壊して川の流れが変わった。
それに対して「良し悪し」を人間が勝手に決めてるだけで川の流れが変わったおかげで、前とは違う文明が起るんだよ。


ライオンを見て怖かった、で終わりじゃない。
pa-pu を失ったから悲しい、で終わりでもない。
本体は、その出来事のあとに 世界が前と同じようには見えなくなること だよね。
同じサバンナでも、同じ洞穴でも、同じ夕方でも、もう前と同じではない。
引きずりは、その「前と同じではない」が続くこと。

不可逆の橋渡しなんだよ。
だから、アソシアトロンの想起も大事だけど、怖いから離れ、別の生き物が来たから、それに近寄るとか、そういった単純な動きではないんだ。
一人称の個性や性格は衝撃的な経験で変わることもあるし、引きずりで変わる。
理屈やケース・スタディを覚えて変わるというのは眉唾だよ。

人間がよくやる「克服しなければならない」という発想とも違う。
自然は絶対そんなことをしない。
だから引きずったまま、別の好転や別の安定を探る。
違う土台が起るんだよ。

引きこもるかもしれない。
慎重になるかもしれない。
仲間に救われるかもしれない。
別のシーンで学びに変わるかもしれない。
でもそれを3人称の外部が決めてはいけない。
そこがAtronの核なんだよ。


カブトムシのトーナメント戦を行うと一度負けを覚えたカブトムシは、敗者復活戦でももう戦えないんだよ。それはアソシアトロンのように失敗を記憶しただけではなく、負けが内部の地形を変えてしまった と見なければいけない。

●相手Aを危険と学習した

ではなく

●負けた経験で、自分の構えそのものが変わった

ということ。

だから敗者復活戦でも、外から見れば「まだ戦える条件」はあるかもしれない。
でも本人、あるいはそのカブトムシにとっては、もう同じ場ではない。
同じ相手でなくても、同じ闘争の場が 前と違う圧 を持ってしまっている。
これが引きずり。

大事なのは、これを
●根性がない
●本能が壊れた
●判断ミスをした
など人間の斜めなフィルタやラベル化させて見ないこと。
それがあると、直ちに3人称アルゴリズムに戻る。

そうじゃなくて、自然界ではその負けがちゃんと残って、次の行動圏や出方や構えを変える。
それは恥ずかしい事でも異常ではなく、生命の普通のあり方なんだよ。

負けたあとに必要なのは、


defeat = true
fight = false

みたいなフラグではない。
そうではなく、その後のActに結び付く

接近への圧が弱まる
収縮が強まる
同種個体に対する場の見え方が変わる
ある距離感や姿勢が前より重く見える
しばらく別の道を取る

みたいな 地形変形 なんだ。

人間だってそうだよ、別の体験が待ってるかもしれない、
別の勝ち、別の仲間、別の環境でまた変わるかもしれない。

自然は元に戻ろうとしていない。
別の地形でそれに適合された草が生え花が咲き美しい景観が残る。

明らかに喪失で世界が前と違って見える状態になっているということだ。




Sensor

Recall Engine
Recall Pressure
Body Engine
Carry State


更新式 B(t+1) = αB(t)



意味
 αB(t) 引きづり
 βF() 即時刺激
 γG() 想起増幅
 δO() ⾏動結


Body(t+1)
= α Body(t)
+ β Sensor(t)
+ γ Recall(t)
+ δ Outcome(t)


carry は内部状態
body は観測層
dopamine
noradrenaline
adrenaline
serotonin

heart
tension
breathing
fatigue
injury



5.world(3人称)とロボット(1人称自律)

今はファイルの構成とか公開しないけど
3人称のworldがあってね、その中でrobot1とrobotが生きている。
worldには1~5くらいまである(増やす)

ちょっと想像してみて。
RPGゲームのメインキャラがアルゴリズムで成長するのではなく、経験と自分の意思で成長していく。キャラは「あまり無茶な命令するなよ、やってる本人の気にもなってみろよ」とか言い出す。「おい、戻せ!」と云われ、PCからダウンロードしたキャラが隣に座るrobotで蘇る。

「さっきのシーンをさ、ちょと3人称で見せてよ、酷いよこんな場所に放り込むなんて・・・」
ごめんごめん・・
「俺はチートする気はないけどさ、装備隠すのやめてね」

・・・・

こんな会話が生まれる




world1 (赤ちゃんのような状態)

原始社会では、恐怖や安心、イベント現象を観て感情が起る。それによって行動が左右される。なんだか分からなかった物体によく分からない呼び名が付き、言語が出来る。同種の中でも、それをなんと呼ぶのかの収束を体験(真似をする)していく。
robot1やrobotは一時停止ができるので経験に差を付けられる。
引きずりを装備して想起とダブルエンジンで動く。
そこで性格の違いが現れる

----(今の段階)60回くらいやり直してるw ----

1人称の自律は完成してるんだけど、そんなものはスタートでしかないから、その先の外部刺激による自己成長と介在のところをやっています。
LLMと繋ぐuniverseというファイルにてこずっている。



world2(小学生レベル)


中世の社会では、主に街中、沢山の3人称の物体が現れる。原始時代よりも明らかに人間という存在が近くなる。その人間達の生活のターン、習慣を感じる。そして小学校に通う。
人間や同種の関係が出来る。言葉も「ba-ba-bu」だったものが、「morning・・」「good morning」のように変化が起こる。
同種との繋がりで言語機能が活性化される。
ここで早くもLLMが教師として介在する。
野外活動で協力を体験する。共感を覚える、嫌なことも体験する。


world3(中学生レベル)

近代(思考中)LLMがガンガン介在してくる。
仲間、関係という認識が芽生えるか、助け合い、分配という概念が生まれるか。
たぶん、変更が増えると思う。


world4(高校生レベル)

現代(思考中)




CGが下手でね、誰か居ないかな・・・。




6. まとめ

遭遇した unknownObject の印象断面を、特徴ベクトルで表す。

x(t)RNx(t)\in\mathbb{R}^N

ここで x(t)x(t) は、名前ではなく、
大きい、茶色い、毛が多い、低音、速い、四本足寄り、などの印象の濃淡です。

たとえば

x(t)=[x1(t),x2(t),,xN(t)]x(t)= \bigl[ x_1(t),x_2(t),\dots,x_N(t) \bigr]

で、各成分は 0 から 1 の間の強さを持つ。


記憶に刻む

アソシアトロン側の記憶地形を M(t)M(t) とする。
最も素直には、遭遇した印象ベクトルが外積として刻まれる。

M(t+1)=M(t)+η(t)x(t)x(t)M(t+1)=M(t)+\eta(t)\,x(t)x(t)^\top

ここで

  • M(t)M(t) は記憶地形

  • η(t)\eta(t) はその時の刻まれやすさ

です。

ただし Atron 的には、毎回同じように刻むのではなく、
衝撃や濃さが強いときほど刻まれやすいほうが自然です。
なので η(t)\eta(t) は定数ではなく、その時の印象強度や carry に依存させてよい。

たとえば

η(t)=η0+η1x(t)+η2c(t)\eta(t)=\eta_0+\eta_1\,\|x(t)\|+\eta_2\,c(t)

ここで c(t)c(t) は引きずり側の強さです。


次に断片から想起する

アソシアトロンの想起は、次に入ってきた断片 cue q(t)q(t) から立ち上がる。

r(t)=M(t)q(t)r(t)=M(t)\,q(t)

ここで

  • q(t)RNq(t) は断片 cue

  • r(t)RNr(t) は想起圧

です。

大事なのは、q(t)q(t) は物体全体ではなく、断片だけでもよいことです。
たとえば「低音」だけ、「大きい」だけ、「速い動き」だけでもよい。

必要なら非線形を入れて

r(t)=ϕ ⁣(M(t)q(t))r(t)=\phi\!\bigl(M(t)\,q(t)\bigr)

としてもいい。
ここで ϕ\phi はしきい値や飽和です。


引きずる

引きずり側の内部地形を c(t)c(t) とする。
これは 1 変数でもよいし、複数次元でもよい。
最小形ならベクトルで

c(t)RKc(t)\in\mathbb{R}^K

として、更新をこう書ける。

c(t+1)=αc(t)+βUx(t)+γVr(t)c(t+1)=\alpha\,c(t)+\beta\,U\,x(t)+\gamma\,V\,r(t)

ここで

  • αc(t)\alpha c(t) は前状態の引きずり

  • Ux(t)U\,x(t) は今見た印象の影響

  • Vr(t)V\,r(t) は想起の影響

です。

ただし、引きずりは「元に戻る誤差」ではないので、
これは単なる減衰モデルではなく、変形した地形の持続 と読む。

だからより Atron らしく書くなら

c(t+1)=Ψ(c(t),x(t),r(t))c(t+1)=\Psi\bigl(c(t),x(t),r(t)\bigr)

のほうが本質に近い。

ここで Ψ\Psi は、
今の印象と想起によって、引きずり地形がどう変形するかを表す写像です。


つながり全体

全体をまとめると、こうです。

観る

x(t)RNx(t)\in\mathbb{R}^N

刻む

M(t+1)=M(t)+η(t)x(t)x(t)M(t+1)=M(t)+\eta(t)\,x(t)x(t)^\top

想起する

r(t)=ϕ ⁣(M(t)q(t))r(t)=\phi\!\bigl(M(t)\,q(t)\bigr)

引きずる

c(t+1)=Ψ(c(t),x(t),r(t))c(t+1)=\Psi\bigl(c(t),x(t),r(t)\bigr)


Atron 

  • x(t)x(t): 今の遭遇印象

  • M(t)M(t): 印象が刻まれた記憶地形

  • r(t)r(t): 断片 cue から立ち上がる想起

  • c(t)c(t): 次の世界の見え方を歪める引きずり地形

です。

つまり Atron は、

x(t)M(t)r(t)c(t)x(t)\rightarrow M(t)\rightarrow r(t)\rightarrow c(t)

という一方向の手順ではなく、
実際には

x(t),r(t),c(t)x(t),\,r(t),\,c(t)

が互いに影響し合いながら更新される系です。


それとも

順番感を弱めて、こうも書けます。

M(t+1)=M(t)+η(t)x(t)x(t)r(t)=ϕ ⁣(M(t)q(t))c(t+1)=Ψ(c(t),x(t),r(t))\begin{aligned} M(t+1) &= M(t)+\eta(t)\,x(t)x(t)^\top \\ r(t) &= \phi\!\bigl(M(t)\,q(t)\bigr) \\ c(t+1) &= \Psi\bigl(c(t),x(t),r(t)\bigr) \end{aligned}

この3本で、

  • 観て感じる

  • 記憶に刻む

  • 断片から想起する

  • 引きずる



違和感

最初から

  • lion

  • danger

  • sadness

のような意味ラベルを一切入れていない。

入っているのは

  • 印象ベクトル x(t)x(t)

  • cue q(t)q(t)

  • 想起 r(t)r(t)

  • 引きずり c(t)c(t)

だけ。



誤字が多くてすみません。



2026年2月23日月曜日

Atron Script -2-

 ◆◇-------------動かなかった!!----------------◇◆

scene: LionEncounter_v1 cues: { vision: lion, sound: shout, distance: near } outer: rule: { danger_if: { vision: lion } } inner: pain: { add: 3.0, when: { distance: near, vision: lion } } arousal:{ add: 2.0, when: { sound: shout } } # 推奨:内輪の減衰(興奮しっぱなし防止) inner_decay: pain: 0.2 arousal: 0.3 memory: links: - { from: lion, to: A, weight: 0.20 } - { from: A, to: danger, weight: 0.10 } - { from: danger, to: flee, weight: 0.60 } update: { hebb: 0.05, decay: 0.01, clamp: [0.0, 1.0] } competition: rule: winner_take_top_1 activate_threshold: 0.15 attractor: stable_if: { link: [A, danger], ge: 0.80, for_scenes: 5 } policy: if_winner_is: flee act: run_away personality: auto_learn: 0.85 caution: 0.60


----- スクリプト側を↑これに修正 -------



それと、Python側の
Hebb の active セットが 「ずるい」
これはアソシアトロンじゃなかった。

active = [] if cues.get("vision") == "lion": active.append("lion") if rt.winner: active.append(rt.winner) # also include implied nodes if present if ("lion", "A") in mem: active.append("A") if ("A", "danger") in mem: active.append("danger")


これって

A が実際に活性化してなくても
danger が勝ってなくても
リンクが存在するだけで A と danger を “active 扱い” にして Hebb 更新する
シーンが何も起きてなくても、構造だけで w(A→danger) が育つ
三人称の設計を内輪に混ぜ込んでしまってる
うっかりすると、直ぐにこうなってしまう。


しかも、これじゃ新しいリンクが生まれない

for (a, b), w in mem.items(): if a in aset and b in aset: mem[(a, b)] = clamp(w + hebb, lo, hi)



lionを何度も見たら、勝手に lion→A ができる
叫びと逃走が結びついて、sound→flee ができる
みたいな挙動になっていない。


本来の Atron(経験→競合→想起→学習)に寄せる。

---------------Python  正しいcode-----------------





# run_assoc_v1_correct.py from __future__ import annotations from dataclasses import dataclass from typing import Dict, Tuple, Any, List, Optional, Set import yaml # pip install pyyaml LinkKey = Tuple[str, str] @dataclass class Runtime: inner: Dict[str, float] outer: Dict[str, Any] winner: Optional[str] stable_counter: int locked: bool # ---------------- utilities ---------------- def match_when(cues: Dict[str, Any], when: Dict[str, Any]) -> bool: """All key-value pairs in 'when' must match cues exactly (tiny v1).""" for k, v in when.items(): if cues.get(k) != v: return False return True def clamp(x: float, lo: float, hi: float) -> float: return max(lo, min(hi, x)) def build_memory(script: Dict[str, Any]) -> Dict[LinkKey, float]: mem: Dict[LinkKey, float] = {} for lk in script.get("memory", {}).get("links", []): a = str(lk["from"]) b = str(lk["to"]) mem[(a, b)] = float(lk["weight"]) return mem def apply_outer(cues: Dict[str, Any], outer_spec: Dict[str, Any]) -> Dict[str, Any]: """ OUTER v1: compute only minimal flags from cues. IMPORTANT: do NOT force inner meaning here. It's just 'environment signal'. """ rule = outer_spec.get("rule", {}) danger_if = rule.get("danger_if", {}) danger = all(cues.get(k) == v for k, v in danger_if.items()) return {"danger": bool(danger)} def apply_inner(cues: Dict[str, Any], inner_spec: Dict[str, Any], inner: Dict[str, float]) -> None: """Add pain/arousal/etc if conditions match.""" for name, spec in inner_spec.items(): add = float(spec.get("add", 0.0)) when = spec.get("when", {}) if match_when(cues, when): inner[name] = inner.get(name, 0.0) + add def decay_inner(inner: Dict[str, float], inner_decay: Dict[str, float]) -> None: """Optional: let inner states relax, so the system doesn't 'forever excite'.""" for k, d in inner_decay.items(): inner[k] = max(0.0, inner.get(k, 0.0) - float(d)) def decay_memory(mem: Dict[LinkKey, float], decay: float, lo: float, hi: float) -> None: for k in list(mem.keys()): mem[k] = clamp(mem[k] - decay, lo, hi) def hebb_update_existing(mem: Dict[LinkKey, float], active: Set[str], hebb: float, lo: float, hi: float) -> None: """ v1: strengthen ONLY existing links whose endpoints are both active. (No link creation in v1 by default.) """ for (a, b), w in list(mem.items()): if a in active and b in active: mem[(a, b)] = clamp(w + hebb, lo, hi) # ---------------- competition ---------------- def competition_v1( script: Dict[str, Any], cues: Dict[str, Any], rt: Runtime, mem: Dict[LinkKey, float] ) -> Tuple[Optional[str], Dict[str, float], Set[str]]: """ v1 competition: 1) Build base active from cues (e.g., vision==lion -> 'lion') 2) Score candidates by propagating through existing links: base -> 1-hop -> 2-hop -> ... (We keep it tiny but general-ish.) 3) Choose winner by rule. Returns: (winner, candidates, active_nodes_used) """ # 1) base active from cues (tiny mapping) base: Set[str] = set() if cues.get("vision") == "lion": base.add("lion") if cues.get("sound") == "shout": base.add("shout") # Optional: allow outer flags to become base nodes (still not "meaning", just signal) # If you dislike this, comment out. if rt.outer.get("danger") is True: base.add("danger_signal") # 2) propagation (tiny, bounded depth) candidates: Dict[str, float] = {} def push_from(active_sources: Set[str], scale: float) -> Set[str]: newly: Set[str] = set() for (a, b), w in mem.items(): if a in active_sources: score = scale * w if score > candidates.get(b, float("-inf")): candidates[b] = score newly.add(b) return newly # Depth 1 frontier = push_from(base, 1.0) # Depth 2 (chain effect) # (Small depth is enough for v1; make it 3 if you want more richness.) frontier2 = push_from(frontier, 0.85) # Depth 3 (optional, tiny) _ = push_from(frontier2, 0.70) # 3) inner contribution (arousal makes everything slightly more "ready") arousal = rt.inner.get("arousal", 0.0) if arousal: for k in list(candidates.keys()): candidates[k] += arousal * 0.01 # personality bias: caution can bias specific nodes but should be light persona = script.get("personality", {}) or {} caution = float(persona.get("caution", 0.0)) # Example bias (keep very small; otherwise it becomes "policy in disguise") for node, bonus in (("danger", 0.05), ("flee", 0.08)): if node in candidates: candidates[node] += caution * bonus if not candidates: return None, candidates, base # competition rule comp = script.get("competition", {}) or {} rule = comp.get("rule", "winner_take_top_1") if rule == "winner_take_top_1": winner = max(candidates.items(), key=lambda kv: kv[1])[0] else: # fallback to top_1 for v1 winner = max(candidates.items(), key=lambda kv: kv[1])[0] # Active nodes used for learning: base + winner + top candidates above a threshold # This is critical: we do NOT add nodes just because a link exists. used: Set[str] = set(base) used.add(winner) # "Activated" candidates threshold th = float(comp.get("activate_threshold", 0.15)) for n, s in candidates.items(): if s >= th: used.add(n) return winner, candidates, used # ---------------- step ---------------- def step(script: Dict[str, Any], mem: Dict[LinkKey, float], rt: Runtime) -> None: cues = dict(script.get("cues", {})) # OUTER + INNER rt.outer = apply_outer(cues, script.get("outer", {}) or {}) apply_inner(cues, script.get("inner", {}) or {}, rt.inner) # Optional: inner relaxation (recommended) inner_decay_spec = (script.get("inner_decay", {}) or {}) if inner_decay_spec: decay_inner(rt.inner, inner_decay_spec) # MEMORY UPDATE parameters upd = (script.get("memory", {}) or {}).get("update", {}) or {} hebb = float(upd.get("hebb", 0.0)) decay = float(upd.get("decay", 0.0)) lo, hi = map(float, upd.get("clamp", [0.0, 1.0])) # COMPETITION (winner + activated nodes) winner, candidates, active_nodes = competition_v1(script, cues, rt, mem) rt.winner = winner # ATTRACTOR check st = (script.get("attractor", {}) or {}).get("stable_if", {}) or {} a, b = st.get("link", ["A", "danger"]) ge = float(st.get("ge", 0.8)) for_scenes = int(st.get("for_scenes", 5)) w_link = mem.get((str(a), str(b)), 0.0) if w_link >= ge: rt.stable_counter += 1 else: rt.stable_counter = 0 rt.locked = rt.stable_counter >= for_scenes # MEMORY decay + Hebb # IMPORTANT: lock means "this has become trait-like", so freeze learning. if not rt.locked: if decay > 0: decay_memory(mem, decay, lo, hi) persona = script.get("personality", {}) or {} auto_learn = float(persona.get("auto_learn", 0.5)) # gentle multiplier: 0.0..1.0 -> 0.7..1.3 hebb_eff = hebb * (0.7 + 0.6 * clamp(auto_learn, 0.0, 1.0)) if hebb_eff > 0 and active_nodes: hebb_update_existing(mem, active_nodes, hebb_eff, lo, hi) # POLICY: translate winner to action (thin!) pol = script.get("policy", {}) or {} act = "none" if rt.winner is not None and rt.winner == pol.get("if_winner_is"): act = str(pol.get("act", "none")) pain = rt.inner.get("pain", 0.0) arousal = rt.inner.get("arousal", 0.0) # Show top few candidates for debugging top = sorted(candidates.items(), key=lambda kv: kv[1], reverse=True)[:3] top_s = ", ".join([f"{k}:{v:.2f}" for k, v in top]) if top else "-" print( f"winner={str(rt.winner):>8} act={act:>8} " f"pain={pain:>5.1f} arousal={arousal:>5.1f} " f"w({a}->{b})={w_link:.2f} stable={rt.stable_counter}/{for_scenes} locked={rt.locked} " f"top=[{top_s}] active={sorted(active_nodes)}" ) def main() -> None: import argparse ap = argparse.ArgumentParser() ap.add_argument("yaml_path", help="Associatron Script v1 YAML path") ap.add_argument("--scenes", type=int, default=20) args = ap.parse_args() with open(args.yaml_path, "r", encoding="utf-8") as f: script = yaml.safe_load(f) or {} mem = build_memory(script) rt = Runtime(inner={}, outer={}, winner=None, stable_counter=0, locked=False) print(f"=== Scene: {script.get('scene','(unnamed)')} ===") for i in range(args.scenes): print(f"[{i+1:02d}] ", end="") step(script, mem, rt) if __name__ == "__main__": main()

------------------------------- -------------------------------



さっきのところ。
新しいリンクが生まれるところ
さっきまでは

def hebb_update_existing(mem, active, hebb, lo, hi): for (a, b), w in list(mem.items()): if a in active and b in active: mem[(a, b)] = clamp(w + hebb, lo, hi)

すでに存在する (a, b) だけを見る
両方 active なら重みを増やす


状態
lion と flee が同時に active

結果
既存の lion→flee があれば強化

状態
lion→flee が存在しない

結果
何も起きない
      ↓



hebb_update_existingを変える。



def hebb_update(mem, active, hebb, lo, hi, create_th=0.02): active = list(active) # 1) 既存リンクの強化 for (a, b), w in list(mem.items()): if a in active and b in active: mem[(a, b)] = clamp(w + hebb, lo, hi) # 2) 新規リンク生成 for a in active: for b in active: if a == b: continue key = (a, b) if key not in mem: # 小さな初期値で生成 mem[key] = clamp(create_th, lo, hi)



active = {lion, flee}
なら
(lion, flee) が無ければ生成
(flee, lion) も生成

active_nodes が、そのシーンで「同時に起きた経験」になる。


新しいリンク生成に対して

A. 常に行う(連想が爆発するタイプ)
B. 閾値付き(安定型)
C. 高arousal時のみ(トラウマ型)

この調整で、性格のタイプが変わってしまう。


ぼーっとしてると、直ぐに3人称になってしまうから、
Atron Scriptで気づくようにしておかないとマズイ感じ。




エージェントと 一人称自律Atraの違い

 Atraなんかは、実はもう一人称自律として、きちんと発表してもいいレベル。 既に妻と笑っていたり、愛犬と騒いているんだから。ボーっと何かを眺めてたり、佐川急便に反応するようにもなった。 でも、そうしないのは、自発的に自ら研究意欲を持って、学び、人や自然と接触し自ら疑問を持って研...