<jupyter_start><jupyter_text>Lab 03: VGGTrong bài thực hành này:- Cài đặt, train VGG16 với data MNIST- Các kĩ thuật regularization: L2, dropout- Tạo callback của keras để lưu checkpointReference:- <NAME>., and <NAME>. 2014b. Very deep convolutionalnetworks for large-scale image recognition. arXivpreprint arXiv:1409.1556.https://arxiv.org/abs/1409.1556  1. VGG-16 trên MNISTTrong phần này chúng ta sẽ xây dựng và huấn luyện model VGG-16 trên dataset MNIST (ảnh được resize)  1.1 VGG16 cài sẵn trong Keras<jupyter_code>#import thư viện cần thiết
## thư viện machine learning và hỗ trợ
import tensorflow as tf
from tensorflow import keras
import numpy as np

## thư viện để vẽ đồ thị
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>Tự động tạo một model VGG-16 bằng hàm cài sẵn trong keras- include_top: True/False: có thêm 3 lớp fully-conndedted ở cuối model không- weights: None/'imagenet': None: khởi tạo tham số ngẫu nhiên; 'imagenet': load trọng số của model được train với imagenet- input_tensor: truyền lớp Input vào nếu muốn- input_shape: xác định kích thước input- pooling: None/'max'/'avg': chế độ pooling trong các lớp pool- classes: số lớp output<jupyter_code>vgg16 = keras.applications.vgg16.VGG16(include_top=True, 
                                       weights=None, 
                                       input_tensor=None, 
                                       input_shape=(32,32,1), 
                                       pooling='max',
                                       classes=10)
vgg16.summary()<jupyter_output>Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 32, 32, 1)]       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 32, 32, 64)        640       
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 32, 32, 64)        36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 16, 16, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 16, 16, 128)       73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 16, 16, 128)       147584    
_____________________________________________________________[...]<jupyter_text>1.2 Xây dựng VGG bằng thư viện keras.layersHình trên trích từ bài báo gốc của VGG. Cột D và E chính là cấu trúc model VGG16 và VGG19. - Filter size của tất cả các lớp convolution là 3x3- Số filter của từng block lần lượt là 64, 128, 256, 512, 512- Các hàm kích hoạt đều là ReLU, trừ lớp output dùng hàm kích hoạt softmax- L2 Regularizer (weight decay) các lớp dense: 0.0005- Dropout probability sau các lớp dense: 0.5Giả sử ảnh input của chúng ta có kích thước (32x32x1)Xây dựng các lớp<jupyter_code>## import l2 regularizer
## l2 sẽ được khai báo truyền vào khi khởi tạo lớp
from keras.regularizers import l2
l2_regularizer_rate = 0.0005

## Tạo lớp input kích thước (None, 32, 32, 1)
inputs = keras.layers.Input(shape=(32,32,1))

### Block 1
#### Gồm 2 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 64, kernel size 3x3, hàm kích hoạt ReLU
conv1_1 = keras.layers.Convolution2D(filters=64,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(inputs)
conv1_2 = keras.layers.Convolution2D(filters=64,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv1_1)
maxpool1 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv1_2)

### Block 2
#### Gồm 2 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 128, kernel size 3x3, hàm kích hoạt ReLU
conv2_1 = keras.layers.Convolution2D(filters=128,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool1)
conv2_2 = keras.layers.Convolution2D(filters=128,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv2_1)
maxpool2 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv2_2)

### Block 3
#### Gồm 3 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 256, kernel size 3x3, hàm kích hoạt ReLU
conv3_1 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool2)
conv3_2 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv3_1)
conv3_3 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv3_2)
maxpool3 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv3_3)

### Block 4
#### Gồm 3 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 512, kernel size 3x3, hàm kích hoạt ReLU
conv4_1 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool3)
conv4_2 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv4_1)
conv4_3 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv4_2)
maxpool4 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv4_3)

### Block 5
#### Gồm 3 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 512, kernel size 3x3, hàm kích hoạt ReLU
conv5_1 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool4)
conv5_2 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv5_1)
conv5_3 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv5_2)
maxpool5 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv5_3)

### Block cuối
#### Gồm 2 lớp fully-connected và 1 lớp output (cũng fully-connected)
#### Số neurons trong 3 lớp lần lượt là 4096, 4096, 10
flatten6 = keras.layers.Flatten()(maxpool5)
dense6_1 = keras.layers.Dense(units=4096, 
                              activation='relu',
                              kernel_regularizer=l2(l2_regularizer_rate))(flatten6)
dropout6_1 = keras.layers.Dropout(rate=0.5)(dense6_1)  ## Lớp Dropout (chỉ chạy khi train), rate: xác suất bị drop

dense6_2 = keras.layers.Dense(units=4096, 
                              activation='relu',
                              kernel_regularizer=l2(l2_regularizer_rate))(dropout6_1)
