You are on page 1of 28

Chương 6: Chạy tinh

chỉnh tham số quy mô


lớn
Hyperparameter tuning hay hyperparameter optimization(HPO) là một quy
trình tìm ra cấu trúc mạng nơ-ron sâu tốt nhất, loại mô hình được tiền huấn
luyện và quy trình huấn luyện mô hình bên trong một ràng buộc tài nguyên
tính toán hợp lý và khung thời gian. Ở đây, siêu tham số đề cập đến các tham
số không thể thay đổi hoặc học được trong quá trình huấn luyện ML, chẳng
hạn như số lớp bên trong một mạng nơ-ron sâu, lựa chọn mô hình ngôn ngữ
được tiền huấn luyện, hoặc tốc độ học, kích thước batch, và bộ tối ưu hóa của
quy trình huấn luyện. Trong chương này, chúng ta sẽ sử dụng HPO như một
cách viết tắt để đề cập đến quá trình tinh chỉnh siêu tham số và tối ưu hóa.
HPO là một bước quan trọng để tạo ra một mô hình ML/DL hiệu suất cao. Với
không gian tìm kiếm của siêu tham số rất lớn, việc chạy HPO hiệu quả ở quy
mô lớn là một thách thức lớn. Sự phức tạp và chi phí cao của việc đánh giá
một mô hình DL, so với các mô hình ML cổ điển, làm tăng thêm những thách
thức. Do đó, chúng ta sẽ cần học các phương pháp HPO tiên tiến và khung
thực thi, triển khai các phương pháp HPO ngày càng phức tạp và có khả năng
mở rộng, và theo dõi chúng với MLflow để đảm bảo quá trình tinh chỉnh có
thể tái tạo được. Đến cuối chương này, bạn sẽ thoải mái với việc triển khai
HPO có khả năng mở rộng cho các đường ống mô hình DL.

Trong chương này, chúng ta sẽ tìm hiểu về các khung thức HPO tự động khác
nhau và ứng dụng của việc tinh chỉnh mô hình DL. Ngoài ra, chúng ta sẽ hiểu
rõ về việc tối ưu hóa điều gì và khi nào chọn khung thức nào để sử dụng.
Chúng ta sẽ so sánh ba khung thức HPO phổ biến: HyperOpt, Optuna và Ray
Tune. Chúng ta sẽ chỉ ra khung thức nào trong số này là lựa chọn tốt nhất cho
việc chạy HPO ở quy mô lớn. Tiếp theo, chúng ta sẽ tập trung vào việc học
cách tạo mã mô hình DL sẵn sàng cho HPO có thể sử dụng Ray Tune và
MLflow. Sau đó, chúng ta sẽ chỉ ra cách chuyển sang sử dụng các thuật toán
HPO khác một cách dễ dàng với Optuna là một ví dụ chính.

Chúng ta sẽ bao quát các chủ đề sau:


- Hiểu về HPO tự động cho các đường ống DL
- Tạo mã mô hình DL sẵn sàng cho HPO bằng Ray Tune và MLflow
- Chạy thử nghiệm HPO Ray Tune đầu tiên với MLflow
- Chạy HPO Ray Tune với Optuna và HyperBand

Yêu cầu kỹ thuật


Để hiểu các ví dụ trong chương này, cần có các yêu cầu kỹ thuật chính sau:
 Ray Tune 1.9.2: This is a flexible and powerful hyperparameter tuning
