0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

意識の5層モデルをPythonで実装する試み25

0
Last updated at Posted at 2026-05-22

意識の5層モデルをPythonで実装する試み25

〜v2.0 実装解剖編:コードを見せろの精神〜


今回は能書きなし。コードを見せろの精神で、v2.0実装のL5周辺を解剖する。

理論の背景は v24 を参照。

リポジトリ:predictive-agency-simulator


ファイル構成

predictive-agency-simulator/
├── Hida_unified_V2/
│   └── hida_unified_v2.py      ← 今回見るやつ(メイン)
├── HIDA_theory_paper_v2_0.md   ← 論文
├── spec_v2.md                  ← AI向け仕様書
└── README_ja_v2.md

hida_unified_v2.py を解剖していく。


1. L5クラスの全体像

class L5Consciousness:
    """意識(主導・追随同期、自認、言語化)"""
    
    # 初期DNA(クラス定数)
    LEADER_THRESHOLD = 0.3   # 主導層判定の閾値
    EMA_ALPHA = 0.35         # EMA平滑化係数
    ON_THRESHOLD = 0.03      # 意識ON判定
    OFF_THRESHOLD = 0.02     # 意識OFF判定(ヒステリシス)
    
    def __init__(self, threshold=None):
        if threshold is not None:
            self.ON_THRESHOLD = threshold
            self.OFF_THRESHOLD = max(0.0, threshold - 0.05)
        
        # 意識状態
        self.is_conscious = False
        self.sync_score = 0.0     # 強度の軸(v2.0)
        self.sync_type = None     # 主導タイプの軸(v2.0)
        self.sync_level = 0.0     # 後方互換(= sync_score)
        
        # EMA平滑化
        self._ema = 0.0
        
        # 前ステップの状態保持(活動量=変化量の計算用)
        self._prev = {
            'l1_energy': 1.0,
            'l1_fatigue': 0.0,
            'l2_valence': 0.0,
            'l2_arousal': 0.0,
            'l2_qualia': {},
            'l3_error_count': 0,
            'l4_found_count': 0,   # ※ v2.0では実質activation_countの前回値
        }

ポイント:

  • 閾値はクラス定数として固定(初期DNA)
  • sync_score と sync_type の二軸出力
  • 変化量計算のため、前ステップの状態を _prev に保持
  • l4_found_count というキー名は後方互換のため残してあるが、v2.0では実質的に activation_count の前回値として使っている

2. 各層の活動量を「変化量」として測る

def _measure_activity(self, l1, l2, l3, l4):
    """各層の活動量を「前ステップからの変化量」として測定"""
    
    # L1: 身体状態の変化量
    l1_change = abs(l1.energy - self._prev['l1_energy']) + \
                abs(l1.fatigue - self._prev['l1_fatigue'])
    l1_act = self._clip01(l1_change / 1.0)
    
    # L2: 価値変化量
    l2_change = abs(l2.valence - self._prev['l2_valence']) + \
                abs(l2.arousal - self._prev['l2_arousal']) + \
                sum(abs(l2.qualia.get(k, 0) - self._prev['l2_qualia'].get(k, 0))
                    for k in l2.qualia)
    l2_act = self._clip01(l2_change / 3.0)
    
    # L3: 予測誤差の強度合計
    l3_magnitude_sum = sum(e.get('magnitude', 0.0) for e in l3.errors) if l3.errors else 0.0
    l3_act = self._clip01(l3_magnitude_sum / 3.0)
    
    # L4: 記憶活性化の増分
    l4_current_count = l4.activation_count if hasattr(l4, 'activation_count') else 0
    l4_change = max(0, l4_current_count - self._prev['l4_found_count'])
    l4_act = self._clip01(l4_change / 3.0)
    
    return {'L1': l1_act, 'L2': l2_act, 'L3': l3_act, 'L4': l4_act}

ポイント:

  • 絶対値ではなく変化量(「今、同期信号を出してるか」を測る)
  • L3だけ件数差分ではなく magnitude合計(毎ステップ更新されるリストなので)
  • 正規化定数(1.0, 3.0, 3.0, 3.0)も初期DNA

3. 主導層を特定する

def _detect_leader(self, activities):
    """主導層の特定: 閾値超過層のうち最大活動量を持つ層"""
    candidates = {k: v for k, v in activities.items()
                  if v >= self.LEADER_THRESHOLD}
    
    if not candidates:
        return None, 0.0  # 同期信号なし
    
    leader = max(candidates, key=candidates.get)
    return leader, candidates[leader]