dropout6_2 = keras.layers.Dropout(rate=0.5)(dense6_2)  ## Lớp Dropout (chỉ chạy khi train), rate: xác suất bị drop

softmax = keras.layers.Dense(units=10, 
                             activation='softmax')(dropout6_2)


## Compile model
model = keras.models.Model(inputs=inputs, outputs=softmax)
model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.0001),    ##tự khai báo Optimizer với learning rate 10^-4
             loss=tf.keras.losses.sparse_categorical_crossentropy,
             metrics=["accuracy"])
    

## In toàn bộ cấu trúc của model
print("Cấu trúc của model: ")
model.summary()<jupyter_output>Cấu trúc của model: 
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 32, 32, 1)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 32, 32, 64)        640       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 32, 32, 64)        36928     
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 16, 16, 128)       73856     
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 16, 16, 128)       147584    
________________________________________[...]<jupyter_text>1.3 Resize MNIST<jupyter_code># Tải dataset MNIST từ tensorflow
## MNIST là bài toán dự đoán một ảnh thể hiện ký tự số nào

## tải MNIST dataset từ keras
(X_train, y_train), (X_test, y_test) = keras.datasets.mnist.load_data()
##resacle ảnh thành ảnh thực trong đoạn [0,1]
X_train, X_test = X_train/255.0, X_test/255.0

##in dataset
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)<jupyter_output>(60000, 28, 28) (60000,) (10000, 28, 28) (10000,)<jupyter_text>Do VGG-16 yêu cầu ảnh kích thước tối thiểu 32x32. Chúng ta resize ảnh thành 32x32 để cho vào VGG<jupyter_code>## import thư viện OpenCV trên python
#!pip3 install opencv-python

### Thử resize một ảnh
import cv2
resized_img = cv2.resize(X_train[0], dsize=(32,32))
print("Kích thước ảnh sau resize: ", resized_img.shape)
## Resize toàn bộ ảnh train tập train và test
X_train = np.array([cv2.resize(img, dsize=(32,32)) for img in X_train])
X_test = np.array([cv2.resize(img, dsize=(32,32)) for img in X_test])
print("Kích thước tập sau khi resize: ", X_train.shape, X_test.shape)

## In xem ảnh còn ổn không sau khi resize
plt.imshow(X_train[0])
plt.show()

## Reshape ảnh để phù hợp với input của model (thêm một trục)
X_train = np.expand_dims(X_train, axis=-1)
X_test = np.expand_dims(X_test, axis=-1)
print("Kích thước tập sau khi reshape: ", X_train.shape, X_test.shape)

plt.imshow(X_train[0,:,:,0])
plt.show()

#Tách một phần tập train thành tập valid
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.1)

## Reshape ảnh để phù hợp với input của model (thêm một trục)<jupyter_output>Kích thước tập sau khi resize:  (60000, 32, 32) (10000, 32, 32)<jupyter_text>1.4 Train<jupyter_code>### Tạo một callback ModelCheckpoint: callback này sẽ lưu model mỗi khi tìm được một model tốt hơn
#### filepath: đường dẫn file muốn lưu
#### monitor: đại lượng quyết định xem model có "tốt hơn" hay không
#### mode='auto'/'max'/'min': đại lượng monitor lớn hay nhỏ là tốt hơn
#### verbose: có thông báo mỗi lần lưu ko
#### save_best_only: chỉ lưu model tốt nhất
mc = keras.callbacks.ModelCheckpoint(filepath="vgg16_mnist.h5", 
                                     monitor='val_loss',
                                     mode='min', 
                                     verbose=0, 
                                     save_best_only=True)

## Train  ## Khuyến cáo chạy COLAB (hoặc tương tự)
history = model.fit(X_train, y_train,
                    batch_size=100,
                    epochs=10,
                    validation_data=(X_valid, y_valid),
                    callbacks=[mc])                     ##sử dụng callback ModelCheckpoint trong quá trình train


## Đánh giá model trên tập test
valid_loss, valid_acc = model.evaluate(X_valid, y_valid)
test_loss, test_acc = model.evaluate(X_test, y_test)
print("Valid: loss {} acc {} -- Test: loss {} valid {}".format(valid_loss, valid_acc, test_loss, test_acc))

