Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: implement quantile loss function #3285

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions fastai/__init__.py
@@ -1,2 +1,2 @@
__version__ = "2.2.8"

__version__ = "2.2.8"
1,789 changes: 895 additions & 894 deletions fastai/_nbdev.py

Large diffs are not rendered by default.

68 changes: 34 additions & 34 deletions fastai/_pytorch_doc.py
@@ -1,35 +1,35 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/99_pytorch_doc.ipynb (unless otherwise specified).

__all__ = ['PYTORCH_URL', 'pytorch_doc_link']

# Cell
PYTORCH_URL = 'https://pytorch.org/docs/stable/'

# Cell
def _mod2page(mod):
if mod == Tensor: return 'tensors.html'
name = mod.__name__
name = name.replace('torch.', '').replace('utils.', '')
if name.startswith('nn.modules'): return 'nn.html'
return f'{name}.html'

# Cell
import importlib

# Cell
def pytorch_doc_link(name):
if name.startswith('F'): name = 'torch.nn.functional' + name[1:]
if not name.startswith('torch.'): name = 'torch.' + name
if name == 'torch.Tensor': return f'{PYTORCH_URL}tensors.html'
try:
mod = importlib.import_module(name)
return f'{PYTORCH_URL}{_mod2page(mod)}'
except: pass
splits = name.split('.')
mod_name,fname = '.'.join(splits[:-1]),splits[-1]
if mod_name == 'torch.Tensor': return f'{PYTORCH_URL}tensors.html#{name}'
try:
mod = importlib.import_module(mod_name)
page = _mod2page(mod)
return f'{PYTORCH_URL}{page}#{name}'
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/99_pytorch_doc.ipynb (unless otherwise specified).
__all__ = ['PYTORCH_URL', 'pytorch_doc_link']
# Cell
PYTORCH_URL = 'https://pytorch.org/docs/stable/'
# Cell
def _mod2page(mod):
if mod == Tensor: return 'tensors.html'
name = mod.__name__
name = name.replace('torch.', '').replace('utils.', '')
if name.startswith('nn.modules'): return 'nn.html'
return f'{name}.html'
# Cell
import importlib
# Cell
def pytorch_doc_link(name):
if name.startswith('F'): name = 'torch.nn.functional' + name[1:]
if not name.startswith('torch.'): name = 'torch.' + name
if name == 'torch.Tensor': return f'{PYTORCH_URL}tensors.html'
try:
mod = importlib.import_module(name)
return f'{PYTORCH_URL}{_mod2page(mod)}'
except: pass
splits = name.split('.')
mod_name,fname = '.'.join(splits[:-1]),splits[-1]
if mod_name == 'torch.Tensor': return f'{PYTORCH_URL}tensors.html#{name}'
try:
mod = importlib.import_module(mod_name)
page = _mod2page(mod)
return f'{PYTORCH_URL}{page}#{name}'
except: return None
92 changes: 46 additions & 46 deletions fastai/callback/azureml.py
@@ -1,47 +1,47 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/74_callback.azureml.ipynb (unless otherwise specified).

__all__ = ['AzureMLCallback']

# Cell
import tempfile
from ..basics import *
from ..learner import Callback

# Cell
from azureml.core.run import Run

# Cell
class AzureMLCallback(Callback):
"Log losses, metrics, model architecture summary to AzureML"
order = Recorder.order+1

def before_fit(self):
self.run = Run.get_context()

self.run.log("n_epoch", self.learn.n_epoch)
self.run.log("model_class", str(type(self.learn.model)))

try:
summary_file = Path("outputs") / 'model_summary.txt'
with summary_file.open("w") as f:
f.write(repr(self.learn.model))
except:
print('Did not log model summary. Check if your model is PyTorch model.')

def after_batch(self):
# log loss and opt.hypers
if self.learn.training:
# self.run.log('batch__smooth_loss', self.learn.smooth_loss)
self.run.log('batch__loss', self.learn.loss)
self.run.log('batch__train_iter', self.learn.train_iter)
for i, h in enumerate(self.learn.opt.hypers):
for k, v in h.items():
self.run.log(f'batch__opt.hypers.{k}', v)