framework (https://docs.ray.io/en/latest/tune/index.html) .
 Optuna 2.10.0: This is an imperative and define-by-run hyperparameter
tuning Python package (https://optuna.org/).
 The code for this chapter can be found in the following GitHub URL, which
also includes the requirements.txt file that contains the preceding key
packages and other dependencies:
https://github.com/PacktPublishing/Practical-Deep- Learning-at-Scale-
with-MLFlow/tree/main/chapter06.

Hiểu về Tinh chỉnh siêu


tham số tự động cho
các ống dẫn học sâu
Tự động tối ưu hóa siêu tham số (HPO) đã được nghiên cứu trong hơn hai
thập kỷ kể từ khi bài báo đầu tiên về chủ đề này được xuất bản vào năm 1995.
(https://www.sciencedirect.com/science/article/pii/
B9781558603776500451) . Việc điều chỉnh siêu tham số cho mô hình học máy
đã được hiểu rộng rãi có thể cải thiện hiệu suất của mô hình - đôi khi là một
cách đáng kể. Sự phát triển của các mô hình học sâu (DL) trong những năm
gần đây đã gây ra một làn sóng đổi mới và phát triển các khung công cụ mới
để giải quyết HPO cho các đường ống DL quy mô lớn. Điều này bởi vì một
đường ống mô hình DL đặt ra nhiều thách thức tối ưu hóa mới và quy mô lớn
mà không thể dễ dàng giải quyết bằng các phương pháp HPO trước đây. Lưu ý
rằng, khác với các tham số mô hình có thể được học trong quá trình huấn
luyện mô hình, một tập hợp các siêu tham số phải được đặt trước quá trình
huấn luyện.
SỰ KHÁC BIỆT GIỮA TỐI ƯU HÓA SIÊU THAM SỐ VÀ TINH CHỈNH FE-FINE-
TUNING TRONG HỌC CHUYỂN GIAO

Trong cuốn sách này, chúng tôi tập trung vào một phương pháp học sâu thành
công được gọi là Học chuyển giao (vui lòng tham khảo Chương 1, Vòng đời
Học sâu và Thách thức MLOps để có cuộc thảo luận đầy đủ). Bước chính trong
quá trình học chuyển giao là tinh chỉnh lại một mô hình đã được huấn luyện
trước đó bằng dữ liệu được gán nhãn cụ thể cho nhiệm vụ và miền tương ứng
để có được một mô hình Học sâu cụ thể cho nhiệm vụ đó. Tuy nhiên, bước
tinh chỉnh lại chỉ là một loại đặc biệt của bước huấn luyện mô hình mà cũng có
rất nhiều siêu tham số để tối ưu hóa. Đó là nơi Tối ưu hóa Siêu tham số (HPO)
được áp dụng.

Các loại siêu tham số và những thách


thức của chúng
Có một số loại siêu tham số phổ biến được sử dụng trong các quy trình học
sâu:

 Loại và kiến trúc mô hình học sâu: Trong trường hợp chuyển giao học,
việc chọn mô hình được trước để sử dụng là một siêu tham số có thể.
Ví dụ, có hơn 27,000 mô hình được trước trong kho mô hình Hugging
Face (https://huggingface.co/models) , bao gồm BERT, RoBERTa, và
nhiều hơn nữa. Đối với một nhiệm vụ dự đoán cụ thể, chúng ta có thể
thử một số mô hình để xác định mô hình nào là tốt nhất để sử dụng.
 Các tham số liên quan đến quá trình học và huấn luyện: Bao gồm các
loại tối ưu hóa khác nhau như stochastic gradient descent (SGD) và
Adam (bạn có thể xem danh sách các tối ưu hóa PyTorch tại
https://machinelearningknowledge.ai/pytorch-optimizers-complete-
guide-for-beginner/) . Nó cũng bao gồm các tham số liên quan như tốc
độ học và kích thước batch. Đề nghị, khi áp dụng, các tham số sau đây
nên được điều chỉnh theo thứ tự quan trọng đối với một mô hình mạng
nơ-ron: tốc độ học, động lượng, kích thước mini-batch, số lớp ẩn, suy
giảm tốc độ học và chế độ chính quy
(https://arxiv.org/abs/2003.05689).
 Cấu hình dữ liệu và quy trình: Một quy trình học sâu có thể bao gồm
các bước xử lý và biến đổi dữ liệu có thể ảnh hưởng đến quá trình huấn
luyện mô hình. Ví dụ, nếu chúng ta muốn so sánh hiệu suất của một mô
hình phân loại cho một tin nhắn email có hoặc không có phần nội dung
chữ ký, thì cần có một siêu tham số để quyết định xem có bao gồm chữ
ký email hay không. Một ví dụ khác là khi chúng ta không có đủ dữ liệu
hoặc biến thể dữ liệu; chúng ta có thể thử nghiệm các kỹ thuật tăng
cường dữ liệu khác nhau, dẫn đến các tập hợp đầu vào khác nhau cho
quá trình huấn luyện mô hình (https://neptune.ai/blog/data-
augmentation-nlp) .

Nhắc lại, không phải tất cả các siêu tham số đều cần được điều chỉnh hoặc có
thể điều chỉnh. Ví dụ, không cần phải điều chỉnh số epoch trong một mô hình
Deep Learning. Điều này bởi vì quá trình huấn luyện nên dừng lại khi chỉ số độ
chính xác không còn cải thiện hoặc không có triển vọng để tốt hơn so với các
cấu hình siêu tham số khác. Điều này được gọi là early stopping hoặc pruning
và là một trong những kỹ thuật chính của một số thuật toán tối ưu siêu tham
số (để biết thêm về early stopping, vui lòng tham khảo
https://databricks.com/blog/2019/08/15/how-not-to-scale-deep-learning-in-
6-easy-steps.html) . Lưu ý rằng cả ba loại siêu tham số này có thể được kết
hợp và kết hợp với nhau, và cấu hình của toàn bộ không gian siêu tham số có
thể rất lớn. Ví dụ, nếu chúng ta muốn chọn loại mô hình được huấn luyện
trước chúng ta muốn sử dụng làm siêu tham số (ví dụ, lựa chọn có thể là BERT
hoặc RoBERTa), hai siêu tham số liên quan đến quá trình học (như tốc độ học
và kích thước batch), và hai kỹ thuật tăng cường dữ liệu khác nhau cho văn
bản NLP (ví dụ như chèn ngẫu nhiên và thay thế từ đồng nghĩa), thì chúng ta
có năm siêu tham số để tối ưu. Lưu ý rằng mỗi siêu tham số có thể có nhiều
giá trị khác nhau để lựa chọn, và nếu mỗi siêu tham số có 5 giá trị khác nhau,
chúng ta sẽ có tổng cộng 55 = 3125 kết hợp của siêu tham số để thử nghiệm.
Trong thực tế, rất phổ biến khi có hàng chục siêu tham số cần thử, và mỗi siêu
tham số có thể có hàng chục lựa chọn hoặc phân phối để lấy mẫu. Điều này
nhanh chóng dẫn đến vấn đề về chiều của không gian tìm kiếm
(https://insaid.medium.com/automated-hyperparameter-tuning-
988b5aeb7f2a) . Thách thức về không gian tìm kiếm có số chiều cao này được
gia tăng bởi chi phí huấn luyện và đánh giá đắt đỏ của các mô hình Deep
Learning; chúng ta biết rằng chỉ 1 epoch của một mô hình BERT nhỏ, mà chúng
ta đã thử trong các chương trước, với một tập dữ liệu huấn luyện và kiểm tra
nhỏ có thể mất từ 1-2 phút. Bây giờ hãy tưởng tượng một mô hình Deep
Learning chất lượng sản xuất thực tế với HPO có thể mất giờ, ngày hoặc thậm
chí tuần nếu không được thực hiện một cách hiệu quả. Nói chung, dưới đây là
danh sách các thách thức chính đòi hỏi áp dụng HPO hiệu năng cao ở quy mô
lớn:
 Không gian tìm kiếm siêu chiều của siêu tham số

 Chi phí cao của quá trình huấn luyện mô hình và thời gian đánh giá cho
các mô hình học sâu ngày càng lớn

 Thời gian đưa sản phẩm vào vận hành và triển khai cho các mô hình
học sâu được sử dụng trong sản xuất

THỰC HIỆN HUẤN LUYỆN MÔ HÌNH VÀ TÌM KIẾM SIÊU THAM SỐ ĐỒNG THỜI
Có thể thay đổi siêu tham số một cách động trong quá trình huấn luyện. Đây là
một phương pháp kết hợp thực hiện huấn luyện mô hình và tìm kiếm siêu
tham số đồng thời, như Population-Based Training (PBT)
(https://deepmind.com/blog/article/population-based-training-neural-
networks) . Tuy nhiên, điều này không thay đổi thực tế rằng khi bắt đầu một
epoch huấn luyện mới, cần xác định trước một tập hợp các siêu tham số. PBT
là một trong những đổi mới nhằm giảm cả chi phí tìm kiếm trong không gian
siêu tham số có số chiều cao và chi phí huấn luyện mô hình học sâu. Độc giả
quan tâm nên tham khảo phần Đọc thêm để tìm hiểu sâu hơn về chủ đề này.
Bây giờ khi đã hiểu về những thách thức và danh mục chung của siêu tham số
cần tối ưu, chúng ta hãy xem cách HPO hoạt động và làm thế nào để chọn một
framework cho ứng dụng của chúng ta.

Bây giờ khi chúng ta đã hiểu về những thách thức chung và các danh mục của
siêu tham số cần tối ưu, hãy xem cách tối ưu hóa siêu tham số (HPO) hoạt
động và cách chọn một framework cho ứng dụng của chúng ta.

Cách HPO hoạt động và lựa chọn


phương pháp HPO

Có nhiều cách khác nhau để hiểu cách HPO hoạt động. Các phương pháp HPO
cổ điển bao gồm tìm kiếm lưới (grid search) và tìm kiếm ngẫu nhiên (random
search), trong đó một tập hợp siêu tham số được chọn với một phạm vi giá trị
ứng cử viên. Mỗi siêu tham số được chạy độc lập cho đến khi hoàn thành, sau
đó chúng ta chọn cấu hình siêu tham số tốt nhất từ tập các thử nghiệm đã
chạy, dựa trên chỉ số hiệu suất của mô hình tốt nhất mà chúng ta tìm thấy.
Mặc dù phương pháp tìm kiếm này dễ triển khai và có thể không yêu cầu một
framework phức tạp để hỗ trợ, nhưng nó có tính không hiệu quả và có thể
không tìm ra cấu hình siêu tham số tốt nhất do tính phi nhân tuyến của HPO.
Thuật ngữ phi nhân tuyến có nghĩa là tồn tại nhiều điểm cực tiểu hoặc cực đại
cục bộ, và một phương pháp tối ưu hóa có thể không thể tìm ra một giá trị cực
đại (tức là tối thiểu hoặc tối đa) toàn cục. Đơn giản nói, một HPO hiện đại cần
làm hai việc:

● Việc lấy mẫu siêu tham số theo cách thích nghi (còn được gọi là Lựa
chọn Cấu hình hoặc CS): Điều này có nghĩa là cần tìm ra bộ siêu tham số nào
để thử nghiệm bằng cách tận dụng kiến thức trước đó. Điều này chủ yếu liên
quan đến việc sử dụng các phương pháp tối ưu hóa Bayesian khác nhau để tự
động xác định các cấu hình mới dựa trên các thử nghiệm trước đó một cách
tuần tự. Điều này đã được chứng minh có hiệu suất cao hơn so với các
phương pháp tìm kiếm lưới truyền thống và tìm kiếm ngẫu nhiên.
● Việc đánh giá thích nghi hiệu suất của một bộ siêu tham số (còn được
gọi là Đánh giá Cấu hình hoặc CE): Các phương pháp này tập trung vào việc
phân bổ nguồn lực một cách thích nghi cho các cấu hình siêu tham số triển
vọng trong khi loại bỏ nhanh chóng những cấu hình kém. Tài nguyên có thể có
dạng khác nhau như kích thước tập dữ liệu huấn luyện (ví dụ: chỉ sử dụng một
phần nhỏ của tập dữ liệu huấn luyện) hoặc số lần lặp (ví dụ: chỉ sử dụng một
số lần lặp để quyết định các thử nghiệm nào cần dừng mà không chạy đến hội
tụ). Có một họ phương pháp được gọi là thuật toán multi-armed bandit, chẳng
hạn như thuật toán Asynchronous Successive Halving (ASHA). Ở đây, tất cả
các thử nghiệm bắt đầu với nguồn lực ban đầu, sau đó nửa tồi nhất được loại
bỏ, nguồn lực được điều chỉnh cho các thử nghiệm còn lại, và quá trình này
lặp lại cho đến khi chỉ còn một thử nghiệm.

Trong thực tế, chúng ta muốn chọn một framework HPO phù hợp sử dụng
năm tiêu chí sau đây:
● Tích hợp Callback với MLflow
● Khả năng mở rộng và hỗ trợ cụm GPU
● Dễ sử dụng và API linh hoạt
● Tích hợp với các thuật toán HPO tiên tiến (CS và CE)
● Hỗ trợ các framework học sâu (DL)

Trong cuốn sách này, đã so sánh ba framework và kết quả được tổng hợp
trong Figure 6.1
Figure 6.1: Comparison of Ray Tune, Optuna, and HyperOpt

Ray Tune(https://docs.ray.io/en/latest/tune/index.html) là giải pháp vượt trội so


với Optuna (https://optuna.org/) và HyperOpt
(https://hyperopt.github.io/hyperopt/) , như được trình bày trong Hình 6.1. Hãy
giải thích năm tiêu chí như sau:
 Tích hợp Callback với MLflow: Optuna chỉ hỗ trợ tích hợp callback với
MLflow dưới dạng tính năng thử nghiệm, trong khi HyperOpt không hỗ
trợ callback, đòi hỏi người dùng phải tự quản lý việc theo dõi MLflow
cho mỗi lần thử nghiệm.

Ray Tune là duy nhất hỗ trợ cả mixin decorator Python và tích hợp callback với
MLflow. Mixin decorator Python là một mẫu thiết kế cho phép một hàm độc
lập được kết hợp vào bất kỳ lúc nào khi cần thiết. Trong trường hợp này, chức
năng của MLflow được tự động kết hợp vào trong quá trình huấn luyện mô
hình thông qua decorator mlflow_mixin. Điều này giúp chuyển đổi bất kỳ hàm
huấn luyện nào thành một hàm huấn luyện có thể được sử dụng trong Ray
Tune, tự động cấu hình MLflow và tạo một lần chạy trong cùng quá trình của
mỗi lần thử nghiệm Tune. Bạn có thể sử dụng API của MLflow bên trong hàm
huấn luyện và nó sẽ tự động được báo cáo cho lần chạy đúng. Ngoài ra, Ray
Tune hỗ trợ cả MLflow autologging, điều này có nghĩa là tất cả thông tin theo
dõi của MLflow sẽ được ghi vào lần thử nghiệm đúng. Ví dụ, đoạn mã sau cho
thấy hàm fine-tuning DL trước đó có thể được chuyển thành một hàm Ray
Tune mlflow_mixin, như sau:
@mlflow_mixin
def train_dl_model():
mlflow.pytorch.autolog()
trainer = flash.Trainer(
max_epochs=num_epochs,
callbacks=[TuneReportCallback(
metrics, on='validation_end')])
trainer.finetune()

Lưu ý rằng khi chúng ta định nghĩa bộ huấn luyện, chúng ta có thể thêm
TuneReportCallback như một trong các callback, điều này sẽ truyền các chỉ số
về lại Ray Tune, trong khi MLflow autologging thực hiện công việc ghi log tất
cả kết quả theo dõi đồng thời. Trong phần tiếp theo, chúng tôi sẽ chỉ cho bạn
cách chuyển ví dụ về fe-tune mô hình DL ở chương trước thành một Ray Tune
trainable.
 Khả năng mở rộng và hỗ trợ của các cụm GPU: Mặc dù Optuna và
HyperOpt hỗ trợ song song hóa, cả hai đều phụ thuộc vào một số cơ sở dữ
liệu bên ngoài (cơ sở dữ liệu quan hệ hoặc MongoDB) hoặc SparkTrials.
Chỉ có Ray Tune hỗ trợ HPO song song và phân tán thông qua Ray
distributed framework một cách tự nhiên, và cũng là duy nhất hỗ trợ chạy
trên một cụm GPU trong số ba nền tảng này.
 Dễ sử dụng và tính linh hoạt của các API: Trong số tất cả ba nền tảng này,
chỉ Optuna hỗ trợ các API define-by-run, cho phép bạn định nghĩa động
các siêu tham số theo kiểu lập trình Pythonic, bao gồm vòng lặp và nhánh.
Điều này khác với các API define-and-run, mà cả Ray Tune và HyperOpt hỗ
trợ, trong đó không gian tìm kiếm được định nghĩa bởi một từ điển được
xác định trước trước khi đánh giá hàm mục tiêu. Hai thuật ngữ này,
define-by-run và define-and-run, thực ra được đặt tên bởi cộng đồng phát
triển các framework DL. Trong những ngày đầu, khi TensorFlow 1.0 được
phát hành ban đầu, một mạng neural cần được định nghĩa trước và sau đó
thực thi theo cách lười biếng sau này, gọi là define-and-run. Hai giai đoạn
này, 1) giai đoạn xây dựng mạng neural và 2) giai đoạn đánh giá, được
thực thi tuần tự, và cấu trúc mạng neural không thể thay đổi sau giai đoạn
xây dựng. Các framework DL mới hơn, chẳng hạn như TensorFlow 2.0
(hoặc phiên bản eager execution của TensorFlow) và PyTorch, hỗ trợ tính
toán mạng neural define-by-run. Không có hai giai đoạn riêng biệt để xây
dựng và đánh giá mạng neural. Người dùng có thể trực tiếp thao tác với
mạng neural trong quá trình tính toán. Mặc dù API define-by-run được
cung cấp bởi Optuna có thể được sử dụng để định nghĩa trực tiếp không
gian tìm kiếm siêu tham số một cách động, nó cũng có một số hạn chế.
Vấn đề chính là sự cùng lúc của các tham số không được biết trước cho
đến thời gian chạy, điều này có thể làm phức tạp việc triển khai phương
pháp tối ưu hóa. Điều này bởi vì việc biết trước sự cùng lúc của các tham
số được hỗ trợ tốt cho nhiều phương pháp. Do đó, trong cuốn sách này,
chúng tôi ưu tiên sử dụng các API define-and-run. Lưu ý rằng Ray Tune có
thể hỗ trợ API define-by-run thông qua việc tích hợp với Optuna (bạn có
thể xem một ví dụ trong kho GitHub của Ray Tune tại địa chỉ
https://github.com/ray-project/ray/blob/master/python/ray/tune/examples/
optuna_define_by_run_example.py#L35).
 Tích hợp với các thuật toán HPO tiên tiến (CS và CE): Về phía CS, trong ba
framework này, HyperOpt có sự phát triển ít nhất để hỗ trợ hoặc tích hợp
với các phương pháp tìm kiếm và lấy mẫu HPO tiên tiến nhất. Phương
pháp tìm kiếm chính của nó là Tree-Structured Parzen Estimators (TPE),
một biến thể của tối ưu hóa Bayesian đặc biệt hiệu quả cho không gian
tìm kiếm siêu tham số hỗn hợp gồm các tham số hạng mục và điều kiện.
Tương tự, phương pháp lấy mẫu chính của Optuna cũng là TPE. Ngược lại,
Ray Tune hỗ trợ tất cả các phương pháp tìm kiếm tiên tiến, bao gồm các
phương pháp sau đây:
 DragonFly (https://dragonfly-opt.readthedocs.io/en/master/) ,
một framework tối ưu hóa Bayesian có khả năng mở rộng cao.
 BlendSearch (https://microsoft.github.io/FLAML/docs/Use-
Cases/Tune-User-Defined-Function/#hyperparameter-
optimization-algorithm) từ Microsoft Research.
Ngoài ra, Ray Tune cũng hỗ trợ TPE thông qua việc tích hợp với Optuna và
HyperOpt.

Về phía CE, HyperOpt không hỗ trợ bất kỳ công cụ cắt tỉa hoặc lập lịch nào để
dừng các cấu hình siêu tham số không triển vọng. Cả Optuna và Ray Tune đều
hỗ trợ khá nhiều công cụ cắt tỉa (trong Optuna) hoặc lập lịch (trong Ray Tune).
Tuy nhiên, chỉ có Ray Tune hỗ trợ PBT. Với cộng đồng phát triển tích cực và
API linh hoạt được phát triển bởi Ray Tune, Ray Tune có thể tiếp tục tích hợp
và hỗ trợ bất kỳ công cụ cắt tỉa hoặc lập lịch mới nào một cách kịp thời.
 Hỗ trợ các framework DL (Deep Learning): HyperOpt không được
thiết kế hoặc tích hợp cụ thể với bất kỳ framework DL nào. Điều
này không có nghĩa là bạn không thể sử dụng HyperOpt để tinh
chỉnh mô hình DL. Tuy nhiên, HyperOpt không cung cấp bất kỳ
công cụ cắt tỉa hoặc lập lịch nào để thực hiện dừng sớm cho các
cấu hình siêu tham số không triển vọng, đây là một nhược điểm
lớn của HyperOpt khi được sử dụng cho tinh chỉnh mô hình DL. Cả
Ray Tune và Optuna đều tích hợp với các framework DL phổ biến
như PyTorch Lightning và TensorFlow/Keras.
Ngoài những tiêu chí quan trọng mà chúng ta vừa thảo luận, Ray Tune cũng có
tài liệu tốt nhất, ví dụ mã nguồn phong phú và một cộng đồng phát triển mã
nguồn mở sôi nổi, đó là lý do tại sao chúng ta ưu tiên sử dụng Ray Tune cho
quá trình học trong chương này. Trong các phần tiếp theo, chúng ta sẽ tìm
hiểu cách tạo ra các mô hình DL sẵn sàng cho HPO với Ray Tune và MLflow.

Tạo mô hình DL sẵn


sàng cho HPO với Ray
Tune và MLflow
Để sử dụng Ray Tune cùng với MLflow cho tối ưu hóa siêu tham số
(hyperparameter optimization), chúng ta sẽ sử dụng bước tinh chỉnh (fine-
tuning) trong ví dụ về pipeline học sâu (deep learning pipeline) trong Chương
5, Chạy các Pipeline Học Sâu trên Môi Trường Khác Nhau. Trước khi bắt đầu,
hãy xem xét một số khái niệm quan trọng liên quan đặc biệt đến việc sử dụng
Ray Tune:

 Hàm mục tiêu (Objective function): Một hàm mục tiêu có thể là việc tối
thiểu hoặc tối đa hóa một số giá trị số liệu cho một cấu hình cụ thể của
siêu tham số. Ví dụ, trong các kịch bản huấn luyện và tinh chỉnh mô
hình học sâu, chúng ta muốn tối đa hóa F1-score để đạt được độ chính
xác của một bộ phân loại văn bản NLP. Hàm mục tiêu này cần được bao
bọc như một hàm có thể huấn luyện (trainable function), trong đó Ray
Tune có thể thực hiện tối ưu hóa siêu tham số. Trong phần tiếp theo,
chúng tôi sẽ minh họa cách bao bọc mô hình phân tích cảm xúc văn bản
NLP của chúng tôi.
 API dựa trên hàm và API dựa trên lớp: Một API dựa trên hàm cho
phép người dùng chèn các câu lệnh Ray Tune vào trong hàm huấn
luyện mô hình (gọi là trainable trong Ray Tune), ví dụ như tune. report
để báo cáo các số liệu mô hình
(https://docs.ray.io/en/latest/tune/api_docs/trainable.html#function-
api) . Một API dựa trên lớp yêu cầu hàm huấn luyện mô hình (trainable)
là một lớp con của tune. Trainable
(https://docs.ray.io/en/latest/tune/api_docs/trainable.html#trainable-
class-api) . API dựa trên lớp cung cấp sự kiểm soát hơn về cách Ray
Tune điều khiển quá trình huấn luyện mô hình. Điều này có thể rất hữu
ích nếu bạn bắt đầu viết một kiến trúc mới cho một mô hình mạng nơ-
ron. Tuy nhiên, khi sử dụng một mô hình cơ sở được huấn luyện trước
để tinh chỉnh, việc sử dụng API dựa trên hàm dễ dàng hơn nhiều vì
chúng ta có thể tận dụng các gói như PyTorch Lightning Flash để thực
hiện tối ưu hóa siêu tham số.
 Thử nghiệm (Trials): Mỗi thử nghiệm là một lần chạy với một cấu hình
cụ thể của siêu tham số. Điều này có thể được thực hiện bằng cách
truyền hàm có thể huấn luyện vào tune.run, trong đó Ray Tune sẽ điều
phối quá trình tối ưu hóa siêu tham số.
 Không gian tìm kiếm: Đây là một tập hợp các cấu hình, trong đó mỗi
siêu tham số sẽ được gán một cách để lấy mẫu từ các phân phối nhất
định (ví dụ: lấy mẫu từ phân phối log-uniform có thể sử dụng tune.
loguniform) hoặc từ một số biến hạng mục (ví dụ: tune. choice (['a',
'b’,'c']) cho phép bạn chọn từ ba lựa chọn này theo phân phối đều).
Thông thường, không gian tìm kiếm này được định nghĩa dưới dạng
một biến từ điển Python được gọi là "config".
 Gợi ý (Suggest): Đây là thuật toán tìm kiếm hoặc thuật toán CS mà bạn
cần chọn để lựa chọn thử nghiệm tốt nhất. Ray Tune cung cấp tích hợp
với nhiều thuật toán tìm kiếm mã nguồn mở phổ biến và có thể tự
động chuyển đổi không gian tìm kiếm được định nghĩa trong Ray Tune
thành định dạng mà các thuật toán tối ưu hóa dưới lying mong đợi.
Một danh sách các thuật toán tìm kiếm có sẵn có thể được tìm thấy
thông qua API tune.suggest
(https://docs.ray.io/en/latest/tune/api_docs/suggestion.html#tune-
search-alg).
 Lập lịch (Scheduler): Đây cũng được gọi là CE, như đã đề cập trước đó.
Trong khi API tune. suggest cung cấp các thuật toán tối ưu hóa cho việc
tìm kiếm, nó không cung cấp khả năng dừng sớm hoặc cắt tỉa để dừng
các thử nghiệm không triển vọng sau chỉ vài lần lặp. Vì dừng sớm hoặc
cắt tỉa có thể tăng đáng kể tốc độ quá trình tối ưu hóa siêu tham số,
nên rất khuyến khích sử dụng một lập lịch kết hợp với một công cụ tìm
kiếm. Ray Tune cung cấp nhiều lập lịch phổ biến thông qua API lập lịch
của nó (tune. schedulers), chẳng hạn như ASHA, HyperBand và nhiều
hơn nữa. (Vui lòng truy cập
https://docs.ray.io/en/latest/tune/api_docs/schedulers.html#trial-
schedulers-tune-schedulers .)

Sau khi đã xem xét các khái niệm cơ bản và API của Ray Tune, trong phần tiếp
theo, chúng ta sẽ thiết lập Ray Tune và MLflow để chạy các thí nghiệm tối ưu
hóa siêu tham số.
Thiết lập Ray Tune và MLflow
Bây giờ chúng ta đã hiểu được các khái niệm cơ bản và các API của Ray Tune,
hãy xem làm thế nào chúng ta có thể thiết lập Ray Tune để thực hiện HPO cho
bước tinh chỉnh mô hình NLP phân loại cảm xúc trước đây. Bạn có thể tải mã
nguồn của chương này (https://github.com/PacktPublishing/Practical-Deep-
Learning-at-Scale-with-MLFlow/blob/main/chapter06/) để làm theo các
hướng dẫn sau:

1. Cài đặt Ray Tune bằng cách nhập lệnh sau vào môi trường ảo conda của
bạn, dl_model_hpo:

pip install ray[tune]==1.9.2

2. Điều này sẽ cài đặt Ray Tune trong môi trường ảo nơi bạn sẽ khởi chạy các
quá trình HPO cho việc tinh chỉnh mô hình DL của bạn. Lưu ý rằng chúng tôi
cũng cung cấp tệp requirements.txt đầy đủ trong kho lưu trữ GitHub của
chương này (https://github.com/PacktPublishing/Practical-Deep-Learning-at-
Scale-with-MLFlow/blob/main/chapter06/requirements.txt) , trong đó bạn
nên chạy lệnh cài đặt sau:

pip install -r requirements.txt

3. Hướng dẫn đầy đủ trong tệp README.md, ở cùng thư mục, sẽ cung cấp
hướng dẫn chi tiết hơn nếu bạn cần biết cách thiết lập môi trường ảo đúng
cách.

4. Đối với thiết lập MLflow, giả sử bạn đã có một máy chủ theo dõi MLflow
hoàn chỉnh, điều duy nhất bạn cần chú ý là đảm bảo bạn đã thiết lập các biến
môi trường đúng để truy cập máy chủ theo dõi MLflow. Chạy các lệnh sau
trong shell để thiết lập chúng. Ngoài ra, bạn cũng có thể ghi đè các biến môi
trường bằng cách gọi os.environ["environmental_name"]=value
trong mã Python. Như lời nhắc, chúng tôi đã hiển thị các biến môi trường sau
có thể được thiết lập trong các dòng lệnh cho mỗi phiên Terminal:

export MLFLOW_TRACKING_URI=http://localhost
export MLFLOW_S3_ENDPOINT_URL=http://localhost:9000
export AWS_ACCESS_KEY_ID="minio"
export AWS_SECRET_ACCESS_KEY="minio123"

5. Chạy bước download_data để tải xuống dữ liệu gốc vào thư mục cục bộ
dưới thư mục cha chapter06:
mlflow run . -P pipeline_steps='download_data' --
experiment-name dl_model_chapter06

Khi quá trình thực thi trên kết thúc, bạn sẽ có thể tìm thấy dữ liệu IMDB
trong thư mục chapter06/data/.

Bây giờ chúng ta đã sẵn sàng tạo một bước HPO để tinh chỉnh mô hình NLP
phân loại cảm xúc mà chúng ta đã xây dựng trước đó.

Tạo trainable cho mô hình DL sử dụng


Ray Tune
Để cho phép Ray Tune chạy tối ưu hóa siêu tham số (HPO) để điều chỉnh mô
hình DL chúng ta đã phát triển trong các chương trước, chúng ta cần thực hiện
một số thay đổi. Hãy đi qua các bước như sau:

1. Đầu tiên, chúng ta hãy xác định danh sách các siêu tham số có thể (cả
có thể điều chỉnh và không thể điều chỉnh) trong mã fine-tuning trước
đó. Nhớ lại rằng mã fine-tuning của chúng ta có dạng tương tự như sau
(chỉ hiển thị các dòng mã quan trọng ở đây; mã đầy đủ có thể được tìm
thấy trong chương05 trong kho GitHub tại
https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-
with-MLFlow/blob/main/chapter05/pipeline/
fine_tuning_model.py#L19) :
datamodule = TextClassificationData.from_csv(
input_fields="review",
target_fields="sentiment",
train_file=f"{data_path}/imdb/train.csv",
val_file=f"{data_path}/imdb/valid.csv",
test_file=f"{data_path}/imdb/test.csv")
classifier_model = TextClassifier(
backbone= "prajjwal1/bert-tiny",
num_classes=datamodule.num_classes,
metrics=torchmetrics.F1(datamodule.num_classes))
trainer = flash.Trainer(max_epochs=3)
trainer.finetune(classifier_model,
datamodule=datamodule, strategy="freeze")
Đoạn mã trên có bốn phần chính:
 Biến datamodule: Định nghĩa nguồn dữ liệu cho huấn luyện, đánh giá
và kiểm tra. Có một tham số batch_size có giá trị mặc định là 1, không
được hiển thị ở đây, nhưng nó là một trong những siêu tham số quan
trọng nhất cần điều chỉnh. Để biết thêm chi tiết, vui lòng xem phần giải
thích trong tài liệu mã Lightning-
Flash(https://github.com/PyTorchLightning/lightning-flash/blob/
450902d713980e0edefcfd2d2a2a35eb875072d7/flash/core/data/
data_module.py#L64).
 classifier_model: Đây xác định một mô hình phân loại với các tham số
được tiết lộ thông qua API TextClassifier của lightning-flash. Có nhiều
siêu tham số trong các đối số đầu vào có thể được điều chỉnh, bao gồm
learning_rate, mô hình backbone, bộ tối ưu hóa và nhiều hơn nữa. Bạn
có thể xem danh sách đầy đủ các đối số đầu vào trong tài liệu mã
nguồn lightning-flash cho API TextClassifier
(https://github.com/PyTorchLightning/lightning-flash/blob/450902d71
3980e0edefcfd2d2a2a35eb875072d7/flash/text/classification/
model.py#L44) .
 trainer: Biến này xác định một biến trainer có thể được sử dụng để
fine-tuning (chuyển giao học). Ở đây, có một số siêu tham số cần được
thiết lập, nhưng không nhất thiết phải được điều chỉnh, chẳng hạn như
num_epochs, như đã thảo luận trước đó.
 trainer. finetune: Thực hiện quá trình fine-tuning (chuyển giao học)
thực tế. Lưu ý rằng cũng có một chiến lược siêu tham số có thể được
điều chỉnh.
Cho mục đích học tập, chúng ta sẽ chọn learning_rate và batch_size là hai
siêu tham số cần điều chỉnh, vì hai tham số này là những siêu tham số quan
trọng nhất cần tối ưu hóa cho một mô hình Deep Learning. Khi bạn hoàn
thành chương này, bạn sẽ có thể dễ dàng thêm các siêu tham số khác vào
danh sách các tham số ứng cử để tối ưu hóa.

1. Ray Tune yêu cầu một hàm trainable được chuyển vào tune.run. Điều
này có nghĩa là chúng ta cần tạo một hàm trainable. Mặc định, một
hàm trainable chỉ nhận một tham số đầu vào bắt buộc là config, chứa
một từ điển các cặp khóa-giá trị của siêu tham số và các tham số khác
để xác định môi trường thực thi như một URL theo dõi MLflow. Tuy
nhiên, Ray Tune cung cấp một hàm bao bọc, được gọi là tune.
with_parameters, cho phép bạn truyền thêm các tham số và đối tượng
tùy ý (https://docs.ray.io/en/latest/tune/tutorials/overview.html#how-
can-i-pass-further-parameter-values-to-my-trainable) . Đầu tiên, chúng
ta hãy tạo một hàm gọi là finetuning_dl_model để bao gồm logic mà
chúng ta vừa xem xét về bước fine-tuning, sử dụng một trình bao
mlflow_mixin. Điều này cho phép MLflow được khởi tạo tự động khi
gọi hàm này:
@mlflow_mixin
def finetuning_dl_model(config, data_dir=None,
num_epochs=3,
num_gpus=0):
Hàm này nhận vào một từ điển config làm đầu vào, nơi có thể truyền vào một
danh sách siêu tham số và cấu hình MLflow. Ngoài ra, chúng ta thêm ba đối số
bổ sung vào chữ ký hàm: data_dir để chỉ định vị trí thư mục, num_epochs để
xác định số epoch tối đa cho mỗi thử nghiệm chạy, và num_gpus để xác định
số lượng GPU sử dụng cho mỗi thử nghiệm nếu có.

1. Trong hàm được trang trí bằng mlflow_mixin, chúng ta có thể sử dụng
tất cả các API theo dõi của MLflow nếu cần thiết, nhưng từ phiên bản
MLflow 1.22.0 trở đi, khi hỗ trợ tự động ghi nhật ký của MLflow không
còn là một tính năng thử nghiệm mà là một tính năng chất lượng sản
xuất chín chắn
(https://github.com/mlflow/mlflow/releases/tag/v1.22.0) , chúng ta
nên chỉ sử dụng autologging trong mã của chúng ta, như sau:

mlflow.pytorch.autolog()
Điều này là hiệu quả và không yêu cầu thay đổi nào. Tuy nhiên, siêu tham số
batch_size không được tự động ghi nhận bởi autologging, vì vậy chúng ta cần
thêm một câu lệnh ghi nhật ký sau khi hoàn thành quá trình fine-tuning, như
sau:

mlflow.log_param('batch_size',config['batch_size'])

1. Trong phần thân cài đặt của hàm finetuning_dl_model, phần lớn mã
code giống như trước đây. Tuy nhiên, có một số thay đổi. Trong câu
gán biến datamodule, chúng ta thêm batch_size=config['batch_size']
để cho phép kích thước mini-batch của dữ liệu huấn luyện có thể được
điều chỉnh, như sau:

datamodule = TextClassificationData.from_csv(
input_fields="review",
target_fields="sentiment",
train_file=f"{data_dir}/imdb/train.csv",
val_file=f"{data_dir}/imdb/valid.csv",
test_file=f"{data_dir}/imdb/test.csv",
batch_size=config['batch_size'])

2. Khi xác định biến classifier_model, thay vì sử dụng các giá trị mặc định

của tập hợp siêu tham số, bây giờ chúng ta cần truyền vào từ điển

config để gán các giá trị này:


classifier_model = TextClassifier(
backbone=config['foundation_model'],
learning_rate=config['lr'],
optimizer=config['optimizer_type'],
num_classes=datamodule.num_classes,
metrics=torchmetrics.F1(datamodule.num_classes))

3. Tiếp theo, chúng ta cần sửa đoạn mã gán giá trị cho biến trainer. Ở đây,
chúng ta cần làm hai việc: đầu tiên, chúng ta cần xác định một từ điển
key-value metrics để chuyển từ PyTorch Lightning sang Ray Tune. Key
trong từ điển metrics này là tên được tham chiếu trong quá trình chạy
thử nghiệm của Ray Tune, trong khi giá trị của key trong từ điển này là
tên chỉ số tương ứng được báo cáo bởi PyTorch Lightning.
TÊN CHỈ SỐ TRONG BƯỚC ĐÁNH GIÁ XÁC THỰC CỦA PYTORCH
LIGHTNING
Khi chuyển các chỉ số sang Ray Tune, trước hết, chúng ta cần biết tên
chỉ số được sử dụng trong PyTorch Lightning trong quá trình đánh giá
xác thực, vì HPO chỉ sử dụng dữ liệu xác thực để đánh giá, không sử
dụng dữ liệu kiểm tra. Rất may, PyTorch Lightning cóĐiều này là hiệu
quả và không yêu cầu thay đổi. Một chỉ số được đặt tên là f1 sẽ được
báo cáo trong PyTorch Lightning dưới dạng train_f1 trong quá trình
huấn luyện, val_f1 trong quá trình xác thực và test_f1 trong quá trình
kiểm tra. (Bạn có thể xem mã logic của PyTorch Lightning tại
https://github.com/PyTorchLightning/lightning-flash/blob/8b244d785c5
569e9aa7d2b878a5f94af976d3f55/flash/core/model.py#L462). Trong ví
dụ của chúng ta, chúng ta có thể chọn cross_entropy và f1 là các chỉ số
trong quá trình xác thực, được đặt tên là val_cross_entropy và val_f1,
để truyền lại cho Ray Tune dưới dạng loss và f1 tương ứng. Điều đó có
nghĩa là trong quá trình chạy thử nghiệm của Ray Tune, chúng ta tham
chiếu hai chỉ số này đơn giản là loss và f1.
Như vậy, chúng ta định nghĩa hai chỉ số mà chúng ta muốn truyền từ bước xác
thực trong PyTorch Lightning, val_cross_entropy và val_f1, cho Ray Tune
tương ứng là loss và f1:

metrics = {"loss":"val_cross_entropy", "f1":"val_f1"}

Bây giờ, chúng ta có thể truyền từ điển metrics này vào phần gán của đối
tượng trainer như sau:
trainer = flash.Trainer(max_epochs=num_epochs,
gpus=num_gpus,
progress_bar_refresh_rate=0,
callbacks=[TuneReportCallback(metrics,on='validation_end'
)])

Lưu ý rằng từ điển metrics được truyền qua TuneReportCallback khi sự kiện
validation_end xảy ra. Điều này có nghĩa là khi bước xác thực hoàn thành
trong PyTorch Lightning, nó sẽ tự động kích hoạt hàm báo cáo của Ray Tune
để báo cáo danh sách các chỉ số trở lại Ray Tune để đánh giá. Danh sách các sự
kiện hợp lệ mà TuneReportCallback có thể sử dụng có thể được tìm thấy
trong mã nguồn tích hợp của Ray Tune với PyTorch Lightning
(https://github.com/ray-project/ray/blob/fb0d6e6b0b48b0a68171943369140
5b96fbea104/python/ray/tune/integration/pytorch_lightning.py#L170) .

1. Cuối cùng, chúng ta có thể gọi trainer. finetune để thực hiện bước
fine-tuning. Ở đây, chúng ta có thể truyền finetuning_strategies như
một trong các siêu tham số có thể điều chỉnh vào danh sách đối số:

trainer.finetune(classifier_model,
datamodule=datamodule,
strategy=config['finetuning_strategies'])

2. Điều này hoàn thành các thay đổi cho hàm ban đầu của việc fine-
tuning mô hình Deep Learning. Bây giờ chúng ta có một hàm
finetuning_dl_model mới đã sẵn sàng được bọc trong
tune.with_parameters để trở thành một hàm train được của Ray
Tune. Nó nên được gọi như sau:

trainable =
tune.with_parameters(finetuning_dl_model, data_dir,
num_epochs, num_gpus)

3. Lưu ý rằng không cần phải truyền tham số config, vì ngầm định được
cho rằng nó là tham số đầu tiên của finetuning_dl_model. Ba tham số
khác cần được truyền vào bọc tune.with_parameters. Đồng thời, hãy
đảm bảo rằng câu lệnh này để tạo một đối tượng trainable cho Ray
Tune được đặt bên ngoài hàm finetuning_dl_model.
Trong phần tiếp theo, nó sẽ được đặt bên trong chức năng chạy HPO của Ray
Tune được gọi là run_hpo_dl_model.
Tạo hàm chạy Ray Tune

Bây giờ, hãy tạo hàm chạy Ray Tune HPO để thực hiện năm việc sau:

 Định nghĩa các thông số cấu hình của MLflow bao gồm URI theo dõi và
tên thí nghiệm.
 Xác định không gian tìm kiếm siêu tham số bằng API phân phối ngẫu
nhiên Ray
Tune(https://docs.ray.io/en/latest/tune/api_docs/search_space.html#rand
om-distributions-api) để lấy mẫu danh sách siêu tham số mà chúng tôi
đã xác định trước đó.
 Xác định đối tượng có thể huấn luyện Ray Tune bằng cách sử dụng
tune.with_parameters, như minh họa ở cuối tiểu mục trước.
 Gọi tune.run. Thao tác này sẽ thực thi quá trình chạy HPO và trả về đối
tượng phân tích thử nghiệm của Ray Tune khi nó hoàn thành.
 Ghi lại các thông số cấu hình tốt nhất khi toàn bộ quá trình chạy HPO
kết thúc.
Chúng ta hãy xem cách triển khai để xem chức năng này có thể được triển
khai như thế nào:
1. Trước tiên, hãy xác định từ điển cấu hình siêu tham số, như sau:
mlflow.set_tracking_uri(tracking_uri)
mlflow.set_experiment(experiment_name)

Đoạn mã này sẽ lấy các tham số tracking_uri và experiment_name của


MLflow làm đầu vào và thiết lập chúng một cách chính xác. Nếu đây là lần đầu
tiên bạn chạy mã này, MLflow cũng sẽ tạo ra thí nghiệm tương ứng.

1. Sau đó, chúng ta có thể định nghĩa từ điển cấu hình (config dictionary),
bao gồm cả các tham số có thể điều chỉnh (tunable parameters) và các
tham số không thể điều chỉnh (non-tunable parameters), cùng với các
tham số cấu hình của MLflow. Như đã thảo luận trong phần trước,
chúng ta sẽ điều chỉnh learning_rate và batch_size nhưng cũng bao
gồm các siêu tham số khác để ghi nhận và sử dụng để điều chỉnh trong
tương lai:

config = {
"lr": tune.loguniform(1e-4, 1e-1),
"batch_size": tune.choice([32, 64, 128]),
"foundation_model": "prajjwal1/bert-tiny",
"finetuning_strategies": "freeze",
"optimizer_type": "Adam",
"mlflow": {
"experiment_name": experiment_name,
"tracking_uri":
mlflow.get_tracking_uri()
},
}

Như bạn có thể thấy từ từ điển cấu hình (config dictionary), chúng ta đã sử
dụng hàm tune.loguniform để lấy mẫu một phân phối log-uniform trong
khoảng từ 1e-4 đến 1e-1 để chọn một learning rate. Đối với batch size, chúng
ta đã sử dụng hàm tune.choice để chọn một trong ba giá trị khác biệt một
cách đồng đều. Đối với các cặp khóa-giá trị còn lại, chúng là các tham số không
thể điều chỉnh vì chúng không sử dụng bất kỳ phương pháp lấy mẫu nào,
nhưng lại cần thiết để chạy các thử nghiệm.
1. Định nghĩa đối tượng trainable bằng cách sử dụng
tune.with_parameters với tất cả các tham số bổ sung ngoại trừ tham số
config:
trainable = tune.with_parameters(
finetuning_dl_model,
data_dir=data_dir,
num_epochs=num_epochs,
num_gpus=gpus_per_trial)
Trong câu lệnh tiếp theo, chúng ta sẽ gọi hàm tune.run.
1. Bây giờ chúng ta đã sẵn sàng để chạy HPO bằng cách gọi hàm tune.run
như sau:
analysis = tune.run(
trainable,
resources_per_trial={
"cpu": 1,
"gpu": gpus_per_trial
},
metric="f1",
mode="max",
config=config,
num_samples=num_samples,
name="hpo_tuning_dl_model")

Ở đây, mục tiêu là tìm bộ siêu tham số mà làm tăng giá trị F1-score cao nhất
trong tất cả các thử nghiệm, vì vậy chế độ (mode) là max và metric là f1. Lưu ý
rằng tên metric f1 này được lấy từ từ điển metrics mà chúng ta đã định nghĩa
trong hàm finetuning_dl_model trước đó, trong đó chúng ta ánh xạ val_f1 của
PyTorch Lightning thành f1. Giá trị f1 này sau đó được truyền cho Ray Tune ở
cuối bước kiểm định của mỗi thử nghiệm. Đối tượng trainable được truyền
cho tune.run như là tham số đầu tiên, và nó sẽ được thực thi số lần tương ứng
với giá trị num_samples. Tiếp theo, resources_per_trial xác định CPU và GPU
sử dụng. Lưu ý rằng trong ví dụ trước đó, chúng ta chưa chỉ định bất kỳ thuật
toán tìm kiếm nào. Điều này có nghĩa là mặc định nó sẽ sử dụng thuật toán
grid search là tune.suggest.basic_variant. Cũng không có lịch trình (scheduler)
được xác định, vì vậy mặc định không có dừng sớm và tất cả các thử nghiệm
sẽ được chạy song song với số lượng CPU tối đa cho phép trên máy thực thi.
Khi quá trình chạy hoàn thành, một biến analysis được trả về, chứa các siêu
tham số tốt nhất được tìm thấy, cùng với thông tin khác.
1. Đăng ký cấu hình tốt nhất của các siêu tham số đã tìm thấy. Điều này có
thể được thực hiện bằng cách sử dụng biến analysis trả về từ tune.run,
như sau:

logger.info("Best hyperparameters found were: %s",


analysis.best_config)

Đó là tất cả. Bây giờ chúng ta có thể thử nghiệm. Nếu bạn tải xuống mã hoàn
chỉnh từ kho lưu trữ GitHub của chương này, bạn sẽ thấy tệp
hpo_finetuning_model.py trong thư mục pipeline.

Với thay đổi trước đó, bây giờ chúng ta đã sẵn sàng để chạy thử nghiệm HPO
đầu tiên của chúng ta.

Chạy thử nghiệm Ray


Tune HPO đầu tiên với
MLflow
Bây giờ, sau khi chúng ta đã cài đặt Ray Tune, MLflow và tạo chức năng chạy
thử nghiệm HPO, chúng ta có thể thử chạy thử nghiệm HPO đầu tiên của
chúng ta bằng cách thực hiện lệnh sau:

python pipeline/hpo_finetuning_model.py

Sau vài giây, bạn sẽ thấy màn hình như hình 6.2 dưới đây, cho thấy rằng tất cả
10 thử nghiệm (tức là các giá trị mà chúng ta đã đặt cho num_samples) đang
chạy đồng thời:
Figure 6.2 – Ray Tune running 10 trials in parallel on a local multi-core laptop

Sau khoảng 12-14 phút, bạn sẽ thấy rằng tất cả các thử nghiệm đã hoàn thành
và các siêu tham số tốt nhất sẽ được in ra trên màn hình, như được hiển thị
dưới đây (kết quả của bạn có thể khác nhau do tính ngẫu nhiên, số lượng mẫu
hạn chế và việc sử dụng tìm kiếm theo lưới, không đảm bảo tìm kiếm toàn cục
tối ưu):

Best hyperparameters found were: {'lr':


0.025639008922511797, 'batch_size': 64,
'foundation_model': 'prajjwal1/bert-tiny',
'finetuning_strategies': 'freeze', 'optimizer_type':
'Adam', 'mlflow': {'experiment_name': 'hpo-tuning-
chapter06', 'tracking_uri': 'http://localhost'}}

Bạn có thể tìm thấy kết quả cho mỗi thử nghiệm trong thư mục nhật ký kết
quả, mặc định là trong thư mục ray_results của người dùng hiện tại. Từ Hình
6.2, chúng ta có thể thấy kết quả nằm trong
/Users/yongliu/ray_results/hpo_tuning_dl_model.

Bạn sẽ thấy kết quả cuối cùng của siêu tham số tốt nhất trên màn hình, điều
này có nghĩa là bạn đã hoàn thành chạy thử nghiệm HPO đầu tiên của mình!
Bạn có thể thấy rằng tất cả 10 thử nghiệm được đăng nhập trong máy chủ
theo dõi MLflow, và bạn có thể hiển thị và so sánh tất cả 10 lần chạy bằng
cách sử dụng đồ thị tọa độ song song do máy chủ theo dõi MLflow cung cấp.
Bạn có thể tạo ra một đồ thị như vậy bằng cách truy cập trang thực nghiệm
MLflow và chọn 10 thử nghiệm bạn vừa hoàn thành, sau đó nhấp vào nút So
sánh gần đầu trang (xem Hình 6.3). Điều này sẽ đưa bạn đến trang so sánh
song song với các tùy chọn đồ thị được hiển thị ở cuối trang:
Figure 6.3 – Clicking Compare to compare all 10 trial runs on the MLflow
experiment page

Bạn có thể nhấp vào mục Parallel Coordinates Plot, cho phép bạn chọn các
tham số và chỉ số để vẽ đồ thị. Ở đây, chúng ta chọn lr và batch_size làm các
tham số và val_f1 và val_cross_entropy là các chỉ số. Đồ thị được hiển thị
trong Hình 6.4:

Figure 6.4 –Parallel Coordinates Plot for comparing the HPO trial results

Như bạn có thể thấy trong Hình 6.4, rất dễ thấy rằng batch_size là 128 và lr là
0.02874 tạo ra điểm val_f1 tốt nhất là 0.6544 và val_cross_entropy (giá trị
mất mát) là 0.62222. Như đã đề cập trước đó, quá trình HPO này không sử
dụng bất kỳ thuật toán tìm kiếm và lập lịch nâng cao nào, vì vậy hãy xem liệu
chúng ta có thể cải thiện hơn với nhiều thử nghiệm hơn trong các phần tiếp
theo bằng cách sử dụng early stopping và pruning.
Chạy HPO với Ray
Tune sử dụng Optuna
và HyperBand
Trong phần này, chúng ta sẽ thực hiện một số thí nghiệm với các thuật toán
tìm kiếm và bộ lập lịch khác nhau bằng cách sử dụng gói Optuna, một thuật
toán tìm kiếm dựa trên TPE, và bộ lập lịch ASHA, một bộ lập lịch tốt thực hiện
các thử nghiệm song song không đồng bộ với việc kết thúc sớm các thử
nghiệm không triển vọng. Điều thú vị là chúng ta chỉ cần thực hiện một số thay
đổi nhỏ để làm việc này thành công, dựa trên những gì chúng ta đã làm trong
phần trước.

Kết quả cho thấy, sự thay đổi là rất nhỏ dựa trên những gì chúng ta đã làm
trong phần trước. Ở đây, chúng tôi sẽ minh họa bốn thay đổi chính:

1. Cài đặt gói Optuna. Điều này có thể được thực hiện bằng cách chạy
lệnh sau:

pip install optuna==2.10.0

Điều này sẽ cài đặt Optuna trong cùng môi trường ảo mà chúng ta đã sử dụng
trước đó. Nếu bạn đã chạy lệnh pip install -r requirements.text
trước đó, thì Optuna đã được cài đặt và bạn có thể bỏ qua bước này.

1. Nhập các module liên quan từ Ray Tune để tích hợp với Optuna và bộ
lập lịch ASHA (ở đây, chúng ta sử dụng phiên bản HyperBand của ASHA)
như sau:

from ray.tune.suggest import ConcurrencyLimiter


from ray.tune.schedulers import
AsyncHyperBandScheduler
from ray.tune.suggest.optuna import OptunaSearch

2. Bây giờ chúng ta sẵn sàng thêm biến thuật toán tìm kiếm và biến bộ lập
lịch vào hàm thực hiện HPO, run_hpo_dl_model, như sau:
searcher = OptunaSearch()
searcher = ConcurrencyLimiter(searcher,
max_concurrent=4)
scheduler = AsyncHyperBandScheduler()

Lưu ý rằng biến searcher bây giờ sử dụng Optuna và chúng ta đặt số lượng
chạy đồng thời tối đa là 4 cho biến searcher này trong quá trình tìm kiếm HPO.
Bộ lập lịch được khởi tạo với bộ lập lịch HyperBand.

1. Gán biến searcher và scheduler cho các tham số tương ứng của cuộc
gọi tune.run, như sau:

analysis = tune.run(
trainable,
resources_per_trial={
"cpu": 1,
"gpu": gpus_per_trial
},
metric="f1",
mode="max",
config=config,
num_samples=num_samples,
search_alg=searcher,
scheduler=scheduler,
name="hpo_tuning_dl_model")
Lưu ý rằng biến searcher được gán cho tham số search_alg, và biến scheduler
được gán cho tham số scheduler. Đó là tất cả. Bây giờ chúng ta đã sẵn sàng để
chạy HPO với Optuna trong khung Ray Tune thống nhất, với tất cả tích hợp
MLflow đã được cung cấp sẵn bởi Ray Tune.

Chúng tôi đã cung cấp mã Python đầy đủ trong tệp


hpo_finetuning_model_optuna.py trong thư mục pipeline. Hãy chạy thí
nghiệm HPO này như sau:
python pipeline/hpo_finetuning_model_optuna.py

Bạn sẽ ngay lập tức nhận thấy những thông tin sau trên màn hình console:

[I 2022-02-06 21:01:27,609] A new study created in memory


with name: optuna

Điều này có nghĩa là chúng ta đang sử dụng Optuna như thuật toán tìm kiếm.
Bên cạnh đó, bạn sẽ nhận thấy có bốn thử nghiệm song song đồng thời trong
đầu ra trạng thái hiển thị trên màn hình. Khi thời gian trôi qua, một số thử
nghiệm sẽ bị chấm dứt sau một hoặc hai vòng lặp (epochs) trước khi hoàn
thành. Điều này có nghĩa là ASHA đã hoạt động và loại bỏ những thử nghiệm
không triển vọng để tiết kiệm tài nguyên tính toán và tăng tốc quá trình tìm
kiếm. Hình 6.5 cho thấy một trong những đầu ra trong quá trình chạy, trong
đó có ba thử nghiệm bị kết thúc chỉ sau một vòng lặp. Bạn có thể tìm thấy
`num_stopped=3` trong đầu ra trạng thái (dòng thứ ba trong Hình 6.5), trong
đó nó nói "Using AsynHyerBand: num_stopped=3". Điều này có nghĩa là
AsyncHyperBand đã chấm dứt ba thử nghiệm này trước khi hoàn thành
chúng.

Figure 6.5 – Running HPO with Ray Tune using Optuna and AsyncHyperBand

Khi kết thúc quá trình chạy, bạn sẽ thấy kết quả sau:

2022-02-06 21:11:59,695 INFO tune.py:626 -- Total run


time: 632.10 seconds (631.91 seconds for the tuning
loop).
2022-02-06 21:11:59,728 Best hyperparameters found were:
{'lr': 0.0009599443695046438, 'batch_size': 128,
'foundation_model': 'prajjwal1/bert-tiny',
'finetuning_strategies': 'freeze', 'optimizer_type':
'Adam', 'mlflow': {'experiment_name': 'hpo-tuning-
chapter06', 'tracking_uri': 'http://localhost'}}

Lưu ý rằng thời gian chạy tổng cộng chỉ là 10 phút. So với phần trước sử dụng
grid search mà không có early stopping, điều này tiết kiệm được 2-4 phút. Dù
có vẻ ngắn gọn, nhưng hãy nhớ rằng chúng ta chỉ sử dụng một mô hình BERT
nhỏ với chỉ 3 epoch. Trong các quá trình HPO thực tế, sử dụng mô hình lớn
hơn với 20 epoch là điều không hiếm, và tốc độ tìm kiếm sẽ đáng kể khi kết
hợp một thuật toán tìm kiếm tốt và một lập lịch như Asynchronous
HyperBand. Việc tích hợp MLflow do Ray Tune cung cấp miễn phí, cho phép
chúng ta chuyển sang một thuật toán tìm kiếm và/hoặc một lập lịch khác dưới
một framework duy nhất.

Mặc dù phần này chỉ hướng dẫn cách sử dụng Optuna trong Ray Tune và
MLflow, việc thay thế Optuna bằng HyperOpt là một thay đổi đơn giản. Thay
vì khởi tạo một searcher với OptunaSearch, chúng ta có thể sử dụng
HyperOptSearch (bạn có thể xem ví dụ tại đây https://github.com/ray-
project/ray/blob/d6b0b9a209e3f693afa6441eb284e48c02b10a80/python/
ray/tune/examples/hyperopt_conditional_search_space_example.py#L80 , và
phần còn lại của mã code là giống nhau. Chúng tôi để điều này như một bài
tập để bạn tự khám phá.

SỬ DỤNG CÁC THUẬT TOÁN TÌM KIẾM VÀ LỊCH TRÌNH KHÁC NHAU VỚI RAY
TUNE

Lưu ý rằng không phải thuật toán tìm kiếm nào cũng hoạt động với bất kỳ lịch
trình nào. Lựa chọn thuật toán tìm kiếm và lịch trình phụ thuộc vào độ phức
tạp và chi phí đánh giá mô hình. Đối với một mô hình Deep Learning, vì chi phí
chạy một epoch thường cao, rất mong muốn sử dụng các thuật toán tìm kiếm
hiện đại như TPE, Dragonfly và BlendSearch, kết hợp với một lịch trình loại
ASHA như lịch trình HyperBand mà chúng tôi sử dụng. Để biết hướng dẫn chi
tiết về việc chọn thuật toán tìm kiếm và lịch trình nào, bạn nên tham khảo tài
liệu sau trên trang web Ray Tune:
https://docs.ray.io/en/latest/tune/tutorials/overview.html#which-search-
algorithm-scheduler-should-i-choose .

Bây giờ chúng ta đã hiểu cách sử dụng Ray Tune và MLflow để thực hiện quá
trình HPO song song và hiệu quả cho các mô hình Deep Learning, điều này tạo
nền tảng cho chúng ta để thực hiện các thí nghiệm HPO phức tạp hơn và quy
mô lớn trong tương lai.

Tóm tắt
Trong chương này, chúng ta đã tìm hiểu về các nguyên tắc cơ bản và thách
thức của HPO, tại sao nó quan trọng đối với quy trình mô hình DL và những gì
một framework HPO hiện đại nên hỗ trợ. Chúng ta đã so sánh ba framework
phổ biến - Ray Tune, Optuna và HyperOpt - và chọn Ray Tune làm framework
hàng đầu cho việc chạy HPO tiên tiến trên quy mô lớn. Chúng ta đã thấy cách
tạo mã mô hình DL sẵn sàng cho HPO bằng cách sử dụng Ray Tune và MLflow
và thực hiện thí nghiệm HPO đầu tiên với Ray Tune và MLflow. Ngoài ra,
chúng ta đã tìm hiểu cách chuyển đổi sang các thuật toán tìm kiếm và lịch
trình khác sau khi đã thiết lập framework mã HPO, sử dụng các lịch trình
Optuna và HyperBand làm ví dụ. Những kiến thức thu được từ chương này sẽ
giúp bạn thực hiện các thí nghiệm HPO quy mô lớn trong môi trường sản xuất
thực tế, giúp bạn tạo ra các mô hình DL hiệu suất cao một cách tiết kiệm chi
phí. Chúng tôi cũng đã cung cấp nhiều tài liệu tham khảo trong phần Đọc thêm
ở cuối chương để khuyến khích bạn nghiên cứu sâu hơn.

Trong chương tiếp theo, chúng ta sẽ tiếp tục tìm hiểu cách xây dựng các bước
tiền xử lý và sau xử lý cho một quy trình suy luận mô hình bằng cách sử dụng
MLflow, đây là một tình huống điển hình trong một môi trường sản xuất thực
tế sau khi có một mô hình DL đã được HPO để sẵn sàng cho sản xuất.

Đọc thêm
 Best Tools for Model Tuning and Hyperparameter Optimization:
https://neptune.ai/blog/best-tools-for-model-tuning-and-
hyperparameter-optimization
 Comparison between Optuna and HyperOpt:
https://neptune.ai/blog/optuna-vs-hyperopt
 How (Not) to Tune Your Model with Hyperopt:
https://databricks.com/blog/2021/04/15/how-not-to-tune-your-
model-with-hyperopt.html
 Why Hyper parameter tuning is important for your model?:
https://medium.com/analytics-vidhya/why-hyper-parameter-tuning-is-
important-for-your-model-1ff4c8f145d3
 The Art of Hyperparameter Tuning in Deep Neural Nets by Example:
https://towardsdatascience.com/the-art-of-hyperparameter-tuning-in-
deep-neural-nets-by-example-685cb5429a38
 Automated Hyperparameter tuning:
https://insaid.medium.com/automated-hyperparameter-tuning-
988b5aeb7f2a
 Get better at building PyTorch models with Lightning and Ray Tune:
https://towardsdatascience.com/get-better-at-building-pytorch-
models-with-lightning-and-ray-tune-9fc39b84e602
 Ray & MLflow: Taking Distributed Machine Learning Applications to
Production: https://medium.com/distributed-computing-with-ray/ray-
mlflow-taking-distributed-machine-learning-applications-to-
production-103f5505cb88
 A Novice's Guide to Hyperparameter Optimization at Scale:
https://wood-b.github.io/post/a-novices-guide-to-hyperparameter-
optimization-at-scale/
 A Databricks notebook to run Ray Tune and MLflow on a Databricks
cluster:
https://databricks-prod-cloudfront.cloud.databricks.com/public/4027e
c902e239c93eaaa8714f173bcfc/
6762389964551879/1089858099311442/7376217192554178/
latest.html
 A Brief Introduction to Ray Distributed Objects, Ray Tune, and a Small
Comparison to Parsl: https://cloud4scieng.org/2021/04/08/a-brief-
introduction-to-ray-distributed-objects-ray-tune-and-a-small-
comparison-to-parsl/

You might also like