?
1、環(huán)境設(shè)置:
此環(huán)節(jié)將加載實(shí)現(xiàn)筆記本無縫功能的基本模塊,包括NumPy、Pandas和TensorFlow等庫。此外,它還建立了關(guān)鍵的環(huán)境常數(shù),如圖像尺寸和學(xué)習(xí)率,這對后續(xù)分析和模型訓(xùn)練至關(guān)重要。
# General
import os
import keras
import numpy as np
import pandas as pd
import tensorflow as tf
# Data
import plotly.express as px
import matplotlib.pyplot as plt
# Data Preprocessing
import tensorflow.data as tfds
from sklearn.model_selection import train_test_split
# Model
from keras.applications import VGG16
from keras.applications import Xception, InceptionV3
from keras.applications import ResNet50V2, ResNet152V2
from keras.applications import MobileNetV3Small, MobileNetV3Large
# Model training
from keras import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten, GlobalAveragePooling2D
from keras.layers import InputLayer
# Model Callbacks
from keras.callbacks import EarlyStopping
from keras.callbacks import ModelCheckpoint
# Setting constants for reproducibility
np.random.seed(42)
tf.random.set_seed(42)
# Constants
BATCH_SIZE = 32
IMAGE_SIZE = 224
2、加載和處理UTKFace數(shù)據(jù)集
在Kaggle上發(fā)現(xiàn)的UTKFace數(shù)據(jù)集是一個全面的面部圖像集合,專門用于年齡和性別識別任務(wù)。它包括2萬多張人臉圖像,并附有年齡、性別和種族的注釋。這些圖像是多樣化的,代表了不同的種族、年齡和性別,使其成為機(jī)器學(xué)習(xí)和計(jì)算機(jī)視覺研究的廣泛而有價(jià)值的資源。
UTKFace數(shù)據(jù)集中的每張圖像都標(biāo)有人的年齡,范圍從0到116歲,以及他們的性別,分類為男性或女性。
此外,數(shù)據(jù)集還包括有關(guān)個人種族的信息,允許進(jìn)行更細(xì)致的分析和應(yīng)用。
研究人員和開發(fā)人員經(jīng)常利用該數(shù)據(jù)集來訓(xùn)練和測試面部識別技術(shù)中年齡估計(jì)、性別分類和其他相關(guān)任務(wù)的算法。它的大尺寸和多樣化的面部特征表示使其成為探索和開發(fā)計(jì)算機(jī)視覺領(lǐng)域模型的熱門選擇。
# Initialize the directory path
dir_path = "/kaggle/input/utkface-new/UTKFace/"
image_paths = os.listdir(dir_path)
# Initialize a Gender Mapping
gender_mapping = ["Male", "Female"]
# Choose and load an image randomly
rand_image_path = np.random.choice(image_paths)
rand_image = plt.imread(dir_path + rand_image_path)/255.
sample_age, sample_gender, *_ = rand_image_path.split("_")
print(f"Total number of images : {len(image_paths)}")
print(f"Sample Image path : {rand_image_path}")
print(f"Sample Age : {sample_age}")
print(f"Sample Gender : {gender_mapping[int(sample_gender)]}\n")
# Show the image
plt.figure(figsize = (5,5))
plt.title("Sample Image")
plt.imshow(rand_image)
plt.axis("off")
plt.show()
結(jié)果:
Total number of images : 23708
Sample Image path : 1_0_3_20161220222642427.jpg.chip.jpg
Sample Age : 1
Sample Gender : Male
處理23,708張圖像需要使用諸如批處理或使用圖像生成器之類的內(nèi)存高效策略來避免壓倒性的內(nèi)存限制。此外,描繪年齡和性別的圖像路徑結(jié)構(gòu)(第一部分表示年齡,第二部分表示性別(0表示男性,1表示女性))為后續(xù)分析和分類提供了重要信息。
仔細(xì)管理這些路徑將有助于根據(jù)年齡和性別屬性進(jìn)行有針對性的數(shù)據(jù)處理。
# Initialize a male counter variable
male_count = 0
# Initialize variable to store all the ages.
ages = []
# Loop over the paths and check for male images.
for path in image_paths:
path_split = path.split("_")
if "0" == path_split[1]:
male_count += 1
ages.append(int(path_split[0]))
# Computee total female counts
female_count = len(image_paths) - male_count
# Visualizing The Class Imbalance
pie_chart = px.pie(
names = gender_mapping,
values = [male_count, female_count],
hole = 0.4,
title = "Gender Distribution (Donut Chart)",
height = 500
)
pie_chart.show()
bar_graph = px.bar(
y = gender_mapping,
x = [male_count, female_count],
title = "Gender Distribution (Bar Graph)",
color = gender_mapping,
height = 500
)
bar_graph.update_layout(
yaxis_title = "Gender",
xaxis_title = "Frequency Count"
)
bar_graph.show()


