<jupyter_start><jupyter_text>**Age Estimation**  1) **Load UTKFace dataset**<jupyter_code>!gdown --id 1Y8EOFLIRCcKpe_e0pO03yCAosTRjRMtC
!unzip -q /content/UTKFace.zip -d data
# To download checkpoints, Keras models, TFLite models
from google.colab import files
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import os
import datetime<jupyter_output><empty_output><jupyter_text>2) **Processing the data*** Đọc tệp hình ảnh dưới dạng mảng 3 chiều NumPy. Lưu ý, chúng tôi sẽ sử dụng hình ảnh RGB, vì vậy mỗi mảng sẽ có hình dạng `[ img_width , img_height , 3 ]`.* Tách tên tệp để phân tích độ tuổi của người trong hình ảnh tương ứng. Chúng tôi sử dụng `tf.strings.split()`.* Độ tuổi cao nhất trong dataset là 116, nên dùng tuổi/116 để chuẩn hóaKhi các thao tác này đã được thực hiện, chúng ta nhận được các mẫu trong đó mỗi mẫu bao gồm mảng hình ảnh `[ 200 , 200 , 3 ]` và label tương ứng, tuổi của người đó`[ 1 , ]`Dùng `tf.data.Dataset` để xử lý nhanh hơn(tính toán song song).  `tf.data.Dataset.map` để map 2 cái ở trên lại.<jupyter_code>n = len(os.listdir('/content/data/UTKFace'))
n
MODEL_INPUT_IMAGE_SIZE = [ 200 , 200 ]
TRAIN_TEST_SPLIT = 0.3

# This method will be mapped for each filename in `list_ds`. 
def parse_image( filename ):

    # Read the image from the filename and resize it.
    image_raw = tf.io.read_file( filename )
    image = tf.image.decode_jpeg( image_raw , channels=3 ) 
    image = tf.image.resize( image , MODEL_INPUT_IMAGE_SIZE ) / 255

    # Split the filename to get the age and the gender. Convert the age ( str ) and the gender ( str ) to dtype float32.
    parts = tf.strings.split( tf.strings.split( filename , '/' )[ 2 ] , '_' )

    # Normalize
    age = tf.strings.to_number( parts[ 0 ] ) / 116

    return image , age

# List all the image files in the given directory.
list_ds = tf.data.Dataset.list_files( 'data/UTKFace/*' , shuffle=True )

# Map `parse_image` method to all filenames.
dataset = list_ds.map( parse_image , num_parallel_calls=tf.data.AUTOTUNE )<jupyter_output><empty_output><jupyter_text>Tạo tập train và test bằng `TRAIN_TEST_SPLIT`.<jupyter_code># Create train and test splits of the dataset.
num_examples_in_test_ds = int( dataset.cardinality().numpy() * TRAIN_TEST_SPLIT )

test_ds = dataset.take( num_examples_in_test_ds )
train_ds = dataset.skip( num_examples_in_test_ds )

print( 'Num examples in train ds {}'.format( train_ds.cardinality() ) )
print( 'Num examples in test ds {}'.format( test_ds.cardinality() ) )<jupyter_output><empty_output><jupyter_text>Vì hướng tới việc triển khai ứng dụng trên mobile, nên chúng tôi chọn mô hình đơn giản nhưng vẫn đủ mạnh để khái quát tốt. Vì vậy nên dùng bài toán hồi quy để dự đoán tuổi.- Model nhận vào [ None , 200 , 200 , 3 ] và đi qua num_blocks- Mỗi block gồm : Conv2D -> BatchNorm -> LeakyReLU<jupyter_code># Negative slope coefficient for LeakyReLU.
leaky_relu_alpha = 0.2

lite_model = False