def after_epoch(self):
# log metrics
for n, v in zip(self.learn.recorder.metric_names, self.learn.recorder.log):
if n not in ['epoch', 'time']:
self.run.log(f'epoch__{n}', v)
if n == 'time':
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/74_callback.azureml.ipynb (unless otherwise specified).
__all__ = ['AzureMLCallback']
# Cell
import tempfile
from ..basics import *
from ..learner import Callback
# Cell
from azureml.core.run import Run
# Cell
class AzureMLCallback(Callback):
"Log losses, metrics, model architecture summary to AzureML"
order = Recorder.order+1
def before_fit(self):
self.run = Run.get_context()
self.run.log("n_epoch", self.learn.n_epoch)
self.run.log("model_class", str(type(self.learn.model)))
try:
summary_file = Path("outputs") / 'model_summary.txt'
with summary_file.open("w") as f:
f.write(repr(self.learn.model))
except:
print('Did not log model summary. Check if your model is PyTorch model.')
def after_batch(self):
# log loss and opt.hypers
if self.learn.training:
# self.run.log('batch__smooth_loss', self.learn.smooth_loss)
self.run.log('batch__loss', self.learn.loss)
self.run.log('batch__train_iter', self.learn.train_iter)
for i, h in enumerate(self.learn.opt.hypers):
for k, v in h.items():
self.run.log(f'batch__opt.hypers.{k}', v)
def after_epoch(self):
# log metrics
for n, v in zip(self.learn.recorder.metric_names, self.learn.recorder.log):
if n not in ['epoch', 'time']:
self.run.log(f'epoch__{n}', v)
if n == 'time':
self.run.log(f'epoch__{n}', str(v))
220 changes: 110 additions & 110 deletions fastai/callback/captum.py
@@ -1,111 +1,111 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/73_callback.captum.ipynb (unless otherwise specified).

__all__ = ['json_clean', 'CaptumInterpretation']

# Cell
import tempfile
from ..basics import *

# Cell
from ipykernel import jsonutil

# Cell
# Dirty hack as json_clean doesn't support CategoryMap type
_json_clean=jsonutil.json_clean
def json_clean(o):
o = list(o.items) if isinstance(o,CategoryMap) else o
return _json_clean(o)

jsonutil.json_clean = json_clean

# Cell
from captum.attr import IntegratedGradients,NoiseTunnel,GradientShap,Occlusion
from captum.attr import visualization as viz

from matplotlib.colors import LinearSegmentedColormap

from captum.insights import AttributionVisualizer, Batch
from captum.insights.attr_vis.features import ImageFeature

# Cell
class CaptumInterpretation():
"Captum Interpretation for Resnet"
def __init__(self,learn,cmap_name='custom blue',colors=None,N=256,methods=('original_image','heat_map'),
signs=("all", "positive"),outlier_perc=1):
if colors is None: colors = [(0, '#ffffff'),(0.25, '#000000'),(1, '#000000')]
store_attr()
self.dls,self.model = learn.dls,self.learn.model
self.supported_metrics=['IG','NT','Occl']

def get_baseline_img(self, img_tensor,baseline_type):
baseline_img=None
if baseline_type=='zeros': baseline_img= img_tensor*0
if baseline_type=='uniform': baseline_img= torch.rand(img_tensor.shape)
if baseline_type=='gauss':
baseline_img= (torch.rand(img_tensor.shape).to(self.dls.device)+img_tensor)/2
return baseline_img.to(self.dls.device)

def visualize(self,inp,metric='IG',n_steps=1000,baseline_type='zeros',nt_type='smoothgrad', strides=(3,4,4), sliding_window_shapes=(3,15,15)):
if metric not in self.supported_metrics:
raise Exception(f"Metric {metric} is not supported. Currently {self.supported_metrics} are only supported")
tls = L([TfmdLists(inp, t) for t in L(ifnone(self.dls.tfms,[None]))])
inp_data=list(zip(*(tls[0],tls[1])))[0]
enc_data,dec_data=self._get_enc_dec_data(inp_data)
attributions=self._get_attributions(enc_data,metric,n_steps,nt_type,baseline_type,strides,sliding_window_shapes)
self._viz(attributions,dec_data,metric)

