You are on page 1of 12

BỘ GIÁO DỤC VÀ ĐÀO TẠO

TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT TP. HCM


KHOA CƠ KHÍ CHẾ TẠO MÁY

BÁO CÁO BÀI TẬP NHÓM

NHẬN DIỆN KHUÔN MẶT BẰNG AI


DỰ ĐOÁN TUỔI VÀ GIỚI TÍNH

MÔN HỌC: TRÍ TUỆ NHÂN TẠO


GVHD: TS BÙI HÀ ĐỨC

SVTH: Phan Thu Huyền 21104014


Chu Tú Trinh 21104088
Nguyễn Thị Thảo Uyên 21104053
Trương Điệp Y 21104093

Tp. Hồ Chí Minh, tháng 11 năm 2023


MỤC LỤC

1. LÝ DO CHỌN ĐỀ TÀI .................................................................................................... 1


2. TỔNG QUAN SƠ BỘ ...................................................................................................... 1
3. PHÂN TÍCH CODE .......................................................................................................... 1
4. KHÓ KHĂN VÀ GIẢI QUYẾT ..................................................................................... 10
1. LÝ DO CHỌN ĐỀ TÀI
Chúng em thực hiện bài tập nhóm lần này theo yêu cầu thầy đưa ra, qua đó có thể tích lũy
cho bản thân một số điều sau:
- Sử dụng thuần thục các thư viện, thuật toán
- Giúp nhận biết được gương mặt, dự đoán được tuổi và giới tính của người ngồi trước
camera, sau đó xem người đó có tập trung vào trong quảng cáo đang chiếu ra trên màn hình
hay không. Từ đó xác định được các nhóm đối tượng phù hợp để xuất hiện quảng cáo, tăng
hiệu quả của công việc.
- Cải thiện và phát triển các kỹ năng làm việc nhóm, khả năng research, ...
2. TỔNG QUAN SƠ BỘ
Thừa hưởng những thành tựu của nền khoa học kỹ thuật phát triển. Nhận diện khuôn mặt
là một công nghệ được ứng dụng rộng rãi trong đời sống hằng ngày của con người như các hệ
thống giám sát tại các tòa nhà, sân bay, trạm ATM, hệ thống chấm công, camera chống trộm,
xác thực danh tính, …có rất nhiều các phương pháp nhận dạng khuôn mặt để nâng cao hiệu
suất, bảo mật hiệu quả, cải thiện độ chính xác, tích hợp dễ dàng hơn. Hệ thống nhận dạng
khuôn mặt được sử dụng trong các trường hợp như đăng nhập ID, phát hiện gian lận, an ninh
mạng, kiểm soát sân bay và biên giới, ngân hàng, chăm sóc sức khỏe. Tuy nhiên dù ít hay
nhiều thì những phương pháp này đang gặp phải những khó khăn, thử thách như về độ sáng,
hướng nghiêng, kích thước hình ảnh, hay ảnh hưởng của tham số môi trường
3. PHÂN TÍCH CODE
Đầu tiên, chúng em import các thư viện và mô đun cần thiết để chạy code
import tensorflow as tf

from tensorflow.keras.utils import load_img

from keras.models import Sequential, Model

from keras.layers import Dense, Conv2D, Dropout, Flatten, MaxPooling2D, Input

import numpy as np

import random

import matplotlib.pyplot as plt

import os

import seaborn as sns

import warnings

from tqdm.notebook import tqdm

warnings.filterwarnings('ignore')

Sau đó, kết nối với GG Drive và giải nén dữ liệu tệp zip UTKFace.zip
from google.colab import drive

drive.mount('/content/drive')

!unzip '/content/drive/MyDrive/UTKFace.zip'

Tiếp theo, cần tạo ra ba danh sách để lưu trữ độ tuổi, giới tính và đường dẫn hình ảnh. Trích
1
xuất tên tệp hình ảnh bằng cách sử dụng thư mục os.list cung cấp tất cả tên tệp bên trong
UTKFace và xáo trộn các tên tệp đó vì dữ liệu khi nhận diện là ngẫu nhiên, không tuần tự.
BASE_DIR = '/content/UTKFace'

age_labels = []

gender_labels = []

image_paths = []

image_filenames = os.listdir(BASE_DIR)

random.shuffle(image_filenames)

