Source code for htr2hpc.train.calculate
import datetime
import math
import re
[docs]
def slurm_get_max_acc(slurm_output, training_mode):
"""Return a tuple of (epoch #, accuracy) for the epoch with highest accuracy"""
if training_mode == "Segment":
re_acc = r"stage ([\d]+).+\n[^i]*val_mean_iu:\s+\n\s+([\d.]+)"
else:
re_acc = r"stage ([\d]+).+\n.+(\d.\d\d\d)\s*\d/10"
accuracies = re.findall(re_acc, slurm_output)
accuracies = [(int(i[0]), float(i[1])) for i in accuracies]
if accuracies:
return max(accuracies, key=lambda x: x[1])
[docs]
def slurm_count_epoch(slurm_output):
"""Return count of epochs"""
epoch_times = re.findall(r"(\d:\d\d:\d\d) •", slurm_output)
if epoch_times:
return len(epoch_times)
[docs]
def slurm_get_avg_epoch(slurm_output):
"""Return average epoch duration, in seconds"""
epoch_times = re.findall(r"(\d:\d\d:\d\d) •", slurm_output)
if epoch_times:
epoch_times = [datetime.datetime.strptime(t, "%H:%M:%S") for t in epoch_times]
epoch_times = [
datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second).seconds
for t in epoch_times
]
epoch_avg = math.ceil(sum(epoch_times) / len(epoch_times))
if epoch_avg == 0:
epoch_avg = 1
return epoch_avg
[docs]
def stats_get_max_cpu(job_stats):
"""Return max CPU usage"""
mem_usage = re.findall(r"\(([\d.]+)([\w]+)\/[\d.]+[\w]+ per core", job_stats)
if mem_usage:
gb_used = (
float(mem_usage[0][0]) / 1000
if mem_usage[0][1] == "MB"
else float(mem_usage[0][0])
)
return gb_used
[docs]
def calc_full_duration(slurm_output, job_stats):
"""Given a preliminary slurm job output, return duration estimate:
Setup time, plus N times the average epoch plus 10% for wiggle room.
Assumes the train task will take 50 epochs. N = 50 - count of completed
epochs from prelim train task.
"""
job_duration = re.findall(r"Run Time: (\d+:\d\d:\d\d)", job_stats)
epoch_avg = slurm_get_avg_epoch(slurm_output)
epoch_count = slurm_count_epoch(slurm_output)
if job_duration:
t = datetime.datetime.strptime(job_duration[0], "%H:%M:%S")
job_duration = datetime.timedelta(
hours=t.hour, minutes=t.minute, seconds=t.second
).seconds
if epoch_avg:
setup_time = job_duration - (epoch_avg * epoch_count)
epoch_request = 50 - epoch_count
# if prelim train task already came close to 50 epochs or overshot it,
# run second train task
# so that --lag 10 is immediately active (epoch_request -> --min-epochs 5)
# and so that the
# estimated job time request allows room for 15 more epochs.
epoch_time_est = 15 if epoch_request < 11 else epoch_request
epoch_request = 5 if epoch_request < 11 else epoch_request
return epoch_request, datetime.timedelta(
minutes=math.ceil(
(setup_time + (epoch_avg * epoch_time_est * 1.1)) / 60
)
)
elif datetime.timedelta(seconds=job_duration) > datetime.timedelta(minutes=14):
# if epoch_avg returns as None (no epochs completed during the first train task),
# but job did not error out early, assume that more time is needed per epoch.
# assume 15min per epoch and 15min setup time.
# this means max train time should be ~14 hrs.
# note the calc_full_duration function should run only when the first train job
# does not crash.
epoch_request = 50
return epoch_request, datetime.timedelta(minutes=(15 * 51 * 1.1))
else:
return None, None
else:
return None, None
[docs]
def calc_cpu_mem(job_stats):
"""Given a preliminary job_stats output, return recommended mem per cpu."""
gb_used = stats_get_max_cpu(job_stats)
if gb_used:
return f"{math.ceil(gb_used + 0.3)}G"
[docs]
def estimate_duration(training_data_size, training_mode):
"""Use files in input data dir to come up with estimate of prelim train duration."""
if training_mode == "Segment":
job_minutes = 5 if training_data_size < 20000000 else 15
else:
job_minutes = 5 if training_data_size < 50000000 else 15
return datetime.timedelta(minutes=job_minutes)
[docs]
def estimate_cpu_mem(training_data_size, training_mode):
"""Use files in input data dir to come up with estimate of prelim mem per cpu."""
if training_mode == "Segment":
if training_data_size < 10000000:
mem_per_cpu = 1
elif training_data_size < 20000000:
mem_per_cpu = 2
elif training_data_size < 40000000:
mem_per_cpu = 3
elif training_data_size < 120000000:
mem_per_cpu = 4
elif training_data_size < 200000000:
mem_per_cpu = 5
else:
mem_per_cpu = 6 + (training_data_size - 200000000) // 100000000
else:
mem_per_cpu = 1 if training_data_size < 50000000 else 2
return f"{mem_per_cpu}G"