def _viz(self,attributions,dec_data,metric):
default_cmap = LinearSegmentedColormap.from_list(self.cmap_name,self.colors, N=self.N)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(dec_data[0].numpy(), (1,2,0)),
methods=self.methods,
cmap=default_cmap,
show_colorbar=True,
signs=self.signs,
outlier_perc=self.outlier_perc, titles=[f'Original Image - ({dec_data[1]})', metric])



def _get_enc_dec_data(self,inp_data):
dec_data=self.dls.after_item(inp_data)
enc_data=self.dls.after_batch(to_device(self.dls.before_batch(dec_data),self.dls.device))
return(enc_data,dec_data)

def _get_attributions(self,enc_data,metric,n_steps,nt_type,baseline_type,strides,sliding_window_shapes):
# Get Baseline
baseline=self.get_baseline_img(enc_data[0],baseline_type)
supported_metrics ={}
if metric == 'IG':
self._int_grads = self._int_grads if hasattr(self,'_int_grads') else IntegratedGradients(self.model)
return self._int_grads.attribute(enc_data[0],baseline, target=enc_data[1], n_steps=200)
elif metric == 'NT':
self._int_grads = self._int_grads if hasattr(self,'_int_grads') else IntegratedGradients(self.model)
self._noise_tunnel= self._noise_tunnel if hasattr(self,'_noise_tunnel') else NoiseTunnel(self._int_grads)
return self._noise_tunnel.attribute(enc_data[0].to(self.dls.device), n_samples=1, nt_type=nt_type, target=enc_data[1])
elif metric == 'Occl':
self._occlusion = self._occlusion if hasattr(self,'_occlusion') else Occlusion(self.model)
return self._occlusion.attribute(enc_data[0].to(self.dls.device),
strides = strides,
target=enc_data[1],
sliding_window_shapes=sliding_window_shapes,
baselines=baseline)

# Cell
@patch
def insights(x: CaptumInterpretation,inp_data,debug=True):
_baseline_func= lambda o: o*0
_get_vocab = lambda vocab: list(map(str,vocab)) if isinstance(vocab[0],bool) else vocab
dl = x.dls.test_dl(L(inp_data),with_labels=True, bs=4)
normalize_func= next((func for func in dl.after_batch if type(func)==Normalize),noop)

# captum v0.3 expects tensors without the batch dimension.
if nested_attr(normalize_func, 'mean.ndim', 4)==4: normalize_func.mean.squeeze_(0)
if nested_attr(normalize_func, 'std.ndim', 4)==4: normalize_func.std.squeeze_(0)

