Skip to content

Commit 52b4fa5

Browse files
author
谢昕辰
authored
[Enhancement] md2yml pre-commit hook (open-mmlab#732)
* init script * update scripts and generate new yml * fix lint: deeplabv3plus.yml * modify resolution representation * remove field * format crop_size
1 parent b559988 commit 52b4fa5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+8074
-7498
lines changed

.dev/md2yml.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!/usr/bin/env python
2+
3+
# This tool is used to update model-index.yml which is required by MIM, and
4+
# will be automatically called as a pre-commit hook. The updating will be
5+
# triggered if any change of model information (.md files in configs/) has been
6+
# detected before a commit.
7+
8+
import glob
9+
import os
10+
import os.path as osp
11+
import sys
12+
13+
import mmcv
14+
15+
MMSEG_ROOT = osp.dirname(osp.dirname((osp.dirname(__file__))))
16+
17+
18+
def dump_yaml_and_check_difference(obj, filename):
19+
"""Dump object to a yaml file, and check if the file content is different
20+
from the original.
21+
22+
Args:
23+
obj (any): The python object to be dumped.
24+
filename (str): YAML filename to dump the object to.
25+
Returns:
26+
Bool: If the target YAML file is different from the original.
27+
"""
28+
original = None
29+
if osp.isfile(filename):
30+
with open(filename, 'r', encoding='utf-8') as f:
31+
original = f.read()
32+
with open(filename, 'w', encoding='utf-8') as f:
33+
mmcv.dump(obj, f, file_format='yaml', sort_keys=False)
34+
is_different = True
35+
if original is not None:
36+
with open(filename, 'r') as f:
37+
new = f.read()
38+
is_different = (original != new)
39+
return is_different
40+
41+
42+
def parse_md(md_file):
43+
"""Parse .md file and convert it to a .yml file which can be used for MIM.
44+
45+
Args:
46+
md_file (str): Path to .md file.
47+
Returns:
48+
Bool: If the target YAML file is different from the original.
49+
"""
50+
collection_name = osp.dirname(md_file).split('/')[-1]
51+
configs = os.listdir(osp.dirname(md_file))
52+
53+
collection = dict(Name=collection_name, Metadata={'Training Data': []})
54+
models = []
55+
datasets = []
56+
57+
with open(md_file, 'r') as md:
58+
lines = md.readlines()
59+
i = 0
60+
current_dataset = ''
61+
while i < len(lines):
62+
line = lines[i].strip()
63+
if len(line) == 0:
64+
i += 1
65+
continue
66+
if line[:3] == '###':
67+
datasets.append(line[4:])
68+
current_dataset = line[4:]
69+
i += 2
70+
elif line[0] == '|' and (
71+
i + 1) < len(lines) and lines[i + 1][:3] == '| -':
72+
cols = [col.strip() for col in line.split('|')]
73+
backbone_id = cols.index('Backbone')
74+
crop_size_id = cols.index('Crop Size')
75+
lr_schd_id = cols.index('Lr schd')
76+
mem_id = cols.index('Mem (GB)')
77+
fps_id = cols.index('Inf time (fps)')
78+
try:
79+
ss_id = cols.index('mIoU')
80+
except ValueError:
81+
ss_id = cols.index('Dice')
82+
try:
83+
ms_id = cols.index('mIoU(ms+flip)')
84+
except ValueError:
85+
ms_id = False
86+
config_id = cols.index('config')
87+
download_id = cols.index('download')
88+
j = i + 2
89+
while j < len(lines) and lines[j][0] == '|':
90+
els = [el.strip() for el in lines[j].split('|')]
91+
config = ''
92+
model_name = ''
93+
weight = ''
94+
for fn in configs:
95+
if fn in els[config_id]:
96+
left = els[download_id].index(
97+
'https://download.openmmlab.com')
98+
right = els[download_id].index('.pth') + 4
99+
weight = els[download_id][left:right]
100+
config = f'configs/{collection_name}/{fn}'
101+
model_name = fn[:-3]
102+
fps = els[fps_id] if els[fps_id] != '-' and els[
103+
fps_id] != '' else -1
104+
mem = els[mem_id] if els[mem_id] != '-' and els[
105+
mem_id] != '' else -1
106+
crop_size = els[crop_size_id].split('x')
107+
assert len(crop_size) == 2
108+
model = {
109+
'Name': model_name,
110+
'In Collection': collection_name,
111+
'Metadata': {
112+
'backbone': els[backbone_id],
113+
'crop size': f'({crop_size[0]},{crop_size[1]})',
114+
'lr schd': int(els[lr_schd_id]),
115+
},
116+
'Results': {
117+
'Task': 'Semantic Segmentation',
118+
'Dataset': current_dataset,
119+
'Metrics': {
120+
'mIoU': float(els[ss_id]),
121+
},
122+
},
123+
'Config': config,
124+
'Weights': weight,
125+
}
126+
if fps != -1:
127+
try:
128+
fps = float(fps)
129+
except Exception:
130+
j += 1
131+
continue
132+
model['Metadata']['inference time (ms/im)'] = [{
133+
'value':
134+
round(1000 / float(fps), 2),
135+
'hardware':
136+
'V100',
137+
'backend':
138+
'PyTorch',
139+
'batch size':
140+
1,
141+
'mode':
142+
'FP32',
143+
'resolution':
144+
f'({crop_size[0]},{crop_size[1]})'
145+
}]
146+
if mem != -1:
147+
model['Metadata']['memory (GB)'] = float(mem)
148+
if ms_id and els[ms_id] != '-' and els[ms_id] != '':
149+
model['Results']['Metrics']['mIoU(ms+flip)'] = float(
150+
els[ms_id])
151+
models.append(model)
152+
j += 1
153+
i = j
154+
else:
155+
i += 1
156+
collection['Metadata']['Training Data'] = datasets
157+
result = {'Collections': [collection], 'Models': models}
158+
yml_file = f'{md_file[:-9]}{collection_name}.yml'
159+
return dump_yaml_and_check_difference(result, yml_file)
160+
161+
162+
def update_model_index():
163+
"""Update model-index.yml according to model .md files.
164+
165+
Returns:
166+
Bool: If the updated model-index.yml is different from the original.
167+
"""
168+
configs_dir = osp.join(MMSEG_ROOT, 'configs')
169+
yml_files = glob.glob(osp.join(configs_dir, '**', '*.yml'), recursive=True)
170+
yml_files.sort()
171+
172+
model_index = {
173+
'Import':
174+
[osp.relpath(yml_file, MMSEG_ROOT) for yml_file in yml_files]
175+
}
176+
model_index_file = osp.join(MMSEG_ROOT, 'model-index.yml')
177+
is_different = dump_yaml_and_check_difference(model_index,
178+
model_index_file)
179+
180+
return is_different
181+
182+
183+
if __name__ == '__main__':
184+
file_list = [fn for fn in sys.argv[1:] if osp.basename(fn) == 'README.md']
185+
if not file_list:
186+
exit(0)
187+
file_modified = False
188+
for fn in file_list:
189+
file_modified |= parse_md(fn)
190+
191+
file_modified |= update_model_index()
192+
193+
exit(1 if file_modified else 0)

.pre-commit-config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,12 @@ repos:
3838
hooks:
3939
- id: docformatter
4040
args: ["--in-place", "--wrap-descriptions", "79"]
41+
- repo: local
42+
hooks:
43+
- id: update-model-index
44+
name: update-model-index
45+
description: Collect model information and update model-index.yml
46+
entry: .dev/md2yml.py
47+
additional_dependencies: [mmcv]
48+
language: python
49+
files: ^configs/.*\.md$

0 commit comments

Comments
 (0)