-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This reverts commit 86bae92.
- Loading branch information
Showing
13 changed files
with
571 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"tasks": { | ||
"build": "pip install -r requirements.txt", | ||
"launch": "python -m deeptuner.run" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from tensorflow.keras import layers, Model, Input | ||
|
||
class SiameseArchitecture: | ||
def __init__(self, input_shape, embedding_model): | ||
self.input_shape = input_shape | ||
self.embedding_model = embedding_model | ||
|
||
def create_siamese_network(self): | ||
anchor_input = Input(name="anchor", shape=self.input_shape) | ||
positive_input = Input(name="positive", shape=self.input_shape) | ||
negative_input = Input(name="negative", shape=self.input_shape) | ||
|
||
anchor_embedding = self.embedding_model(anchor_input) | ||
positive_embedding = self.embedding_model(positive_input) | ||
negative_embedding = self.embedding_model(negative_input) | ||
|
||
outputs = [anchor_embedding, positive_embedding, negative_embedding] | ||
model = Model(inputs=[anchor_input, positive_input, negative_input], outputs=outputs, name="siamese_network") | ||
return model |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from tensorflow.keras.applications import ResNet50 | ||
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout, BatchNormalization | ||
from tensorflow.keras.models import Model | ||
from tensorflow.keras import Input | ||
|
||
class ResNetBackbone: | ||
def __init__(self, input_shape=(224, 224, 3), weights='imagenet'): | ||
self.input_shape = input_shape | ||
self.weights = weights | ||
|
||
def create_model(self): | ||
inputs = Input(shape=self.input_shape) | ||
base_model = ResNet50(weights=self.weights, include_top=False, input_tensor=inputs) | ||
x = base_model.output | ||
x = GlobalAveragePooling2D()(x) | ||
x = Dense(1024, activation='relu')(x) | ||
x = Dropout(0.2)(x) | ||
x = BatchNormalization()(x) | ||
x = Dense(512, activation='relu')(x) | ||
x = Dropout(0.2)(x) | ||
x = BatchNormalization()(x) | ||
x = Dense(256, activation='relu')(x) | ||
x = Dropout(0.2)(x) | ||
x = BatchNormalization()(x) | ||
outputs = Dense(128)(x) | ||
model = Model(inputs, outputs, name='resnet_backbone') | ||
return model |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import tensorflow as tf | ||
from tensorflow.keras.callbacks import Callback | ||
from tensorflow.keras.optimizers import Adam | ||
|
||
class FineTuneCallback(Callback): | ||
def __init__(self, base_model, patience=5, unfreeze_layers=10): | ||
super(FineTuneCallback, self).__init__() | ||
self.base_model = base_model | ||
self.patience = patience | ||
self.unfreeze_layers = unfreeze_layers | ||
self.best_weights = None | ||
self.best_loss = float('inf') | ||
self.wait = 0 | ||
|
||
def on_epoch_end(self, epoch, logs=None): | ||
current_loss = logs.get('val_loss') | ||
if current_loss < self.best_loss: | ||
self.best_loss = current_loss | ||
self.best_weights = self.model.get_weights() | ||
self.wait = 0 | ||
else: | ||
self.wait += 1 | ||
if self.wait >= self.patience: | ||
# Restore the best weights | ||
self.model.set_weights(self.best_weights) | ||
self.wait = 0 | ||
# Unfreeze the last few layers | ||
for layer in self.base_model.layers[-self.unfreeze_layers:]: | ||
if hasattr(layer, 'trainable'): | ||
layer.trainable = True | ||
# Recompile the model to apply the changes | ||
self.model.compile(optimizer=Adam(learning_rate=1e-5)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import tensorflow as tf | ||
from tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array | ||
from sklearn.preprocessing import LabelEncoder | ||
import numpy as np | ||
from tensorflow.keras.applications import resnet50 as resnet | ||
|
||
class TripletDataGenerator(tf.keras.utils.Sequence): | ||
def __init__(self, image_paths, labels, batch_size, image_size, num_classes): | ||
self.image_paths = image_paths | ||
self.labels = labels | ||
self.batch_size = batch_size | ||
self.image_size = image_size | ||
self.num_classes = num_classes | ||
self.label_encoder = LabelEncoder() | ||
self.encoded_labels = self.label_encoder.fit_transform(labels) | ||
self.image_data_generator = ImageDataGenerator(preprocessing_function=resnet.preprocess_input) | ||
self.on_epoch_end() | ||
print(f"Initialized TripletDataGenerator with {len(self.image_paths)} images") | ||
|
||
def __len__(self): | ||
return max(1, len(self.image_paths) // self.batch_size) # Ensure at least one batch | ||
|
||
def __getitem__(self, index): | ||
batch_image_paths = self.image_paths[index * self.batch_size:(index + 1) * self.batch_size] | ||
batch_labels = self.encoded_labels[index * self.batch_size:(index + 1) * self.batch_size] | ||
return self._generate_triplet_batch(batch_image_paths, batch_labels) | ||
|
||
def on_epoch_end(self): | ||
# Shuffle the data at the end of each epoch | ||
combined = list(zip(self.image_paths, self.encoded_labels)) | ||
np.random.shuffle(combined) | ||
self.image_paths[:], self.encoded_labels[:] = zip(*combined) | ||
|
||
def _generate_triplet_batch(self, batch_image_paths, batch_labels): | ||
anchor_images = [] | ||
positive_images = [] | ||
negative_images = [] | ||
|
||
for i in range(len(batch_image_paths)): | ||
anchor_path = batch_image_paths[i] | ||
anchor_label = batch_labels[i] | ||
|
||
positive_path = np.random.choice( | ||
[p for p, l in zip(self.image_paths, self.encoded_labels) if l == anchor_label] | ||
) | ||
negative_path = np.random.choice( | ||
[p for p, l in zip(self.image_paths, self.encoded_labels) if l != anchor_label] | ||
) | ||
|
||
anchor_image = load_img(anchor_path, target_size=self.image_size) | ||
positive_image = load_img(positive_path, target_size=self.image_size) | ||
negative_image = load_img(negative_path, target_size=self.image_size) | ||
|
||
anchor_images.append(img_to_array(anchor_image)) | ||
positive_images.append(img_to_array(positive_image)) | ||
negative_images.append(img_to_array(negative_image)) | ||
|
||
# Convert lists to numpy arrays | ||
anchor_array = np.array(anchor_images) | ||
positive_array = np.array(positive_images) | ||
negative_array = np.array(negative_images) | ||
|
||
# Return inputs and dummy targets (zeros) since the loss is computed in the model | ||
return ( | ||
{ | ||
"anchor": anchor_array, | ||
"positive": positive_array, | ||
"negative": negative_array | ||
}, | ||
np.zeros((len(batch_image_paths),)) # Dummy target values | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import tensorflow as tf | ||
from tensorflow.keras import backend as K | ||
|
||
def arcface_loss(y_true, y_pred, scale=64.0, margin=0.5): | ||
y_true = tf.cast(y_true, dtype=tf.int32) | ||
y_true = tf.one_hot(y_true, depth=y_pred.shape[-1]) | ||
|
||
cos_theta = y_pred | ||
theta = tf.acos(K.clip(cos_theta, -1.0 + K.epsilon(), 1.0 - K.epsilon())) | ||
target_logits = tf.cos(theta + margin) | ||
|
||
logits = y_true * target_logits + (1 - y_true) * cos_theta | ||
logits *= scale | ||
|
||
loss = tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=logits) | ||
return tf.reduce_mean(loss) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import tensorflow as tf | ||
|
||
def triplet_loss(margin=1.0): | ||
def loss(y_true, y_pred): | ||
anchor, positive, negative = y_pred[0], y_pred[1], y_pred[2] | ||
pos_dist = tf.reduce_sum(tf.square(anchor - positive), axis=-1) | ||
neg_dist = tf.reduce_sum(tf.square(anchor - negative), axis=-1) | ||
basic_loss = pos_dist - neg_dist + margin | ||
loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0), axis=0) | ||
return loss | ||
return loss |
Oops, something went wrong.