This is the current list of functions
- Profiling Utilities
@profile_call(fname=None)- a function or method decorator to record execution timesprint_prof_data(fname=None)- prints out profile data for functionfnameor all functions iffnameis Noneclear_prof_data(fname=None)- clears out profile data for functionfnameor all functions iffnameis Noneget_prof_data(fname)- get exec times forfnamestart_record(fname)- start recording time forfname(alternative to decorator if function cannot decorated)end_record(fname)- stop recording and add elapsed time forfnamesave_prof_data(file_name)- save profile data tofile_nameload_prof_data(file_name)- load profile data fromfile_name
- Profiling Callback
MyProfileCallback- a fastaiCallbackthat provides a hierarchical view of model training time executionLearner.to_my_profile- method added to a fastaiLearnerifmy_timesaver_utils.profiling_callbackis imported. Call it to add aMyProfileCallbackto theLearnerinstance.Learner.my_profile-MyProfileCallbackinstance attached to the fastaiLearnerobject ifto_my_profilemethod is called.print_stats-MyProfileCallbackmethod to show a hierarchical view of the training lifecycle execution stats (execution counts, avg time, max time)get_stats-MyProfileCallbackmethod to get the execution stats as a listclear_stats-MyProfileCallbackmethod to reset the execution statsreset-MyProfileCallbackattribute to set so each call toLearner.fitresets the execution counts before starting the training.
- Enhanced Image Classifier Cleaner
class EnhancedImageClassifierCleaner- a drop-in replacement for thefastai.vision.widgets.ImageClassifierCleanerwhich addsApplyandResetbuttons to allow the user to apply or revert changes to actual dataset.(In the fastai, the application of the changes is done in a separate step, making it more cumbersome to apply across a lot of categories -- doubling it since it segregates the validation and training datasets as well)
pip install git+https://github.com/butchland/my_timesaver_utils.git
Decorate method or function you want to profile
import time
@profile_call
def test_func(t=2.0):
time.sleep(1)
Call your method or function
for i in range(10):
test_func(i)
You can also add an optional funcname if you want to replace the name its stored in the profile data
@profile_call('wachacha')
def test_func2():
time.sleep(1.0)
for i in range(3):
test_func2()
Print your profile data
print_prof_data('test_func')
Print your profile data for the test_func2 (aka wachacha)
print_prof_data('wachacha')
Get your profile data (e.g. good for graphing)
times = get_prof_data('test_func'); times
If you can't add a decorator, you can start and end the recording manually and it will be added to the profile data
for i in range(10):
start_record('my_sleep')
time.sleep(1.)
end_record('my_sleep')
As an alternative, the decorator can be invoked this way
sleep = profile_call(time.sleep)
for i in range(5):
sleep(i)
You can also pass in an optional function name to replace the func name used in the profile data
sleep2 = profile_call(time.sleep, 'maluman')
for i in range(3): sleep2(1)
If you call print_prof_data without any arguments, it will print all the timings for all the functions
print_prof_data()
You can also get the profile data for the manually recorded calls as well.
times2 = get_prof_data('sleep');times2
You can also save the profile data to a file
save_file = 'my_profile_data.pickle'
save_prof_data(save_file)
Calling clear_prof_data for a function will clear out the data for that function
clear_prof_data('sleep')
print_prof_data()
Calling the clear_prof_data with no arguments will clear out all the previously recorded timings.
clear_prof_data()
print_prof_data()
You can reload the profile data from a previously saved file
load_prof_data(save_file)
print_prof_data()
from fastai.vision.all import *
Import the whole my_timesaver_utils package
from my_timesaver_utils.all import *
or as an alternative, just import the profiling_callback package
from my_timesaver_utils.profiling_callback import *
Setup your path, data, datablock, dataloaders and learner as usual.
path = untar_data(URLs.MNIST_TINY)
Path.BASE_PATH = path
datablock = DataBlock(
blocks=(ImageBlock,CategoryBlock),
get_items=get_image_files,
get_y=parent_label,
splitter=GrandparentSplitter(),
item_tfms=Resize(28),
batch_tfms=[]
)
dls = datablock.dataloaders(path)
learner = cnn_learner(dls,resnet18,metrics=accuracy)
Importing the profiling callback adds a method to_my_profile to the learner object
learner.to_my_profile()
Calling the to_my_profile method on the learner object adds a callback called MyProfileCallback which can be accessed through the learner attribute my_profile.
learner.summary()
learner.my_profile
Call the print_stats method on the my_profile attribute of the Learner object displays a hierarchical list of the training lifecycle events -- in this case with no data yet as the fit method has not been called.
learner.my_profile.print_stats()
learner.fit(1)
The print_stats method now prints the execution counts, max time and avg time (in secs) for each part of the training lifecycle.
learner.my_profile.print_stats()
The stats can also be collected as a list of tuples where each tuple consists of the lifecycle event name, the level, and the elapsed times.
fit_stats = learner.my_profile.get_stats();fit_stats
The print_stats can also print just the stats for one lifecycle event
learner.my_profile.print_stats('train_batch')
The get_stats can also just collect the stats for one lifecycle event
train_batch_stats = learner.my_profile.get_stats('train_batch'); train_batch_stats
Call the clear_stats to clear the stats. You can also pass a lifecycle event to clear a single event
learner.my_profile.clear_stats()
learner.my_profile.print_stats()
learner.my_profile.print_stats('train')
learner.fine_tune(1)
learner.my_profile.print_stats()
My Profile Reset Attribute
Setting the reset attribute to true on the my_profile attribute will cause the stats to be reset each time the fit method of the Learner is called. So only the accumulated stats for the last call to fit (e.g fine_tune calls fit twice, but setting the reset attribute to true will show only the stats for the second fit call.
The default value of reset is False.
learner.my_profile.reset = True
learner.fine_tune(1)
learner.my_profile.print_stats()
learner.my_profile.reset
Enhanced Image Classifier Cleaner
Import the utils -- You can either import all the utilities
from my_timesaver_utils.all import *
Or you can import only the enhanced image classifier cleaner package
from my_timesaver_utils.enhanced_imageclassifiercleaner import *
Prerequisites
In order to use the EnhancedImageClassifierCleaner (just like the fastai ImageClassifierCleaner), we will need to have an existing fastai Learner object.
Usage
Just create an instance of EnhancedImageClassifierCleaner object.
cleaner = EnhancedImageClassifierCleaner(learner)
cleaner # on a separate line, this will display the widget
If you updated your dataset, you can refresh your dataloader by running
newdls = datablock.dataloaders(path)
learner.dls = newdls # assume learner was previously created or loaded
cleaner = EnhancedImageClassifierCleaner(learner)
This will then display the updated images from your dataset.
cleaner = EnhancedImageClassifierCleaner(learner)
cleaner

Custom Valid/Train folder structures and labeled categories
The default folder structure for the Enhanced Image Classifier Cleaner
assumes that you are using RandomSplitter or GrandparentSplitter as
your splitter and parent_label as the get_y parameter in your datablock.
If you have a custom valid/train splitter and your image files are not segregated by categories within them, you will need to create a custom file mover in order to update your image labels (aka categories).
The default implementation of the file_mover is shown below:
export
import shutil
def parent_move_file(fn, newcat):
new_path = fn.parent.parent/newcat
if new_path.is_dir():
shutil.move(str(fn), new_path)
new_file = new_path/fn.name
return new_file
return fn
The default implementation will work with either a RandomSplitter or GrandparentSplitter
and if parent_label is used to get the labels.
Otherwise, you will need to modify the file_mover argument with a custom one and pass it as follows
cleaner = EnhancedImageClassifierCleaner(learner, file_mover=custom_file_mover)