Environment and Hardware Configuration
To ensure efficient computation, the environment is configured to utilize available GPU resources dynamically. Non-critical warnings are suppressed to maintain a clean log output.
import os
import pathlib
import warnings
import tensorflow as tf
import matplotlib.pyplot as plt
# Configure GPU memory growth to prevent allocation errors
gpus = tf.config.list_physical_devices('GPU')
if gpus:
try:
tf.config.experimental.set_memory_growth(gpus[0], True)
tf.config.set_visible_devices(gpus[0], 'GPU')
except RuntimeError as e:
print(e)
# Ignore unnecessary warnings
warnings.filterwarnings('ignore')
Data Pipeline Construction
The dataset is loaded from a specified directory and split into training and validation subsets. Image dimensions are standardized to 336x336 pixels with a batch size of 8.
DATA_ROOT = pathlib.Path("D:/BaiduNetdiskDownload/T6")
TARGET_SIZE = (336, 336)
BATCH_SIZE = 8
# Create training dataset
train_ds = tf.keras.utils.image_dataset_from_directory(
DATA_ROOT,
validation_split=0.2,
subset="training",
seed=42,
image_size=TARGET_SIZE,
batch_size=BATCH_SIZE
)
# Create validation dataset
val_ds = tf.keras.utils.image_dataset_from_directory(
DATA_ROOT,
validation_split=0.2,
subset="validation",
seed=42,
image_size=TARGET_SIZE,
batch_size=BATCH_SIZE
)
class_names = train_ds.class_names
Data Optimization and Normalization
Performance is enhanced by caching, shuffling, and prefetching the datasets. A normalization function is applied to scale pixel values to the range [0, 1].
AUTOTUNE = tf.data.AUTOTUNE
def normalize_img(image, label):
return image / 255.0, label
def optimize_pipeline(dataset):
return (dataset
.cache()
.shuffle(1000)
.map(normalize_img, num_parallel_calls=AUTOTUNE)
.prefetch(buffer_size=AUTOTUNE))
train_ds = optimize_pipeline(train_ds)
val_ds = optimize_pipeline(val_ds)
Sample Data Visualization
A grid of sample images is generated to verify data integrity and label correspondence before training begins.
plt.figure(figsize=(12, 10))
images, labels = next(iter(train_ds))
for i in range(16):
ax = plt.subplot(4, 4, i + 1)
plt.imshow(images[i])
plt.title(class_names[labels[i]])
plt.axis("off")
plt.show()
Model Architecture Definition
A transfer learning approach is employed using VGG16 as the feature extractor. The top layers are replaced with a custom classifier containing a Dense layer, Batch Normalization, and Dropout. A function dynamically creates two instances of this model: one utilizing the Adam optimizer and the other using SGD.
from tensorflow.keras.layers import Dense, Dropout, BatchNormalization
from tensorflow.keras.models import Model
def build_classifier(optimizer):
# Load pre-trained VGG16 base
base_model = tf.keras.applications.VGG16(
weights='imagenet',
include_top=False,
input_shape=(*TARGET_SIZE, 3),
pooling='avg'
)
# Freeze base model layers
base_model.trainable = False
# Construct custom head
x = base_model.output
x = Dense(170, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
outputs = Dense(len(class_names), activation='softmax')(x)
# Assemble full model
model = Model(inputs=base_model.input, outputs=outputs)
model.compile(
optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
return model
# Initialize models with different optimizers
adam_model = build_classifier(tf.keras.optimizers.Adam())
sgd_model = build_classifier(tf.keras.optimizers.SGD())
sgd_model.summary()
Training Execution
Both models are trained for 50 epochs to compare convergence rates and final performance metrics on the validation set.
EPOCH_COUNT = 50
print("Starting training with Adam optimizer...")
history_adam = adam_model.fit(
train_ds,
epochs=EPOCH_COUNT,
validation_data=val_ds,
verbose=1
)
print("Starting training with SGD optimizer...")
history_sgd = sgd_model.fit(
train_ds,
epochs=EPOCH_COUNT,
validation_data=val_ds,
verbose=1
)
Performance Benchmarking
The training histories of both models are visualized to compare loss and accuracy trajectories. This analysis highlights the impact of the optimization algorithm choice on model stability and convergence speed.
def plot_comparison(hist1, hist2, name1, name2):
acc1 = hist1.history['accuracy']
val_acc1 = hist1.history['val_accuracy']
loss1 = hist1.history['loss']
val_loss1 = hist1.history['val_loss']
acc2 = hist2.history['accuracy']
val_acc2 = hist2.history['val_accuracy']
loss2 = hist2.history['loss']
val_loss2 = hist2.history['val_loss']
epochs = range(len(acc1))
plt.figure(figsize=(16, 6))
# Plot Accuracy
plt.subplot(1, 2, 1)
plt.plot(epochs, acc1, label=f'Train Acc ({name1})')
plt.plot(epochs, val_acc1, label=f'Val Acc ({name1})')
plt.plot(epochs, acc2, label=f'Train Acc ({name2})')
plt.plot(epochs, val_acc2, label=f'Val Acc ({name2})')
plt.title('Accuracy Metrics')
plt.legend(loc='lower right')
# Plot Loss
plt.subplot(1, 2, 2)
plt.plot(epochs, loss1, label=f'Train Loss ({name1})')
plt.plot(epochs, val_loss1, label=f'Val Loss ({name1})')
plt.plot(epochs, loss2, label=f'Train Loss ({name2})')
plt.plot(epochs, val_loss2, label=f'Val Loss ({name2})')
plt.title('Loss Metrics')
plt.legend(loc='upper right')
plt.show()
plot_comparison(history_adam, history_sgd, 'Adam', 'SGD')
# Final Evaluation
def evaluate_model(model, model_name):
loss, acc = model.evaluate(val_ds, verbose=0)
print(f"{model_name} - Loss: {loss:.4f}, Accuracy: {acc:.4f}")
evaluate_model(adam_model, "Adam Optimizer")
evaluate_model(sgd_model, "SGD Optimizer")