??????
我們的數(shù)據(jù)集似乎顯示出輕微的類別不平衡,男性圖像的數(shù)量高于女性圖像——大約52%的男性和48%的女性代表。雖然這種不平衡不足以顯著影響模型的準(zhǔn)確性,但值得注意的是,要全面了解數(shù)據(jù)集對男性圖像的偏見。管理這種輕微的偏差可以提高模型的穩(wěn)健性和預(yù)測的公平性,盡管它目前對準(zhǔn)確性的影響可能有限。
# Histogram
fig = px.histogram(sorted(ages), title = "Age Distribution")
fig.update_layout(
xaxis_title = "Age",
yaxis_title = "Value Counts"
)
fig.show()
# Violin Plot
fig = px.violin(x = sorted(ages), title = "Age Distribution")
fig.update_layout(
xaxis_title = "Age",
yaxis_title = "Distribution"
)
fig.show()
# Box Plot
fig = px.box(x = sorted(ages), notched=True, title = "Age Distribution")
fig.update_layout(
xaxis_title = "Age",
)
fig.show()


直方圖分析在我們的數(shù)據(jù)集分布中顯示了兩個突出的峰值。第一個簇包含年齡從0到12歲左右的人,而第二個簇包含16到45歲左右的人。
有趣的是,這些峰值表現(xiàn)出不同的強(qiáng)度,后一個峰值明顯更強(qiáng)。具體來說,我們在1歲左右觀察到大約1123張圖像的峰值,而在26歲時(shí)觀察到大約2197張圖像的峰值。
值得注意的是,雖然我們的數(shù)據(jù)集確實(shí)包含了一些80歲以上的老年人的圖像,但這些事件構(gòu)成了較小的峰值。總的來說,這種分布表明了對年輕群體的潛在偏見,強(qiáng)調(diào)了數(shù)據(jù)集強(qiáng)調(diào)的是描繪早期和成年期個體的圖像,而不是老年。
從小提琴圖和箱形圖中獲得的見解證實(shí)了我們從直方圖中得出的初步觀察結(jié)果。數(shù)據(jù)明顯分為兩個子集:一個是非常小的孩子的圖像,證實(shí)了直方圖在1歲左右的峰值,另一個是26到45歲之間的成年人的大量集中。小提琴的情節(jié)特別強(qiáng)調(diào)這種雙峰分布。
在仔細(xì)檢查箱形圖后,向左側(cè)的明顯擠壓與我們在直方圖中觀察到的相呼應(yīng)。這種壓縮源于較年長年齡范圍內(nèi)的數(shù)值分布拉長和相對稀疏。它重申了我們的理解,即數(shù)據(jù)集顯示出對年輕人的輕微傾斜。
這種固有的偏見可能確實(shí)會影響我們模型的準(zhǔn)確性,因?yàn)樗鼉A向于更有效地從大量的年輕人口統(tǒng)計(jì)數(shù)據(jù)中學(xué)習(xí)和概括,而可能忽略了老年群體中普遍存在的模式或細(xì)微差別。解決這一問題對于確保我們的模式在不同年齡段的公平和全面的學(xué)習(xí)至關(guān)重要。
# SHuffling the Images
np.random.shuffle(image_paths)
# Split data into training, testing and validation set
train_images, test_images = train_test_split(
image_paths,
train_size = 0.9,
test_size = 0.1
)
train_images, valid_images = train_test_split(
image_paths,
train_size = 0.9,
test_size = 0.1
)
print(f"Training Size : {len(train_images)}")
print(f"Testing Size : {len(test_images)}")
# Extract age and gender
train_ages = [int(path.split("_")[0]) for path in train_images]
train_genders = [int(path.split("_")[1]) for path in train_images]
valid_ages = [int(path.split("_")[0]) for path in valid_images]
valid_genders = [int(path.split("_")[1]) for path in valid_images]
test_ages = [int(path.split("_")[0]) for path in test_images]
test_genders = [int(path.split("_")[1]) for path in test_images]
Training Size : 21337
Testing Size : 2371
由于我們有大量的圖像,因此使用Tensorflow數(shù)據(jù)集進(jìn)行高效處理會更好。
def show_image(image, show=False):
"""
Displays the provided image without axis.
Args:
- image (array-like): The image data to be displayed.
- show (bool): If True, displays the image immediately. Defaults to False.
Returns:
- None
"""
plt.imshow(image)
plt.axis("off")
if show:
plt.show()
def preprocess_age_data(image_path, age, gender, dir_path=dir_path, IMAGE_SIZE = IMAGE_SIZE):
"""
Preprocesses an image for analysis by extracting age and gender from the image path,
loading and decoding the image, resizing it to (IMAGE_SIZE,IMAGE_SIZE), normalizing pixel values,
and returning the preprocessed image along with age and gender labels.
Args:
- image_path (str): The path to the image file.
- dir_path (str): The directory path where the image is located. Defaults to `dir_path`.
Returns:
- tuple: A tuple containing the preprocessed image as a TensorFlow tensor,
the age (int), and the gender (int) extracted from the image path.
"""
# Load the Image
image = tf.io.read_file(dir_path + image_path)
image = tf.io.decode_jpeg(image)
# Resize and Normalize the Image
image = tf.image.resize(image, (IMAGE_SIZE, IMAGE_SIZE))
image = image / 255.
image = tf.cast(image, tf.float32)
return image, age
def preprocess_gender_data(image_path, age, gender, dir_path=dir_path, IMAGE_SIZE = IMAGE_SIZE):
"""
Preprocesses an image for analysis by extracting age and gender from the image path,
loading and decoding the image, resizing it to (IMAGE_SIZE,IMAGE_SIZE), normalizing pixel values,
and returning the preprocessed image along with age and gender labels.
Args:
- image_path (str): The path to the image file.
- dir_path (str): The directory path where the image is located. Defaults to `dir_path`.
Returns:
- tuple: A tuple containing the preprocessed image as a TensorFlow tensor,
the age (int), and the gender (int) extracted from the image path.
"""
# Load the Image
image = tf.io.read_file(dir_path + image_path)
image = tf.io.decode_jpeg(image)
# Resize and Normalize the Image
image = tf.image.resize(image, (IMAGE_SIZE, IMAGE_SIZE))
image = image / 255.
image = tf.cast(image, tf.float32)
return image, gender
# Obtain training, testing and validation datasets
train_ds = tfds.Dataset.from_tensor_slices((train_images, train_ages, train_genders)).shuffle(2000)
train_age_ds = train_ds.map(preprocess_age_data, num_parallel_calls=BATCH_SIZE).batch(BATCH_SIZE).prefetch(tfds.AUTOTUNE)
train_gender_ds = train_ds.map(preprocess_gender_data, num_parallel_calls=BATCH_SIZE).batch(BATCH_SIZE).prefetch(tfds.AUTOTUNE)
valid_ds = tfds.Dataset.from_tensor_slices((valid_images, valid_ages, valid_genders)).shuffle(2000)
valid_age_ds = valid_ds.map(preprocess_age_data, num_parallel_calls=BATCH_SIZE).batch(BATCH_SIZE).prefetch(tfds.AUTOTUNE)
valid_gender_ds = valid_ds.map(preprocess_gender_data, num_parallel_calls=BATCH_SIZE).batch(BATCH_SIZE).prefetch(tfds.AUTOTUNE)
test_ds = tfds.Dataset.from_tensor_slices((test_images, test_ages, test_genders)).shuffle(500)
test_age_ds = test_ds.map(preprocess_age_data, num_parallel_calls=BATCH_SIZE).batch(BATCH_SIZE).prefetch(tfds.AUTOTUNE)
test_gender_ds = test_ds.map(preprocess_gender_data, num_parallel_calls=BATCH_SIZE).batch(BATCH_SIZE).prefetch(tfds.AUTOTUNE)
plt.figure(figsize=(15, 10))
for images, ages, genders in train_ds.batch(BATCH_SIZE).take(1):
for index in range(len(images)):
image = tf.io.read_file(dir_path + images[index])
image = tf.io.decode_jpeg(image)
plt.subplot(4, 8, index + 1)
plt.imshow(image)
plt.title(f"Age: {ages[index]}\nGender: {gender_mapping[genders[index]]}")
plt.axis("off")
plt.tight_layout()
plt.show()

