1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
| """ CW-Net: Concept-Wrapper Network 论文: Nature, 2026
核心思想: 1. 保留预训练黑盒模型的特征提取层 2. 替换最终决策层为概念分类器+新奖励层 3. 概念分类器输出人类可理解标签 4. 新奖励层基于概念做决策
适用: 任意预训练DNN的即插即用改造 """
import torch import torch.nn as nn from typing import List
class ConceptClassifier(nn.Module): """ 概念分类器 将黑盒模型的嵌入zi映射到人类可理解概念 如: 靠近停止车辆/接近自行车/跟车过近 """ def __init__(self, embed_dim: int = 256, concepts: List[str] = None): super().__init__() if concepts is None: concepts = [ 'ASV', 'CLOSE', 'BIKE', 'LANE_CHG', 'TURN', 'PED', 'SPEED', 'CROWD', ] self.num_concepts = len(concepts) self.concept_names = concepts self.classifier = nn.Sequential( nn.Linear(embed_dim, 128), nn.ReLU(), nn.Dropout(0.2), nn.Linear(128, self.num_concepts) ) def forward(self, z: torch.Tensor) -> torch.Tensor: """ Args: z: 场景-轨迹嵌入 (B, embed_dim) Returns: concept_logits: 概念logits (B, num_concepts) """ return self.classifier(z) def explain(self, z: torch.Tensor) -> dict: """生成人类可读解释""" logits = self.forward(z) probs = torch.sigmoid(logits) active_concepts = [] for i, p in enumerate(probs[0]): if p > 0.5: active_concepts.append({ 'concept': self.concept_names[i], 'probability': p.item() }) return { 'active_concepts': active_concepts, 'explanation': self._generate_text(active_concepts) } def _generate_text(self, concepts: list) -> str: """概念→自然语言解释""" if not concepts: return "Normal driving condition" parts = [] for c in concepts: name = c['concept'] if name == 'ASV': parts.append("approaching a stopped vehicle") elif name == 'CLOSE': parts.append("close to another vehicle") elif name == 'BIKE': parts.append("a cyclist is nearby") elif name == 'LANE_CHG': parts.append("lane change is available") elif name == 'TURN': parts.append("approaching an intersection") elif name == 'PED': parts.append("a pedestrian is nearby") return "AV is " + ", ".join(parts) + " → adjusting trajectory"
class CWNet(nn.Module): """ Concept-Wrapper Network 包装任意预训练planner,添加可解释性 """ def __init__(self, pretrained_planner: nn.Module, embed_dim: int = 256): super().__init__() self.planner = pretrained_planner for param in self.planner.parameters(): param.requires_grad = False self.concept_classifier = ConceptClassifier(embed_dim) self.new_reward = nn.Sequential( nn.Linear(self.concept_classifier.num_concepts, 64), nn.ReLU(), nn.Linear(64, 1) ) def forward(self, scene: torch.Tensor, trajectories: torch.Tensor) -> dict: """ Args: scene: 场景上下文 trajectories: 候选轨迹集 Returns: reward: 轨迹奖励 concepts: 概念分类 explanation: 人类可读解释 """ with torch.no_grad(): h = self.planner.scene_encoder(scene) embeddings = [] for traj in trajectories: z = self.planner.encoder(h, traj) embeddings.append(z) z = torch.stack(embeddings).squeeze(1) concept_logits = self.concept_classifier(z) rewards = self.new_reward(torch.sigmoid(concept_logits)) best_idx = rewards.argmax() explanation = self.concept_classifier.explain( z[best_idx:best_idx+1] ) return { 'rewards': rewards, 'concept_logits': concept_logits, 'explanation': explanation }
if __name__ == "__main__": classifier = ConceptClassifier() z = torch.randn(1, 256) result = classifier.explain(z) print("概念分类结果:") for c in result['active_concepts']: print(f" {c['concept']}: {c['probability']:.2f}") print(f"\n解释: {result['explanation']}")
|