Where are transforms stored in exported learner?

I exported my learner using learn.export and imported using load_learner but I’m not sure how to access the transforms that are stored as part of the state so that I can remove them. I am doing a series of predictions over a bunch of windows and I’m handling the transforms myself manually, but I need to turn off the learners transforms to get it to work with the object im passing to learn.predict. Thank you.

I’m not 100% sure on this, as this only works if our learner has a train_ds or valid or test_ds, but if it does (I’m not in front of a computer atm I will be in a little while) try in here

(Probably test_ds if you feed in a label list with it? I’ll verify momentarily)
procs = learn.data.train_ds.x.processor

1 Like

Thanks, but I wasn’t able to find it there. I have no test set. Nothing in train_ds/valid ds. train_dl and valid_dl both have empty tfms. I’m trying to pass a tensor of a spectrogram directly to learn.predict which has worked before when I didn’t use transforms on the original learner. I have found another way but would still like to know where the learner state is kept.

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-102-ef3a1e912f78> in <module>
----> 1 string = predict_window(learn, ex, 20)

<ipython-input-99-626c26f9502b> in predict_window(learn, item, duration)
     23         display(Audio(ai.sig[start*hop:(start+window_width)*hop], rate=16000))
     24         ai_new = AudioItem(spectro=temp_sg)
---> 25         prediction = learn.predict(temp_sg)
     26     return string

/opt/anaconda3/lib/python3.7/site-packages/fastai/basic_train.py in predict(self, item, return_x, batch_first, with_dropout, **kwargs)
    363     def predict(self, item:ItemBase, return_x:bool=False, batch_first:bool=True, with_dropout:bool=False, **kwargs):
    364         "Return predicted class, label and probabilities for `item`."
--> 365         batch = self.data.one_item(item)
    366         res = self.pred_batch(batch=batch, with_dropout=with_dropout)
    367         raw_pred,x = grab_idx(res,0,batch_first=batch_first),batch[0]

/opt/anaconda3/lib/python3.7/site-packages/fastai/basic_data.py in one_item(self, item, detach, denorm, cpu)
    179         ds = self.single_ds
    180         with ds.set_item(item):
--> 181             return self.one_batch(ds_type=DatasetType.Single, detach=detach, denorm=denorm, cpu=cpu)
    182 
    183     def show_batch(self, rows:int=5, ds_type:DatasetType=DatasetType.Train, reverse:bool=False, **kwargs)->None:

/opt/anaconda3/lib/python3.7/site-packages/fastai/basic_data.py in one_batch(self, ds_type, detach, denorm, cpu)
    166         w = self.num_workers
    167         self.num_workers = 0
--> 168         try:     x,y = next(iter(dl))
    169         finally: self.num_workers = w
    170         if detach: x,y = to_detach(x,cpu=cpu),to_detach(y,cpu=cpu)

/opt/anaconda3/lib/python3.7/site-packages/fastai/basic_data.py in __iter__(self)
     73     def __iter__(self):
     74         "Process and returns items from `DataLoader`."
---> 75         for b in self.dl: yield self.proc_batch(b)
     76 
     77     @classmethod

/opt/anaconda3/lib/python3.7/site-packages/torch/utils/data/dataloader.py in __next__(self)
    558         if self.num_workers == 0:  # same-process loading
    559             indices = next(self.sample_iter)  # may raise StopIteration
--> 560             batch = self.collate_fn([self.dataset[i] for i in indices])
    561             if self.pin_memory:
    562                 batch = _utils.pin_memory.pin_memory_batch(batch)

/opt/anaconda3/lib/python3.7/site-packages/torch/utils/data/dataloader.py in <listcomp>(.0)
    558         if self.num_workers == 0:  # same-process loading
    559             indices = next(self.sample_iter)  # may raise StopIteration
--> 560             batch = self.collate_fn([self.dataset[i] for i in indices])
    561             if self.pin_memory:
    562                 batch = _utils.pin_memory.pin_memory_batch(batch)