3、骨干架構(gòu)比較
當(dāng)我們的目標(biāo)是為最終模型建立一個最優(yōu)的架構(gòu)時(shí),我們正在探索各種候選模型來作為基礎(chǔ)架構(gòu)。這個過程包括評估多個主干,以全面衡量它們的性能。
通過進(jìn)行比較分析,我們試圖辨別每個骨干架構(gòu)的優(yōu)點(diǎn)和缺點(diǎn)。我們的目標(biāo)是確定最合適的骨干體系結(jié)構(gòu),它將作為我們最終模型的健壯基礎(chǔ)。隨后,將采用微調(diào)對選定的骨干進(jìn)行細(xì)化和定制,確保開發(fā)出優(yōu)化有效的模型。
# Initializing all the Backbones
backbones = [
(
"VGG16",
VGG16(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
(
"ResNet50V2",
ResNet50V2(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
(
"ResNet152V2",
ResNet152V2(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
(
"Xception",
Xception(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
(
"InceptionV3",
InceptionV3(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
(
"MobileNetV3Small",
MobileNetV3Small(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
(
"MobileNetV3Large",
MobileNetV3Large(
input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
weights = "imagenet",
include_top = False
)
),
為了找到最好的骨干架構(gòu),我們首先需要訓(xùn)練一個相當(dāng)小的網(wǎng)絡(luò)進(jìn)行幾次迭代,然后我們將看到哪個骨干架構(gòu)將更快更好地帶來魯棒性能。
請注意
在進(jìn)行了幾次實(shí)驗(yàn)之后,很明顯,為年齡和性別預(yù)測建立一個單一的模型并不能產(chǎn)生最佳結(jié)果。盡管在預(yù)測年齡方面取得了顯著的成績,但性別預(yù)測的準(zhǔn)確性并不理想。因此,更有效的方法是采用兩種不同的模型來預(yù)測年齡和性別。
在這個修改后的策略中,模型的主干保持不變。然而,重點(diǎn)是在年齡預(yù)測的基礎(chǔ)上確定表現(xiàn)最好的骨干。由于準(zhǔn)確的年齡預(yù)測涉及到回歸,這是一個更復(fù)雜的任務(wù),因此預(yù)期在年齡預(yù)測中表現(xiàn)出色的骨干在性別預(yù)測中也會表現(xiàn)良好。
通過采用不同的年齡和性別預(yù)測模型,我們可以根據(jù)每個任務(wù)提出的獨(dú)特挑戰(zhàn)來定制架構(gòu)和訓(xùn)練策略。這種專門的方法允許對每個模型進(jìn)行獨(dú)立的優(yōu)化,從而提高整體性能。這種任務(wù)的解耦承認(rèn)了年齡和性別預(yù)測的微妙本質(zhì),并允許對模型進(jìn)行微調(diào),以解決與每種預(yù)測類型相關(guān)的具體挑戰(zhàn)。
BACKBONE_HISTORIES = {}
for (name, backbone) in backbones:
print(f"Testing : {name}")
# Freeze the Model weights
backbone.trainable = False
# Creating a base model
model = keras.Sequential([
InputLayer((IMAGE_SIZE, IMAGE_SIZE, 3), name = "InputLayer"),
backbone,
Dropout(0.2, name = "SlightDropout"),
Flatten(name = "FlattenEmbeddings"),
Dense(1, name = "Age")
])
# Train the model for few iterations
model.compile(
loss = ["mae"],
optimizer = "adam",
weighted_metrics=[]
)
history = model.fit(
train_age_ds,
validation_data = valid_age_ds,
epochs = 5,
batch_size = BATCH_SIZE
)
BACKBONE_HISTORIES[name] = pd.DataFrame(history.history)
cls()
print("\n")
metric = "loss"
plt.figure(figsize=(15, 5))
for i, sub in enumerate(['Train', 'Val']):
plt.subplot(1, 2, i+1)
plt.title(f"{sub} {metric} Plot")
for name, history in BACKBONE_HISTORIES.items():
plt.plot(history[metric] if sub=="Train" else history[f"val_{metric}"], label = name)
plt.xlabel("Epochs")
plt.ylabel(metric.title())
plt.legend()
plt.grid()
plt.tight_layout()
plt.show()

在仔細(xì)檢查年齡模型學(xué)習(xí)曲線后,可以明確排除某些骨干,特別是與MobileNet相關(guān)的骨干。他們一貫的糟糕表現(xiàn)使他們不適合做候選人。
VGG16是最有希望的主干,在訓(xùn)練和驗(yàn)證損失方面都表現(xiàn)出穩(wěn)定的下降,收斂在平均絕對誤差8.0左右。這種一致性表明了魯棒性,使VGG16成為一個有利的選擇。
雖然其他一些模型,如Inception和exception,產(chǎn)生較低的訓(xùn)練損失,但它們在訓(xùn)練和驗(yàn)證曲線之間的相當(dāng)大的差距表明潛在的不穩(wěn)定性。考慮到速度和健壯性之間的權(quán)衡,盜夢空間作為一個值得稱贊的選擇脫穎而出。
其平滑的增長軌跡和相對的可預(yù)測性使其成為一個合適的折衷方案,特別是與異常的更不穩(wěn)定的性能相比時(shí)。這種細(xì)致入微的評估指導(dǎo)了選擇過程,確保選擇的主干與所需的模型特征保持一致。
BACKBONE_HISTORIES = {}
for (name, backbone) in backbones:
print(f"Testing : {name}")
# Freeze the Model weights
backbone.trainable = False
# Creating a base model
model = keras.Sequential([
InputLayer((IMAGE_SIZE, IMAGE_SIZE, 3), name = "InputLayer"),
backbone,
Dropout(0.2, name = "SlightDropout"),
Flatten(name = "FlattenEmbeddings"),
Dense(1, activation="sigmoid", name = "Age")
])
# Train the model for few iterations
model.compile(
loss = ["binary_crossentropy"],
optimizer = "adam",
metrics = ['accuracy'],
weighted_metrics=[]
)
history = model.fit(
train_gender_ds,
validation_data = valid_gender_ds,
epochs = 5,
batch_size = BATCH_SIZE
)
BACKBONE_HISTORIES[name] = pd.DataFrame(history.history)
cls()
print("\n")
metric = "accuracy"
plt.figure(figsize=(15, 5))
for i, sub in enumerate(['Train', 'Val']):
plt.subplot(1, 2, i+1)
plt.title(f"{sub} {metric} Plot")
for name, history in BACKBONE_HISTORIES.items():
plt.plot(history[metric] if sub=="Train" else history[f"val_{metric}"], label = name)
plt.xlabel("Epochs")
plt.ylabel(metric.title())
plt.legend()
plt.grid()
plt.tight_layout()
plt.show()

在評估性別預(yù)測準(zhǔn)確性的模型時(shí),一個值得注意的消除包括MobileNet模型,它一直表現(xiàn)出較差的性能。
深入研究訓(xùn)練和驗(yàn)證精度曲線揭示了一個杰出的表演者:ResNet152V2模型,由獨(dú)特的綠色曲線表示。該模型達(dá)到了最高的訓(xùn)練精度和驗(yàn)證精度,將其定位為一個引人注目的選擇。
然而,必須解決所有模型的訓(xùn)練和驗(yàn)證精度之間的巨大差異。這強(qiáng)調(diào)了在最終模型創(chuàng)建期間需要更復(fù)雜的基線模型,合并多個層以更好地處理這些差異。
盡管考慮到這一點(diǎn),ResNet152V2仍然保持著突出的地位,擁有出色的訓(xùn)練和驗(yàn)證性能。值得注意的是,ResNet50V2在訓(xùn)練性能上與ResNet152V2非常接近,盡管在驗(yàn)證精度上有所欠缺。
4、VGG16 Age Network??
經(jīng)過深入分析,VGG16網(wǎng)絡(luò)因其在最小化訓(xùn)練和驗(yàn)證損失方面的一致性能而成為最佳骨干網(wǎng)。這導(dǎo)致決定使用VGG16作為年齡模型的共享主干。
年齡模型側(cè)重于準(zhǔn)確預(yù)測年齡,性別模型側(cè)重于精確預(yù)測性別。結(jié)合這兩個模型,我們創(chuàng)建了一個統(tǒng)一的網(wǎng)絡(luò),用于同時(shí)預(yù)測年齡和性別,利用VGG16的優(yōu)勢進(jìn)行有效的面部屬性檢測。
# Loading backbone
vgg_16 = VGG16(input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3), include_top = False, weights = "imagenet")
# Freezing the backbone weights
vgg_16.trainable = False
# Creating a Age Network
age_net = Sequential([
InputLayer((IMAGE_SIZE, IMAGE_SIZE, 3), name="ImageInput"),
vgg_16,
Dropout(0.4, name = "SlightDroput"),
Flatten(name="FlattenEmbeddings"),
Dense(256, activation="relu", kernel_initializer="he_normal"),
Dense(1, name="AgeOutput")
], name="AgeNet")
# Compiling Model
age_net.compile(
loss = "mae",
optimizer = 'adam',
weighted_metrics=[]
)
# Trining the Age Model
age_history = age_net.fit(
train_age_ds,
validation_data = valid_age_ds,
epochs = 20,
batch_size = BATCH_SIZE,
callbacks = [
EarlyStopping(
patience = 5,
monitor = "val_loss",
restore_best_weights = True
),
ModelCheckpoint(
"Age-VGG16.keras",
save_best_only=True
)
]
)
# Converting history into data frame.
age_his_df = pd.DataFrame(age_history.history)
# Visualization
age_his_df.plot()
plt.title("Age Network Learning Curve")
plt.xlabel("Epochs")
plt.ylabel("Mean Abs. Error")
plt.grid()
plt.show()

值得注意的是,我們的年齡預(yù)測模型的性能超過了基線模型。該模型的平均絕對誤差,最初徘徊在8左右,已經(jīng)成功地減少到大約6.7,這表明了實(shí)質(zhì)性的進(jìn)展。
雖然可以通過參數(shù)調(diào)優(yōu)和細(xì)致的架構(gòu)調(diào)整來實(shí)現(xiàn)進(jìn)一步的改進(jìn),但在我們的方法范圍內(nèi),當(dāng)前的損失代表了值得贊揚(yáng)的結(jié)果。
此外,早期停止的結(jié)合證明有利于防止過擬合。在第12和第13個時(shí)代左右,訓(xùn)練損失和驗(yàn)證損失之間出現(xiàn)分歧的跡象,表明可能存在過擬合。
早期停止機(jī)制,加上模型檢查點(diǎn),通過恢復(fù)到性能最好的模型版本,有效地減輕了這種擔(dān)憂,這明顯發(fā)生在具有最小差值的第7 epoch附近。
5、ResNet152V2 性別網(wǎng)絡(luò)
# Initializing the backbone layer
resnet = ResNet152V2(input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3), include_top = False, weights="imagenet")
# Freeze the backbone weights
resnet.trainable = False
# Creating a gender model
gender_net = Sequential([
InputLayer((IMAGE_SIZE, IMAGE_SIZE, 3), name="ImageInput"),
resnet,
Dropout(0.2, name = "SlightDroput"),
GlobalAveragePooling2D(name="GlobalAvgPooling"),
Dense(1, activation="sigmoid", name="gender")
], name="GenderNet")
gender_net.compile(
loss = "binary_crossentropy",
optimizer = 'adam',
metrics = ['accuracy'],
weighted_metrics=[]
)
# Training the Gender Model
gender_history = gender_net.fit(
train_gender_ds,
validation_data = valid_gender_ds,
epochs = 20,
batch_size = BATCH_SIZE,
callbacks = [
EarlyStopping(
patience = 5,
monitor = "val_accuracy",
restore_best_weights = True
),
ModelCheckpoint(
"Gender-ResNet152.keras",
save_best_only=True
)
]
)
# Converting history into data frame.
gender_his_df = pd.DataFrame(gender_history.history)
# Visualization
plt.figure(figsize=(10, 5 ))
plt.subplot(1,2,1)
plt.suptitle("Gender Network Learning Curve")
plt.plot(gender_his_df['loss'], label="Loss")
plt.plot(gender_his_df['val_loss'], label="Val. Loss")
plt.xlabel("Epochs")
plt.ylabel("$Loss$")
plt.grid()
plt.legend()
plt.subplot(1,2,2)
plt.plot(gender_his_df['accuracy'], label="Accuracy")
plt.plot(gender_his_df['val_accuracy'], label="Val. Accuracy")
plt.xlabel("Epochs")
plt.ylabel("$Accuracy$")
plt.grid()
plt.legend()
plt.show()

已對性別網(wǎng)絡(luò)的結(jié)構(gòu)作出重大調(diào)整,以增強(qiáng)其穩(wěn)健性。通過去除flatten層和Dense層,加入GlobalAveragePooling層,解決了模型的過擬合傾向。
初始配置顯示超過90%的訓(xùn)練精度,但驗(yàn)證精度明顯下降到85%左右,表明易受過擬合的影響。
修正后的模型雖然精度略低,但成功地保持了高度的魯棒性。損失差異很小,驗(yàn)證損失約為0.30,訓(xùn)練損失約為0.26。這種邊際差異反映在準(zhǔn)確性上,其中訓(xùn)練準(zhǔn)確性為88%,驗(yàn)證準(zhǔn)確性為87%,僅產(chǎn)生1%的差異-就模型穩(wěn)定性而言,這是一個值得稱贊的成就。
6、模型評價(jià)
在這個關(guān)鍵時(shí)刻,我們完成了核心部分--模型訓(xùn)練和數(shù)據(jù)處理,達(dá)到了一個關(guān)鍵的里程碑。下一個關(guān)鍵步驟是評估模型在測試數(shù)據(jù)集上的性能。通過評估,我們可以深入了解模型在現(xiàn)實(shí)世界中的預(yù)期表現(xiàn)。測試階段的結(jié)果是評估模型在未見數(shù)據(jù)上的泛化和預(yù)測能力的重要基準(zhǔn)。
# Loading saved models
age_net = tf.keras.models.load_model("/kaggle/input/age-and-gender-prediction-vgg16-resnet152v2/Weights/Age-VGG16.keras", compile=True)
gender_net = tf.keras.models.load_model("/kaggle/input/age-and-gender-prediction-vgg16-resnet152v2/Weights/Gender-ResNet152.keras", compile=True)
# Evaluating test performance
age_net.evaluate(test_age_ds)
gender_net.evaluate(test_gender_ds)
在測試數(shù)據(jù)上對我們的最終模型進(jìn)行的結(jié)論性評估顯示,與我們只關(guān)注主干比較的初步版本相比,我們的性能有了顯著提高。這表明我們在改進(jìn)和優(yōu)化模型方面的成功努力,導(dǎo)致整體性能的大幅提高。精細(xì)調(diào)整和參數(shù)調(diào)整的反復(fù)過程顯然取得了成效,表明該模型在預(yù)測年齡和性別方面的有效性和可靠性有所提高。
7、模型預(yù)測
可視化模型在測試樣本中的表現(xiàn)為我們的評估增添了一個引人入勝、富有洞察力的維度。通過圖形表示,我們可以更直觀地了解模型在不同實(shí)例中的表現(xiàn)。通過這種可視化的探索,我們可以發(fā)現(xiàn)一些模式、趨勢或潛在的改進(jìn)領(lǐng)域,而這些在數(shù)字指標(biāo)中可能并不明顯。讓我們深入研究可視化表示,揭示模型在測試數(shù)據(jù)上表現(xiàn)的細(xì)微差別。
plt.figure(figsize=(15, 10))
for images, ages, genders in test_ds.batch(BATCH_SIZE).take(1):
for index in range(len(images)):
# Image Processing
image = tf.io.read_file(dir_path + images[index])
image = tf.io.decode_jpeg(image)
image = tf.cast(image, tf.float32)
image = image/255.
image = tf.image.resize(image, [224,224])
image = tf.reshape(image, [-1,224,224,3])
# Predictions
pred_age = tf.round(tf.squeeze(age_net.predict(image, verbose=0)), 2)
pred_gender = tf.round(tf.squeeze(gender_net.predict(image, verbose=0)))
# Visualization
plt.subplot(4, 8, index + 1)
plt.imshow(image[0])
plt.title(f"Age: {int(pred_age)}\nGender: {gender_mapping[int(pred_gender)]}")
plt.axis("off")
plt.tight_layout()
plt.show()

觀察模型的預(yù)測,我對它的整體表現(xiàn)有很高的信心。不考慮嬰兒圖像,該模型在準(zhǔn)確識別性別和為大多數(shù)個體提供精確的年齡估計(jì)方面始終表現(xiàn)出色。這一成就值得注意,反映了模型對年齡和性別預(yù)測相關(guān)任務(wù)的熟練程度。
然而,數(shù)據(jù)集中嬰兒圖像的存在帶來了一定程度的懷疑和不安。對我來說,預(yù)測嬰兒的年齡和性別的概念有點(diǎn)異想天開,類似于小說中的概念。雖然該模型在這一領(lǐng)域取得了一定程度的成功,但我對這種預(yù)測的合理性仍持謹(jǐn)慎態(tài)度。
?
可視化模型在測試樣本中的表現(xiàn)為我們的評估增添了一個引人入勝、富有洞察力的維度。通過圖形表示,我們可以更直觀地了解模型在不同實(shí)例中的表現(xiàn)。通過這種可視化的探索,我們可以發(fā)現(xiàn)一些模式、趨勢或潛在的改進(jìn)領(lǐng)域,而這些在數(shù)字指標(biāo)中可能并不明顯。讓我們深入研究可視化表示,揭示模型在測試數(shù)據(jù)上表現(xiàn)的細(xì)微差別。
浙公網(wǎng)安備 33010602011771號