Spaces:
Sleeping
Sleeping
import itertools | |
from omegaconf import OmegaConf | |
def bifpn_config(min_level, max_level, weight_method=None): | |
"""BiFPN config. | |
Adapted from https://github.com/google/automl/blob/56815c9986ffd4b508fe1d68508e268d129715c1/efficientdet/keras/fpn_configs.py | |
""" | |
p = OmegaConf.create() | |
weight_method = weight_method or 'fastattn' | |
num_levels = max_level - min_level + 1 | |
node_ids = {min_level + i: [i] for i in range(num_levels)} | |
level_last_id = lambda level: node_ids[level][-1] | |
level_all_ids = lambda level: node_ids[level] | |
id_cnt = itertools.count(num_levels) | |
p.nodes = [] | |
for i in range(max_level - 1, min_level - 1, -1): | |
# top-down path. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [level_last_id(i), level_last_id(i + 1)], | |
'weight_method': weight_method, | |
}) | |
node_ids[i].append(next(id_cnt)) | |
for i in range(min_level + 1, max_level + 1): | |
# bottom-up path. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': level_all_ids(i) + [level_last_id(i - 1)], | |
'weight_method': weight_method, | |
}) | |
node_ids[i].append(next(id_cnt)) | |
return p | |
def panfpn_config(min_level, max_level, weight_method=None): | |
"""PAN FPN config. | |
This defines FPN layout from Path Aggregation Networks as an alternate to | |
BiFPN, it does not implement the full PAN spec. | |
Paper: https://arxiv.org/abs/1803.01534 | |
""" | |
p = OmegaConf.create() | |
weight_method = weight_method or 'fastattn' | |
num_levels = max_level - min_level + 1 | |
node_ids = {min_level + i: [i] for i in range(num_levels)} | |
level_last_id = lambda level: node_ids[level][-1] | |
id_cnt = itertools.count(num_levels) | |
p.nodes = [] | |
for i in range(max_level, min_level - 1, -1): | |
# top-down path. | |
offsets = [level_last_id(i), level_last_id(i + 1)] if i != max_level else [level_last_id(i)] | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': offsets, | |
'weight_method': weight_method, | |
}) | |
node_ids[i].append(next(id_cnt)) | |
for i in range(min_level, max_level + 1): | |
# bottom-up path. | |
offsets = [level_last_id(i), level_last_id(i - 1)] if i != min_level else [level_last_id(i)] | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': offsets, | |
'weight_method': weight_method, | |
}) | |
node_ids[i].append(next(id_cnt)) | |
return p | |
def qufpn_config(min_level, max_level, weight_method=None): | |
"""A dynamic quad fpn config that can adapt to different min/max levels. | |
It extends the idea of BiFPN, and has four paths: | |
(up_down -> bottom_up) + (bottom_up -> up_down). | |
Paper: https://ieeexplore.ieee.org/document/9225379 | |
Ref code: From contribution to TF EfficientDet | |
https://github.com/google/automl/blob/eb74c6739382e9444817d2ad97c4582dbe9a9020/efficientdet/keras/fpn_configs.py | |
""" | |
p = OmegaConf.create() | |
weight_method = weight_method or 'fastattn' | |
quad_method = 'fastattn' | |
num_levels = max_level - min_level + 1 | |
node_ids = {min_level + i: [i] for i in range(num_levels)} | |
level_last_id = lambda level: node_ids[level][-1] | |
level_all_ids = lambda level: node_ids[level] | |
level_first_id = lambda level: node_ids[level][0] | |
id_cnt = itertools.count(num_levels) | |
p.nodes = [] | |
for i in range(max_level - 1, min_level - 1, -1): | |
# top-down path 1. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [level_last_id(i), level_last_id(i + 1)], | |
'weight_method': weight_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
node_ids[max_level].append(node_ids[max_level][-1]) | |
for i in range(min_level + 1, max_level): | |
# bottom-up path 2. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': level_all_ids(i) + [level_last_id(i - 1)], | |
'weight_method': weight_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
i = max_level | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [level_first_id(i)] + [level_last_id(i - 1)], | |
'weight_method': weight_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
node_ids[min_level].append(node_ids[min_level][-1]) | |
for i in range(min_level + 1, max_level + 1, 1): | |
# bottom-up path 3. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [ | |
level_first_id(i), level_last_id(i - 1) if i != min_level + 1 else level_first_id(i - 1)], | |
'weight_method': weight_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
node_ids[min_level].append(node_ids[min_level][-1]) | |
for i in range(max_level - 1, min_level, -1): | |
# top-down path 4. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [node_ids[i][0]] + [node_ids[i][-1]] + [level_last_id(i + 1)], | |
'weight_method': weight_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
i = min_level | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [node_ids[i][0]] + [level_last_id(i + 1)], | |
'weight_method': weight_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
node_ids[max_level].append(node_ids[max_level][-1]) | |
# NOTE: the order of the quad path is reversed from the original, my code expects the output of | |
# each FPN repeat to be same as input from backbone, in order of increasing reductions | |
for i in range(min_level, max_level + 1): | |
# quad-add path. | |
p.nodes.append({ | |
'reduction': 1 << i, | |
'inputs_offsets': [node_ids[i][2], node_ids[i][4]], | |
'weight_method': quad_method | |
}) | |
node_ids[i].append(next(id_cnt)) | |
return p | |
def get_fpn_config(fpn_name, min_level=3, max_level=7): | |
if not fpn_name: | |
fpn_name = 'bifpn_fa' | |
name_to_config = { | |
'bifpn_sum': bifpn_config(min_level=min_level, max_level=max_level, weight_method='sum'), | |
'bifpn_attn': bifpn_config(min_level=min_level, max_level=max_level, weight_method='attn'), | |
'bifpn_fa': bifpn_config(min_level=min_level, max_level=max_level, weight_method='fastattn'), | |
'pan_sum': panfpn_config(min_level=min_level, max_level=max_level, weight_method='sum'), | |
'pan_fa': panfpn_config(min_level=min_level, max_level=max_level, weight_method='fastattn'), | |
'qufpn_sum': qufpn_config(min_level=min_level, max_level=max_level, weight_method='sum'), | |
'qufpn_fa': qufpn_config(min_level=min_level, max_level=max_level, weight_method='fastattn'), | |
} | |
return name_to_config[fpn_name] | |