シンプル。閾値超えた層のうち最大値が主導層。なければ None。


4. 追随整合性を評価する

def _measure_follower_coherence(self, activities, leader, leader_strength):
    """追随整合性の評価(簡易版:同時応答指標)"""
    follower_activities = [v for k, v in activities.items() if k != leader]
    follower_mean = sum(follower_activities) / 3
    coherence = self._clip01(follower_mean / leader_strength)
    return coherence

主導層を除いた3層の活動量平均を、主導強度で正規化。1に近いほど追随層が応答してる。

これは簡易版。本来は時間遅れ相関や位相同期で測るべきだが、v2.0初期実装では同時応答で近似してる。


5. 統合:check_sync 関数

def check_sync(self, l1, l2, l3, l4):
    """第5層の同期判定(v2.0: 主導・追随モデル)"""
    
    # Step 1: 各層の活動量を測定
    activities = self._measure_activity(l1, l2, l3, l4)
    
    # Step 2: 主導層の特定
    leader, leader_strength = self._detect_leader(activities)
    
    if leader is None:
        raw_sync = 0.0
        self.sync_type = None
    else:
        # Step 3: 追随整合性の評価
        follower_coherence = self._measure_follower_coherence(
            activities, leader, leader_strength
        )
        # Step 4: 同期スコアの算出
        raw_sync = leader_strength * follower_coherence
        self.sync_type = leader
    
    # Step 5: EMA平滑化とヒステリシス
    self._ema = (1 - self.EMA_ALPHA) * self._ema + self.EMA_ALPHA * raw_sync
    self.sync_score = self._ema
    self.sync_level = self._ema  # 後方互換
    
    if not self.is_conscious:
        self.is_conscious = (self._ema >= self.ON_THRESHOLD)
    else:
        self.is_conscious = (self._ema >= self.OFF_THRESHOLD)
    
    # Step 6: 次ステップ用の状態保存
    self._update_prev(l1, l2, l3, l4)
    
    return self.is_conscious

sync_score = leader_strength × follower_coherence。これが v2.0 の中核。

EMAとヒステリシスで点滅を抑制。

なお、ここで決まった sync_score・sync_type を含むL1〜L4の状態は、L5の self_recognize() → format_for_llm() → LLMVerbalizer.verbalize() を経由してLLMに渡し、1人称で語らせることができる(第23回の内省の話の延長)。詳細は別の回で扱う。


6. step() の処理順序

def step(self, world, verbose=True):
    self.step_count += 1
    
    # 1. 変調値適用、L2更新
    self.l2.apply_modulation(self.l4.get_modulation())
    self.l2.update_from_body(self.l1.get_state())
    
    # 2. 感覚・思考・行動
    errors = self.sense(world)
    action, details = self.think(world)
    event = self.act(world, action, details)  # "goal_reached"/"grabbed_ball"/None
    goal_reached = (event == "goal_reached")
    
    # 3. 訪問記録
    self.l4.mark_visited(tuple(self.l1.position))
    
    # 4. イベント別処理(check_syncの前に!)
    if event == "grabbed_ball":
        self.l4.remember_consciously(...)
    if goal_reached:
        self.l2.qualia['joy'] = min(1.0, self.l2.qualia['joy'] + 0.5)
        self.l2.valence += 0.3
        self.l4.remember_consciously(...)
    
    # 5. クオリア減衰
    self.l2.decay_qualia()
    
    # 6. 同期判定(毎ステップ!)
    is_conscious = self.l5.check_sync(self.l1, self.l2, self.l3, self.l4)
    
    # 7. 必要なら言語化
    if verbose and is_conscious and self.step_count % 5 == 0:
        reflection = self.reflect(action, details)

順序のポイント(GPT指摘で修正済み):

  • 感情イベント処理は check_sync の前(同期判定に反映させるため)
  • check_sync は毎ステップ呼ぶ(5ステップに1回ではない)
  • reflect は check_sync の後、意識ON時のみ
  • act() の戻り値は bool ではなくイベント名(ボール取得とゴール到達を区別)

7. L4Memory への追加

