Update README.md
Browse files
README.md
CHANGED
@@ -1,6 +1,4 @@
|
|
1 |
---
|
2 |
-
license: mit
|
3 |
-
pipeline_tag: image-classification
|
4 |
tags:
|
5 |
- cloud
|
6 |
- weather
|
@@ -8,7 +6,10 @@ tags:
|
|
8 |
- cloud types
|
9 |
- wmo
|
10 |
- cloud image classification
|
|
|
|
|
11 |
---
|
|
|
12 |
# Genera - Cloud Image Classification Model
|
13 |
|
14 |
**Version:** 1.0.0
|
@@ -72,18 +73,22 @@ The model was trained on the **UGCI (Ultimate Ground-level Cloud Image) dataset*
|
|
72 |
|
73 |
## You can install necessary packages using pip:
|
74 |
|
75 |
-
|
76 |
|
77 |
## Loading the Model
|
78 |
|
79 |
The model is saved in the Keras native format (.keras). You will need to provide the definitions of the custom layers (RepVGGBlock and NECALayer) when loading.
|
80 |
|
81 |
-
|
82 |
-
|
|
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
|
|
|
|
|
|
|
87 |
def __init__(self, in_channels, out_channels, kernel_size=3, stride=1,
|
88 |
groups=1, deploy=False, use_se=False, **kwargs):
|
89 |
super(RepVGGBlock, self).__init__(**kwargs)
|
@@ -95,7 +100,7 @@ class RepVGGBlock(layers.Layer):
|
|
95 |
self._deploy_mode_internal = deploy
|
96 |
self.config_use_se = use_se # Placeholder, not used in this version of RepVGGBlock
|
97 |
self.actual_in_channels = None
|
98 |
-
|
99 |
self.rbr_dense_conv = layers.Conv2D(
|
100 |
filters=self.config_out_channels, kernel_size=self.config_kernel_size,
|
101 |
strides=self.config_strides_val, padding='same',
|
@@ -201,10 +206,11 @@ class RepVGGBlock(layers.Layer):
|
|
201 |
return config
|
202 |
@classmethod
|
203 |
def from_config(cls, config): return cls(**config)
|
204 |
-
|
|
|
|
|
205 |
|
206 |
-
|
207 |
-
class NECALayer(layers.Layer):
|
208 |
def __init__(self, channels, gamma=2, b=1, **kwargs):
|
209 |
super(NECALayer, self).__init__(**kwargs)
|
210 |
self.channels = channels
|
@@ -220,7 +226,7 @@ class NECALayer(layers.Layer):
|
|
220 |
self.gap = layers.GlobalAveragePooling2D(keepdims=True)
|
221 |
self.conv1d = layers.Conv1D(filters=1, kernel_size=kernel_size_for_conv1d, padding='same', use_bias=False, name=self.name + '_eca_conv1d')
|
222 |
self.sigmoid = layers.Activation('sigmoid')
|
223 |
-
|
224 |
def call(self, inputs):
|
225 |
if self.channels != inputs.shape[-1]: raise ValueError(f"Input channels {inputs.shape[-1]} != layer channels {self.channels} for {self.name}")
|
226 |
x = self.gap(inputs)
|
@@ -238,36 +244,40 @@ class NECALayer(layers.Layer):
|
|
238 |
return config
|
239 |
@classmethod
|
240 |
def from_config(cls, config): return cls(**config)
|
241 |
-
|
242 |
-
|
243 |
|
244 |
-
import tensorflow as tf
|
245 |
-
from tensorflow import keras
|
246 |
|
247 |
-
|
248 |
-
LABEL_MAPPING_FILE = 'path/to/your/label_mapping.json' # Replace with actual path
|
249 |
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
|
255 |
# Load label mapping
|
256 |
-
import json
|
257 |
-
with open(LABEL_MAPPING_FILE, 'r') as f:
|
258 |
-
|
259 |
-
int_to_label = {int(k): v for k, v in label_map_data['int_to_label'].items()}
|
260 |
|
261 |
**Making Predictions**
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
def preprocess_image_for_prediction(image_path_or_pil_image, target_size=(299, 299)):
|
266 |
-
if isinstance(image_path_or_pil_image, str):
|
267 |
-
img = Image.open(image_path_or_pil_image)
|
268 |
-
else: # Assuming PIL image
|
269 |
-
img = image_path_or_pil_image
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
img = img.convert('RGB') # Ensure 3 channels
|
272 |
img = img.resize(target_size)
|
273 |
img_array = np.array(img, dtype=np.float32)
|
@@ -275,24 +285,24 @@ def preprocess_image_for_prediction(image_path_or_pil_image, target_size=(299, 2
|
|
275 |
img_array = np.expand_dims(img_array, axis=0) # Add batch dimension
|
276 |
return img_array
|
277 |
|
278 |
-
# Example prediction:
|
279 |
-
image_path = 'path/to/your/cloud_image.jpg' # Replace with your image path
|
280 |
-
input_tensor = preprocess_image_for_prediction(image_path)
|
281 |
-
predictions = loaded_model.predict(input_tensor)
|
282 |
-
predicted_probabilities = predictions[0]
|
283 |
-
|
284 |
-
# Get top prediction
|
285 |
-
predicted_class_index = np.argmax(predicted_probabilities)
|
286 |
-
predicted_class_name = int_to_label.get(predicted_class_index, "Unknown Class")
|
287 |
-
confidence = predicted_probabilities[predicted_class_index]
|
288 |
-
|
289 |
-
print(f"Predicted Cloud Type: {predicted_class_name}")
|
290 |
-
print(f"Confidence: {confidence*100:.2f}%")
|
291 |
-
|
292 |
-
# Display all class probabilities (optional)
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
|
297 |
## 4. Training Procedure
|
298 |
Dataset: UGCI
|
@@ -347,6 +357,8 @@ Random Contrast adjustments (factor 0.3)
|
|
347 |
|
348 |
Framework: TensorFlow/Keras
|
349 |
|
|
|
|
|
350 |
Optimizer: AdamW (learning_rate=1e-4, weight_decay=5e-5)
|
351 |
|
352 |
Loss Function: Sparse Categorical Crossentropy
|
@@ -361,7 +373,7 @@ EarlyStopping (monitoring val_loss, patience 20, restore_best_weights=True)
|
|
361 |
|
362 |
ReduceLROnPlateau (monitoring val_loss, patience 10)
|
363 |
|
364 |
-
Epochs: Trained for
|
365 |
|
366 |
Batch Size: 32
|
367 |
|
@@ -468,4 +480,4 @@ This project, including the model weights and source code, is licensed under the
|
|
468 |
|
469 |
## 11. Acknowledgements
|
470 |
|
471 |
-
This work was inspired by the methodologies presented in "Improved RepVGG ground-based cloud image classification with attention convolution" by Shi et al. (2024).
|
|
|
1 |
---
|
|
|
|
|
2 |
tags:
|
3 |
- cloud
|
4 |
- weather
|
|
|
6 |
- cloud types
|
7 |
- wmo
|
8 |
- cloud image classification
|
9 |
+
license: mit
|
10 |
+
pipeline_tag: image-classification
|
11 |
---
|
12 |
+
|
13 |
# Genera - Cloud Image Classification Model
|
14 |
|
15 |
**Version:** 1.0.0
|
|
|
73 |
|
74 |
## You can install necessary packages using pip:
|
75 |
|
76 |
+
pip install tensorflow numpy Pillow
|
77 |
|
78 |
## Loading the Model
|
79 |
|
80 |
The model is saved in the Keras native format (.keras). You will need to provide the definitions of the custom layers (RepVGGBlock and NECALayer) when loading.
|
81 |
|
82 |
+
**IMPORTANT: You must have the RepVGGBlock and NECALayer class definitions available in your Python environment before running this.**
|
83 |
+
|
84 |
+
**--- CUSTOM LAYER DEFINITIONS ---**
|
85 |
|
86 |
+
|
87 |
+
**--- RepVGGBlock Class Definition ---**
|
88 |
+
|
89 |
+
|
90 |
+
|
91 |
+
class RepVGGBlock(layers.Layer):
|
92 |
def __init__(self, in_channels, out_channels, kernel_size=3, stride=1,
|
93 |
groups=1, deploy=False, use_se=False, **kwargs):
|
94 |
super(RepVGGBlock, self).__init__(**kwargs)
|
|
|
100 |
self._deploy_mode_internal = deploy
|
101 |
self.config_use_se = use_se # Placeholder, not used in this version of RepVGGBlock
|
102 |
self.actual_in_channels = None
|
103 |
+
|
104 |
self.rbr_dense_conv = layers.Conv2D(
|
105 |
filters=self.config_out_channels, kernel_size=self.config_kernel_size,
|
106 |
strides=self.config_strides_val, padding='same',
|
|
|
206 |
return config
|
207 |
@classmethod
|
208 |
def from_config(cls, config): return cls(**config)
|
209 |
+
**--- End of RepVGGBlock ---**
|
210 |
+
|
211 |
+
**--- NECALayer Class Definition ---**
|
212 |
|
213 |
+
class NECALayer(layers.Layer):
|
|
|
214 |
def __init__(self, channels, gamma=2, b=1, **kwargs):
|
215 |
super(NECALayer, self).__init__(**kwargs)
|
216 |
self.channels = channels
|
|
|
226 |
self.gap = layers.GlobalAveragePooling2D(keepdims=True)
|
227 |
self.conv1d = layers.Conv1D(filters=1, kernel_size=kernel_size_for_conv1d, padding='same', use_bias=False, name=self.name + '_eca_conv1d')
|
228 |
self.sigmoid = layers.Activation('sigmoid')
|
229 |
+
|
230 |
def call(self, inputs):
|
231 |
if self.channels != inputs.shape[-1]: raise ValueError(f"Input channels {inputs.shape[-1]} != layer channels {self.channels} for {self.name}")
|
232 |
x = self.gap(inputs)
|
|
|
244 |
return config
|
245 |
@classmethod
|
246 |
def from_config(cls, config): return cls(**config)
|
247 |
+
|
248 |
+
**--- End of NECALayer ---**
|
249 |
|
|
|
|
|
250 |
|
251 |
+
**--- END OF CUSTOM LAYER DEFINITIONS ---**
|
|
|
252 |
|
253 |
+
import tensorflow as tf
|
254 |
+
from tensorflow import keras
|
255 |
+
|
256 |
+
MODEL_FILE = 'path/to/your/repvgg_neca_deploy_final.keras' # Replace with actual path
|
257 |
+
LABEL_MAPPING_FILE = 'path/to/your/label_mapping.json' # Replace with actual path
|
258 |
+
|
259 |
+
custom_objects = {'RepVGGBlock': RepVGGBlock, 'NECALayer': NECALayer}
|
260 |
+
loaded_model = tf.keras.models.load_model(MODEL_FILE, custom_objects=custom_objects, compile=False)
|
261 |
+
print("Model loaded successfully!")
|
262 |
+
loaded_model.summary() # Optional: to see the loaded architecture
|
263 |
|
264 |
# Load label mapping
|
265 |
+
import json
|
266 |
+
with open(LABEL_MAPPING_FILE, 'r') as f:
|
267 |
+
label_map_data = json.load(f)
|
268 |
+
int_to_label = {int(k): v for k, v in label_map_data['int_to_label'].items()}
|
269 |
|
270 |
**Making Predictions**
|
271 |
+
|
272 |
+
from PIL import Image
|
273 |
+
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
274 |
|
275 |
+
def preprocess_image_for_prediction(image_path_or_pil_image, target_size=(299, 299)):
|
276 |
+
if isinstance(image_path_or_pil_image, str):
|
277 |
+
img = Image.open(image_path_or_pil_image)
|
278 |
+
else: # Assuming PIL image
|
279 |
+
img = image_path_or_pil_image
|
280 |
+
|
281 |
img = img.convert('RGB') # Ensure 3 channels
|
282 |
img = img.resize(target_size)
|
283 |
img_array = np.array(img, dtype=np.float32)
|
|
|
285 |
img_array = np.expand_dims(img_array, axis=0) # Add batch dimension
|
286 |
return img_array
|
287 |
|
288 |
+
# Example prediction:
|
289 |
+
image_path = 'path/to/your/cloud_image.jpg' # Replace with your image path
|
290 |
+
input_tensor = preprocess_image_for_prediction(image_path)
|
291 |
+
predictions = loaded_model.predict(input_tensor)
|
292 |
+
predicted_probabilities = predictions[0]
|
293 |
+
|
294 |
+
# Get top prediction
|
295 |
+
predicted_class_index = np.argmax(predicted_probabilities)
|
296 |
+
predicted_class_name = int_to_label.get(predicted_class_index, "Unknown Class")
|
297 |
+
confidence = predicted_probabilities[predicted_class_index]
|
298 |
+
|
299 |
+
print(f"Predicted Cloud Type: {predicted_class_name}")
|
300 |
+
print(f"Confidence: {confidence*100:.2f}%")
|
301 |
+
|
302 |
+
# Display all class probabilities (optional)
|
303 |
+
for i, prob in enumerate(predicted_probabilities):
|
304 |
+
class_name = int_to_label.get(i, f"Class_{i}")
|
305 |
+
print(f"- {class_name}: {prob*100:.2f}%")
|
306 |
|
307 |
## 4. Training Procedure
|
308 |
Dataset: UGCI
|
|
|
357 |
|
358 |
Framework: TensorFlow/Keras
|
359 |
|
360 |
+
Compute - Nvidia A100 GPU (Google Colab)
|
361 |
+
|
362 |
Optimizer: AdamW (learning_rate=1e-4, weight_decay=5e-5)
|
363 |
|
364 |
Loss Function: Sparse Categorical Crossentropy
|
|
|
373 |
|
374 |
ReduceLROnPlateau (monitoring val_loss, patience 10)
|
375 |
|
376 |
+
Epochs: Trained for 200 epochs (~7 hours) (EarlyStopping intervened). The best model was restored from Epoch 171 of the final run.
|
377 |
|
378 |
Batch Size: 32
|
379 |
|
|
|
480 |
|
481 |
## 11. Acknowledgements
|
482 |
|
483 |
+
This work was inspired by the methodologies presented in "Improved RepVGG ground-based cloud image classification with attention convolution" by Shi et al. (2024).
|