/opt/anaconda3/lib/python3.7/site-packages/fastai/data_block.py in __getitem__(self, idxs)
    648             else:                 x,y = self.item   ,0
    649             if self.tfms or self.tfmargs:
--> 650                 x = x.apply_tfms(self.tfms, **self.tfmargs)
    651             if hasattr(self, 'tfms_y') and self.tfm_y and self.item is None:
    652                 y = y.apply_tfms(self.tfms_y, **{**self.tfmargs_y, 'do_resolve':False})

AttributeError: 'Tensor' object has no attribute 'apply_tfms'

Try in learn.data.train_ds.tfms. Think that’s what you want :slight_smile:

When I compare a model exported with learn.export() on pets using get_transforms(), here is the list of validation transforms:

eg: learn.data.valid_ds.tfms

[RandTransform(tfm=TfmCrop (crop_pad), kwargs={}, p=1.0, resolved={'padding_mode': 'reflection', 'row_pct': 0.5, 'col_pct': 0.5}, do_run=True, is_random=True, use_on_y=True)]

Here is the list after exporting:

[RandTransform(tfm=TfmCrop (crop_pad), kwargs={}, p=1.0, resolved={'padding_mode': 'reflection', 'row_pct': 0.5, 'col_pct': 0.5}, do_run=True, is_random=True, use_on_y=True)]
2 Likes

That did it, thanks a lot!

Does anyone else know if there’s like a model state dict where I can see all of the stuff that was exported? I know I’m going to need it later when I go back to test some stuff we’re working on for an audio module.

1 Like

No problem! :slight_smile:

Just to be sure I’m understanding correctly, what sort of items are you looking for in this exported state dict? (In case we can’t find it and have to write one :wink: )

Hi @muellerzr, I use default tfms = get_transforms()
but when I check which transforms have been executed learn.data.train_ds.tfms, I got:

[RandTransform(tfm=TfmCrop (crop_pad), kwargs={'row_pct': (0, 1), 'col_pct': (0, 1), 'padding_mode': 'reflection'}, p=1.0, resolved={'row_pct': 0.584006265996043, 'col_pct': 0.37821348485348993, 'padding_mode': 'reflection'}, do_run=True, is_random=True, use_on_y=True),
 RandTransform(tfm=TfmPixel (flip_lr), kwargs={}, p=0.5, resolved={}, do_run=True, is_random=True, use_on_y=True),
 RandTransform(tfm=TfmCoord (symmetric_warp), kwargs={'magnitude': (-0.2, 0.2)}, p=0.75, resolved={'magnitude': tensor([ 0.0010, -0.1883, -0.0140,  0.1138]), 'invert': False}, do_run=True, is_random=True, use_on_y=True),
 RandTransform(tfm=TfmAffine (rotate), kwargs={'degrees': (-10.0, 10.0)}, p=0.75, resolved={'degrees': 8.016843745492636}, do_run=True, is_random=True, use_on_y=True),
 RandTransform(tfm=TfmAffine (zoom), kwargs={'scale': (1.0, 1.1), 'row_pct': (0, 1), 'col_pct': (0, 1)}, p=0.75, resolved={'scale': 1.062585317969829, 'row_pct': 0.680038148905311, 'col_pct': 0.13455748606642703}, do_run=True, is_random=True, use_on_y=True),
 RandTransform(tfm=TfmLighting (brightness), kwargs={'change': (0.4, 0.6)}, p=0.75, resolved={'change': 0.5177815273717555}, do_run=True, is_random=True, use_on_y=True),
 RandTransform(tfm=TfmLighting (contrast), kwargs={'scale': (0.8, 1.25)}, p=0.75, resolved={'scale': 0.84033491010206}, do_run=False, is_random=True, use_on_y=True)]

It have (tfm=TfmCrop (crop_pad), but as I know, we dont have crop_pad by default, could you explain it to me