ash12321 commited on
Commit
0e9dcc1
·
verified ·
1 Parent(s): 31d83fe

Upload Deep SVDD anomaly detection model

Browse files
Files changed (10) hide show
  1. .gitattributes +1 -34
  2. README.md +154 -0
  3. config.json +41 -0
  4. deepsvdd_model.pth +3 -0
  5. example.py +25 -0
  6. model.py +142 -0
  7. requirements.txt +4 -0
  8. thresholds.json +24 -0
  9. thresholds.pkl +3 -0
  10. thresholds_report.txt +99 -0
.gitattributes CHANGED
@@ -1,35 +1,2 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.pth filter=lfs diff=lfs merge=lfs -text
2
+ *.pkl filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
README.md ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ tags:
4
+ - anomaly-detection
5
+ - deep-svdd
6
+ - computer-vision
7
+ - pytorch
8
+ datasets:
9
+ - cifar10
10
+ - cifar100
11
+ metrics:
12
+ - accuracy
13
+ - precision
14
+ - recall
15
+ - f1
16
+ library_name: pytorch
17
+ ---
18
+
19
+ # Deep SVDD Anomaly Detection Model
20
+
21
+ A Deep Support Vector Data Description (Deep SVDD) model trained for anomaly detection on natural images.
22
+
23
+ ## Model Description
24
+
25
+ This model uses a ResNet-based encoder to learn a hypersphere representation of normal data. Images are classified as anomalies based on their distance from the center of this hypersphere.
26
+
27
+ **Training Data:**
28
+ - CIFAR-10 (50,000 images)
29
+ - CIFAR-100 (50,000 images)
30
+ - STL-10 (100,000 images)
31
+
32
+ **Architecture:**
33
+ - ResNet-based encoder with residual blocks
34
+ - Latent dimension: 512
35
+ - Input size: 128x128x3
36
+
37
+ ## Performance
38
+
39
+ Evaluated on CIFAR-10 (normal) vs MNIST (anomaly):
40
+
41
+ | Metric | Value |
42
+ |--------|-------|
43
+ | Accuracy | 87.00% |
44
+ | Precision | 80.33% |
45
+ | Recall | 98.00% |
46
+ | F1 Score | 88.29% |
47
+
48
+ **Anomaly Score Separation:** 6.15x (anomalies score ~6x higher than normal images)
49
+
50
+ ## Usage
51
+
52
+ ### Quick Start
53
+
54
+ ```python
55
+ from model import DeepSVDDAnomalyDetector
56
+
57
+ # Load model
58
+ detector = DeepSVDDAnomalyDetector.from_pretrained('.')
59
+
60
+ # Predict on image
61
+ score, is_anomaly = detector.predict('test.jpg')
62
+ print(f"Anomaly Score: {score:.6f}")
63
+ print(f"Is Anomaly: {is_anomaly}")
64
+ ```
65
+
66
+ ### Download from Hugging Face
67
+
68
+ ```python
69
+ from huggingface_hub import snapshot_download
70
+
71
+ # Download model
72
+ model_path = snapshot_download(repo_id="ash12321/deep-svdd-anomaly-detection")
73
+
74
+ # Load
75
+ detector = DeepSVDDAnomalyDetector.from_pretrained(model_path)
76
+ ```
77
+
78
+ ### Threshold Options
79
+
80
+ The model supports three threshold presets:
81
+
82
+ ```python
83
+ # Optimal F1 (default, recommended)
84
+ detector.set_threshold('optimal') # threshold = 0.001618
85
+
86
+ # 95th percentile (balanced)
87
+ detector.set_threshold('95th') # threshold = 0.008501
88
+
89
+ # 99th percentile (conservative, fewer false positives)
90
+ detector.set_threshold('99th') # threshold = 0.015922
91
+ ```
92
+
93
+ **Threshold Comparison:**
94
+
95
+ | Threshold | Accuracy | Precision | Recall | Use Case |
96
+ |-----------|----------|-----------|--------|----------|
97
+ | Optimal (0.0016) | 87% | 80% | 98% | **Recommended** - Best F1 |
98
+ | 95th (0.0085) | 75% | 95% | 53% | Few false alarms |
99
+ | 99th (0.0159) | 68% | 100% | 35% | Zero false alarms |
100
+
101
+ ## Training Details
102
+
103
+ - **Framework:** PyTorch 2.9.1+cu128
104
+ - **Precision:** bfloat16 mixed precision
105
+ - **Optimizer:** Fused AdamW
106
+ - **Hardware:** NVIDIA H200
107
+ - **Epochs:** 50
108
+ - **Batch Size:** 1536
109
+
110
+ ## Model Files
111
+
112
+ - `deepsvdd_model.pth` - Model weights and hypersphere parameters
113
+ - `thresholds.pkl` - All threshold configurations
114
+ - `thresholds.json` - Thresholds in JSON format
115
+ - `config.json` - Model configuration
116
+ - `model.py` - Inference code
117
+ - `requirements.txt` - Python dependencies
118
+
119
+ ## Citation
120
+
121
+ ```bibtex
122
+ @misc{deep-svdd-anomaly-detection,
123
+ title={Deep SVDD Anomaly Detection Model},
124
+ author={ash12321},
125
+ year={2024},
126
+ publisher={Hugging Face},
127
+ url={https://huggingface.co/ash12321/deep-svdd-anomaly-detection}
128
+ }
129
+ ```
130
+
131
+ ## License
132
+
133
+ Apache 2.0
134
+
135
+ ## Limitations
136
+
137
+ - Trained on natural images (CIFAR-10/100, STL-10)
138
+ - Best suited for detecting distribution shift in natural images
139
+ - May not generalize well to very different domains
140
+ - Requires RGB images, resized to 128x128
141
+
142
+ ## Intended Use
143
+
144
+ **Primary Use:** Anomaly detection in natural image datasets
145
+
146
+ **Good for:**
147
+ - Quality control in image datasets
148
+ - Detecting out-of-distribution samples
149
+ - Filtering unusual/corrupted images
150
+ - Content moderation
151
+
152
+ **Not recommended for:**
153
+ - Critical safety systems without human review
154
+ - Domains very different from natural images
config.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "deep-svdd",
3
+ "task": "anomaly-detection",
4
+ "architecture": "resnet-encoder",
5
+ "latent_dim": 512,
6
+ "image_size": 128,
7
+ "input_channels": 3,
8
+ "training_datasets": [
9
+ "cifar10",
10
+ "cifar100",
11
+ "stl10"
12
+ ],
13
+ "normalization": {
14
+ "mean": [
15
+ 0.485,
16
+ 0.456,
17
+ 0.406
18
+ ],
19
+ "std": [
20
+ 0.229,
21
+ 0.224,
22
+ 0.225
23
+ ]
24
+ },
25
+ "thresholds": {
26
+ "optimal_f1": 0.001618,
27
+ "95th_percentile": 0.008500736206769943,
28
+ "99th_percentile": 0.015921616926789284,
29
+ "recommended": 0.001618
30
+ },
31
+ "performance": {
32
+ "threshold": 0.001618,
33
+ "accuracy": 0.87,
34
+ "precision": 0.8033,
35
+ "recall": 0.98,
36
+ "f1": 0.8829
37
+ },
38
+ "framework": "pytorch",
39
+ "pytorch_version": "2.9.1+cu128",
40
+ "license": "apache-2.0"
41
+ }
deepsvdd_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:390c53bce6d0b2f1eaad7d28f76403f3b276c4ca01d511c47d9bf130a0bc88b2
3
+ size 99812590
example.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Example usage of Deep SVDD Anomaly Detection Model
3
+ """
4
+
5
+ from model import DeepSVDDAnomalyDetector
6
+ from huggingface_hub import snapshot_download
7
+
8
+ # Download model from HuggingFace
9
+ print("Downloading model...")
10
+ model_path = snapshot_download(repo_id="ash12321/deep-svdd-anomaly-detection")
11
+
12
+ # Load model
13
+ print("Loading model...")
14
+ detector = DeepSVDDAnomalyDetector.from_pretrained(model_path)
15
+
16
+ # Example: Predict on image
17
+ score, is_anomaly = detector.predict('test.jpg')
18
+ print(f"Score: {score:.6f}")
19
+ print(f"Anomaly: {is_anomaly}")
20
+
21
+ # Try different thresholds
22
+ for threshold in ['optimal', '95th', '99th']:
23
+ detector.set_threshold(threshold)
24
+ score, is_anomaly = detector.predict('test.jpg')
25
+ print(f"{threshold}: Score={score:.6f}, Anomaly={is_anomaly}")
model.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Deep SVDD Anomaly Detection Model
3
+ Trained on CIFAR-10, CIFAR-100, and STL-10
4
+ """
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+ from torchvision import transforms
10
+ from PIL import Image
11
+ import pickle
12
+ import json
13
+ from pathlib import Path
14
+
15
+
16
+ class ResidualBlock(nn.Module):
17
+ def __init__(self, in_ch: int, out_ch: int, stride: int = 1):
18
+ super().__init__()
19
+ self.conv1 = nn.Conv2d(in_ch, out_ch, 3, stride=stride, padding=1, bias=False)
20
+ self.bn1 = nn.BatchNorm2d(out_ch)
21
+ self.conv2 = nn.Conv2d(out_ch, out_ch, 3, stride=1, padding=1, bias=False)
22
+ self.bn2 = nn.BatchNorm2d(out_ch)
23
+
24
+ self.shortcut = nn.Sequential()
25
+ if stride != 1 or in_ch != out_ch:
26
+ self.shortcut = nn.Sequential(
27
+ nn.Conv2d(in_ch, out_ch, 1, stride=stride, bias=False),
28
+ nn.BatchNorm2d(out_ch)
29
+ )
30
+
31
+ def forward(self, x):
32
+ out = F.relu(self.bn1(self.conv1(x)))
33
+ out = self.bn2(self.conv2(out))
34
+ out += self.shortcut(x)
35
+ return F.relu(out)
36
+
37
+
38
+ class DeepSVDDEncoder(nn.Module):
39
+ def __init__(self, latent_dim: int = 512):
40
+ super().__init__()
41
+ self.conv1 = nn.Conv2d(3, 64, 7, stride=2, padding=3, bias=False)
42
+ self.bn1 = nn.BatchNorm2d(64)
43
+ self.layer1 = self._make_layer(64, 128, stride=2)
44
+ self.layer2 = self._make_layer(128, 256, stride=2)
45
+ self.layer3 = self._make_layer(256, 512, stride=2)
46
+ self.layer4 = self._make_layer(512, 512, stride=2)
47
+ self.fc = nn.Linear(512 * 4 * 4, latent_dim, bias=False)
48
+
49
+ def _make_layer(self, in_ch: int, out_ch: int, stride: int = 1):
50
+ return nn.Sequential(
51
+ ResidualBlock(in_ch, out_ch, stride),
52
+ ResidualBlock(out_ch, out_ch, 1)
53
+ )
54
+
55
+ def forward(self, x):
56
+ x = F.relu(self.bn1(self.conv1(x)))
57
+ x = self.layer1(x)
58
+ x = self.layer2(x)
59
+ x = self.layer3(x)
60
+ x = self.layer4(x)
61
+ x = x.view(x.size(0), -1)
62
+ return self.fc(x)
63
+
64
+
65
+ class DeepSVDDAnomalyDetector:
66
+ """
67
+ Deep SVDD Anomaly Detection Model
68
+
69
+ Usage:
70
+ from model import DeepSVDDAnomalyDetector
71
+
72
+ detector = DeepSVDDAnomalyDetector.from_pretrained('.')
73
+ score, is_anomaly = detector.predict('image.jpg')
74
+ """
75
+
76
+ def __init__(self, model_path, thresholds_path, config_path, device='cuda'):
77
+ self.device = torch.device(device if torch.cuda.is_available() else 'cpu')
78
+
79
+ # Load config
80
+ with open(config_path, 'r') as f:
81
+ self.config = json.load(f)
82
+
83
+ # Load model
84
+ checkpoint = torch.load(model_path, map_location=self.device)
85
+ self.latent_dim = checkpoint['latent_dim']
86
+ self.center = checkpoint['center'].to(self.device)
87
+ self.radius = checkpoint['radius'].item()
88
+
89
+ self.encoder = DeepSVDDEncoder(self.latent_dim).to(self.device)
90
+ self.encoder.load_state_dict(checkpoint['encoder_state_dict'])
91
+ self.encoder.eval()
92
+
93
+ # Load thresholds
94
+ with open(thresholds_path, 'rb') as f:
95
+ thresholds = pickle.load(f)
96
+
97
+ self.threshold_95 = thresholds['95th_percentile']
98
+ self.threshold_99 = thresholds['99th_percentile']
99
+ self.threshold_optimal = thresholds['optimal_f1']
100
+ self.threshold = self.threshold_optimal
101
+
102
+ # Image preprocessing
103
+ self.transform = transforms.Compose([
104
+ transforms.Resize((128, 128)),
105
+ transforms.ToTensor(),
106
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
107
+ ])
108
+
109
+ @classmethod
110
+ def from_pretrained(cls, model_path='.', device='cuda'):
111
+ """Load pretrained model from directory or HuggingFace Hub"""
112
+ model_path = Path(model_path)
113
+ return cls(
114
+ model_path=model_path / 'deepsvdd_model.pth',
115
+ thresholds_path=model_path / 'thresholds.pkl',
116
+ config_path=model_path / 'config.json',
117
+ device=device
118
+ )
119
+
120
+ def set_threshold(self, threshold_type='optimal'):
121
+ """Set threshold: 'optimal', '95th', or '99th'"""
122
+ if threshold_type == 'optimal':
123
+ self.threshold = self.threshold_optimal
124
+ elif threshold_type == '95th':
125
+ self.threshold = self.threshold_95
126
+ elif threshold_type == '99th':
127
+ self.threshold = self.threshold_99
128
+
129
+ @torch.no_grad()
130
+ def predict(self, image_path):
131
+ """Predict if image is anomaly"""
132
+ if isinstance(image_path, (str, Path)):
133
+ image = Image.open(image_path).convert('RGB')
134
+ else:
135
+ image = image_path
136
+
137
+ image_tensor = self.transform(image).unsqueeze(0).to(self.device)
138
+ embeddings = self.encoder(image_tensor)
139
+ score = torch.sum((embeddings - self.center) ** 2, dim=1).item()
140
+ is_anomaly = score > self.threshold
141
+
142
+ return score, is_anomaly
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ torch>=2.0.0
2
+ torchvision>=0.15.0
3
+ pillow>=9.0.0
4
+ numpy>=1.21.0
thresholds.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "95th_percentile": 0.008500736206769943,
3
+ "99th_percentile": 0.015921616926789284,
4
+ "optimal_f1": 0.001618,
5
+ "conservative": 0.015,
6
+ "balanced": 0.006,
7
+ "sensitive": 0.001618,
8
+ "radius": 0.01232091523706913,
9
+ "latent_dim": 512,
10
+ "optimal_metrics": {
11
+ "threshold": 0.001618,
12
+ "accuracy": 0.87,
13
+ "precision": 0.8033,
14
+ "recall": 0.98,
15
+ "f1": 0.8829
16
+ },
17
+ "recommendations": {
18
+ "default": "optimal_f1",
19
+ "production": "optimal_f1",
20
+ "zero_false_positives": "99th_percentile",
21
+ "balanced": "balanced",
22
+ "maximum_detection": "sensitive"
23
+ }
24
+ }
thresholds.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:24895454dc9ebc0de9f2377ce7e32d36b15a480fdb401295250e301d4c8119d6
3
+ size 407
thresholds_report.txt ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ================================================================================
3
+ DEEP SVDD ANOMALY DETECTION - THRESHOLD CONFIGURATION
4
+ ================================================================================
5
+
6
+ MODEL INFORMATION
7
+ -----------------
8
+ Latent Dimension: 512
9
+ Hypersphere Radius: 0.012321
10
+ Center Location: torch.Size([512])
11
+
12
+ ================================================================================
13
+ AVAILABLE THRESHOLDS
14
+ ================================================================================
15
+
16
+ 1. OPTIMAL F1 (RECOMMENDED FOR PRODUCTION)
17
+ Threshold: 0.001618
18
+ Accuracy: 87.00%
19
+ Precision: 80.33%
20
+ Recall: 98.00%
21
+ F1 Score: 88.29%
22
+
23
+ Use Case: Best overall performance, maximizes F1 score
24
+ Command: detector.set_custom_threshold(0.001618)
25
+
26
+ 2. 95TH PERCENTILE (BALANCED)
27
+ Threshold: 0.008501
28
+
29
+ Use Case: Few false positives, moderate recall
30
+ Command: detector.set_threshold('95th')
31
+
32
+ 3. 99TH PERCENTILE (CONSERVATIVE)
33
+ Threshold: 0.015922
34
+
35
+ Use Case: Zero or near-zero false positives
36
+ Command: detector.set_threshold('99th')
37
+
38
+ 4. BALANCED (MIDDLE GROUND)
39
+ Threshold: 0.006000
40
+
41
+ Use Case: Good balance between precision and recall
42
+ Command: detector.set_custom_threshold(0.006000)
43
+
44
+ 5. SENSITIVE (MAXIMUM DETECTION)
45
+ Threshold: 0.001618
46
+
47
+ Use Case: Catch as many anomalies as possible
48
+ Command: detector.set_custom_threshold(0.001618)
49
+
50
+ 6. CONSERVATIVE (ZERO FALSE POSITIVES)
51
+ Threshold: 0.015000
52
+
53
+ Use Case: Critical systems where false alarms are unacceptable
54
+ Command: detector.set_custom_threshold(0.015000)
55
+
56
+ ================================================================================
57
+ USAGE RECOMMENDATIONS
58
+ ================================================================================
59
+
60
+ SCENARIO 1: General Production Use
61
+ → Use: OPTIMAL F1 (threshold = 0.001618)
62
+ Best overall performance with 87.0% accuracy
63
+
64
+ SCENARIO 2: False Alarms Are Costly
65
+ → Use: 99TH PERCENTILE (threshold = 0.015922)
66
+ Minimizes false positives at cost of lower recall
67
+
68
+ SCENARIO 3: Must Catch All Anomalies
69
+ → Use: SENSITIVE (threshold = 0.001618)
70
+ Maximum recall, accepts higher false positive rate
71
+
72
+ SCENARIO 4: Balanced Approach
73
+ → Use: BALANCED (threshold = 0.006000)
74
+ Good middle ground for most applications
75
+
76
+ ================================================================================
77
+ QUICK START CODE
78
+ ================================================================================
79
+
80
+ # Load model with optimal threshold (recommended)
81
+ from inference import DeepSVDDAnomalyDetector
82
+
83
+ detector = DeepSVDDAnomalyDetector(
84
+ model_dir='./saved_model',
85
+ custom_threshold=0.001618 # Optimal F1
86
+ )
87
+
88
+ # Or switch thresholds dynamically
89
+ detector.set_threshold('95th') # Use 95th percentile
90
+ detector.set_threshold('99th') # Use 99th percentile
91
+ detector.set_custom_threshold(0.001618) # Use optimal
92
+
93
+ # Predict
94
+ score, is_anomaly = detector.predict_image('test.jpg')
95
+ print(f"Score: {score:.6f}, Anomaly: {is_anomaly}")
96
+
97
+ ================================================================================
98
+ Generated: 2025-12-14 13:03:54
99
+ ================================================================================