Unsupervised Intra-domain Adaptation for Semantic Segmentation through Self-Supervision
1.官方readme.md
Step 1. 通过训练ADVENT进行跨领域适应: ADVENT:
$ cd <root_dir>/ADVENT/advent/scripts
$ python train.py --cfg ./config/advent.yml
$ python train.py --cfg ./config/advent.yml --tensorboard % using tensorboard
经过域间训练后,需要通过运行test.py得到最佳IoU迭代:
$ cd <root_dir>/ADVENT/advent/scripts
$ python test.py --cfg ./config/advent.yml
最佳IoU迭代BEST_ID
将是Step 2的一个参数
Step 2. 基于熵的排序将Cityscapes训练集划分为易分割和难分割:
$ cd <root_dir>/entropy_rank
$ python entropy.py --best_iter BEST_ID --normalize False --lambda1 0.67
您将看到生成的伪标签 color_masks
, 容易分割文件名称在 easy_split.txt
, 难分割文件名称在 hard_split.txt
.
Step 3. 通过运行以下代码进行域内适配:
$ cd <root_dir>/intrada
$ python train.py --cfg ./intrada.yml
$ python train.py --cfg ./intrada.yml --tensorboard % using tensorboard
Testing
测试性能,请运行:
$ cd <root_dir>/intrada
$ python test.py --cfg ./intrada.yml
2.代码文件编排
3.部分代码注释
3.1.ADVENT
3.1.1.advent/domain_adaptation/train_UDA.py
主函数train_domain_adaptation,根据所选择的方法进行域间适应:
train_domain_adaptation(model, trainloader, targetloader, cfg):
if cfg.TRAIN.DA_METHOD == 'MinEnt':
train_minent(model, trainloader, targetloader, cfg)
elif cfg.TRAIN.DA_METHOD == 'AdvEnt':
train_advent(model, trainloader, targetloader, cfg)
train_minent :根据minent方法进行域间适应训练:
train_minent(model, trainloader, targetloader, cfg):
#先通过有标记的源图像最小化交叉熵损失,监督训练Ginter
pred_src_aux, pred_src_main =model(images_source.cuda(device))
loss_seg_src_aux = loss_calc(pred_src_aux, labels, device)
loss_seg_src_main = loss_calc(pred_src_main, labels, device)
loss = (cfg.TRAIN.LAMBDA_SEG_MAIN * loss_seg_src_main+ cfg.TRAIN.LAMBDA_SEG_AUX * loss_seg_src_aux)
loss.backward()#通过最小化模型预测目标图像的熵损失,优化Ginter
pred_trg_aux, pred_trg_main = model(images.cuda(device))
pred_trg_aux = interp_target(pred_trg_aux);
pred_trg_main = interp_target(pred_trg_main)
pred_prob_trg_aux = F.softmax(pred_trg_aux);pred_prob_trg_main = F.softmax(pred_trg_main)
loss_target_entp_aux = entropy_loss(pred_prob_trg_aux)
loss_target_entp_main = entropy_loss(pred_prob_trg_main)
loss = (cfg.TRAIN.LAMBDA_ENT_AUX * loss_target_entp_aux+ cfg.TRAIN.LAMBDA_ENT_MAIN * loss_target_entp_main)
loss.backward()
optimizer.step()
train_advent:根据ADVENT方法进行域间适应性训练:
train_advent(model, trainloader, targetloader, cfg):
#discriminator是作用于熵图的
d_aux = get_fc_discriminator(num_classes=num_classes);
d_main = get_fc_discriminator(num_classes=num_classes)
interp = nn.Upsample(size=(input_size_source[1], input_size_source[0]))
interp_target = nn.Upsample(size=(input_size_target[1], input_size_target[0]))
source_label = 0;target_label = 1
# UDA Training:#先使用源和源标记监督训练分割网络
pred_src_aux, pred_src_main = model(images_source.cuda(device))
pred_src_aux = interp(pred_src_aux);
loss_seg_src_aux = loss_calc(pred_src_aux, labels, device)
pred_src_main = interp(pred_src_main);
loss_seg_src_main = loss_calc(pred_src_main, labels, device)
loss = (cfg.TRAIN.LAMBDA_SEG_MAIN * loss_seg_src_main + cfg.TRAIN.LAMBDA_SEG_AUX * loss_seg_src_aux)
loss.backward()
#对抗训练,训练model,希望model足够好,好到可以让discriminator无法分辨图像来自哪个域,这里固定不训练discriminator
pred_trg_aux, pred_trg_main = model(images.cuda(device))
#生成目标的伪软分割图,后面生成伪标签
pred_trg_aux = interp_target(pred_trg_aux)
d_out_aux = d_aux(prob_2_entropy(F.softmax(pred_trg_aux)))
loss_adv_trg_aux = bce_loss(d_out_aux, source_label)
pred_trg_main = interp_target(pred_trg_main)
d_out_main = d_main(prob_2_entropy(F.softmax(pred_trg_main)))
loss_adv_trg_main = bce_loss(d_out_main, source_label)
loss = (cfg.TRAIN.LAMBDA_ADV_MAIN * loss_adv_trg_main+ cfg.TRAIN.LAMBDA_ADV_AUX * loss_adv_trg_aux)
loss = loss;
loss.backward()
#训练discriminator,使用源图的熵图,通过最小化discriminator对源预测出是源还是目标所产生的损失
pred_src_aux = pred_src_aux.detach()
d_out_aux = d_aux(prob_2_entropy(F.softmax(pred_src_aux)))
loss_d_aux = bce_loss(d_out_aux, source_label)
loss_d_aux = loss_d_aux / 2;loss_d_aux.backward()
pred_src_main = pred_src_main.detach()
d_out_main = d_main(prob_2_entropy(F.softmax(pred_src_main)))
loss_d_main = bce_loss(d_out_main, source_label);
loss_d_main = loss_d_main / 2;
loss_d_main.backward()
# 训练discriminator,使用目标图像的熵图,通过最小化discriminator对目标预测出是源还是目标所产生的损失
pred_trg_aux = pred_trg_aux.detach();
d_out_aux = d_aux(prob_2_entropy(F.softmax(pred_trg_aux)))
loss_d_aux = bce_loss(d_out_aux, target_label);
loss_d_aux = loss_d_aux / 2;loss_d_aux.backward()
pred_trg_main = pred_trg_main.detach()
d_out_main = d_main(prob_2_entropy(F.softmax(pred_trg_main)))
loss_d_main = bce_loss(d_out_main, target_label);
loss_d_main = loss_d_main / 2
loss_d_main.backward()
3.1.2.advent/domain_adaptation/eval_UDA.py
验证模型的精度,输出对应结果的IoU
主函数evaluate_domain_adaptation:根据模式进行相关的模型验证
evaluate_domain_adaptation( models, test_loader, cfg,fixed_test_size=True,verbose=True):
if cfg.TEST.MODE == 'single':
eval_single(cfg, models,device, test_loader, interp, fixed_test_size,verbose)
elif cfg.TEST.MODE == 'best':
eval_best(cfg, models,device, test_loader, interp, fixed_test_size,verbose)
eval_single验证单个模型的性能:
eval_single(cfg, models,device, test_loader, interp,fixed_test_size, verbose):
#计算IOU
hist = np.zeros((cfg.NUM_CLASSES, cfg.NUM_CLASSES))
hist += fast_hist(label.flatten(), output.flatten(), cfg.NUM_CLASSES)
#输出每一类的IoU
inters_over_union_classes = per_class_iu(hist)
eval_best验证所有模型,输出最好的那个:
eval_best(cfg, models,device, test_loader, interp,fixed_test_size, verbose):
for i_iter in range(start_iter, max_iter + 1, step):
hist += fast_hist(label.flatten(), output.flatten(), cfg.NUM_CLASSES)
inters_over_union_classes = per_class_iu(hist)
all_res[i_iter] = inters_over_union_classes
pickle_dump(all_res, cache_path)
#计算每一类的mIoU,保存最好的那个
computed_miou = round(np.nanmean(inters_over_union_classes) * 100, 2)
if cur_best_miou < computed_miou:
cur_best_miou = computed_miou
cur_best_model = restore_from
3.2.entropy_rank
3.2.1.entropy.py
对目标图像按经过生成器后的熵进行排序分为难分割和易分割
主函数main:
main(arg):
model_gen = get_deeplab_v2
load_checkpoint_for_evaluation(model_gen, restore_from, device)
_, pred_trg_main = model_gen(image.cuda(device))
pred_trg_main = interp_target(pred_trg_main) p
red_trg_entropy = prob_2_entropy(F.softmax(pred_trg_main))
entropy_list.append((name[0], pred_trg_entropy.mean().item() * normalizor))
##将预测的mask和转换成指定色彩的彩色图存下来
colorize_save(pred_trg_main, name[0])
#根据entropy_list排序将easy和hard图像的名字写入对应的txt文件
cluster_subdomain(entropy_list, args.lambda1)
3.3.intrada
3.3.1.train.py
进行域内适配训练
主函数main
main():
model = get_deeplab_v2(num_classes=cfg.NUM_CLASSES, multi_level=cfg.TRAIN.MULTI_LEVEL)
saved_state_dict = torch.load(cfg.TRAIN.RESTORE_FROM)
#根据之前存下来的easy的图像的名单加载数据
easy_dataset = CityscapesDataSet_easy(root=cfg.DATA_DIRECTORY_SOURCE);
easy_loader = data.DataLoader(easy_dataset)
#根据之前存下来的hard的图像的名单加载数据
hard_dataset = CityscapesDataSet_easy(root=cfg.DATA_DIRECTORY_TARGET);
hard_loader = data.DataLoader(hard_dataset)
#将easy作为源hard作为目标。进行域内适应训练,训练结构和域间一样
train_domain_adaptation(model, easy_loader, hard_loader, cfg)