Đoạn code dưới đây đang duyệt qua danh sách các hình ảnh để lấy thông tin và giới tính từ
tên tệp:
for image in tqdm(image_filenames):

image_path = os.path.join(BASE_DIR, image)

img_components = image.split('_')

age_label = int(img_components[0])

gender_label = int(img_components[1])

# Append the image_path, age_label, and gender_label

age_labels.append(age_label)

gender_labels.append(gender_label)

image_paths.append(image_path)

Sau đó in ra để xác định xem số lượng nhãn độ tuổi, nhãn giới tính và đường dẫn hình ảnh có
giống nhau hay không
print(f'Number of age_labels: {len(age_labels)}, Number of gender_labels: {len(gender_labels)}, Number of
image_paths: {len(image_paths)}')

Tiếp theo tạo một từ điển đơn giản, 0 là Male, 1 là Female


gender_mapping = {

1: 'Female',

0: 'Male'

Chuyển đổi từng danh sách đã tạo thành từng cột riêng để khung dữ liệu ngắn gọn và đẹp mắt
hơn
import pandas as pd

df = pd.DataFrame()

df['image_path'], df['age'], df['gender'] = image_paths, age_labels, gender_labels

df.head(5)

2
Và chúng em có đoạn code cho hiển thị một hình ảnh ngẫu nhiên, phân tích dữ liệu và cho ra
tuổi cùng giới tính của tấm hình ấy.
from PIL import Image

rand_index = random.randint(0, len(image_paths))

age = df['age'][rand_index]

gender = df['gender'][rand_index]

IMG = Image.open(df['image_path'][rand_index])

plt.title(f'Age: {age} Gender: {gender_mapping[gender]}')

plt.axis('off')

plt.imshow(IMG)

Dưới đây là code tạo biểu đồ phân phối tuổi và giới tính trong tệp dữ liệu
sns.distplot(df['age'])

sns.countplot(df['gender'])

Sau đó, sẽ cho xuất hiện một loạt các hình ảnh trong tệp với các thông tin như độ tuổi và giới
tính tương ứng
for index, sample, age, gender in samples.itertuples():

plt.subplot(4, 4, index + 1)

img = load_img(sample)

img = np.array(img)

plt.axis('off')

plt.title(f'Age: {age} Gender: {gender_mapping[gender]}')

plt.imshow(img)

def extract_image_features(images):
features = list()
Đây là khai báo hàm Python có tên là ‘extract_image_features’, nhận một tham số là
images. Tham số này là một danh sách chứa đường dẫn đến các hình ảnh hoặc nội dung hình
ảnh.
features = list(): Tạo một biến features là một danh sách rỗng được sử dụng để lưu trữ các
đặc trưng của hình ảnh sau khi được xử lý
for image in tqdm(images):
img = load_img(image, grayscale=True)
img = img.resize((128, 128), Image.ANTIALIAS)
img = np.array(img)
features.append(img)
features = np.array(features)
features = features.reshape(len(features), 128, 128, 1)
return features

3
Thực hiện việc xử lý từng hình ảnh trong danh sách ‘images’, duyệt và thay đổi hình ảnh sau
đó thay đổi kích thước ảnh thành 128x128 pixel, chuyển đổi thành Numpy thêm vào danh
sách ‘features’
X = extract_image_features(df['image_path'])
X.shape
X = X / 255.0
Gán kết quả trả về chứa các đặc trưng của tất cả hình ảnh
y_gender = np.array(df['gender'])
y_age = np.array(df['age'])
Đoạn code này giúp chuyển đổi dữ liệu từ cột của DataFrame thành các mảng NumPy để
thuận tiện cho việc xử lý và tính toán.
input_shape = (128, 128, 1)
Dòng code này định nghĩa biến input_shape là một tuple có giá trị là (128, 128, 1)
inputs = Input((input_shape))
conv_1 = Conv2D(32, kernel_size=(3, 3), activation='relu')(inputs)
max_1 = MaxPooling2D(pool_size=(2, 2))(conv_1)
conv_2 = Conv2D(64, kernel_size=(3, 3), activation='relu')(max_1)
max_2 = MaxPooling2D(pool_size=(2, 2))(conv_2)
conv_3 = Conv2D(128, kernel_size=(3, 3), activation='relu')(max_2)
max_3 = MaxPooling2D(pool_size=(2, 2))(conv_3)
conv_4 = Conv2D(256, kernel_size=(3, 3), activation='relu')(max_3)
max_4 = MaxPooling2D(pool_size=(2, 2))(conv_4)
flatten = Flatten()(max_4)
Định nghĩa đầu vào của mô hình với kích thước là input_shape. Sau đó, Tích chập đầu vào
với 32 bộ lọc kích thước (3, 3) và hàm kích hoạt ReLU, Lấy giá trị lớn nhất từ các vùng 2x2,
giúp giảm kích thước của đầu ra. Lớp Flatten được sử dụng để biến đổi đầu ra của lớp
pooling cuối cùng (max_4) thành một vector 1 chiều. Nó "làm phẳng" dữ liệu, giữ nguyên
thông tin từ các bức ảnh đã được trích xuất các đặc trưng.
dense_1 = Dense(256, activation='relu')(flatten)
dense_2 = Dense(256, activation='relu')(flatten)
dropout_1 = Dropout(0.3)(dense_1)
dropout_2 = Dropout(0.3)(dense_2)
output_1 = Dense(1, activation='sigmoid', name='gender_out')(dropout_1)
output_2 = Dense(1, activation='relu', name='age_out')(dropout_2)
model = Model(inputs=[inputs], outputs=[output_1, output_2])
model.compile(loss=['binary_crossentropy', 'mae'],
optimizer='adam', metrics=['accuracy'])
Đoạn code đang xây dựng một mô hình mạng nơ-ron sâu (deep learning) để thực hiện hai
nhiệm vụ: dự đoán giới tính và tuổi từ ảnh đầu vào.
from tensorflow.keras.utils import plot_model
plot_model(model)
Sau khi chạy đoạn mã trên sẽ vẽ biểu đồ cấu trúc của mô hình. Biểu đồ này sẽ mô tả mối
quan hệ giữa các lớp trong mô hình, giúp hiểu cấu trúc tổng quan của mô hình nhanh chóng.
history = model.fit(x=X, y=[y_gender, y_age],
batch_size=32, epochs=50, validation_split=0.2)
Tiến hành training

4
# plot results for gender
acc = history.history['gender_out_accuracy']
val_acc = history.history['val_gender_out_accuracy']
epochs = range(len(acc))
plt.plot(epochs, acc, 'b', label='Training Accuracy')
plt.plot(epochs, val_acc, 'r', label='Validation Accuracy')
plt.title('Accuracy Graph')
plt.legend()
plt.figure()
Dòng code này giúp theo dõi sự phát triển của độ chính xác trên cả tập training và tập kiểm
thử qua các epoch, giúp đánh giá hiệu suất của mô hình và phát hiện hiện tượng overfitting
hoặc underfitting.
loss = history.history['gender_out_loss']
val_loss = history.history['val_gender_out_loss']
Lấy thông tin về giá trị mất mát trên tập training từ lịch sử training (history). Trong đoạn mã
này, "gender_out_loss" là mất mát của lớp đầu ra dự đoán giới tính và lấy thông tin về giá trị
mất mát trên tập kiểm thử từ lịch sử training
plt.plot(epochs, loss, 'b', label='Training Loss')
plt.plot(epochs, val_loss, 'r', label='Validation Loss')
plt.title('Loss Graph')
plt.legend()
plt.show()
- 'b' và 'r' trong ‘plt.plot’ chỉ định màu của các đường ('b' cho màu xanh lam và 'r' cho
màu đỏ).
- Tham số ‘label’ trong ‘plt.plot’ được sử dụng để gắn nhãn cho các dòng cho chú giải.
- ‘plt.title’ đặt tiêu đề cho plot.
- ‘plt.legend()’ hiển thị chú giải, rất hữu ích khi vẽ nhiều dòng để phân biệt giữa
chúng.
Mã này thường được sử dụng trong đào tạo machine learning model trong đó 'epochs' biểu
thị số lần thuật toán học sẽ hoạt động thông qua toàn bộ tập dữ liệu đào tạo. 'Training Loss'
và 'Validation Loss' là các số liệu được sử dụng để đánh giá mức độ hoạt động của mô hình
trong quá trình đào tạo và trên dữ liệu không nhìn thấy tương ứng. Plot giúp hình dung các
giá trị mất mát này thay đổi như thế nào qua các giai đoạn huấn luyện.
# plot results for age
loss = history.history['age_out_loss']
val_loss = history.history['val_age_out_loss']
epochs = range(len(loss))
- Vẽ đồ thị các giá trị tổn thất huấn luyện và xác thực cho đầu ra 'age' của mô hình.
'age_out_loss' và 'val_age_out_loss' là các phần giữ chỗ cho các khóa thực tế trong đối
tượng 'history' của bạn để lưu trữ các giá trị tổn thất trong quá trình đào tạo và xác thực cho
đầu ra 'age'.
- Đảm bảo điều chỉnh mã theo các khóa thực tế được sử dụng trong đối tượng 'history' của
bạn. Ngoài ra, việc cung cấp nhãn trục với ‘plt.xlabel’ và ‘plt.ylabel’ có thể nâng cao khả
năng diễn giải biểu đồ của bạn.
plt.plot(epochs, loss, 'b', label='Training Loss')
plt.plot(epochs, val_loss, 'r', label='Validation Loss')
plt.title('Loss Graph')
5
plt.legend()
plt.show()
Sử dụng lại đoạn mã để vẽ sơ đồ mất dữ liệu đào tạo và xác thực qua các epochs. Mã này
được sử dụng để trực quan hóa xu hướng mất đi quá trình đào tạo và xác thực trong quá
trình đào tạo.
def get_image_features(image):
img = load_img(image, grayscale=True)
img = img.resize((128, 128), Image.ANTIALIAS)
img = np.array(img)
img = img.reshape(1, 128, 128, 1)
img = img / 255.0
return img
- Load Image:Hàm sử dụng hàm Load_img từ Keras để tải hình ảnh ở thang độ xám.
- Resize Image: Nó thay đổi kích thước hình ảnh thành kích thước cố định 128x128 pixel
bằng cách sử dụng tính năng khử răng cưa để thay đổi kích thước mượt mà.
- Convert to NumPy Array: Hình ảnh được chuyển đổi thành mảng NumPy bằng np.array.
- Reshape: Mảng được định hình lại để có kích thước (1, 128, 128, 1). Các kích thước bổ sung
thường được sử dụng khi làm việc với mạng nơ ron tích chập (CNN).
- Normalization: Các giá trị pixel được chuẩn hóa thành phạm vi [0, 1] bằng cách chia mỗi giá
trị pixel cho 255,0.
Kết quả có thể được sử dụng làm đầu vào cho mô hình mong đợi hình ảnh có kích thước
128x128 pixel
img_to_test = '/content/drive/MyDrive/2.png'
features = get_image_features(img_to_test)
pred = model.predict(features)
gender = gender_mapping[round(pred[0][0][0])]
age = round(pred[1][0][0])
plt.title(f'Predicted Age: {age} Predicted Gender: {gender}')
plt.axis('off')
plt.imshow(np.array(load_img(img_to_test)))
- Sử dụng hàm ‘get_image_features’ để xử lý trước hình ảnh rồi đưa ra dự đoán bằng mô
hình. Sau khi nhận được dự đoán, bạn sẽ hiển thị hình ảnh gốc cùng với độ tuổi và giới tính
được dự đoán.
- Giới tính dự đoán được lấy từ ‘gender_mapping’, có lẽ là mapping từ các giá trị số sang
nhãn giới tính.
- Độ tuổi dự đoán được làm tròn, giả sử đó là dự đoán hồi quy.
- ‘plt.title’, ‘plt.axis('off')’ và ‘plt.imshow’ được sử dụng để hiển thị hình ảnh gốc với độ tuổi
và giới tính được dự đoán.
from IPython.display import display, Javascript, Image
from google.colab.output import eval_js
from base64 import b64decode, b64encode
import cv2
import numpy as np
import PIL
import io
import html
import time
6
Import các thư viện cần thiết
def js_to_image(js_reply):
"""
Params:
js_reply: JavaScript object containing image from webcam
Returns:
img: OpenCV BGR image
"""
# decode base64 image
image_bytes = b64decode(js_reply.split(',')[1])
# convert bytes to numpy array
jpg_as_np = np.frombuffer(image_bytes, dtype=np.uint8)
# decode numpy array into OpenCV BGR image
img = cv2.imdecode(jpg_as_np, flags=1)
return img
- Decode Base64 Image: Hàm lấy hình ảnh được mã hóa base64 từ đối tượng JavaScript
(‘js_reply’), phân tách nó để lấy nội dung được mã hóa base64 thực tế, sau đó giải mã nó
bằng ‘b64decode’. Bước này chuyển đổi hình ảnh được mã hóa base64 thành byte.
- Convert Bytes to NumPy Array: Các byte được giải mã sau đó được chuyển đổi thành mảng
NumPy bằng cách sử dụng ‘np.frombuffer’.
- Decode NumPy Array into OpenCV BGR Image: Mảng NumPy được giải mã thành hình
ảnh OpenCV BGR bằng ‘cv2.imdecode’. Tham số ‘flags=1’ chỉ ra rằng hình ảnh phải được
đọc ở dạng màu.
Kết quả là hình ảnh OpenCV BGR có thể được xử lý hoặc hiển thị thêm bằng các hàm
OpenCV.
# function to convert OpenCV Rectangle bounding box image into base64 byte string to be overlayed on video
stream
def bbox_to_bytes(bbox_array):
"""
Params:
bbox_array: Numpy array (pixels) containing rectangle to overlay on video stream.
Returns:
bytes: Base64 image byte string
"""
# convert array into PIL image
bbox_PIL = PIL.Image.fromarray(bbox_array, 'RGBA')
iobuf = io.BytesIO()
# format bbox into png for return
bbox_PIL.save(iobuf, format='png')
# format return string
bbox_bytes = 'data:image/png;base64,{}'.format((str(b64encode(iobuf.getvalue()), 'utf-8')))
return bbox_bytes
- Convert Array to PIL Image: Hàm chuyển đổi mảng NumPy đầu vào (được giả sử ở định
dạng RGBA) thành Hình ảnh PIL với chế độ 'RGBA'.
- Create Binary Stream: Một luồng nhị phân trong bộ nhớ (‘io.BytesIO()’) được tạo.
- Save Image to Binary Stream:: Hình ảnh PIL được lưu vào luồng nhị phân ở định dạng
PNG.
7
- Convert to Base64 Byte String: Luồng nhị phân sau đó được chuyển đổi thành chuỗi byte
được mã hóa base64.
Kết quả ‘bbox_byte’ có thể được sử dụng để phủ hình chữ nhật trên luồng video bằng cách
nhúng nó vào HTML hoặc sử dụng nó trong ứng dụng web.
face_cascade = cv2.CascadeClassifier(cv2.samples.findFile(cv2.data.haarcascades +
'haarcascade_frontalface_default.xml'))
def take_photo(filename='photo.png', quality=0.8):
js = Javascript('''
async function takePhoto(quality) {
const div = document.createElement('div');
const capture = document.createElement('button');
capture.textContent = 'Capture';
div.appendChild(capture);
const video = document.createElement('video');
video.style.display = 'block';
const stream = await navigator.mediaDevices.getUserMedia({video: true});
document.body.appendChild(div);
div.appendChild(video);
video.srcObject = stream;
await video.play();

// Resize the output to fit the video element.


google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);

// Wait for Capture to be clicked.


await new Promise((resolve) => capture.onclick = resolve);
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
stream.getVideoTracks()[0].stop();
div.remove();
return canvas.toDataURL('image/jpeg', quality);
}
''')
display(js)
Ở bước này, chúng ta cung cấp một chức năng để chụp ảnh từ webcam của người dùng bằng
môi trường Google Colab. Hàm này được đặt tên là takePhoto và nhận hai tham số tùy chọn
như sau: tên tệp mặc định là 'photo.png' và chất lượng mặc định là 0,8. Chức năng này thiết
lập một nút để chụp ảnh, khởi tạo thành phần video để hiển thị nguồn cấp dữ liệu webcam
và trả về hình ảnh đã chụp dưới dạng URL dữ liệu ở định dạng JPEG. Để sử dụng hàm này
trong Jupyter, có thể gọi nó từ ô mã sau khi chạy mã được cung cấp.
Thao tác này sẽ hiển thị nút "Chụp" và khi nhấp vào nút này, nó sẽ chụp ảnh từ webcam và
trả về URL dữ liệu của hình ảnh. Chúng ta có thể tùy chỉnh tên tệp và thông số chất lượng nếu
cần. Mã này được thiết kế cho môi trường Google Colab và dựa trên các tính năng dành riêng
cho môi trường đó, chẳng hạn như google.colab.output.setIframeHeight. Nếu sử dụng một
môi trường khác, ta cần phải điều chỉnh code.
8
# get photo data
data = eval_js('takePhoto({})'.format(quality))
# get OpenCV format image
img = js_to_image(data)
# grayscale img
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
print(gray.shape)
# get face bounding box coordinates using Haar Cascade
faces = face_cascade.detectMultiScale(gray)
# draw face bounding box on image
for (x,y,w,h) in faces:
img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
# save image
cv2.imwrite(filename, img)
return filename
Khi chụp ảnh khuôn mặt và chuyển hình ảnh về thang độ xám xong, phát hiện các khuôn mặt
bằng bộ phân loại Haar Cascade, vẽ hình chữ nhật xung quanh các khuôn mặt được phát hiện,
sau đó là lưu hình ảnh thu được và trả về kết quả.
try:
filename = take_photo('photo.png')
print('Saved to {}'.format(filename))

# Show the image which was just taken.


display(Image(filename))
except Exception as err:
# Errors will be thrown if the user does not have a webcam or if they do not
# grant the page permission to access it.
print(str(err))
Ở bước này, máy sẽ hiện thị hình ảnh mà nó vừa chụp được hoặc báo lỗi khi máy tính không
có webcam hoặc người dùng không cấp quyền truy cập để chụp ảnh.
from PIL import Image
from keras.preprocessing.image import load_img
Nhập lớp hình ảnh từ thư viện PIL và hàm Load_img từ mô đun keras.preprocessing.image.
def get_image_features2(image):
img = load_img(image, grayscale=True)
img = img.resize((128, 128), Image.ANTIALIAS) # Use Image.ANTIALIAS directly in resize
img = np.array(img)
img = img.reshape(1, 128, 128, 1)
return img
Xác định hàm get_image_features2 để lấy đường dẫn tệp hình ảnh làm đầu vào, tải hình ảnh
bằng hàm Load_img của Keras, thực hiện một số quy trình trước khi xử lý hình ảnh và sau đó
trả về hình ảnh đã xử lý dưới dạng mảng NumPy. Định hình lại mảng hình ảnh thành (1, 128,
128, 1) và kích thước 128x128 pixel.
img_to_test2 = '/content/photo.png'
features = get_image_features2(img_to_test2)
pred = model.predict(features)
gender = gender_mapping[round(pred[0][0][0])]
9
age = round(pred[1][0][0])

plt.title(f'Predicted Age: {age} Predicted Gender: {gender}')


plt.axis('off')
plt.imshow(np.array(load_img(img_to_test2)))
Sử dụng hàm get_image_features2 để xử lý trước một hình ảnh, sau đó sử dụng một model để
đưa ra dự đoán về hình ảnh được xử lý trước. Độ tuổi và giới tính dự đoán sau đó được hiển
thị bằng matplotlib. Trước khi xử lý, đảm bảo đã nhập các thư viện cần thiết, bao gồm
matplotlib.pyplot, các biến mô hình và ánh xạ giới tính được xác định trước khi chạy mã này
và cần phải kiểm tra xem đường dẫn và tên tệp có chính xác không.

4. KHÓ KHĂN VÀ GIẢI QUYẾT


- Trong quá trình thực hiện bài tập nhóm lần này, nhóm chúng em nhận thấy phần khó khăn
nhất của bài tập là quá trình train AI. Đầu tiên là do dữ liệu lớn nên tốc độ xử lý lúc train
rất chậm có lúc lên đến mấy tiếng và dễ bị lỗi. Do tốc độ xử lý của các máy khác nhau vậy
nên để giải quyết khó khăn này nhóm em dùng nhiều máy để chạy nhiều phương án viết
code để tối ưu hoá thời gian nhất có thể.
- Vấn đề thứ hai, nhóm em làm trên Google Colab, khi viết code rất dễ bị lỗi và reset toàn
bộ. Lúc đó chúng em phải chạy lại từng phần từ đầu rất mất thời gian. Để khắc phục việc
đó chúng em phải làm 2,3 notebook khác để chạy thử code để sửa lỗi sai mà không bị reset
phần trên.

10

You might also like