# Define the conv block.
def conv( x , num_filters , kernel_size=( 3 , 3 ) , strides=1 ):
    if lite_model:
        x = tf.keras.layers.SeparableConv2D( num_filters ,
                                            kernel_size=kernel_size ,
                                            strides=strides, 
                                            use_bias=False ,
                                            kernel_initializer=tf.keras.initializers.HeNormal() ,
                                            kernel_regularizer=tf.keras.regularizers.L2( 1e-5 )
                                             )( x )
    else:
        x = tf.keras.layers.Conv2D( num_filters ,
                                   kernel_size=kernel_size ,
                                   strides=strides ,
                                   use_bias=False ,
                                   kernel_initializer=tf.keras.initializers.HeNormal() ,
                                   kernel_regularizer=tf.keras.regularizers.L2( 1e-5 )
                                    )( x )

    x = tf.keras.layers.BatchNormalization()( x )
    x = tf.keras.layers.LeakyReLU( leaky_relu_alpha )( x )
    return x

def dense( x , filters , dropout_rate ):
    x = tf.keras.layers.Dense( filters , kernel_regularizer=tf.keras.regularizers.L2( 0.1 ) , bias_regularizer=tf.keras.regularizers.L2( 0.1 ) )( x )
    x = tf.keras.layers.LeakyReLU( alpha=leaky_relu_alpha )( x )
    x = tf.keras.layers.Dropout( dropout_rate )( x )
    return x


# No. of convolution layers to be added.
num_blocks = 6
# Num filters for each conv layer.
num_filters = [ 16 , 32 , 64 , 128 , 256 , 256 ]
# Kernel sizes for each conv layer.
kernel_sizes = [ 3 , 3 , 3 , 3 , 3 , 3 ]

# Init a Input Layer.
inputs = tf.keras.layers.Input( shape=MODEL_INPUT_IMAGE_SIZE + [ 3 ] )

# Add conv blocks sequentially
x = inputs
for i in range( num_blocks ):
    x = conv( x , num_filters=num_filters[ i ] , kernel_size=kernel_sizes[ i ] )
    x = tf.keras.layers.MaxPooling2D()( x )

# Flatten the output of the last Conv layer.
x = tf.keras.layers.Flatten()( x )
conv_output = x 

# Add Dense layers ( Dense -> LeakyReLU -> Dropout )
x = dense( conv_output , 256 , 0.6 )
x = dense( x , 64 , 0.4 )
x = dense( x , 32 , 0.2 )
outputs = tf.keras.layers.Dense( 1 , activation='relu' )( x )

# Build the Model
model = tf.keras.models.Model( inputs , outputs )

# Uncomment the below to view the summary of the model.
model.summary()<jupyter_output><empty_output><jupyter_text>4) **Compiling the model**Khi đã định nghĩa xong kiến trúc mô hình, chúng ta sẽ compile model bằng một số hàm sau:* Dùng Mean Absolute Error(MAE) để làm loss function. `tf.keras.losses.mean_absolute_error`* Dùng Adam optimizer để tối ưu model `tf.keras.optimizers.Adam`* Vẫn dùng Mean Absolute Error để đánh giá. `tf.keras.metrics.MeanAbsoluteError`* `tf.keras.callbacks.ModelCheckpoint` save Kereras model sau mỗi epoch.* `tf.keras.callbacks.TensorBoard` để trực quan hóa training trên TensorBoard * `tf.keras.callbacks.LearningRateScheduler` để giam learning rate sau số epoch nhất định.```def scheduler( epochs , learning_rate ):    if epochs < num_epochs * 0.25:        return learning_rate    elif epochs < num_epochs * 0.5:        return 0.0005    elif epochs < num_epochs * 0.75:        return 0.0001    else:        return 0.000095```* `tf.keras.callbacks.EarlyStopping` dùng để dừng train khi kết quả evaluate không được cải thiện nữa.<jupyter_code>learning_rate = 0.001

num_epochs =  50
batch_size = 128
# Batch and repeat `train_ds` and `test_ds`.
train_ds = train_ds.batch( batch_size )
test_ds = test_ds.batch( batch_size )

# Init ModelCheckpoint callback
save_dir_ = 'model_1'  
save_dir = save_dir_ + '/{epoch:02d}-{val_mae:.2f}.h5'
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint( 
    save_dir , 
    save_best_only=True , 
    monitor='val_mae' , 
    mode='min' , 
)