class L4Memory:
    def __init__(self):
        # ... 既存属性 ...
        self.activation_count = 0  # v2.0追加:記憶活性化カウンタ
    
    def update_from_sense(self, sense_data):
        for pos, data in sense_data.items():
            if pos not in self.internal_map:
                self.activation_count += 1     # 新規セル
            self.internal_map[pos] = data['cell']
            if data['object']:
                if pos not in self.found_objects:
                    self.activation_count += 1  # 新規オブジェクト
                else:
                    self.activation_count += 0.3  # 再観測
                self.found_objects[pos] = data['object']
    
    def update_from_errors(self, errors, qualia_intensity):
        for error in errors:
            pos = error.get('pos')
            if pos and error.get('object'):
                self.found_objects[pos] = {
                    **error['object'],
                    'memory_strength': qualia_intensity
                }
                self.activation_count += 1 + qualia_intensity
    
    def mark_visited(self, pos):
        if pos not in self.visited:
            self.activation_count += 0.5
        self.visited.add(pos)

増加ルールも初期DNA。新規発見・再観測・予測誤差由来でそれぞれ加算。


8. 動作確認スクリプト

# 1. リセット
python hida_unified_v2.py reset
記憶と変調値をリセットしました
# 2. 通常実行(赤好き、エネルギー満タン)
python hida_unified_v2.py
=== HIDA統合版テスト ===
【テスト1】赤好き、通常エネルギー
  Step 1: go_to_red
    ○ sync_score=0.021 leader=L4
  Step 2: go_to_red
    ○ sync_score=0.013 leader=-
  ...

○ が意識OFF、● が意識ON。leader が主導タイプ(L1/L2/L3/L4/-)。leader=- の表示は内部的には sync_type=None(主導層なし)を表す。


9. 出力例:sync_typeの切り替え観察

実際のシミュレーションログから抜粋:

=== テスト1:赤好き、通常エネルギー ===
  Step 1: go_to_red
    ○ sync_score=0.021 leader=L4    ← 探索開始、新規発見でL4主導
  Step 3: go_to_red
    ○ sync_score=0.015 leader=L4
  Step 4: go_to_red
    ○ sync_score=0.010 leader=-     ← 活動量低下、主導層なし
  Step 7: go_to_red
    ○ sync_score=0.008 leader=L4
  Step 8: go_to_red                  ← ボール取得
    ● sync_score=0.038 leader=L2    ← 感情変化でL2主導、意識ON
  Step 17: go_to_goal                ← ゴール到達
    ● sync_score=0.024 leader=L2    ← joy上昇でL2主導
🏆 ゴール到達!

=== テスト2:低エネルギー ===
  Step 1: ● sync_score=0.062 leader=L1   ← 低エネで身体変化、L1主導
  Step 2: ● sync_score=0.057 leader=L4
  Step 4: ● sync_score=0.024 leader=-
🏆 ゴール到達!

観察できる挙動:

  • 探索中:L4主導⇄主導層なし の切り替え
  • ボール取得:L2主導(取得イベントに伴う価値変化)
  • ゴール到達:L2主導(joy上昇)
  • 低エネルギー:L1主導(身体危機)

同じエージェント内で、同じシミュレーション中に複数種類の意識状態がログに記録される。


10. sync_type分布の測定スクリプト

実験用:

import sys
sys.path.insert(0, '.')
from hida_unified_v2 import HIDA, create_test_world

world = create_test_world()
hida = HIDA(color_preference={'red': 1.0, 'blue': 0.3, 'green': 0.3})
hida.l1.position = [3, 6]
hida.l1.energy = 1.0

type_counts = {'L1': 0, 'L2': 0, 'L3': 0, 'L4': 0, None: 0}
conscious_count = 0
transitions = 0
prev_type = None

for step in range(50):
    result = hida.step(world, verbose=False)
    type_counts[result['sync_type']] += 1
    if result['conscious']:
        conscious_count += 1
    if result['sync_type'] != prev_type and prev_type is not None:
        transitions += 1
    prev_type = result['sync_type']
    
    if result.get('goal_reached'):
        break

print(f'意識ON率: {100*conscious_count/(step+1):.1f}%')
print(f'sync_type切り替え回数: {transitions}')
print(f'sync_type分布: {type_counts}')

このスクリプトで「主導タイプの切り替え頻度」と「意識ON率」を測定できる。

論文§6 予測9「健康な意識では sync_type が状況に応じて頻繁に切り替わる」の検証材料になる。


まとめ

  • v2.0実装の中核は check_sync() 関数
  • 活動量を「変化量」として測る
  • leader_strength × follower_coherence で同期スコア
  • 各層の正規化定数・閾値・増加ルールは初期DNAとして固定
  • 閾値キャリブレーションはコミュニティ検証に開かれている

コードを動かして、sync_typeが切り替わるログを見るのが一番早い。

理論の背景は第24回、論文はHIDA_theory_paper_v2_0.mdを参照。


関連リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?