|
|
|
|
|
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import matplotlib.pyplot as plt
|
|
import seaborn as sns
|
|
import os
|
|
import cv2
|
|
import tensorflow as tf
|
|
from sklearn.model_selection import train_test_split
|
|
from keras.preprocessing.image import ImageDataGenerator
|
|
from keras.utils import to_categorical
|
|
from keras.applications import MobileNetV2
|
|
from keras.layers import Dense, GlobalAveragePooling2D, Dropout
|
|
from keras.models import Model, load_model
|
|
from keras.preprocessing import image
|
|
from keras.applications.mobilenet_v2 import preprocess_input
|
|
from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint
|
|
|
|
|
|
|
|
|
|
|
|
data_dir = 'dataset'
|
|
categories = os.listdir(data_dir)
|
|
|
|
data = []
|
|
for category in categories:
|
|
category_path = os.path.join(data_dir, category)
|
|
for img_name in os.listdir(category_path):
|
|
img_path = os.path.join(category_path, img_name)
|
|
data.append((img_path, category))
|
|
|
|
data = pd.DataFrame(data, columns=['Filepath', 'Label'])
|
|
|
|
print(f"Total samples: {len(data)}")
|
|
print(data.head())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plt.figure(figsize=(8,6))
|
|
sns.countplot(x='Label', data=data)
|
|
plt.title('Blood Group Class Distribution')
|
|
plt.xticks(rotation=45)
|
|
plt.show()
|
|
|
|
|
|
plt.figure(figsize=(12,8))
|
|
for i in range(9):
|
|
sample = data.sample(n=1).iloc[0]
|
|
img = cv2.imread(sample['Filepath'])
|
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
plt.subplot(3,3,i+1)
|
|
plt.imshow(img)
|
|
plt.title(sample['Label'])
|
|
plt.axis('off')
|
|
plt.tight_layout()
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
train, temp = train_test_split(data, test_size=0.3, random_state=42, stratify=data['Label'])
|
|
valid, test = train_test_split(temp, test_size=0.5, random_state=42, stratify=temp['Label'])
|
|
|
|
print(f"Training samples: {len(train)}")
|
|
print(f"Validation samples: {len(valid)}")
|
|
print(f"Testing samples: {len(test)}")
|
|
|
|
|
|
|
|
|
|
|
|
train_datagen = ImageDataGenerator(
|
|
preprocessing_function=preprocess_input,
|
|
rotation_range=20,
|
|
zoom_range=0.2,
|
|
width_shift_range=0.2,
|
|
height_shift_range=0.2,
|
|
shear_range=0.2,
|
|
horizontal_flip=True
|
|
)
|
|
|
|
valid_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
|
|
|
|
target_size = (224, 224)
|
|
|
|
train_gen = train_datagen.flow_from_dataframe(
|
|
dataframe=train,
|
|
x_col='Filepath',
|
|
y_col='Label',
|
|
target_size=target_size,
|
|
class_mode='categorical',
|
|
batch_size=32,
|
|
shuffle=True,
|
|
seed=42
|
|
)
|
|
|
|
valid_gen = valid_datagen.flow_from_dataframe(
|
|
dataframe=valid,
|
|
x_col='Filepath',
|
|
y_col='Label',
|
|
target_size=target_size,
|
|
class_mode='categorical',
|
|
batch_size=32,
|
|
shuffle=False
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
base_model = MobileNetV2(weights='imagenet', include_top=False, input_shape=(224,224,3))
|
|
|
|
|
|
|
|
|
|
|
|
for layer in base_model.layers:
|
|
layer.trainable = False
|
|
|
|
|
|
|
|
|
|
|
|
def build_model(dropout_rate=0.3, learning_rate=0.001):
|
|
x = base_model.output
|
|
x = GlobalAveragePooling2D()(x)
|
|
x = Dropout(dropout_rate)(x)
|
|
x = Dense(128, activation='relu')(x)
|
|
x = Dropout(dropout_rate)(x)
|
|
predictions = Dense(len(categories), activation='softmax')(x)
|
|
|
|
model = Model(inputs=base_model.input, outputs=predictions)
|
|
|
|
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)
|
|
model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
|
|
|
|
return model
|
|
|
|
|
|
|
|
|
|
|
|
dropout_rates = [0.3, 0.4]
|
|
learning_rates = [0.001, 0.0005]
|
|
|
|
best_val_accuracy = 0
|
|
best_model = None
|
|
best_params = {}
|
|
|
|
for dr in dropout_rates:
|
|
for lr in learning_rates:
|
|
print(f"\nTraining with Dropout: {dr}, Learning Rate: {lr}")
|
|
|
|
model = build_model(dropout_rate=dr, learning_rate=lr)
|
|
|
|
callbacks = [
|
|
EarlyStopping(monitor='val_accuracy', patience=5, restore_best_weights=True),
|
|
ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1),
|
|
ModelCheckpoint('best_model.h5', monitor='val_accuracy', save_best_only=True, verbose=1)
|
|
]
|
|
|
|
history = model.fit(
|
|
train_gen,
|
|
validation_data=valid_gen,
|
|
epochs=20,
|
|
callbacks=callbacks,
|
|
verbose=1
|
|
)
|
|
|
|
val_acc = max(history.history['val_accuracy'])
|
|
print(f"Validation Accuracy: {val_acc:.4f}")
|
|
|
|
if val_acc > best_val_accuracy:
|
|
best_val_accuracy = val_acc
|
|
best_model = model
|
|
best_params = {'dropout_rate': dr, 'learning_rate': lr}
|
|
|
|
print("\nBest Parameters:", best_params)
|
|
print(f"Best Validation Accuracy: {best_val_accuracy:.4f}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for layer in base_model.layers[-30:]:
|
|
layer.trainable = True
|
|
|
|
|
|
optimizer = tf.keras.optimizers.Adam(learning_rate=best_params['learning_rate'] / 10)
|
|
best_model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
|
|
|
|
|
|
history_finetune = best_model.fit(
|
|
train_gen,
|
|
validation_data=valid_gen,
|
|
epochs=10,
|
|
callbacks=[
|
|
EarlyStopping(monitor='val_accuracy', patience=5, restore_best_weights=True),
|
|
ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1)
|
|
],
|
|
verbose=1
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
best_model.save('bloodgroup_mobilenet_finetuned.h5')
|
|
print("Fine-tuned model saved as bloodgroup_mobilenet_finetuned.h5")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plt.figure(figsize=(14,5))
|
|
|
|
plt.subplot(1,2,1)
|
|
plt.plot(history_finetune.history['accuracy'], label='Fine-tuned Train Accuracy')
|
|
plt.plot(history_finetune.history['val_accuracy'], label='Fine-tuned Validation Accuracy')
|
|
plt.title('Fine-tuned Model Accuracy')
|
|
plt.legend()
|
|
|
|
plt.subplot(1,2,2)
|
|
plt.plot(history_finetune.history['loss'], label='Fine-tuned Train Loss')
|
|
plt.plot(history_finetune.history['val_loss'], label='Fine-tuned Validation Loss')
|
|
plt.title('Fine-tuned Model Loss')
|
|
plt.legend()
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
model = load_model('bloodgroup_mobilenet_finetuned.h5')
|
|
|
|
|
|
labels = {'A+': 0, 'A-': 1, 'AB+': 2, 'AB-': 3, 'B+': 4, 'B-': 5, 'O+': 6, 'O-': 7}
|
|
labels = dict((v, k) for k, v in labels.items())
|
|
|
|
|
|
img_path = 'dataset/AB+/augmented_cluster_4_4.BMP'
|
|
|
|
img = image.load_img(img_path, target_size=(224, 224))
|
|
x = image.img_to_array(img)
|
|
x = np.expand_dims(x, axis=0)
|
|
x = preprocess_input(x)
|
|
|
|
result = model.predict(x)
|
|
predicted_class = np.argmax(result)
|
|
predicted_label = labels[predicted_class]
|
|
confidence = result[0][predicted_class] * 100
|
|
|
|
plt.imshow(image.array_to_img(image.img_to_array(img)/255.0))
|
|
plt.axis('off')
|
|
plt.title(f"Prediction: {predicted_label} with confidence {confidence:.2f}%")
|
|
plt.show()
|
|
|