From 32b1a0a446861f7223c09143e3faa63089fb52fb Mon Sep 17 00:00:00 2001 From: "Ahuja, Nilesh" Date: Sat, 9 Apr 2022 13:58:22 -0700 Subject: [PATCH 1/4] Allow specifying feature layer and pool factor in DFM --- anomalib/models/dfm/config.yaml | 2 ++ anomalib/models/dfm/dfm_model.py | 12 ++++++++++-- anomalib/models/dfm/model.py | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/anomalib/models/dfm/config.yaml b/anomalib/models/dfm/config.yaml index b8d8faa626..4fe05a62ca 100755 --- a/anomalib/models/dfm/config.yaml +++ b/anomalib/models/dfm/config.yaml @@ -14,6 +14,8 @@ dataset: model: name: dfm backbone: resnet18 + layer: layer3 + pool: 4 pca_level: 0.97 score_type: fre # nll: for Gaussian modeling, fre: pca feature reconstruction error project_path: ./results diff --git a/anomalib/models/dfm/dfm_model.py b/anomalib/models/dfm/dfm_model.py index 844afb9358..76172c78bb 100644 --- a/anomalib/models/dfm/dfm_model.py +++ b/anomalib/models/dfm/dfm_model.py @@ -19,6 +19,7 @@ import torch import torchvision from torch import Tensor, nn +import torch.nn.functional as F from anomalib.models.components import PCA, DynamicBufferModule, FeatureExtractor @@ -89,14 +90,15 @@ class DFMModel(nn.Module): score_type (str, optional): Scoring type. Options are `fre` and `nll`. Defaults to "fre". """ - def __init__(self, backbone: str, n_comps: float = 0.97, score_type: str = "fre"): + def __init__(self, backbone: str, layer: str, pool: int, n_comps: float = 0.97, score_type: str = "fre"): super().__init__() self.backbone = getattr(torchvision.models, backbone) + self.pool = pool self.n_components = n_comps self.pca_model = PCA(n_components=self.n_components) self.gaussian_model = SingleClassGaussian() self.score_type = score_type - self.feature_extractor = FeatureExtractor(backbone=self.backbone(pretrained=True), layers=["avgpool"]).eval() + self.feature_extractor = FeatureExtractor(backbone=self.backbone(pretrained=True), layers=[layer]).eval() def fit(self, dataset: Tensor) -> None: """Fit a pca transformation and a Gaussian model to dataset. @@ -143,6 +145,12 @@ def get_features(self, batch: Tensor) -> Tensor: """ self.feature_extractor.eval() layer_outputs = self.feature_extractor(batch) + for k in layer_outputs: + s0 = len(layer_outputs[k]) + if self.pool > 1: + layer_outputs[k] = F.avg_pool2d(layer_outputs[k], self.pool) + layer_outputs[k] = layer_outputs[k].view(s0, -1) + layer_outputs = torch.cat(list(layer_outputs.values())).detach() return layer_outputs diff --git a/anomalib/models/dfm/model.py b/anomalib/models/dfm/model.py index 944c885677..ce37637bff 100644 --- a/anomalib/models/dfm/model.py +++ b/anomalib/models/dfm/model.py @@ -32,7 +32,8 @@ def __init__(self, hparams: Union[DictConfig, ListConfig]): super().__init__(hparams) self.model: DFMModel = DFMModel( - backbone=hparams.model.backbone, n_comps=hparams.model.pca_level, score_type=hparams.model.score_type + backbone=hparams.model.backbone, layer=hparams.model.layer, pool=hparams.model.pool, + n_comps=hparams.model.pca_level, score_type=hparams.model.score_type ) self.automatic_optimization = False self.embeddings: List[Tensor] = [] From 02e34b384a186e4f3dcc314cd08dd564f4b4a0f2 Mon Sep 17 00:00:00 2001 From: Samet Akcay Date: Tue, 12 Apr 2022 03:52:53 -0700 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8F=B7=20Rename=20pool=20to=20pooling?= =?UTF-8?q?=5Fkernel=5Fsize=20to=20make=20it=20more=20descriptive.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- anomalib/models/dfm/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anomalib/models/dfm/config.yaml b/anomalib/models/dfm/config.yaml index 4fe05a62ca..48f35ab143 100755 --- a/anomalib/models/dfm/config.yaml +++ b/anomalib/models/dfm/config.yaml @@ -15,7 +15,7 @@ model: name: dfm backbone: resnet18 layer: layer3 - pool: 4 + pooling_kernel_size: 4 pca_level: 0.97 score_type: fre # nll: for Gaussian modeling, fre: pca feature reconstruction error project_path: ./results From 1d7b08ffb5f4ed256059dca431936f3299ad1553 Mon Sep 17 00:00:00 2001 From: Samet Akcay Date: Tue, 12 Apr 2022 03:53:47 -0700 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=8F=B7=20renamed=20variable=20names?= =?UTF-8?q?=20in=20`get=5Ffeatures`=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- anomalib/models/dfm/dfm_model.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/anomalib/models/dfm/dfm_model.py b/anomalib/models/dfm/dfm_model.py index 76172c78bb..bab8785ee9 100644 --- a/anomalib/models/dfm/dfm_model.py +++ b/anomalib/models/dfm/dfm_model.py @@ -17,9 +17,9 @@ import math import torch +import torch.nn.functional as F import torchvision from torch import Tensor, nn -import torch.nn.functional as F from anomalib.models.components import PCA, DynamicBufferModule, FeatureExtractor @@ -86,14 +86,18 @@ class DFMModel(nn.Module): Args: backbone (str): Pre-trained model backbone. + layer (str): Layer from which to extract features. + pool (int): _description_ n_comps (float, optional): Ratio from which number of components for PCA are calculated. Defaults to 0.97. score_type (str, optional): Scoring type. Options are `fre` and `nll`. Defaults to "fre". """ - def __init__(self, backbone: str, layer: str, pool: int, n_comps: float = 0.97, score_type: str = "fre"): + def __init__( + self, backbone: str, layer: str, pooling_kernel_size: int, n_comps: float = 0.97, score_type: str = "fre" + ): super().__init__() self.backbone = getattr(torchvision.models, backbone) - self.pool = pool + self.pooling_kernel_size = pooling_kernel_size self.n_components = n_comps self.pca_model = PCA(n_components=self.n_components) self.gaussian_model = SingleClassGaussian() @@ -144,15 +148,15 @@ def get_features(self, batch: Tensor) -> Tensor: Tensor: Tensor containing extracted features. """ self.feature_extractor.eval() - layer_outputs = self.feature_extractor(batch) - for k in layer_outputs: - s0 = len(layer_outputs[k]) - if self.pool > 1: - layer_outputs[k] = F.avg_pool2d(layer_outputs[k], self.pool) - layer_outputs[k] = layer_outputs[k].view(s0, -1) - - layer_outputs = torch.cat(list(layer_outputs.values())).detach() - return layer_outputs + features = self.feature_extractor(batch) + for layer in features: + batch_size = len(features[layer]) + if self.pooling_kernel_size > 1: + features[layer] = F.avg_pool2d(input=features[layer], kernel_size=self.pooling_kernel_size) + features[layer] = features[layer].view(batch_size, -1) + + features = torch.cat(list(features.values())).detach() + return features def forward(self, batch: Tensor) -> Tensor: """Computer score from input images. From 6d7db25959d1ca2deb28140f2a302d68203894d3 Mon Sep 17 00:00:00 2001 From: Samet Akcay Date: Tue, 12 Apr 2022 03:57:28 -0700 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=8F=B7=20Change=20the=20keyword=20arg?= =?UTF-8?q?ument=20in=20Dfm=20Lightning=20Module=20to=20properly=20import?= =?UTF-8?q?=20the=20Dfm=20Model.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- anomalib/models/dfm/model.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/anomalib/models/dfm/model.py b/anomalib/models/dfm/model.py index ce37637bff..14589222af 100644 --- a/anomalib/models/dfm/model.py +++ b/anomalib/models/dfm/model.py @@ -32,8 +32,11 @@ def __init__(self, hparams: Union[DictConfig, ListConfig]): super().__init__(hparams) self.model: DFMModel = DFMModel( - backbone=hparams.model.backbone, layer=hparams.model.layer, pool=hparams.model.pool, - n_comps=hparams.model.pca_level, score_type=hparams.model.score_type + backbone=hparams.model.backbone, + layer=hparams.model.layer, + pooling_kernel_size=hparams.model.pooling_kernel_size, + n_comps=hparams.model.pca_level, + score_type=hparams.model.score_type, ) self.automatic_optimization = False self.embeddings: List[Tensor] = []