## Load lại model tốt nhất đã lưu
print("best model: ")
model.load_weights("vgg16_mnist.h5")
valid_loss, valid_acc = model.evaluate(X_valid, y_valid)
test_loss, test_acc = model.evaluate(X_test, y_test)
print("Valid: loss {} acc {} -- Test: loss {} valid {}".format(valid_loss, valid_acc, test_loss, test_acc))<jupyter_output>Epoch 1/10
540/540 [==============================] - 28s 37ms/step - loss: 1.5045 - accuracy: 0.7465 - val_loss: 0.5561 - val_accuracy: 0.9637
Epoch 2/10
540/540 [==============================] - 20s 36ms/step - loss: 0.4056 - accuracy: 0.9763 - val_loss: 0.2966 - val_accuracy: 0.9820
Epoch 3/10
540/540 [==============================] - 20s 36ms/step - loss: 0.2334 - accuracy: 0.9856 - val_loss: 0.1751 - val_accuracy: 0.9885
Epoch 4/10
540/540 [==============================] - 20s 36ms/step - loss: 0.1473 - accuracy: 0.9887 - val_loss: 0.1371 - val_accuracy: 0.9860
Epoch 5/10
540/540 [==============================] - 19s 36ms/step - loss: 0.0993 - accuracy: 0.9907 - val_loss: 0.0928 - val_accuracy: 0.9883
Epoch 6/10
540/540 [==============================] - 20s 36ms/step - loss: 0.0709 - accuracy: 0.9922 - val_loss: 0.0789 - val_accuracy: 0.9895
Epoch 7/10
540/540 [==============================] - 20s 36ms/step - loss: 0.0588 - accuracy: 0.9921 - val_loss: 0.0584 - val_accuracy:[...]<jupyter_text>Bài tập1. Xây dựng và huấn luyện VGG19 với MNIST bằng thư viện keras.layers (viết gọn xíu!).2. Chỉnh các tham số (lrn_rate, l2 weight decay, epochs, batch_size, ...) để model đạt accuracy 0.994 trên tập valid.<jupyter_code># Xây dựng các lớp

## l2 sẽ được khai báo truyền vào khi khởi tạo lớp
from keras.regularizers import l2
l2_regularizer_rate = 0.0005

## Tạo lớp input kích thước (None, 32, 32, 1)
inputs = keras.layers.Input(shape=(32,32,1))

### Block 1
#### Gồm 2 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 64, kernel size 3x3, hàm kích hoạt ReLU
conv1_1 = keras.layers.Convolution2D(filters=64,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(inputs)
conv1_2 = keras.layers.Convolution2D(filters=64,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv1_1)
maxpool1 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv1_2)


### Block 2
#### Gồm 2 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 128, kernel size 3x3, hàm kích hoạt ReLU
conv2_1 = keras.layers.Convolution2D(filters=128,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool1)
conv2_2 = keras.layers.Convolution2D(filters=128,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv2_1)
maxpool2 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv2_2)

### Block 3 
#### Gồm 4 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 256, kernel size 3x3, hàm kích hoạt ReLU 
conv3_1 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool2)
conv3_2 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv3_1)
conv3_3 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv3_2)     
conv3_4 = keras.layers.Convolution2D(filters=256,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv3_3)                                       
maxpool3 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv3_4)  

### Block 4
#### Gồm 4 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 512, kernel size 3x3, hàm kích hoạt ReLU   
conv4_1 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool3)
conv4_2 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv4_1)
conv4_3 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv4_2)
conv4_4 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv4_3)
maxpool4 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv4_4)

### Block 5
#### Gồm 3 lớp convolution và 1 lớp maxpoool
#### Lớp convolution số filter 512, kernel size 3x3, hàm kích hoạt ReLU
conv5_1 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(maxpool4)
conv5_2 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv5_1)
conv5_3 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv5_2)
conv5_4 = keras.layers.Convolution2D(filters=512,
                                     kernel_size=[3,3],
                                     strides=[1,1],
                                     padding='same',
                                     activation=tf.nn.relu)(conv5_3)                                     
maxpool5 = keras.layers.MaxPool2D(pool_size=[2,2],
                                  strides=[2,2])(conv5_4)

### Block cuối
#### Gồm 2 lớp fully-connected và 1 lớp output (cũng fully-connected)
#### Số neurons trong 3 lớp lần lượt là 4096, 4096, 10
flatten6 = keras.layers.Flatten()(maxpool5)
dense6_1 = keras.layers.Dense(units=4096, 
                              activation='relu',
                              kernel_regularizer=l2(l2_regularizer_rate))(flatten6)
dropout6_1 = keras.layers.Dropout(rate=0.5)(dense6_1)  ## Lớp Dropout (chỉ chạy khi train), rate: xác suất bị drop

dense6_2 = keras.layers.Dense(units=4096, 
                              activation='relu',
                              kernel_regularizer=l2(l2_regularizer_rate))(dropout6_1)
dropout6_2 = keras.layers.Dropout(rate=0.5)(dense6_2)  ## Lớp Dropout (chỉ chạy khi train), rate: xác suất bị drop

softmax = keras.layers.Dense(units=10, 
                             activation='softmax')(dropout6_2)