visualizer = AttributionVisualizer(
models=[x.model],
score_func=lambda o: torch.nn.functional.softmax(o, 1),
classes=_get_vocab(dl.vocab),
features=[ImageFeature("Image", baseline_transforms=[_baseline_func], input_transforms=[normalize_func])],
dataset=x._formatted_data_iter(dl,normalize_func))
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/73_callback.captum.ipynb (unless otherwise specified).
__all__ = ['json_clean', 'CaptumInterpretation']
# Cell
import tempfile
from ..basics import *
# Cell
from ipykernel import jsonutil
# Cell
# Dirty hack as json_clean doesn't support CategoryMap type
_json_clean=jsonutil.json_clean
def json_clean(o):
o = list(o.items) if isinstance(o,CategoryMap) else o
return _json_clean(o)
jsonutil.json_clean = json_clean
# Cell
from captum.attr import IntegratedGradients,NoiseTunnel,GradientShap,Occlusion
from captum.attr import visualization as viz
from matplotlib.colors import LinearSegmentedColormap
from captum.insights import AttributionVisualizer, Batch
from captum.insights.attr_vis.features import ImageFeature
# Cell
class CaptumInterpretation():
"Captum Interpretation for Resnet"
def __init__(self,learn,cmap_name='custom blue',colors=None,N=256,methods=('original_image','heat_map'),
signs=("all", "positive"),outlier_perc=1):
if colors is None: colors = [(0, '#ffffff'),(0.25, '#000000'),(1, '#000000')]
store_attr()
self.dls,self.model = learn.dls,self.learn.model
self.supported_metrics=['IG','NT','Occl']
def get_baseline_img(self, img_tensor,baseline_type):
baseline_img=None
if baseline_type=='zeros': baseline_img= img_tensor*0
if baseline_type=='uniform': baseline_img= torch.rand(img_tensor.shape)
if baseline_type=='gauss':
baseline_img= (torch.rand(img_tensor.shape).to(self.dls.device)+img_tensor)/2
return baseline_img.to(self.dls.device)
def visualize(self,inp,metric='IG',n_steps=1000,baseline_type='zeros',nt_type='smoothgrad', strides=(3,4,4), sliding_window_shapes=(3,15,15)):
if metric not in self.supported_metrics:
raise Exception(f"Metric {metric} is not supported. Currently {self.supported_metrics} are only supported")
tls = L([TfmdLists(inp, t) for t in L(ifnone(self.dls.tfms,[None]))])
inp_data=list(zip(*(tls[0],tls[1])))[0]
enc_data,dec_data=self._get_enc_dec_data(inp_data)
attributions=self._get_attributions(enc_data,metric,n_steps,nt_type,baseline_type,strides,sliding_window_shapes)
self._viz(attributions,dec_data,metric)
def _viz(self,attributions,dec_data,metric):
default_cmap = LinearSegmentedColormap.from_list(self.cmap_name,self.colors, N=self.N)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(dec_data[0].numpy(), (1,2,0)),
methods=self.methods,
cmap=default_cmap,
show_colorbar=True,
signs=self.signs,
outlier_perc=self.outlier_perc, titles=[f'Original Image - ({dec_data[1]})', metric])
def _get_enc_dec_data(self,inp_data):
dec_data=self.dls.after_item(inp_data)
enc_data=self.dls.after_batch(to_device(self.dls.before_batch(dec_data),self.dls.device))
return(enc_data,dec_data)
def _get_attributions(self,enc_data,metric,n_steps,nt_type,baseline_type,strides,sliding_window_shapes):
# Get Baseline
baseline=self.get_baseline_img(enc_data[0],baseline_type)
supported_metrics ={}
if metric == 'IG':
self._int_grads = self._int_grads if hasattr(self,'_int_grads') else IntegratedGradients(self.model)
return self._int_grads.attribute(enc_data[0],baseline, target=enc_data[1], n_steps=200)
elif metric == 'NT':
self._int_grads = self._int_grads if hasattr(self,'_int_grads') else IntegratedGradients(self.model)
self._noise_tunnel= self._noise_tunnel if hasattr(self,'_noise_tunnel') else NoiseTunnel(self._int_grads)
return self._noise_tunnel.attribute(enc_data[0].to(self.dls.device), n_samples=1, nt_type=nt_type, target=enc_data[1])
elif metric == 'Occl':
self._occlusion = self._occlusion if hasattr(self,'_occlusion') else Occlusion(self.model)
return self._occlusion.attribute(enc_data[0].to(self.dls.device),
strides = strides,
target=enc_data[1],
sliding_window_shapes=sliding_window_shapes,
baselines=baseline)
# Cell
@patch
def insights(x: CaptumInterpretation,inp_data,debug=True):
_baseline_func= lambda o: o*0
_get_vocab = lambda vocab: list(map(str,vocab)) if isinstance(vocab[0],bool) else vocab
dl = x.dls.test_dl(L(inp_data),with_labels=True, bs=4)
normalize_func= next((func for func in dl.after_batch if type(func)==Normalize),noop)
# captum v0.3 expects tensors without the batch dimension.
if nested_attr(normalize_func, 'mean.ndim', 4)==4: normalize_func.mean.squeeze_(0)
if nested_attr(normalize_func, 'std.ndim', 4)==4: normalize_func.std.squeeze_(0)
visualizer = AttributionVisualizer(
models=[x.model],
score_func=lambda o: torch.nn.functional.softmax(o, 1),
classes=_get_vocab(dl.vocab),
features=[ImageFeature("Image", baseline_transforms=[_baseline_func], input_transforms=[normalize_func])],
dataset=x._formatted_data_iter(dl,normalize_func))
visualizer.render(debug=debug)