Cách chọn Neural network hyperparameters
Mọi người có thể cho mình một số lời khuyên hay phương pháp cụ thể trong việc lựa chọn hyperparameters tốt nhất khi training DNN không. Mình có đọc qua một số tài liệu thì việc này phụ thuộc vào khá nhiều yếu tố cũng như bài toán cụ thể. Tuy nhiên tổng quát nhất thì mình nên bắt đầu từ đâu, các bước cơ bản cũng như những lưu ý khi thực hiện?
Mình cảm ơn!
1 CÂU TRẢ LỜI
Mình thấy việc lựa chọn hyperparameters cho DNN là công việc khá nhạt nhẽo và đòi hỏi khá nhiều công sức. Lý do cũng như bạn nói và hiện tại cũng không có một phương pháp cụ thể nào có thể áp dụng cho tất cả các bài toán (theo mình nghĩ vậy)
Thông thường NN sẽ có khá khá hyperparameters cũng như việc xây dựng NN là khá mềm dẻo (nhiều loại network topology - cách mà các artificial neurons liên kết với nhau). Ví dụ như khi sử dụng MLP (Multi-Layer Perceptron) có khá nhiều hyperparameter mình cần quan tâm như: số lượng hidden layers, weight initialization technique, số lượng neurons cho một layer, loại activation function (logistic, ReLU, tanh),...
Sử dụng Grid Search và Cross-validation có lẽ là phương pháp quen thuộc nhất khi đi tìm hyperparameter set, và nó cũng hoạt động khá hiệu quả trong nhiều bài toán. Tuy nhiên với những bài toán có lượng dữ liệu lớn hoặc có quá nhiều hyperparameters như NN chẳng hạn, phương pháp này khá chậm. Để tìm được hyperparameter set tốt nhất trong hyperparameter space với một khoảng thời gian chấp nhận được không phải là việc đơn giản. Bạn có thể sử dụng Randomized Search hay công cụ http://oscar.calldesk.ai (mình chưa thử nó bao giờ, google thấy cái tool này hay hay ).
Số lượng Hidden Layers
Trên thực tế NN với 1 hidden layer (shallow net) có thể được sử dụng để mô tả những hàm khá phức tạp nếu có đủ số lượng neurons Nếu bài toán không quá phức tạp thì mình có thể thử nghiệm với NN với 1 hidden layer trước xem nó ra kết quả tốt không. Sau đó có thể add thêm hidden layer nếu cần thiết. Vậy thì mình cần gì đến DNN Trên thực tế DNN có thể cho ra kết quả tốt với số lượng neuron cho mỗi layer giảm đi đáng kể so với shallow net, từ đó giúp cho việc training nhanh hơn đáng kể.
- Thử với 1 hoặc 2 hidden layer và kiểm tra kết quả xem đã như mình mong muốn !?
- Tăng số lượng hidden layer lên cho đến khi overfit tập training.
Số lượng Neurons cho một Hidden Layer
Đối với input và output layer thì số lượng neurons sẽ phụ thuộc vào loại input và output cần thiết của bài toán. Ví dụ bài toàn cổ điển phân loại digit (MNIST dataset), số lượng input neuron có thể là 784 (28*28) và 10 neuron cho output (do phân loại 10 chữ số từ 0 đến 9). Do vậy mình chỉ cần quan tấm đến số lượng neuron cho hidden layer.
- Tương tự như số lượng hidden layer, mình có thể tăng số lượng neurons cho các hidden layer cho đến khi overfit tập training.
- Ngược lại với cách đầu tiên, mình có thể bắt đầu với một DNN với số lượng layer và lượng neurons nhiều hơn cần thiết, sau đó sử dụng một số kĩ thuật như early stopping, regularization (l1, l2, max-norm) hay dropout để tránh model bị overfitting
Activation Function
Trong nhiều trường hợp ReLU (và các biến thể của nó) có thể là lựa chọn khá tốt cho các hidden layer vì việc tính toán là khá nhanh.
Đối với output layer, softmax có lẽ là lựa chọn tốt cho các bài toán classification, còn với các bài toán regression thì không cần activation function cho output layer.
Mình có tham khảo thêm một số nguồn thì thấy có một configuration sau (performance khá tốt trong nhiều trường hợp)
- Initialization: He initialization (tránh các vấn đề liên quan đến vanishing/exploding graidents khi bắt đầu training)
- Activation function: ELU (có performance tốt hơn ReLU trong khá nhiều trường hợp), Sigmoid hoặc Tanh cho các bài toán classification (các giá trị có thể được đẩy về [0, 1]).
- Regularization: Dropout
- Normalization: Batch Normalization (tránh các vấn đề liên quan đến vanishing/exploding graidents trong quá trình training)
- Optimizer: Nesterov Accelerated Gradient !?
- Learning rate schedule: không
Đây là một số suy nghĩ của mình, chắc vẫn còn thiếu nhiều nhưng mong giúp giải quyết câu hỏi này một phần nào đó.
Cảm ơn bạn vì câu trả lời khá chi tiết này nhé. Còn nhiều chỗ mình chưa hiểu nhưng có keywords để tìm hiểu rồi. Thanks 🙃🙃🙃
@vinhnguyen Bạn có thể nói rõ hơn cho mình về cách chọn activation function được không? Mình cảm ơn.
Mình thấy một vấn đề khá quen thuộc khi training một NN đó là Vanishing Gradients - gradient error sẽ nhỏ dần trong quá trình backpropagation. Đến một thời điểm nào đó connection weights của các layer thấp hơn sẽ không thay đổi (khi sử dụng Gradient Descent chẳng hạn), quá trình trainning sẽ không tìm được một kết quả tốt. Ngược lại nếu gradient lớn dần trong quá trình backpropagation thì sẽ dẫn đến hiện tượng Exploding Gradients - cũng không tốt một chút nào.
Có hai nguyên nhân chính dẫn đến hiện tượng trên:
- Sử dụng saturating activation function (ví dụ như signmoid function)
- Kĩ thuật khởi tạo các connection weights (weight initialization)
Ví dụ trong trường hợp sigmoid function, khi input lớn thì function sẽ trở nên bão hòa (saturating) tại 0 và 1, đạo hàm rất gần với 0, gradients trong quá trình backpropagation sẽ nhỏ dần khi đến các layer thấp hơn.
ReLU và các biến thể của nó thường được sử dụng thay cho sigmoid function, do nó không có hiện tượng bão hòa và khá nhanh khi tính toán.
Tuy nhiên khi sử dụng ReLU chúng ta gặp phải một hiện tượng đó là Dying ReLUs, khi một số neuron sẽ chỉ cho ra giá trị là 0 trong quá trình trainning (khi connection weights của neuron được update và weighted sum của các inputs nhỏ hơn 0 -> output = 0 -> gradient = 0). Để giải quyết vấn đề này thì chúng ta sẽ dùng một số biến thể của ReLU.
Leaky ReLU
Ref: https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Leaky_ReLUs Ref: https://arxiv.org/pdf/1505.00853.pdf
Ở đây alpha có tác dụng điều chỉnh độ dốc của hàm khi z < 0. Thay vì bằng 0 như trong ReLU output sẽ có giá trị thay đổi, tránh được hiện tượng dying ReLUs.
RReLU - Randomized Leaky ReLU
Tương tự như Leaky ReLU nhưng alpha sẽ được chọn ngẫu nhiên trong một khoảng nào đó khi training và được giữ nguyên trong quá trình testing -> tránh được hiện tượng overfitting.
PReLU - Parametric Leaky ReLU
Ở biến thể này thì alpha sẽ được "learn" trong quá trình trainning thay vì là một hyperparameter. Nó hoạt động khá tốt khi dataset lớn nhưng có thể dẫn đến overfitting nếu dataset quá nhỏ.
ELU - Exponential Linear Unit
Ref: https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#ELUs
def elu(z, alpha=1):
return np.where(z<0, alpha*(np.exp(z)-1), z)
ELU có performance khá tốt so với các biến thể trước đó của ReLU, với một số cải tiến như sau:
- Cho ra giá trị âm khi z < 0, giá trị trung bình của đầu ra sẽ gần với 0 -> tránh được hiện tượng vanishing gradients
- Gradient sẽ khác 0 khi z < 0 -> tránh được hiện tượng dying ReLUs
- ELU là một hàm khá trơn -> việc sử dụng Gradient Descent sẽ hiệu quả hơn
SELU (Scaled Exponential Linear Units)
Ref: https://arxiv.org/pdf/1706.02515.pdf Ref: SELU + Dropout: https://github.com/bioinf-jku/SNNs/blob/master/selu.py
def elu(z, alpha=1):
return np.where(z < 0, alpha * (np.exp(z) - 1), z)
def selu(z,
scale=1.0507009873554804934193349852946,
alpha=1.6732632423543772848170429916717):
return scale * elu(z, alpha)
Một loại activation function khá mới (2017) và có performance rất tốt so với các loại activation function khác.
Kết luận
Thông thường: ELU > Leaky ReLU (và các biến thể của nó) > ReLU > tanh > logistic (signmoid) Nếu quan tâm đến thời gian training: Leaky ReLUs > ELUs Thông thường giá trị của alpha sẽ là 0.01 cho leaky ReLU và 1 cho ELU Sử dụng RReLU nếu model overfitting tập training, PReLU nếu tập training lớn
:slight_smile: