|
|
|
import re
|
|
import shutil
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
|
|
from modelindex.load_model_index import load
|
|
from modelindex.models.Result import Result
|
|
from tabulate import tabulate
|
|
from utils import replace_link
|
|
|
|
MMACT_ROOT = Path(__file__).absolute().parents[2]
|
|
PAPERS_ROOT = Path('model_zoo')
|
|
GITHUB_PREFIX = 'https://github.com/open-mmlab/mmaction2/blob/main/'
|
|
MODELZOO_TEMPLATE = """\
|
|
# 模型库统计
|
|
|
|
在本页面中,我们列举了我们支持的[所有算法](#所有已支持的算法)。你可以点击链接跳转至对应的模型详情页面。
|
|
|
|
另外,我们还列出了我们提供的所有模型权重文件。你可以使用排序和搜索功能找到需要的模型权重,并使用链接跳转至模型详情页面。
|
|
|
|
## 所有已支持的算法
|
|
|
|
* 论文数量:{num_papers}
|
|
{type_msg}
|
|
|
|
* 模型权重文件数量:{num_ckpts}
|
|
{paper_msg}
|
|
|
|
"""
|
|
|
|
METRIC_ALIAS = {
|
|
'Top 1 Accuracy': 'Top-1 (%)',
|
|
'Top 5 Accuracy': 'Top-5 (%)',
|
|
}
|
|
|
|
TASK_MAP = dict(
|
|
detection='时空行为检测模型',
|
|
localization='时序动作定位模型',
|
|
recognition='行为识别模型',
|
|
skeleton='基于骨骼点的行为识别模型',
|
|
retrieval='视频检索模型',
|
|
recognition_audio='基于声音的行为识别模型')
|
|
|
|
model_index = load(str(MMACT_ROOT / 'model-index.yml'))
|
|
|
|
|
|
def build_collections(model_index):
|
|
|
|
col_by_name = {}
|
|
for col in model_index.collections:
|
|
setattr(col, 'models', [])
|
|
col_by_name[col.name] = col
|
|
|
|
for model in model_index.models:
|
|
col = col_by_name[model.in_collection]
|
|
col.models.append(model)
|
|
setattr(model, 'collection', col)
|
|
if model.results is None:
|
|
setattr(model, 'tasks', [])
|
|
else:
|
|
setattr(model, 'tasks', [result.task for result in model.results])
|
|
|
|
|
|
build_collections(model_index)
|
|
|
|
|
|
model2title = dict()
|
|
|
|
|
|
def count_papers(collections):
|
|
total_num_ckpts = 0
|
|
type_count = defaultdict(int)
|
|
paper_msgs = []
|
|
|
|
for collection in collections:
|
|
with open(MMACT_ROOT / collection.readme) as f:
|
|
readme = f.read()
|
|
|
|
ckpts = set(x.lower().strip()
|
|
for x in re.findall(r'\[ckpt.*\]\((https?.*)\)', readme))
|
|
total_num_ckpts += len(ckpts)
|
|
title = collection.paper['Title']
|
|
papertype = collection.data.get('type', 'Algorithm')
|
|
type_count[papertype] += 1
|
|
|
|
readme_title = re.search(r'^#\s+.+', readme)
|
|
|
|
readme = Path(collection.filepath).parents[1].with_suffix('.md').name
|
|
model = Path(collection.filepath).parent.name
|
|
model2title[model] = readme_title.group()[2:].replace(' ', '-')
|
|
paper_msgs.append(f'\t- [{papertype}] [{title}]({PAPERS_ROOT / readme}'
|
|
f'#{model2title[model]}) ({len(ckpts)} ckpts)')
|
|
|
|
type_msg = '\n'.join(
|
|
[f'\t- {type_}: {count}' for type_, count in type_count.items()])
|
|
paper_msg = '\n'.join(paper_msgs)
|
|
|
|
modelzoo = MODELZOO_TEMPLATE.format(
|
|
num_papers=len(collections),
|
|
num_ckpts=total_num_ckpts,
|
|
type_msg=type_msg,
|
|
paper_msg=paper_msg,
|
|
)
|
|
|
|
with open('modelzoo_statistics.md', 'w') as f:
|
|
f.write(modelzoo)
|
|
|
|
|
|
count_papers(model_index.collections)
|
|
|
|
|
|
def generate_paper_page(collection):
|
|
|
|
|
|
with open(MMACT_ROOT / collection.readme) as f:
|
|
content = f.read()
|
|
readme_path = Path(collection.filepath)
|
|
copy = PAPERS_ROOT / readme_path.parents[1].with_suffix('.md').name
|
|
if not copy.exists():
|
|
with open(copy, 'w') as copy_file:
|
|
task = readme_path.parents[1].name
|
|
head_content = f'# {TASK_MAP[task]}\n'
|
|
copy_file.write(head_content)
|
|
|
|
def lower_heading(match):
|
|
return '#' + match.group()
|
|
|
|
content = replace_link(r'\[([^\]]+)\]\(([^)]+)\)', '[{}]({})', content,
|
|
Path(collection.readme))
|
|
content = replace_link(r'\[([^\]]+)\]: (.*)', '[{}]: {}', content,
|
|
Path(collection.readme))
|
|
|
|
content = re.sub(r'^#+\s+.+', lower_heading, content, flags=re.M)
|
|
|
|
with open(copy, 'a') as copy_file:
|
|
copy_file.write(content)
|
|
|
|
|
|
if PAPERS_ROOT.exists():
|
|
shutil.rmtree(PAPERS_ROOT)
|
|
PAPERS_ROOT.mkdir(exist_ok=True)
|
|
for collection in model_index.collections:
|
|
generate_paper_page(collection)
|
|
|
|
|
|
def scatter_results(models):
|
|
model_result_pairs = []
|
|
for model in models:
|
|
if model.results is None:
|
|
result = Result(task=None, dataset=None, metrics={})
|
|
model_result_pairs.append((model, result))
|
|
else:
|
|
for result in model.results:
|
|
model_result_pairs.append((model, result))
|
|
return model_result_pairs
|
|
|
|
|
|
def generate_summary_table(task, model_result_pairs, title=None):
|
|
metrics = set()
|
|
for model, result in model_result_pairs:
|
|
if result.task == task:
|
|
metrics = metrics.union(result.metrics.keys())
|
|
metrics = sorted(list(metrics))
|
|
|
|
rows = []
|
|
|
|
def convert2float(number):
|
|
units = {'M': 1e6, 'G': 1e9, 'T': 1e12}
|
|
if isinstance(number, str):
|
|
num = float(number.rstrip('MGT'))
|
|
number = num * units[number[-1]]
|
|
return number
|
|
|
|
for model, result in model_result_pairs:
|
|
if result.task != task:
|
|
continue
|
|
name = model.name
|
|
if model.metadata.parameters is not None:
|
|
params = convert2float(model.metadata.parameters)
|
|
params = f'{params / 1e6:.2f}'
|
|
else:
|
|
params = None
|
|
if model.metadata.flops is not None:
|
|
flops = convert2float(model.metadata.flops)
|
|
flops = f'{flops / 1e9:.2f}'
|
|
else:
|
|
flops = None
|
|
|
|
readme = Path(
|
|
model.collection.filepath).parents[1].with_suffix('.md').name
|
|
model = Path(model.collection.filepath).parent.name
|
|
page = f'[链接]({PAPERS_ROOT / readme}#{model2title[model]})'
|
|
model_metrics = []
|
|
for metric in metrics:
|
|
model_metrics.append(str(result.metrics.get(metric, '')))
|
|
|
|
rows.append([name, params, flops, *model_metrics, page])
|
|
|
|
with open('modelzoo_statistics.md', 'a') as f:
|
|
if title is not None:
|
|
f.write(f'\n{title}')
|
|
f.write("""\n```{table}\n:class: model-summary\n""")
|
|
header = [
|
|
'模型',
|
|
'参数量 (M)',
|
|
'Flops (G)',
|
|
*[METRIC_ALIAS.get(metric, metric) for metric in metrics],
|
|
'Readme',
|
|
]
|
|
table_cfg = dict(
|
|
tablefmt='pipe',
|
|
floatfmt='.2f',
|
|
numalign='right',
|
|
stralign='center')
|
|
f.write(tabulate(rows, header, **table_cfg))
|
|
f.write('\n```\n')
|
|
|
|
|
|
def generate_dataset_wise_table(task, model_result_pairs, title=None):
|
|
dataset_rows = defaultdict(list)
|
|
for model, result in model_result_pairs:
|
|
if result.task == task:
|
|
dataset_rows[result.dataset].append((model, result))
|
|
|
|
if title is not None:
|
|
with open('modelzoo_statistics.md', 'a') as f:
|
|
f.write(f'\n{title}')
|
|
for dataset, pairs in dataset_rows.items():
|
|
generate_summary_table(task, pairs, title=f'### {dataset}')
|
|
|
|
|
|
model_result_pairs = scatter_results(model_index.models)
|
|
|
|
|
|
generate_dataset_wise_table(
|
|
task='Action Recognition',
|
|
model_result_pairs=model_result_pairs,
|
|
title='## 行为识别',
|
|
)
|
|
|
|
|
|
generate_dataset_wise_table(
|
|
task='Action Detection',
|
|
model_result_pairs=model_result_pairs,
|
|
title='## 时空行为检测',
|
|
)
|
|
|
|
|
|
generate_dataset_wise_table(
|
|
task='Skeleton-based Action Recognition',
|
|
model_result_pairs=model_result_pairs,
|
|
title='## 骨骼点行为识别',
|
|
)
|
|
|
|
|
|
generate_dataset_wise_table(
|
|
task='Video Retrieval',
|
|
model_result_pairs=model_result_pairs,
|
|
title='## 视频检索',
|
|
)
|
|
|
|
|
|
generate_dataset_wise_table(
|
|
task='Temporal Action Localization',
|
|
model_result_pairs=model_result_pairs,
|
|
title='## 时序动作定位',
|
|
)
|
|
|