caliangandrew commited on
Commit
7009c6f
·
verified ·
1 Parent(s): 25e0b8e

Upload 2 files

Browse files
Files changed (2) hide show
  1. bm-general-v1.pth +3 -0
  2. ucf_detector.py +162 -0
bm-general-v1.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:afb892f2ad1a0c3417ab9a6f7a92332e819e60ef29416b5120d788a917055893
3
+ size 188001914
ucf_detector.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # Ignore INFO and WARN messages
3
+
4
+ import random
5
+ import warnings
6
+ warnings.filterwarnings("ignore", category=FutureWarning)
7
+ from pathlib import Path
8
+
9
+ import numpy as np
10
+ import torch
11
+ import torch.backends.cudnn as cudnn
12
+ import torchvision.transforms as transforms
13
+ import yaml
14
+ from PIL import Image
15
+ from huggingface_hub import hf_hub_download
16
+ import gc
17
+
18
+ from base_miner.UCF.config.constants import CONFIGS_DIR, WEIGHTS_DIR
19
+ from base_miner.gating_mechanisms import FaceGate
20
+
21
+ from base_miner.UCF.detectors import DETECTOR
22
+ from base_miner.deepfake_detectors import DeepfakeDetector
23
+ from base_miner import DETECTOR_REGISTRY, GATE_REGISTRY
24
+
25
+ import bittensor as bt
26
+
27
+ @DETECTOR_REGISTRY.register_module(module_name='UCF')
28
+ class UCFDetector(DeepfakeDetector):
29
+ """
30
+ DeepfakeDetector subclass that initializes a pretrained UCF model
31
+ for binary classification of fake and real images.
32
+
33
+ Attributes:
34
+ model_name (str): Name of the detector instance.
35
+ config (str): Name of the YAML file in deepfake_detectors/config/ to load
36
+ attributes from.
37
+ device (str): The type of device ('cpu' or 'cuda').
38
+ """
39
+
40
+ def __init__(self, model_name: str = 'UCF', config: str = 'ucf.yaml', device: str = 'cpu'):
41
+ super().__init__(model_name, config, device)
42
+
43
+ def ensure_weights_are_available(self, weight_filename):
44
+ destination_path = Path(WEIGHTS_DIR) / Path(weight_filename)
45
+ if not destination_path.parent.exists():
46
+ destination_path.parent.mkdir(parents=True, exist_ok=True)
47
+ if not destination_path.exists():
48
+ model_path = hf_hub_download(self.hf_repo, weight_filename)
49
+ model = torch.load(model_path, map_location=self.device)
50
+ torch.save(model, destination_path)
51
+
52
+ def load_train_config(self):
53
+ destination_path = Path(CONFIGS_DIR) / Path(self.train_config)
54
+
55
+ if not destination_path.exists():
56
+ local_config_path = hf_hub_download(self.hf_repo, self.train_config)
57
+ print(f"Downloaded {self.hf_repo}/{self.train_config} to {local_config_path}")
58
+ config_dict = {}
59
+ with open(local_config_path, 'r') as f:
60
+ config_dict = yaml.safe_load(f)
61
+ with open(destination_path, 'w') as f:
62
+ yaml.dump(config_dict, f, default_flow_style=False)
63
+ with destination_path.open('r') as f:
64
+ return yaml.safe_load(f)
65
+ else:
66
+ print(f"Loaded local config from {destination_path}")
67
+ with destination_path.open('r') as f:
68
+ return yaml.safe_load(f)
69
+
70
+ def init_cudnn(self):
71
+ if self.train_config.get('cudnn'):
72
+ cudnn.benchmark = True
73
+
74
+ def init_seed(self):
75
+ seed_value = self.train_config.get('manualSeed')
76
+ if seed_value:
77
+ random.seed(seed_value)
78
+ torch.manual_seed(seed_value)
79
+ torch.cuda.manual_seed_all(seed_value)
80
+
81
+ def load_model(self):
82
+ self.train_config = self.load_train_config()
83
+ self.init_cudnn()
84
+ self.init_seed()
85
+ self.ensure_weights_are_available(self.weights)
86
+ self.ensure_weights_are_available(self.train_config['pretrained'].split('/')[-1])
87
+ model_class = DETECTOR[self.train_config['model_name']]
88
+ bt.logging.info(f"Loaded config from training run: {self.train_config}")
89
+ self.model = model_class(self.train_config).to(self.device)
90
+ self.model.eval()
91
+ weights_path = Path(WEIGHTS_DIR) / self.weights
92
+ checkpoint = torch.load(weights_path, map_location=self.device)
93
+ try:
94
+ self.model.load_state_dict(checkpoint, strict=True)
95
+ except RuntimeError as e:
96
+ if 'size mismatch' in str(e):
97
+ # Create a custom error message
98
+ custom_message = (
99
+ "\n\n Error: Incorrect specific_task_num in model config. The 'specific_task_num' "
100
+ "in 'config_path' yaml should match the value used during training. "
101
+ "A mismatch results in an incorrect output layer shape for UCF's learned disentanglement"
102
+ " of different forgery methods/sources.\n\n"
103
+ "Solution: Use the same config.yaml to intialize UCFDetector ('config_path' arg) "
104
+ "as output during training (config.yaml saved alongside weights in the training run's "
105
+ "logs directory). Or simply modify your config.yaml to ensure 'specific_task_num' equals "
106
+ "the value set during training (defaults to num fake training datasets + 1).\n"
107
+ )
108
+ raise RuntimeError(custom_message) from e
109
+ else: raise e
110
+
111
+ def preprocess(self, image, res=256):
112
+ """Preprocess the image for model inference.
113
+
114
+ Returns:
115
+ torch.Tensor: The preprocessed image tensor, ready for model inference.
116
+ """
117
+ # Convert image to RGB format to ensure consistent color handling.
118
+ image = image.convert('RGB')
119
+
120
+ # Define transformation sequence for image preprocessing.
121
+ transform = transforms.Compose([
122
+ transforms.Resize((res, res), interpolation=Image.LANCZOS), # Resize image to specified resolution.
123
+ transforms.ToTensor(), # Convert the image to a PyTorch tensor.
124
+ transforms.Normalize(mean=self.train_config['mean'], std=self.train_config['std']) # Normalize the image tensor.
125
+ ])
126
+
127
+ # Apply transformations and add a batch dimension for model inference.
128
+ image_tensor = transform(image).unsqueeze(0)
129
+
130
+ # Move the image tensor to the specified device (e.g., GPU).
131
+ return image_tensor.to(self.device)
132
+
133
+ def infer(self, image_tensor):
134
+ """ Perform inference using the model. """
135
+ with torch.no_grad():
136
+ self.model({'image': image_tensor}, inference=True)
137
+ return self.model.prob[-1]
138
+
139
+ def __call__(self, image: Image) -> float:
140
+ image_tensor = self.preprocess(image)
141
+ return self.infer(image_tensor)
142
+
143
+ def free_memory(self):
144
+ """ Frees up memory by setting model and large data structures to None. """
145
+ if self.model is not None:
146
+ self.model.cpu() # Move model to CPU to free up GPU memory (if applicable)
147
+ del self.model
148
+ self.model = None
149
+
150
+ if self.face_detector is not None:
151
+ del self.face_detector
152
+ self.face_detector = None
153
+
154
+ if self.face_predictor is not None:
155
+ del self.face_predictor
156
+ self.face_predictor = None
157
+
158
+ gc.collect()
159
+
160
+ # If using GPUs and PyTorch, clear the cache as well
161
+ if torch.cuda.is_available():
162
+ torch.cuda.empty_cache()