如何为Siamese网络构建支持集、正负样本对适配多视角数据集
带视角层级目录的孪生网络样本生成适配方案
核心改造逻辑是在原有单级目录处理的基础上,额外记录每张图像的视角属性,建立「类别-视角」的分组索引,采样时严格匹配定义好的正负样本规则,同时按视角分层构建支持集即可。
改造后的数据加载函数
原有load_data_set只记录图像和类别标签,现在需要新增视角标签记录,同时建立分组索引,避免采样时每次全量遍历数据集:
import os import numpy as np from tensorflow.keras.preprocessing.image import load_img, img_to_array from random import choice def load_data_set(data_root, img_size=(224, 224)): images = [] class_labels = [] view_labels = [] # 分组索引:键为(类别名, 视角名),值为对应分组下所有样本在图像数组中的索引列表 group_index = {} # 读取所有一级类别目录 class_list = [d for d in os.listdir(data_root) if os.path.isdir(os.path.join(data_root, d))] for class_name in class_list: class_path = os.path.join(data_root, class_name) # 读取类别下的视角子目录 view_list = [d for d in os.listdir(class_path) if d.startswith("view") and os.path.isdir(os.path.join(class_path, d))] for view_name in view_list: view_path = os.path.join(class_path, view_name) img_list = [f for f in os.listdir(view_path) if f.endswith(".png")] group_key = (class_name, view_name) group_index[group_key] = [] for img_name in img_list: img_path = os.path.join(view_path, img_name) # 按要求转换为RGB格式、缩放到指定尺寸 img = load_img(img_path, target_size=img_size, color_mode="rgb") img_arr = img_to_array(img) / 255.0 # 像素值归一化到0-1区间,和原有预处理逻辑对齐 images.append(img_arr) class_labels.append(class_name) view_labels.append(view_name) group_index[group_key].append(len(images) - 1) # 转换为numpy数组方便后续索引操作 images = np.array(images) class_labels = np.array(class_labels) view_labels = np.array(view_labels) return images, class_labels, view_labels, group_index, class_list
改造后的正样本对生成函数
按照规则,正样本对只要求同类别,视角相同/不同都符合要求,采样时从锚点所属类别的所有视角样本池中抽取配对图即可,保留原有的图像增强开关:
def positive_pair(images, class_labels, group_index, augment=False, aug_pipeline=None): # 随机采样锚点 anchor_idx = np.random.randint(0, len(images)) anchor_class = class_labels[anchor_idx] # 收集同类别下所有视角的样本,排除锚点自身 pos_candidates = [] for view in ["view1", "view2", "view3"]: key = (anchor_class, view) if key in group_index: pos_candidates.extend([idx for idx in group_index[key] if idx != anchor_idx]) pair_idx = choice(pos_candidates) anchor_img = images[anchor_idx] pair_img = images[pair_idx] # 执行图像增强 if augment and aug_pipeline is not None: anchor_img = aug_pipeline(anchor_img) pair_img = aug_pipeline(pair_img) return [anchor_img, pair_img], 1.0
改造后的负样本对生成函数
负样本对要求同时满足「不同类别」+「不同视角」两个条件,采样时先过滤掉和锚点同类别、同视角的样本,再从剩余候选池中抽取配对图:
def negative_pair(images, class_labels, view_labels, group_index, augment=False, aug_pipeline=None): # 随机采样锚点 anchor_idx = np.random.randint(0, len(images)) anchor_class = class_labels[anchor_idx] anchor_view = view_labels[anchor_idx] # 收集满足条件的负样本候选:类别不同、视角也不同 neg_candidates = [] for (cls_name, view_name), idx_list in group_index.items(): if cls_name != anchor_class and view_name != anchor_view: neg_candidates.extend(idx_list) pair_idx = choice(neg_candidates) anchor_img = images[anchor_idx] pair_img = images[pair_idx] # 执行图像增强 if augment and aug_pipeline is not None: anchor_img = aug_pipeline(anchor_img) pair_img = aug_pipeline(pair_img) return [anchor_img, pair_img], 0.0
支持集构建方法
支持集按「类别-视角」两层结构存储,每个类别每个视角抽取固定数量的参考样本,推理时将查询样本和支持集样本逐对输入孪生网络计算相似度,取最高相似度对应的类别作为预测结果:
def build_support_set(images, class_labels, group_index, shot_num=1): """ 构建少样本训练/评估用支持集 shot_num: 每个类别每个视角抽取的参考样本数量 """ support_set = {} for class_name in set(class_labels): support_set[class_name] = {} for view_name in ["view1", "view2", "view3"]: key = (class_name, view_name) if key not in group_index: continue # 从对应分组随机抽取指定数量的样本,不重复采样 selected_idx = np.random.choice(group_index[key], size=shot_num, replace=False) support_set[class_name][view_name] = images[selected_idx] return support_set
训练批次生成示例
可以直接用下面的函数批量生成正负样本比例1:1的训练批次,适配孪生网络的双输入结构:
def generate_train_batch(images, class_labels, view_labels, group_index, batch_size=32, augment=False, aug_pipeline=None): pairs = [] labels = [] # 正负样本按1:1比例采样 for _ in range(batch_size // 2): pos_pair, pos_label = positive_pair(images, class_labels, group_index, augment, aug_pipeline) neg_pair, neg_label = negative_pair(images, class_labels, view_labels, group_index, augment, aug_pipeline) pairs.extend([pos_pair, neg_pair]) labels.extend([pos_label, neg_label]) # 拆分出孪生网络需要的两路输入 input_1 = np.array([p[0] for p in pairs]) input_2 = np.array([p[1] for p in pairs]) return [input_1, input_2], np.array(labels)
实现注意事项:
- 若数据集存在某个视角样本缺失的情况,需要补充候选池为空的边界判断,避免采样报错
- 图像增强建议对配对的两张图独立执行随机变换,不要使用完全相同的增强参数,防止模型学到增强操作的关联特征
- 评估阶段构建支持集时建议固定随机种子,保证评估结果可复现
- 可以根据训练需求调整正负样本的采样比例,不需要严格固定为1:1
内容的提问来源于stack exchange,提问作者Borhan Uddin
相关产品推荐
相关产品推荐

