Hướng dẫn toàn diện về Tối ưu hóa Bayes để tinh chỉnh siêu tham số, bao gồm các nguyên tắc, ưu điểm, triển khai thực tế và các kỹ thuật nâng cao.
Tinh chỉnh Siêu tham số: Làm chủ Tối ưu hóa Bayes
Trong lĩnh vực học máy, hiệu suất của một mô hình thường bị ảnh hưởng đáng kể bởi các siêu tham số của nó. Không giống như các tham số mô hình được học trong quá trình huấn luyện, các siêu tham số được thiết lập trước khi quá trình huấn luyện bắt đầu. Việc tìm ra cấu hình siêu tham số tối ưu có thể là một nhiệm vụ đầy thách thức và tốn thời gian. Đây là lúc các kỹ thuật tinh chỉnh siêu tham số phát huy tác dụng, và trong số đó, Tối ưu hóa Bayes nổi bật như một phương pháp mạnh mẽ và hiệu quả. Bài viết này cung cấp một hướng dẫn toàn diện về Tối ưu hóa Bayes, bao gồm các nguyên tắc, ưu điểm, triển khai thực tế và các kỹ thuật nâng cao.
Siêu tham số là gì?
Siêu tham số là các tham số không được học từ dữ liệu trong quá trình huấn luyện. Chúng kiểm soát chính quá trình học, ảnh hưởng đến độ phức tạp, tốc độ học và hành vi tổng thể của mô hình. Các ví dụ về siêu tham số bao gồm:
- Tốc độ học (Learning Rate): Kiểm soát kích thước bước trong quá trình hạ gradient trong mạng nơ-ron.
- Số lớp/Nơ-ron: Xác định kiến trúc của một mạng nơ-ron.
- Độ mạnh của điều chuẩn (Regularization Strength): Kiểm soát độ phức tạp của mô hình để ngăn chặn overfitting.
- Tham số Kernel: Xác định hàm kernel trong Máy Vector Hỗ trợ (SVM).
- Số cây (Number of Trees): Xác định số lượng cây quyết định trong một Rừng Ngẫu nhiên (Random Forest).
Việc tìm ra sự kết hợp đúng đắn của các siêu tham số có thể cải thiện đáng kể hiệu suất của mô hình, dẫn đến độ chính xác, khả năng tổng quát hóa và hiệu quả tốt hơn.
Thách thức của việc Tinh chỉnh Siêu tham số
Tối ưu hóa siêu tham số không phải là một nhiệm vụ đơn giản do một số thách thức:
- Không gian tìm kiếm đa chiều: Không gian của các kết hợp siêu tham số có thể rất lớn, đặc biệt đối với các mô hình có nhiều siêu tham số.
- Tối ưu hóa không lồi: Mối quan hệ giữa các siêu tham số và hiệu suất mô hình thường không lồi, gây khó khăn cho việc tìm ra điểm tối ưu toàn cục.
- Đánh giá tốn kém: Việc đánh giá một cấu hình siêu tham số đòi hỏi phải huấn luyện và xác thực mô hình, điều này có thể tốn kém về mặt tính toán, đặc biệt đối với các mô hình phức tạp và bộ dữ liệu lớn.
- Đánh giá có nhiễu: Hiệu suất mô hình có thể bị ảnh hưởng bởi các yếu tố ngẫu nhiên như lấy mẫu dữ liệu và khởi tạo, dẫn đến các đánh giá có nhiễu về cấu hình siêu tham số.
Các phương pháp truyền thống như Tìm kiếm Lưới (Grid Search) và Tìm kiếm Ngẫu nhiên (Random Search) thường không hiệu quả và tốn thời gian, đặc biệt khi xử lý các không gian tìm kiếm đa chiều và các đánh giá tốn kém.
Giới thiệu về Tối ưu hóa Bayes
Tối ưu hóa Bayes là một kỹ thuật tối ưu hóa dựa trên mô hình xác suất nhằm tìm kiếm hiệu quả điểm tối ưu toàn cục của một hàm mục tiêu, ngay cả khi hàm đó không lồi, có nhiễu và tốn kém để đánh giá. Nó tận dụng định lý Bayes để cập nhật một niềm tin tiên nghiệm về hàm mục tiêu với dữ liệu quan sát được, tạo ra một phân phối hậu nghiệm được sử dụng để hướng dẫn việc tìm kiếm cấu hình siêu tham số tối ưu.
Các khái niệm chính
- Mô hình thay thế (Surrogate Model): Một mô hình xác suất (thường là Quy trình Gaussian) xấp xỉ hàm mục tiêu. Nó cung cấp một phân phối trên các giá trị hàm có thể có tại mỗi điểm trong không gian tìm kiếm, cho phép chúng ta định lượng sự không chắc chắn về hành vi của hàm.
- Hàm thu nhận (Acquisition Function): Một hàm hướng dẫn việc tìm kiếm cấu hình siêu tham số tiếp theo để đánh giá. Nó cân bằng giữa thăm dò (tìm kiếm ở các vùng chưa được khám phá của không gian tìm kiếm) và khai thác (tập trung vào các vùng có tiềm năng cao).
- Định lý Bayes: Được sử dụng để cập nhật mô hình thay thế với dữ liệu quan sát được. Nó kết hợp niềm tin tiên nghiệm về hàm mục tiêu với thông tin khả năng từ dữ liệu để tạo ra một phân phối hậu nghiệm.
Quy trình Tối ưu hóa Bayes
Quy trình Tối ưu hóa Bayes có thể được tóm tắt như sau:- Khởi tạo: Đánh giá hàm mục tiêu tại một vài cấu hình siêu tham số được chọn ngẫu nhiên.
- Xây dựng Mô hình thay thế: Khớp một mô hình thay thế (ví dụ: Quy trình Gaussian) với dữ liệu đã quan sát.
- Tối ưu hóa Hàm thu nhận: Sử dụng mô hình thay thế để tối ưu hóa hàm thu nhận, hàm này đề xuất cấu hình siêu tham số tiếp theo cần đánh giá.
- Đánh giá Hàm mục tiêu: Đánh giá hàm mục tiêu tại cấu hình siêu tham số được đề xuất.
- Cập nhật Mô hình thay thế: Cập nhật mô hình thay thế với quan sát mới.
- Lặp lại: Lặp lại các bước 3-5 cho đến khi đáp ứng một tiêu chí dừng (ví dụ: số lần lặp tối đa, đạt được hiệu suất mục tiêu).
Tìm hiểu về Quy trình Gaussian (GPs)
Quy trình Gaussian là một công cụ mạnh mẽ để mô hình hóa các hàm và định lượng sự không chắc chắn. Chúng thường được sử dụng làm mô hình thay thế trong Tối ưu hóa Bayes do khả năng cung cấp một phân phối trên các giá trị hàm có thể có tại mỗi điểm trong không gian tìm kiếm.
Các thuộc tính chính của Quy trình Gaussian
- Phân phối trên các hàm: Một Quy trình Gaussian định nghĩa một phân phối xác suất trên các hàm có thể có.
- Được xác định bởi trung bình và hiệp phương sai: Một Quy trình Gaussian được xác định hoàn toàn bởi hàm trung bình m(x) và hàm hiệp phương sai k(x, x'). Hàm trung bình đại diện cho giá trị kỳ vọng của hàm tại mỗi điểm, trong khi hàm hiệp phương sai mô tả sự tương quan giữa các giá trị hàm tại các điểm khác nhau.
- Hàm Kernel: Hàm hiệp phương sai, còn được gọi là hàm kernel, xác định độ trơn và hình dạng của các hàm được lấy mẫu từ Quy trình Gaussian. Các hàm kernel phổ biến bao gồm kernel Hàm cơ sở xuyên tâm (RBF), kernel Matérn và kernel Tuyến tính.
- Suy luận hậu nghiệm: Với dữ liệu quan sát được, một Quy trình Gaussian có thể được cập nhật bằng định lý Bayes để có được một phân phối hậu nghiệm trên các hàm. Phân phối hậu nghiệm này đại diện cho niềm tin được cập nhật của chúng ta về hành vi của hàm sau khi quan sát dữ liệu.
Cách Quy trình Gaussian được sử dụng trong Tối ưu hóa Bayes
Trong Tối ưu hóa Bayes, Quy trình Gaussian được sử dụng để mô hình hóa hàm mục tiêu. GP cung cấp một phân phối trên các giá trị hàm có thể có tại mỗi cấu hình siêu tham số, cho phép chúng ta định lượng sự không chắc chắn của mình về hành vi của hàm. Sự không chắc chắn này sau đó được sử dụng bởi hàm thu nhận để hướng dẫn việc tìm kiếm cấu hình siêu tham số tối ưu.
Ví dụ, hãy tưởng tượng bạn đang tinh chỉnh tốc độ học của một mạng nơ-ron. Quy trình Gaussian sẽ mô hình hóa mối quan hệ giữa tốc độ học và độ chính xác xác thực của mạng. Nó sẽ cung cấp một phân phối trên các độ chính xác xác thực có thể có cho mỗi tốc độ học, cho phép bạn đánh giá tiềm năng của các tốc độ học khác nhau và hướng dẫn việc tìm kiếm giá trị tối ưu của bạn.
Hàm thu nhận: Cân bằng giữa Thăm dò và Khai thác
Hàm thu nhận đóng một vai trò quan trọng trong Tối ưu hóa Bayes bằng cách hướng dẫn việc tìm kiếm cấu hình siêu tham số tiếp theo để đánh giá. Nó cân bằng giữa thăm dò (tìm kiếm ở các vùng chưa được khám phá của không gian tìm kiếm) và khai thác (tập trung vào các vùng có tiềm năng cao). Một số hàm thu nhận thường được sử dụng trong Tối ưu hóa Bayes:
- Xác suất Cải thiện (PI): Xác suất mà giá trị hàm mục tiêu tại một cấu hình siêu tham số nhất định tốt hơn giá trị tốt nhất đã quan sát được cho đến nay. PI ưu tiên khai thác bằng cách tập trung vào các vùng có tiềm năng cao.
- Cải thiện Kỳ vọng (EI): Lượng cải thiện kỳ vọng mà giá trị hàm mục tiêu tại một cấu hình siêu tham số nhất định tốt hơn giá trị tốt nhất đã quan sát được cho đến nay. EI cung cấp một cách tiếp cận cân bằng hơn giữa thăm dò và khai thác so với PI.
- Giới hạn Tin cậy trên (UCB): Một hàm thu nhận kết hợp trung bình dự đoán của hàm mục tiêu với một giới hạn tin cậy trên dựa trên sự không chắc chắn của mô hình thay thế. UCB ưu tiên thăm dò bằng cách ưu tiên các vùng có độ không chắc chắn cao.
Chọn Hàm thu nhận phù hợp
Việc lựa chọn hàm thu nhận phụ thuộc vào vấn đề cụ thể và sự cân bằng mong muốn giữa thăm dò và khai thác. Nếu hàm mục tiêu tương đối trơn và có hành vi tốt, một hàm thu nhận ưu tiên khai thác (ví dụ: PI) có thể phù hợp. Tuy nhiên, nếu hàm mục tiêu rất không lồi hoặc có nhiễu, một hàm thu nhận ưu tiên thăm dò (ví dụ: UCB) có thể hiệu quả hơn.
Ví dụ: Hãy tưởng tượng bạn đang tối ưu hóa các siêu tham số của một mô hình học sâu để phân loại hình ảnh. Nếu bạn có một ước tính ban đầu tốt về cấu hình siêu tham số tối ưu, bạn có thể chọn một hàm thu nhận như Cải thiện Kỳ vọng để tinh chỉnh mô hình và đạt được hiệu suất tốt nhất có thể. Mặt khác, nếu bạn không chắc chắn về cấu hình tối ưu, bạn có thể chọn một hàm thu nhận như Giới hạn Tin cậy trên để khám phá các vùng khác nhau của không gian siêu tham số và khám phá các giải pháp có thể tốt hơn.
Triển khai thực tế Tối ưu hóa Bayes
Một số thư viện và framework có sẵn để triển khai Tối ưu hóa Bayes trong Python, bao gồm:
- Scikit-optimize (skopt): Một thư viện Python phổ biến cung cấp một loạt các thuật toán Tối ưu hóa Bayes và hàm thu nhận. Nó tương thích với Scikit-learn và các thư viện học máy khác.
- GPyOpt: Một thư viện Tối ưu hóa Bayes tập trung vào các mô hình Quy trình Gaussian và cung cấp các tính năng nâng cao như tối ưu hóa đa mục tiêu và tối ưu hóa có ràng buộc.
- BayesianOptimization: Một thư viện Tối ưu hóa Bayes đơn giản và dễ sử dụng, phù hợp cho người mới bắt đầu.
Ví dụ sử dụng Scikit-optimize (skopt)
Đây là một ví dụ về cách sử dụng Scikit-optimize để tối ưu hóa các siêu tham số của một bộ phân loại Máy Vector Hỗ trợ (SVM):
```python from skopt import BayesSearchCV from sklearn.svm import SVC from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # Tải bộ dữ liệu Iris iris = load_iris() X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42) # Định nghĩa không gian tìm kiếm siêu tham số param_space = { 'C': (1e-6, 1e+6, 'log-uniform'), 'gamma': (1e-6, 1e+1, 'log-uniform'), 'kernel': ['rbf'] } # Định nghĩa mô hình model = SVC() # Định nghĩa tìm kiếm Tối ưu hóa Bayes opt = BayesSearchCV( model, param_space, n_iter=50, # Số lần lặp cv=3 # Số fold kiểm tra chéo ) # Chạy tối ưu hóa opt.fit(X_train, y_train) # In ra các tham số và điểm số tốt nhất print("Best parameters: %s" % opt.best_params_) print("Best score: %s" % opt.best_score_) # Đánh giá mô hình trên tập kiểm tra accuracy = opt.score(X_test, y_test) print("Test accuracy: %s" % accuracy) ```Ví dụ này minh họa cách sử dụng Scikit-optimize để định nghĩa không gian tìm kiếm siêu tham số, định nghĩa mô hình và chạy tìm kiếm Tối ưu hóa Bayes. Lớp `BayesSearchCV` tự động xử lý việc mô hình hóa Quy trình Gaussian và tối ưu hóa hàm thu nhận. Đoạn mã sử dụng phân phối log-uniform cho các tham số `C` và `gamma`, điều này thường phù hợp với các tham số có thể thay đổi trong nhiều bậc độ lớn. Tham số `n_iter` kiểm soát số lần lặp, quyết định mức độ thăm dò được thực hiện. Tham số `cv` chỉ định số lần kiểm tra chéo được sử dụng để đánh giá mỗi cấu hình siêu tham số.
Các kỹ thuật nâng cao trong Tối ưu hóa Bayes
Một số kỹ thuật nâng cao có thể cải thiện hơn nữa hiệu suất của Tối ưu hóa Bayes:
- Tối ưu hóa đa mục tiêu: Tối ưu hóa đồng thời nhiều mục tiêu (ví dụ: độ chính xác và thời gian huấn luyện).
- Tối ưu hóa có ràng buộc: Tối ưu hóa hàm mục tiêu tuân theo các ràng buộc trên các siêu tham số (ví dụ: ràng buộc ngân sách, ràng buộc an toàn).
- Tối ưu hóa Bayes song song: Đánh giá nhiều cấu hình siêu tham số song song để tăng tốc quá trình tối ưu hóa.
- Học chuyển giao: Tận dụng kiến thức từ các lần tối ưu hóa trước để tăng tốc quá trình tối ưu hóa cho các vấn đề mới.
- Tối ưu hóa dựa trên Bandit: Kết hợp Tối ưu hóa Bayes với các thuật toán bandit để thăm dò hiệu quả không gian siêu tham số.
Ví dụ: Tối ưu hóa Bayes song song
Tối ưu hóa Bayes song song có thể giảm đáng kể thời gian cần thiết để tinh chỉnh siêu tham số, đặc biệt khi việc đánh giá các cấu hình siêu tham số tốn kém về mặt tính toán. Nhiều thư viện cung cấp hỗ trợ tích hợp cho việc song song hóa, hoặc bạn có thể tự triển khai bằng cách sử dụng các thư viện như `concurrent.futures` trong Python.
Ý tưởng chính là đánh giá đồng thời nhiều cấu hình siêu tham số do hàm thu nhận đề xuất. Điều này đòi hỏi phải quản lý cẩn thận mô hình thay thế và hàm thu nhận để đảm bảo rằng các đánh giá song song được tích hợp đúng cách vào quá trình tối ưu hóa.
Ví dụ: Tối ưu hóa Bayes có ràng buộc
Trong nhiều tình huống thực tế, việc tinh chỉnh siêu tham số phải tuân theo các ràng buộc. Ví dụ, bạn có thể có ngân sách hạn chế để huấn luyện mô hình, hoặc bạn có thể cần đảm bảo rằng mô hình đáp ứng các yêu cầu an toàn nhất định.
Các kỹ thuật Tối ưu hóa Bayes có ràng buộc có thể được sử dụng để tối ưu hóa hàm mục tiêu trong khi thỏa mãn các ràng buộc này. Những kỹ thuật này thường bao gồm việc kết hợp các ràng buộc vào hàm thu nhận hoặc mô hình thay thế.
Ưu và Nhược điểm của Tối ưu hóa Bayes
Ưu điểm
- Hiệu quả: Tối ưu hóa Bayes thường yêu cầu ít lần đánh giá hàm mục tiêu hơn so với các phương pháp truyền thống như Tìm kiếm Lưới và Tìm kiếm Ngẫu nhiên, làm cho nó hiệu quả hơn để tối ưu hóa các hàm tốn kém.
- Xử lý được hàm không lồi: Tối ưu hóa Bayes có thể xử lý các hàm mục tiêu không lồi, vốn phổ biến trong học máy.
- Định lượng sự không chắc chắn: Tối ưu hóa Bayes cung cấp một thước đo về sự không chắc chắn của hàm mục tiêu, điều này có thể hữu ích để hiểu quá trình tối ưu hóa và đưa ra quyết định sáng suốt.
- Thích ứng: Tối ưu hóa Bayes thích ứng với hình dạng của hàm mục tiêu, tập trung vào các vùng hứa hẹn của không gian tìm kiếm.
Nhược điểm
- Độ phức tạp: Tối ưu hóa Bayes có thể phức tạp hơn để triển khai và hiểu so với các phương pháp đơn giản hơn như Tìm kiếm Lưới và Tìm kiếm Ngẫu nhiên.
- Chi phí tính toán: Chi phí tính toán để xây dựng và cập nhật mô hình thay thế có thể đáng kể, đặc biệt đối với các không gian tìm kiếm đa chiều.
- Nhạy cảm với tiên nghiệm: Việc lựa chọn phân phối tiên nghiệm cho mô hình thay thế có thể ảnh hưởng đến hiệu suất của Tối ưu hóa Bayes.
- Khả năng mở rộng: Tối ưu hóa Bayes có thể khó mở rộng sang các không gian tìm kiếm có chiều rất cao.
Khi nào nên sử dụng Tối ưu hóa Bayes
Tối ưu hóa Bayes đặc biệt phù hợp cho các kịch bản sau:
- Đánh giá tốn kém: Khi việc đánh giá hàm mục tiêu tốn kém về mặt tính toán (ví dụ: huấn luyện một mô hình học sâu).
- Hàm mục tiêu không lồi: Khi mối quan hệ giữa các siêu tham số và hiệu suất mô hình không lồi.
- Ngân sách hạn chế: Khi số lần đánh giá bị hạn chế do ràng buộc về thời gian hoặc tài nguyên.
- Không gian tìm kiếm đa chiều: Khi không gian tìm kiếm có chiều cao, và các phương pháp truyền thống như Tìm kiếm Lưới và Tìm kiếm Ngẫu nhiên không hiệu quả.
Ví dụ, Tối ưu hóa Bayes thường được sử dụng để tinh chỉnh các siêu tham số của các mô hình học sâu, chẳng hạn như mạng nơ-ron tích chập (CNN) và mạng nơ-ron hồi quy (RNN), vì việc huấn luyện các mô hình này có thể tốn kém về mặt tính toán và không gian siêu tham số có thể rất lớn.
Vượt ra ngoài Tinh chỉnh Siêu tham số truyền thống: AutoML
Tối ưu hóa Bayes là một thành phần cốt lõi của nhiều hệ thống Học máy Tự động (AutoML). AutoML nhằm mục đích tự động hóa toàn bộ quy trình học máy, bao gồm tiền xử lý dữ liệu, kỹ thuật đặc trưng, lựa chọn mô hình và tinh chỉnh siêu tham số. Bằng cách tích hợp Tối ưu hóa Bayes với các kỹ thuật khác, các hệ thống AutoML có thể tự động xây dựng và tối ưu hóa các mô hình học máy cho nhiều tác vụ khác nhau.
Một số framework AutoML có sẵn, bao gồm:
- Auto-sklearn: Một framework AutoML sử dụng Tối ưu hóa Bayes để tối ưu hóa toàn bộ quy trình học máy, bao gồm lựa chọn mô hình và tinh chỉnh siêu tham số.
- TPOT: Một framework AutoML sử dụng lập trình di truyền để khám phá các quy trình học máy tối ưu.
- H2O AutoML: Một nền tảng AutoML cung cấp một loạt các thuật toán và tính năng để tự động hóa quy trình học máy.
Ví dụ và Cân nhắc toàn cầu
Các nguyên tắc và kỹ thuật của Tối ưu hóa Bayes có thể áp dụng phổ biến trên các khu vực và ngành công nghiệp khác nhau. Tuy nhiên, khi áp dụng Tối ưu hóa Bayes trong bối cảnh toàn cầu, điều quan trọng là phải xem xét các yếu tố sau:
- Sự đa dạng của dữ liệu: Đảm bảo rằng dữ liệu được sử dụng để huấn luyện và xác thực mô hình là đại diện cho dân số toàn cầu. Điều này có thể yêu cầu thu thập dữ liệu từ các khu vực và nền văn hóa khác nhau.
- Cân nhắc về văn hóa: Lưu ý đến sự khác biệt văn hóa khi diễn giải kết quả của quá trình tối ưu hóa. Ví dụ, cấu hình siêu tham số tối ưu có thể thay đổi tùy thuộc vào bối cảnh văn hóa.
- Tuân thủ quy định: Đảm bảo rằng mô hình tuân thủ tất cả các quy định hiện hành ở các khu vực khác nhau. Ví dụ, một số khu vực có thể có các quy định nghiêm ngặt về quyền riêng tư và bảo mật dữ liệu.
- Cơ sở hạ tầng tính toán: Sự sẵn có của tài nguyên tính toán có thể khác nhau giữa các khu vực. Cân nhắc sử dụng các nền tảng dựa trên đám mây để cung cấp đủ sức mạnh tính toán cho Tối ưu hóa Bayes.
Ví dụ: Một công ty phát triển hệ thống phát hiện gian lận toàn cầu có thể sử dụng Tối ưu hóa Bayes để tinh chỉnh các siêu tham số của một mô hình học máy. Để đảm bảo mô hình hoạt động tốt ở các khu vực khác nhau, công ty sẽ cần thu thập dữ liệu từ nhiều quốc gia và nền văn hóa khác nhau. Họ cũng sẽ cần xem xét sự khác biệt văn hóa trong các mô hình chi tiêu và hành vi gian lận. Hơn nữa, họ sẽ cần tuân thủ các quy định về quyền riêng tư dữ liệu ở mỗi khu vực.
Kết luận
Tối ưu hóa Bayes là một kỹ thuật mạnh mẽ và hiệu quả để tinh chỉnh siêu tham số. Nó cung cấp một số lợi thế so với các phương pháp truyền thống như Tìm kiếm Lưới và Tìm kiếm Ngẫu nhiên, bao gồm hiệu quả, khả năng xử lý hàm không lồi và định lượng sự không chắc chắn. Bằng cách hiểu các nguyên tắc và kỹ thuật của Tối ưu hóa Bayes, bạn có thể cải thiện đáng kể hiệu suất của các mô hình học máy và đạt được kết quả tốt hơn trong nhiều ứng dụng khác nhau. Hãy thử nghiệm với các thư viện, hàm thu nhận và kỹ thuật nâng cao khác nhau để tìm ra phương pháp tốt nhất cho vấn đề cụ thể của bạn. Khi AutoML tiếp tục phát triển, Tối ưu hóa Bayes sẽ đóng một vai trò ngày càng quan trọng trong việc tự động hóa quy trình học máy và làm cho nó dễ tiếp cận hơn với nhiều đối tượng hơn. Hãy xem xét các tác động toàn cầu của mô hình của bạn và đảm bảo độ tin cậy và công bằng của nó trên các quần thể đa dạng bằng cách kết hợp dữ liệu đại diện và giải quyết các thiên vị tiềm ẩn.