## Compile model
model = keras.models.Model(inputs=inputs, outputs=softmax)
model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.0001),    ##tự khai báo Optimizer với learning rate 10^-4
             loss=tf.keras.losses.sparse_categorical_crossentropy,
             metrics=["accuracy"])
    

## In toàn bộ cấu trúc của model
print("Cấu trúc của model: ")
model.summary()
# Tải dataset MNIST từ tensorflow
## MNIST là bài toán dự đoán một ảnh thể hiện ký tự số nào

## tải MNIST dataset từ keras
(X_train, y_train), (X_test, y_test) = keras.datasets.mnist.load_data()
##resacle ảnh thành ảnh thực trong đoạn [0,1]
X_train, X_test = X_train/255.0, X_test/255.0

##in dataset
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
## import thư viện OpenCV trên python
#!pip3 install opencv-python

### Thử resize một ảnh
import cv2
resized_img = cv2.resize(X_train[0], dsize=(32,32))
print("Kích thước ảnh sau resize: ", resized_img.shape)

## Resize toàn bộ ảnh train tập train và test
X_train = np.array([cv2.resize(img, dsize=(32,32)) for img in X_train])
X_test = np.array([cv2.resize(img, dsize=(32,32)) for img in X_test])
print("Kích thước tập sau khi resize: ", X_train.shape, X_test.shape)

## In xem ảnh còn ổn không sau khi resize
plt.imshow(X_train[0])
plt.show()

## Reshape ảnh để phù hợp với input của model (thêm một trục)
X_train = np.expand_dims(X_train, axis=-1)
X_test = np.expand_dims(X_test, axis=-1)
print("Kích thước tập sau khi reshape: ", X_train.shape, X_test.shape)

plt.imshow(X_train[0,:,:,0])
plt.show()

#Tách một phần tập train thành tập valid
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.1)

## Reshape ảnh để phù hợp với input của model (thêm một trục)
### Tạo một callback ModelCheckpoint: callback này sẽ lưu model mỗi khi tìm được một model tốt hơn
#### filepath: đường dẫn file muốn lưu
#### monitor: đại lượng quyết định xem model có "tốt hơn" hay không
#### mode='auto'/'max'/'min': đại lượng monitor lớn hay nhỏ là tốt hơn
#### verbose: có thông báo mỗi lần lưu ko
#### save_best_only: chỉ lưu model tốt nhất
mc = keras.callbacks.ModelCheckpoint(filepath="vgg19_mnist.h5", 
                                     monitor='val_loss',
                                     mode='min', 
                                     verbose=0, 
                                     save_best_only=True)

## Train  ## Khuyến cáo chạy COLAB (hoặc tương tự)
history = model.fit(X_train, y_train,
                    batch_size=100,
                    epochs=10,
                    validation_data=(X_valid, y_valid),
                    callbacks=[mc])                     ##sử dụng callback ModelCheckpoint trong quá trình train


## Đánh giá model trên tập test
valid_loss, valid_acc = model.evaluate(X_valid, y_valid)
test_loss, test_acc = model.evaluate(X_test, y_test)
print("Valid: loss {} acc {} -- Test: loss {} valid {}".format(valid_loss, valid_acc, test_loss, test_acc))

## Load lại model tốt nhất đã lưu
print("best model: ")
model.load_weights("vgg19_mnist.h5")
valid_loss, valid_acc = model.evaluate(X_valid, y_valid)
test_loss, test_acc = model.evaluate(X_test, y_test)
print("Valid: loss {} acc {} -- Test: loss {} valid {}".format(valid_loss, valid_acc, test_loss, test_acc))<jupyter_output>Epoch 1/10
540/540 [==============================] - 25s 43ms/step - loss: 1.7509 - accuracy: 0.5903 - val_loss: 0.4442 - val_accuracy: 0.9648
Epoch 2/10
540/540 [==============================] - 23s 43ms/step - loss: 0.3328 - accuracy: 0.9737 - val_loss: 0.2455 - val_accuracy: 0.9818
Epoch 3/10
540/540 [==============================] - 24s 44ms/step - loss: 0.1943 - accuracy: 0.9852 - val_loss: 0.1747 - val_accuracy: 0.9813
Epoch 4/10
540/540 [==============================] - 23s 43ms/step - loss: 0.1313 - accuracy: 0.9881 - val_loss: 0.1228 - val_accuracy: 0.9873
Epoch 5/10
540/540 [==============================] - 24s 44ms/step - loss: 0.0871 - accuracy: 0.9914 - val_loss: 0.0872 - val_accuracy: 0.9900
Epoch 6/10
540/540 [==============================] - 24s 44ms/step - loss: 0.0702 - accuracy: 0.9913 - val_loss: 0.0808 - val_accuracy: 0.9902
Epoch 7/10
540/540 [==============================] - 23s 43ms/step - loss: 0.0581 - accuracy: 0.9925 - val_loss: 0.0684 - val_accuracy:[...]