tb_log_name = 'model_1'
# Init TensorBoard Callback
logdir = os.path.join( "tb_logs" , tb_log_name )
tensorboard_callback = tf.keras.callbacks.TensorBoard( logdir )

# Init LR Scheduler
def scheduler( epochs , learning_rate ):
    if epochs < num_epochs * 0.25:
        return learning_rate
    elif epochs < num_epochs * 0.5:
        return 0.0005
    elif epochs < num_epochs * 0.75:
        return 0.0001
    else:
        return 0.000095

lr_schedule_callback = tf.keras.callbacks.LearningRateScheduler( scheduler )

# Init Early Stopping callback
early_stopping_callback = tf.keras.callbacks.EarlyStopping( monitor='val_mae' , patience=10 )

# Compile the model
model.compile( 
    loss=tf.keras.losses.mean_absolute_error ,
    optimizer = tf.keras.optimizers.Adam( learning_rate ) , 
    metrics=[ 'mae' ]
)<jupyter_output><empty_output><jupyter_text>visualize the training of the model in TensorBoard<jupyter_code>%load_ext tensorboard
%tensorboard --logdir tb_logs/<jupyter_output><empty_output><jupyter_text>5) **Train and Evaluate the Model**<jupyter_code>model.fit( 
    train_ds, 
    epochs=num_epochs,  
    validation_data=test_ds, 
    callbacks=[ checkpoint_callback , tensorboard_callback , lr_schedule_callback , early_stopping_callback ]
)

# Evaluate Model
p = model.evaluate( test_ds )
print( p )
batch_size = 128
model = tf.keras.models.load_model( '/content/model_1/48-0.02.h5' )
# Save model
model_name = 'model_age' 
model_name_ = model_name + '.h5'

model.save( model_name_ )
files.download( model_name_ ) 
model.summary()<jupyter_output><empty_output><jupyter_text>6) **Visualize the results**<jupyter_code>fig = plt.figure( figsize=( 10 , 15 ) )
rows = 5
columns = 2

i = 1
for image , label in test_ds.unbatch().take( 10 ):
    image = image.numpy()
    fig.add_subplot( rows , columns , i )
    plt.imshow( image )
    label_ = int( model.predict( np.expand_dims( image , 0 ) ) * 116 )
    plt.axis( 'off' )
    plt.title( 'Predicted age : {} , actual age : {}'.format( label_ , int( label.numpy() * 116 ) ) )
    i += 1

# test real image<jupyter_output><empty_output><jupyter_text>7) **Convert to TensorFlow Lite format**Để model chạy được trên mobile, chúng ta sẽ dùng TF Lite AndroidDùng `TFLiteConverter` API để chuyển Keras Model ( `.h5` ) thành ( `.tflite`).chuyển thành 2 TF Lite, quantization và non-quantized model.<jupyter_code>#quantization
converter = tf.lite.TFLiteConverter.from_keras_model( model )
converter.optimizations = [ tf.lite.Optimize.DEFAULT ]
converter.target_spec.supported_types = [ tf.float16 ]
buffer = converter.convert()
# open( '{}_q.tflite'.format( model_name ) , 'wb' ).write( buffer )
# files.download( '{}_q.tflite'.format( model_name ) )

#non-quantized
converter = tf.lite.TFLiteConverter.from_keras_model( model )
buffer = converter.convert()

open( '{}_nonq.tflite'.format( model_name ) , 'wb' ).write( buffer )
files.download( '{}_nonq.tflite'.format( model_name ) )<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10700805875752321, 'fraction_numerical': 0.02152402325818627, 'mean_word_length': 3.251517779705117, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '355735', 'license': 'Unlicense', 'n_tokens_mistral': 3870, 'n_tokens_neox': 3652, 'n_words': 923, 'path': 'notebook/Age_Estimation.ipynb', 'repo_name': 'AVL1/CS338.L21-AgeGenderEstimation', 'size': '9803'}