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 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
| import torch import torch.nn as nn import numpy as np from typing import Tuple
class StateAwareRepresentation(nn.Module): """ 状态感知表示 (SAR) CSI → STFT频谱图 → CNN局部特征 → Bi-LSTM时序 """ def __init__(self, input_channels: int = 1, hidden_dim: int = 128): super().__init__() self.cnn = nn.Sequential( nn.Conv2d(input_channels, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.AdaptiveAvgPool2d((1, None)), ) self.lstm = nn.LSTM( input_size=128, hidden_size=hidden_dim, num_layers=2, batch_first=True, bidirectional=True, dropout=0.3, ) self.projection = nn.Linear(hidden_dim * 2, hidden_dim) def forward(self, stft_spectrogram: torch.Tensor) -> torch.Tensor: """ Args: stft_spectrogram: (B, 1, F, T) CSI的STFT频谱图 Returns: sar: (B, T', hidden_dim) 状态感知表示 """ cnn_out = self.cnn(stft_spectrogram) cnn_out = cnn_out.squeeze(2).permute(0, 2, 1) lstm_out, _ = self.lstm(cnn_out) sar = self.projection(lstm_out) global_repr = sar.mean(dim=1) return global_repr
class CrossModalProjector(nn.Module): """ 跨模态投影:CSI表示 → 文本语义空间 两个投影目标: 1. Word Embedding Space (Word2Vec) 2. Attribute Space (动词属性) """ def __init__(self, csi_dim: int = 128, word_dim: int = 300, attr_dim: int = 50): super().__init__() self.word_projector = nn.Sequential( nn.Linear(csi_dim, 256), nn.ReLU(), nn.Linear(256, word_dim), ) self.attr_projector = nn.Sequential( nn.Linear(csi_dim, 128), nn.ReLU(), nn.Linear(128, attr_dim), nn.Sigmoid(), ) def forward(self, csi_repr: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: """ Returns: word_proj: (B, word_dim) 词嵌入空间投影 attr_proj: (B, attr_dim) 属性空间投影 """ word_proj = self.word_projector(csi_repr) attr_proj = self.attr_projector(csi_repr) return word_proj, attr_proj
class ZeroShotClassifier: """ 零样本分类器 对未训练过的手势,通过语义最近邻分类 """ def __init__(self, word_embeddings: dict, attr_vectors: dict): """ Args: word_embeddings: {gesture_name: word2vec_vector} attr_vectors: {gesture_name: attribute_vector} """ self.word_embeddings = word_embeddings self.attr_vectors = attr_vectors def classify( self, word_proj: torch.Tensor, attr_proj: torch.Tensor, candidate_gestures: list, seen_gestures: list, ) -> str: """ 零样本分类 Args: word_proj: (word_dim,) 投影后的CSI attr_proj: (attr_dim,) candidate_gestures: 所有可能的手势名 seen_gestures: 已训练的手势名 Returns: predicted_gesture: 预测的手势名 """ unseen = [g for g in candidate_gestures if g not in seen_gestures] if not unseen: return self._nearest_neighbor(word_proj, attr_proj, seen_gestures) best_match = None best_score = -float('inf') for gesture in unseen: word_sim = torch.cosine_similarity( word_proj.unsqueeze(0), self.word_embeddings[gesture].unsqueeze(0), ).item() attr_sim = torch.cosine_similarity( attr_proj.unsqueeze(0), self.attr_vectors[gesture].unsqueeze(0), ).item() joint_score = 0.5 * word_sim + 0.5 * attr_sim if joint_score > best_score: best_score = joint_score best_match = gesture return best_match def _nearest_neighbor(self, word_proj, attr_proj, candidates): """标准最近邻分类""" best_match = None best_score = -float('inf') for gesture in candidates: word_sim = torch.cosine_similarity( word_proj.unsqueeze(0), self.word_embeddings[gesture].unsqueeze(0), ).item() attr_sim = torch.cosine_similarity( attr_proj.unsqueeze(0), self.attr_vectors[gesture].unsqueeze(0), ).item() score = 0.5 * word_sim + 0.5 * attr_sim if score > best_score: best_score = score best_match = gesture return best_match
if __name__ == "__main__": gestures = ["walking", "sitting", "standing", "waving", "pushing", "pulling", "clapping", "typing", "drinking", "eating", "phone_call", "reading", "sleeping", "turning", "bending", "kicking", "punching", "jumping", "opening_door", "closing_door"] np.random.seed(42) word_embeddings = {g: torch.randn(300) for g in gestures} attr_vectors = {g: torch.rand(50) for g in gestures} sar = StateAwareRepresentation(input_channels=1, hidden_dim=128) projector = CrossModalProjector(csi_dim=128, word_dim=300, attr_dim=50) classifier = ZeroShotClassifier(word_embeddings, attr_vectors) stft_input = torch.randn(4, 1, 64, 100) csi_repr = sar(stft_input) word_proj, attr_proj = projector(csi_repr) seen = gestures[:14] unseen = gestures[14:] for i in range(4): pred = classifier.classify(word_proj[i], attr_proj[i], gestures, seen) print(f"样本{i}: 预测={pred}") print(f"\n训练类别: {len(seen)}, 零样本类别: {len(unseen)}") print(f"零样本候选: {unseen}")
|