Tips and tricks for training neural networks

tips-tricks

Bài viết này sẽ đề cập đến một số mẹo và thủ thuật cần lưu tâm về huấn luyện mô hình mạng nơron. Được Andrej Karpathy tổng kết lại trong quá trình giải quyết các bài toán dùng mạng nơron trong các dự án thực tế mà anh ta làm việc. Andrej Karpathy hiện đang là giám đốc trí tuệ nhân tạo tại Tesla (Director of Artificial Intelligence at Tesla). Chúng ta sẽ học hỏi được một quy trình đúng để huấn luyện mạng nơron sao cho kết quả tốt nhất. Bài này được dịch từ blog A Recipe for Training Neural Networks của Andrej Karpathy, nên văn phong và nhân xưng là của tác giả. Nếu có gì sai sót mong các bạn góp ý. Các bạn cùng tham khảo.

 

Vài tuần trước (25/04/2019) tôi đã đăng một tweet trên mạng "the most common neural net mistakes - một số lỗi hầu hết mạng nơron hay mắc phải", liệt kê một vài vấn đề phổ biến liên quan đến huấn luyện mạng nơron. Tweet đã nhận được sự quan tâm khá nhiều hơn một chút so với tôi dự đoán (bao gồm cả một hội thảo trên web). Rõ ràng, rất nhiều người đã trực tiếp gặp phải khoảng cách lớn giữa "đây là cách mà một lớp chập (convolutional layer) hoạt động" và "convnet của chúng ta đạt được kết quả tốt nhất (state of the art results)".

Vì vậy, tôi nghĩ rằng sẽ rất vui nếu phủi bụi blog của tôi bằng cách mở rộng tweet của mình sang dạng dài hơn để xứng đáng với chủ đề này. Tuy nhiên, thay vì liệt kê các lỗi phổ biến hoặc loại bỏ chúng, tôi muốn tìm hiểu sâu hơn một chút và nói về cách để một người có thể tránh mắc các lỗi này (hoặc sửa chúng rất nhanh). Mẹo (trick) để làm điều này là tuân theo một quy trình nhất định, mà theo như tôi có thể nói là không thường xuyên được ghi lại. Hãy bắt đầu với 2 quan sát quan trọng thúc đẩy điều này.

1. Huấn luyện mạng nơron là một rò rỉ trừu tượng (Neural net training is a leaky abstraction)

Nó được cho là dễ dàng để bắt đầu với huấn luyện mạng nơron. Nhiều thư viện và khung làm việc (frameworks) tự hào khi hiển thị các đoạn phép lạ 30 dòng code để giải quyết các vấn đề dữ liệu của bạn, tạo ấn tượng (sai) rằng công cụ này được cắm phát chạy luôn (plug and play). Ta thường thấy những thứ như:

>>> your_data = # plug your awesome dataset here
>>> model = SuperCrossValidator(SuperDuper.fit, your_data, ResNet50, SGDOptimizer)
# conquer world here
                    

Các thư viện và ví dụ này kích hoạt phần não bộ của chúng ta quen thuộc với tiêu chuẩn phần mềm - nơi thường có thể đạt được các API và trừu tượng sạch sẽ. Như thư viện Requests sau:

>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
                    

Thật tuyệt vời! Một nhà phát triển can đảm đã chịu trách nhiệm tìm hiểu các chuỗi truy vấn (query strings), urls, GET/POST requests, HTTP connections,v.v… từ bạn và hầu như che giấu phần lớn sự phức tạp đằng sau một vài dòng mã. Đây là những gì chúng ta quen thuộc và mong đợi. Thật không may, mạng nơron không có gì giống như vậy. Chúng không phải là công nghệ "ngoài kệ - off-the-shelf", thứ hai bạn chệch 1 chút so với việc huấn luyện một bộ phân lớp ImageNet. Tôi đã cố gắng đưa ra quan điểm này trong bài viết của mình "Yes you should understand backprop - Vâng bạn nên hiểu lan truyền ngược" bằng cách chọn lan truyền ngược (backpropagation) và gọi nó là một "sự rò rỉ trừu tượng - leaky abstraction", nhưng tình huống này không may hơn nhiều. Backprop + SGD không làm cho mạng của bạn hoạt động một cách kỳ diệu. Batch norm không kỳ diệu làm cho nó hội tụ nhanh hơn. RNN không kỳ diệu cho phép bạn cắm vào (plug in) văn bản. Và chỉ vì bạn có thể hình thành vấn đề của mình mà RL không có nghĩa là bạn nên làm. Nếu bạn khăng khăng sử dụng công nghệ mà không hiểu cách thức hoạt động của nó, bạn có khả năng thất bại. Điều này đưa tôi đến...

 

2. Huấn luyện mạng nơron thất bại âm thầm (Neural net training fails silently)

Khi bạn phá vỡ hoặc cấu hình sai (misconfigure) mã, bạn sẽ thường nhận được một số ngoại lệ (exception). Bạn đã cắm vào một số nguyên (integer) vào một nơi mong đợi 1 chuỗi (string). Hàm chỉ mong đợi 3 đối số. Import này thất bại. Khóa (key) đó không tồn tại. Số lượng phần tử trong 2 danh sách không bằng nhau. Ngoài ra, thường có thể tạo các bài kiểm tra đơn vị (unit tests) cho 1 chức năng nhất định.

Đây chỉ là một khởi đầu khi huấn luyện mạng nơron. Mọi thứ đều có thể đúng về mặt cú pháp, nhưng toàn bộ mọi thứ đều được sắp xếp hợp lý, và nó thật sự khó để nói. "Bề mặt lỗi có thể xảy ra - possible error surface" là rất lớn, logic (trái ngược với cú pháp), và rất khó để kiểm tra đơn vị - unit test. Ví dụ, có lẽ bạn đã quên lật nhãn (label) của mình khi bạn lật trái-phải (left-right flipped) hình ảnh trong quá trình tăng cường dữ liệu (data augmentation). Mạng của bạn vẫn có thể (gây sốc) hoạt động khá tốt vì mạng của bạn có thể học cách phát hiện các hình ảnh bị lật và sau đó nó lật trái-phải dự đoán của nó. Hoặc có thể mô hình tự tuyến tính (autoregressive) của bạn vô tình lấy thứ mà nó cố gắng dự đoán làm đầu vào do lỗi ngoài luồng (off-by-one bug). Hoặc bạn đã cố gắng cắt độ dốc (gradients) của mình nhưng thay vào đó là cắt độ lỗi, gây ra bởi các mẫu ngoại lệ bị bỏ qua trong quá trình huấn luyện. Hoặc bạn đã khởi tạo trọng số của mình từ một điểm huấn luyện trước (pretrained checkpoint) nhưng không sử dụng giá trị trung bình ban đầu. Hoặc bạn chỉ cần cài đặt các cường độ hiệu chỉnh (regularization strengths), tỷ lệ học (learning rate), tỷ lệ phân rã (its decay rate), kích thước mô hình (model size), v.v… Do đó, mạng nơron được cấu hình sai của bạn sẽ ném các ngoại lệ chỉ khi bạn may mắn; Hầu hết thời gian nó sẽ huấn luyện nhưng âm thầm làm việc tồi tệ hơn 1 chút.

Kết quả là, (và điều này thực sự khó để nhấn mạnh quá mức) một cách tiếp cận "quá nhanh quá nguy hiểm - fast and furious" của huấn luyện mạng nơron không hoạt động và chỉ dẫn đến đau khổ. Bây giờ, đau khổ (suffering) là một phần hoàn toàn tự nhiên để làm cho một mạng nơron hoạt động tốt, nhưng nó có thể được giảm nhẹ bằng cách thấu đáo, phòng thủ, hoang tưởng (paranoid) và bị ám ảnh bởi những biễu diễn (visualizations) về mọi thứ có thể. Những phẩm chất mà theo kinh nghiệm của tôi tương quan mạnh mẽ nhất với thành công trong học tập sâu (deep learning) là sự kiên nhẫn (patience) và chú ý (attention) đến chi tiết.

 

3. Công thức (The recipe)

Trước 2 sự thật trên, tôi đã phát triển 1 quy trình cụ thể cho bản thân mà tôi tuân theo khi áp dụng mạng nơron cho 1 vấn đề mới, mà tôi sẽ cố gắng mô tả. Bạn sẽ thấy rằng nó rất coi trọng 2 nguyên tắc trên. Cụ thể, nó được xây dựng từ đơn giản đến phức tạp và ở mỗi bước chúng ta đưa ra những giả thuyết cụ thể vẻ những gì sẽ xảy ra và sau đó xác nhận chúng bằng 1 thử nghiệm hoặc điều tra cho đến khi chúng ta tìm thấy 1 số vấn đề. Điều chúng tôi cố gắng ngăn chặn rất khó khăn là việc giới thiệu rất nhiều sự phức tạp không được xác minh một lúc, điều này chắc chắn sẽ giới thiệu các lỗi (bugs) / cấu hình sai (misconfigurations) sẽ mất rất lâu để tìm (nếu có). Nếu việc viết mã mạng nơron của bạn giống như huấn luyện, bạn sẽ muốn sử dụng 1 tỷ lệ học (learning rate) rất nhỏ và đoán và sau đó đánh giá toàn bộ tập kiểm tra (test set) sau mỗi lần lặp.

 

3.1. Trở thành một với dữ liệu (Become one with the data)

Bước đầu tiên để huấn luyện một mạng nơron là không chạm vào bất kỳ mã mạng nơron nào và thay vào đó bắt đầu bằng cách kiểm tra kỹ lưỡng dữ liệu của bạn. Bước này rất quan trọng. Tôi thích dành nhiều thời gian (tính bằng đơn vị giờ) quét qua hàng ngàn mẫu, hiểu phân phối của chúng và tìm kiếm các khuôn mẫu (patterns). May mắn thay, bộ não của bạn khá tốt trong việc này. Một lần tôi phát hiện ra rằng dữ liệu chứa các mẫu trùng lặp. Một lần khác tôi tìm thấy hình ảnh/nhãn (images/labels) bị sai. Tôi tìm kiếm sự mất cân bằng và sai lệch (imbalances and biases) của dữ liệu. Thông thường tôi cũng sẽ chú ý đến quy trình của riêng tôi để phân loại dữ liệu, gợi ý về các loại kiến trúc mà cuối cùng chúng tôi sẽ khám phá. Ví dụ - các đặc trưng cục bộ có đủ hoặc chúng ta có cần bối cảnh toàn cục? Có bao nhiêu biến thể (variation) ở đó và nó có hình thức gì? Biến thể nào là giả và có thể được xử lý trước? Liệu vị trí không gian có vấn đề hay chúng ta muốn trung bình gộp (average pool) nó ra? Bao nhiêu chi tiết quan trọng và chúng ta có thể đủ khả năng để làm giảm mẫu (downsample) hình ảnh? Làm thế nào nhiễu (noisy) là các nhãn (labels)?

Ngoài ra, do mạng nơron thực sự là một phiên bản nén/biên dịch (compressed/compiled) của bộ dữ liệu của bạn, bạn sẽ có thể xem các dự đoán ((mis)predictions) mạng của bạn và hiểu chúng có thể đến từ đâu. Và nếu mạng của bạn đưa ra 1 số dự đoán không phù hợp với những gì bạn đã thấy trong dữ liệu, thì có gì đó không ổn.

Khi bạn hiểu được định tính, bạn cũng nên viết 1 số mã đơn giản để tìm kiếm/lọc/sắp xếp (search/filter/sort) theo bất cứ điều gì bạn có thể nghĩ đến (ví dụ: loại nhãn, kích thước chú thích, số chú thích, v.v…) và hình dung (visualize) các phân phối của chúng và các ngoại lệ dọc theo bất kỳ trục (axis) nào. Các ngoại lệ (outliers - dị biệt) đặc biệt hầu như luôn luôn phát hiện ra một số lỗi về chất lượng dữ liệu hoặc tiền xử lý (preprocessing).

 

3.2. Thiết lập bộ khung huấn luyện/đánh giá từ đầu đến cuối + nhận các đường cơ sở câm (Set up the end-to-end training/evaluation skeleton + get dumb baselines)

Bây giờ chúng ta hiểu dữ liệu của mình, chúng ta có thể tiếp cận với Multi-scale ASPP FPN ResNet siêu lạ của chúng ta và bắt đầu huấn luyện các mô hình tuyệt vời chưa? Chắc chắn là không. Đó là con đường đau khổ. Bước tiếp theo của chúng ta là thiết lập một bộ khung huấn luyện + đánh giá đầy đủ và có được sự tin tưởng vào tính chính xác của nó thông qua 1 loạt các thí nghiệm. Ở giai đoạn này, tốt nhất là chọn 1 số mô hình đơn giản mà bạn không thể làm hỏng bằng cách nào đó - ví dụ: một bộ phân lớp tuyến tính, hoặc một ConvNet rất nhỏ. Chúng tôi sẽ muốn huấn luyện nó, trực quan hóa độ lỗi (ví dụ: độ chính xác), dự đoán mô hình, và thực hiện 1 loạt các thử nghiệm cắt bỏ với các giả thuyết rõ ràng trên đường đi.

Mẹo và thủ thuật (Tips & tricks) cho giai đoạn này:

  1. Cố định các hạt giống ngẫu nhiên (fix random seed): Luôn sử dụng 1 hạt giống ngẫu nhiên cố định để đảm bảo rằng khi bạn chạy mã 2 lần, bạn sẽ nhận được kết quả như nhau. Điều này loại bỏ 1 yếu tố biến thể và sẽ giúp bạn tỉnh táo.
  2. Đơn giản hóa (simplify): Hãy chắc chắn để vô hiệu hóa bất kỳ phức tạp hóa (fanciness) không cần thiết. Ví dụ, chắc chắn tắt bất kỳ sự tăng cường (augmentation) dữ liệu nào ở giai đoạn này. Tăng cường dữ liệu là một chiến lược chính quy (regularization strategy) mà chúng ta có thể kết hợp sau này, nhưng bây giờ nó chỉ là 1 cơ hội khác để giới thiệu 1 số lỗi ngu ngốc (dumb bug).
  3. Thêm các chữ số có nghĩa vào đánh giá của bạn (add significant digits to your eval): Khi vẽ sơ đồ độ lỗi kiểm tra (test loss), hãy chạy đánh giá trên toàn bộ tập kiểm tra (lớn). Không chỉ vẽ sơ đồ độ lỗi qua các đợt (batches) và sau đó dựa vào việc làm mịn chúng trong Tensorboard. Chúng ta đang theo đuổi sự đúng đắn và rất sẵn sàng bỏ thời gian để tỉnh táo.
  4. Xác minh độ lỗi khởi tạo (verify loss @ init): Xác minh rằng độ lỗi của bạn bắt đầu ở giá trị độ lỗi chính xác. Ví dụ: nếu bạn khởi tạo lớp cuối cùng của mình 1 cách chính xác, bạn nên đo -log(1/n_classes) trên softmax khi khởi tạo. Các giá trị mặc định tương tự có thể được lấy từ hồi quy L2, độ lỗi Huber, v.v...
  5. Khởi tạo tốt (init well): Khởi tạo trong lượng lớp cuối cùng 1 cách chính sác. Ví dụ, nếu bạn đang hồi quy 1 số giá trị có giá trị trung bình là 50 thì hãy khởi tạo độ lệch (bias) cuối cùng thành 50. Nếu bạn có bộ dữ liệu không cân bằng với tỷ lệ 1:10 của positive:negatives, hãy đặt độ lệch (bias) trên các bản ghi của bạn để mạng của bạn dự đoán xác suất 0,1 khi khởi tạo. Đặt các cài đặt này 1 cách chính xác sẽ tăng tốc độ hội tụ và loại bỏ các “đường gấp khúc - hockey stick” đường cong độ lỗi, trong đó trong vài lần lặp đầu tiên, mạng của bạn về cơ bản chỉ học về các độ lệch (bias).
  6. Đường cơ sở của con người (human baseline): Theo dõi các số liệu khác với độ lỗi mà con người có thể hiểu và kiểm tra được (ví dụ: độ chính xác). Bất cứ khi nào có thể đánh giá độ chính xác của bạn (con người) và so sánh với nó. Ngoài ra, chú thích dữ liệu kiểm tra 2 lần và cho mỗi mẫu coi 1 chú thích là dự đoán và lần thứ hai là sự thật cơ bản.
  7. Đường cơ sở độc lập đầu vào (input-indepent baseline): Huấn luyện 1 đường cơ sở độc lập đầu vào, (ví dụ, dễ nhất là chỉ đặt tất cả các đầu vào của bạn về 0). Điều này sẽ hoạt động tồi tệ hơn so với khi bạn thực sự cắm dữ liệu của mình mà không bỏ qua nó. Phải không? Tức là mô hình của bạn có học được cách trích xuất bất kỳ thông tin nào ra khỏi đầu vào không?
  8. Quá khớp 1 đợt (overfit one batch): Quá khớp 1 lô chỉ có 1 vài mẫu (ví dụ như chỉ có 2 mẫu). Làm như vậy vì chúng ta tăng công suất của mô hình của chúng ta (ví dụ: thêm các lớp layers hoặc bộ lọc filters) và xác minh rằng chúng ta có thể đạt được mức độ lỗi thấp nhất có thể (ví dụ: không). Tôi cũng thích trực quan hóa trong cùng một biểu đồ có cả nhãn (label) và dự đoán và đảm bảo rằng chúng sẽ hoàn toàn thẳng hàng một khi chúng ta đạt đến mức độ lỗi tối thiểu. Nếu không, có 1 lỗi ở đâu đó và chúng ta không thể tiếp tục giai đoạn tiếp theo.
  9. Xác minh giản độ lỗi huấn luyện (verify decreasing training loss): Ở giai đoạn này, bạn hy vọng sẽ đánh giá thấp (underfitting) dữ liệu của mình vì bạn đã làm việc với 1 mô hình đồ chơi. Cố gắng tăng công suất của nó chỉ 1 chút. Có phải độ lỗi huấn luyện của bạn đi xuống vì nó nên như vậy?
  10. Trực quan hóa ngay trước mạng (visualize just before the net): Vị trí chính xác (unambiguously) rõ ràng để trực quan hóa dữ liệu của bạn ngay lập tức trước y_hat = model(x) (hoặc sess.run trong TF). Đó là - bạn muốn trực quan hóa chính xác những gì đi vào mạng của mình, giải mã tensor raw thô của dữ liệu và nhãn labels thành trực quan hóa. Đây là nguồn duy nhất của sự thật (source of truth). Tôi có thể đếm số lần điều này đã cứu tôi và tiết lộ các vấn đề trong quá trình tiền xử lý và gia tăng dữ liệu.
  11. Trực quan hóa dự đoán động (visualize prediction dynamics): Tôi thích trực quan dự đoán mô hình trên 1 lô thử nghiệm test cố định trong quá trình huấn luyện. Động lực của cách dự đoán này sẽ cho bạn trực giác cực kỳ tốt về quá trình huấn luyện hoạt động. Nhiều lần có thể cảm nhận thấy mạng “đấu tranh – struggle” để phù hợp với dữ liệu của bạn nếu nó vặn vẹo quá nhiều theo 1 cách nào đó, cho thấy sự bất ổn. Tỷ lệ học cao hay thấp cũng dễ dàng nhận thấy trong số lượng hốt hoảng (jitter).
  12. Sử dụng lan truyền ngược để biểu diễn sự phụ thuộc (use backprop to chart dependencies): Mã học sâu của bạn sẽ chứa các hoạt động phức tạp, vectơ – vectorized, và phát sóng (broadcasted). Một lỗi tương đối phổ biến mà tôi gặp phải 1 vài lần là mọi người vẫn mắc phải lỗi này (ví dụ: họ sử dụng chế độ xem view thay vì chuyển đổi/hoán vị (transpose/permute) ở đâu đó) và vô tình trộn thông tin theo kích thước lô (batch). Một thực tế đáng buồn là mạng của bạn thường sẽ vẫn ổn vì nó sẽ học cách bỏ qua dữ liệu từ các mẫu khác. Một cách để gỡ lỗi này (và các vấn đề liên quan khác) là đặt độ lỗi cho mẫu i là 1.0, chạy ngược lại tất cả các cách đến đầu vào, và đảm bảo rằng bạn chỉ nhận được độ dốc gradient khác 0 chỉ trên mẫu thứ i. Tổng quát hơn, độ dốc gradients cung cấp cho bạn thông tin về những gì phụ thuộc vào những thứ trong mạng của bạn, có thể hữu ích cho việc gỡ lỗi (debugging).
  13. Tổng quát hóa một trường hợp cụ thể (generalize a special case): Đây là 1 chút mẹo về tổng quát hóa mã, nhưng tôi đã thấy mọi người tạo ra lỗi khi họ cắn nhiều hơn là họ nhai, viết một hàm tổng quát hóa ngay từ đầu. Tôi thích viết 1 hàm rất cụ thể cho những gì tôi làm ngay bây giờ, làm cho nó hoạt động, và sau đó khái quát hóa nó sau đó để đảm bảo rằng tôi nhận được kết quả tương tự. Thông thường, điều này áp dụng cho mã vector hóa, trong đó tôi hầu như luôn luôn viết ra phiên bản hoàn toàn sơ đồ và chỉ sau đó chuyển đổi nó thành mã vectơ một vòng lặp một lần.

 

3.3. Quá khớp (Overfit)

Ở giai đoạn này, chúng ta nên có 1 sự hiểu biết tốt về bộ dữ liệu và chúng ta có quá trình huấn luyện + đánh giá (training + evaluation) đầy đủ. Đối với bất kỳ mô hình cụ thể nào, chúng ta có thể (tái tạo) tính toán 1 số liệu mà chúng ta tin tưởng. Chúng tôi cũng được trang bị hiệu suất của chúng tôi cho một đường cơ sở độc lập đầu vào (input-independent baseline), hiệu suất của một vài đường cơ sở câm (dumb baselines) (chúng tôi tốt hơn là đánh bại chúng) và chúng tôi có ý thức sơ bộ về hiệu suất của con người (chúng tôi hy vọng đạt được điều này). Giai đoạn này bây giờ được thiết lập để lặp lại trên một mô hình tốt.

Cách tiếp cận tôi muốn thực hiện để tìm ra 1 mô hình tốt có 2 giai đoạn: đầu tiên hãy lấy 1 mô hình đủ lớn để nó quá khớp (nghĩa là tập trung vào độ lỗi huấn luyện) và sau đó tinh chỉnh nó 1 cách thích hợp (từ bỏ độ lỗi huấn luyện (training loss) để cải thiện độ lỗi xác minh (validation loss)). Lý do tôi thích 2 giai đoạn này là nếu chúng tôi không thể đạt được tỷ lệ lỗi thấp với bất kỳ mô hình nào, một lần nữa có thể chỉ ra 1 số vấn đề, lỗi bugs, hoặc cấu hình sai misconfiguration.

Một vài mẹo và thủ thuật (Tips & Tricks) cho giai đoạn này:

  1. Chọn 1 mô hình (picking the model): Để đạt được 1 độ lỗi huấn luyện tốt, bạn sẽ muốn chọn 1 kiến trúc phù hợp cho dữ liệu. Khi chọn điều này, lời khuyên số #1 của tôi là: Đừng trở thành anh hùng – Don’t be a hero. Tôi đã thấy rất nhiều người mong muốn phát điên và sáng tạo trong việc sắp xếp các khối lego của hộp công cụ mạng nơron trong các kiến trúc kỳ lạ khác nhau có ý nghĩa với họ. Chống lại sự cám dỗ mạnh mẽ này trong giai đoạn đầu của dự án của bạn. Tôi luôn khuyên mọi người chỉ cần tìm những bài báo có liên quan nhất và sao chép kiến trúc đơn giản nhất của họ để đạt được hiệu suất tốt. Ví dụ, nếu bạn đang phân lớp ảnh thì đừng trở thành một anh hùng và chỉ cần sao chép một ResNet-50 cho lần chạy đầu tiên của bạn. Bạn được phép làm 1 cái gì đó tùy chỉnh hơn sau đó và đánh bại điều này.
  2. Adam là an toàn (adam is safe): Trong giai đoạn đầu thiết lập đường cơ sở, tôi thích sử dụng Adam với tỷ lệ học (learning rate) là 3e-4. Theo kinh nghiệm của tôi, Adam dễ tha thứ (forgiving - khoan dung) hơn cho các siêu tham số hyperparameters, bao gồm cả tỷ lệ học tệ. Đối với ConvNet, một SGD được điều chỉnh tốt (well-tuned) sẽ hầu như luôn vượt trội hơn so với Adam, nhưng khu vực tỷ lệ học tối ưu hẹp hơn nhiều và cho những vấn đề cụ thể (problem-specific). (Lưu ý: Nếu bạn đang sử dụng RNNs và các mô hình trình tự (sequence models) liên quan, việc sử dụng Adam sẽ phổ biến hơn. Ở giai đoạn đầu của dự án, một lần nữa, đừng trở thành anh hùng và làm theo bất cứ điều gì ở các bài báo liên nhất đã làm).
  3. Phức tạp hóa chỉ 1 cái 1 lần (complexify only one at a time): Nếu bạn có nhiều tín hiệu (signals) để cắm vào bộ phân lớp của mình, tôi sẽ khuyên bạn nên cắm từng tín hiệu một và mỗi lần đảm bảo rằng bạn có được hiệu suất tăng mà bạn mong đợi. Đừng ném chậu rửa chén vào mô hình của bạn. Có nhiều cách khác để xây dựng sự phức tạp - ví dụ, bạn có thể thử cắm các hình ảnh nhỏ hơn trước và làm cho chúng lớn hơn sau, v.v...
  4. Đừng tin vào tỷ lệ học phân rã mặc định (do not trust learning rate decay defaults): Nếu bạn đang tái sử dụng mã từ 1 số miền (domain) khác, hãy luôn cẩn thận với việc giảm tốc độ học tập. Bạn không chỉ muốn sử dụng các lịch trình phân rã khác nhau, mà thậm chí tệ hơn - trong một triển khai điểm hình, lịch trình sẽ dựa trên số epoch hiện tại, có thể thay đổi rộng rãi tùy thuộc vào kích thức của tập dữ liệu của bạn. Ví dụ, ImageNet sẽ phân rã trước 10 trên mỗi epoch 30. Nếu bạn không huấn luyện ImageNet thì bạn gần như chắc chắn không muốn điều này. Nếu bạn không cẩn thận, mã của bạn có thể bí mật khiến tỷ lệ học tập của bạn về 0 quá sớm, không cho phép mô hình của bạn hội tụ. Trong công việc của mình, tôi luôn vô hiệu hóa hoàn toàn việc giảm tỷ lệ học (tôi sử dụng 1 hằng số LR) và điều chỉnh tất cả các cách này vào cuối cùng.

 

3.4. Tinh chỉnh (Regularize)

Lý tưởng nhất là chúng ta đang ở 1 nơi mà chúng ta có 1 mô hình lớn phù hợp với ít nhất tập huấn luyện (learning set). Bây giờ là lúc để tinh chỉnh hóa nó và đạt được 1 số độ chính xác xác nhận (validation accuracy) bằng cách từ bỏ 1 số độ chính xác huấn luyện (training accuracy).

Một số mẹo và thủ thuật (Tips & Tricks):

  1. Lấy thêm dữ liệu (get more data): Đầu tiên, cách tốt nhất và được ưu thích nhất để tinh chỉnh hóa 1 mô hình trong bất kỳ cài đặt thực tế nào là thêm dữ liệu huấn luyện thực tế hơn. Đó là 1 sai lầm rất phổ biến khi dành nhiều chu kỳ kỹ thuật cố gắng ép nước trái cây trong 1 tập dữ liệu nhỏ khi bạn có thể thu thập thêm dữ liệu. Theo như tôi biết, việc thêm nhiều dữ liệu là cách duy nhất được đảm bảo để cải thiện hiệu năng của mạng nơron được cấu hình tốt gần như là vô hạn. Cái còn lại sẽ là quần thể (nếu bạn có đủ khả năng), nhưng nó đứng đầu sau ~5 mô hình.
  2. Tăng dữ liệu (data augment): Điều tốt nhất tiếp theo đối với dữ liệu thực tế là dữ liệu nửa giả (haft-fake data) - thử tăng cường dữ liệu mạnh mẽ hơn.
  3. Tăng cường sáng tạo (creative augmentation): Nếu dữ liệu nửa giả không làm điều gì đó, dữ liệu giả cũng có thể làm điều gì đó. Mọi người đang tìm cách sáng tạo để mở rộng bộ dữ liệu; Ví dụ, miền ngẫu nhiên, sử dụng mô phỏng simulation, các phép lai thông minh như chèn dữ liệu (có khả năng mô phỏng) vào các cảnh hoặc thậm chí là GAN.
  4. Huấn luyện trước (pretrain): Nếu có thể, bạn có thể sử dụng một mạng đã huấn luyện trước đó khi có đủ dữ liệu.
  5. Gắn bó với việc học có giám sát (stick with supervised learning): Đừng quá phấn khích về sự huấn luyện trước không giám sát. Không giống như những gì bài đăng trên blog từ 2008 nói với bạn, theo như tôi viết, không có phiên bản nào của nó báo cáo kết quả mạnh mẽ trong mô hình thị giác máy tính (mặc dù NLP dường như đang làm khá tốt với BERT và các biến thể gần đây, rất có thể do tính chất văn bản có chủ ý hơn và tỷ lệ nhiễu tín hiệu cao hơn).
  6. Kích thước đầu vào nhỏ hơn (smaller input dimensionality): Xóa các đặc trưng có thể chứa tín hiệu giả (sprurious signal). Bất kỳ đầu vào giả nào được thêm vào chỉ là 1 cơ hội khác để quá khớp (overfit) nếu tập dữ liệu của bạn nhỏ. Tương tự, nếu các chi tiết cấp thấp không có vấn đề gì, hãy cố gắng nhập một hình ảnh nhỏ hơn.
  7. Kích thước mô hình nhỏ hơn (smaller model size): Trong nhiều trường hợp, bạn có thể sử dụng các ràng buộc miền tri thức hẹp (domain knowledge contraints) để giảm kích thước của nó. Ví dụ, thường có xu hướng sử dụng các lớp kết nối đầy đủ Fully Connected layers ở đầu bộ khung cho ImageNet nhưng chúng đã được thay thế bằng cách gộp chung trung bình đơn giản (simple average pooling), loại bỏ hàng tấn tham số trong quy trình.
  8. Giảm kích thước lô (decrease the batch size): Do sự chuẩn hóa bên trong batch norm nhỏ hơn kích thước lô tương ứng với chính quy hóa mạnh hơn (stronger regularization). Điều này là do giá trị trung bình/chuẩn lô theo kinh nghiệm (batch empirical mean/std) là các phiên bản gần đúng hơn của trung bình/chuẩn đầy đủ (full mean/std), do đó độ mở rộng & chỉ mục (scale & offset) "rung lắc – wiggles" batch của bạn xung quanh hơn.
  9. Bỏ học (drop): Thêm bỏ học (add dropout). Sử dụng dropout2d (bỏ không gian) cho ConvNets. Sử dụng 1 cách tiết kiệm/cẩn thận vì học sinh bỏ học dường như không chơi đẹp với chuẩn hóa lô (batch normalization).
  10. Giảm trọng số (weight decay): Tăng hình phạt giảm trọng số.
  11. Dừng lại sớm (early stopping): Dừng huấn luyện dựa trên đại lượng độ lỗi xác minh của bạn để giữ lại mô hình của bạn giống như nó sắp sửa quá khớp (overfit).
  12. Thử 1 mô hình lớn hơn (try a larger model): Tôi đã đề cập đến vấn đề này cuối cùng và chỉ sau khi dừng sớm (early stopping) nhưng tôi đã phát hiện ra một vài lần trong quá khứ rằng các mô hình lớn hơn tất nhiên sẽ quá khớp overfit nhiều hơn, nhưng hiệu suất "early stopped" của chúng có thể tốt hơn so với các mô hình nhỏ hơn.

Cuối cùng, để có thêm niềm tin rằng mạng của bạn là một bộ phân lớp hợp lý, tôi muốn trực quan hóa các trọng số lớp đầu tiên của mạng và đảm bảo bạn có được các cạnh đẹp có ý nghĩa (make sense). Nếu các bộ lọc lớp đầu tiên của bạn trông giống như nhiễu thì một cái gì đó có thể bị tắt. Tương tự, activations bên trong mạng đôi khi có thể hiển thị các tạo tác kỳ lạ và gợi ý về các vấn đề.

 

3.5. Điều chỉnh (Tune)

Bây giờ bạn là "trong 1 vòng lặp - in the loop" với bộ dữ liệu khám phá 1 không gian mô hình rộng cho các kiến trúc đạt được độ lỗi xác minh thấp.

Một vài mẹo và thủ thuật (tips & tricks) cho bước này:

  1. Ngẫu nhiên trên lưới tìm kiếm (random over grid search): Để điều chỉnh đồng thời nhiều siêu tham số hyperparameters, có vẻ hấp dẫn khi sử dụng tìm kiếm dạng lưới để đảm bảo phạm vi của tất cả các cài đặt, nhưng hãy nhớ rằng tốt nhất là sử dụng tìm kiếm ngẫu nhiên thay thế. Theo trực giác, điều này là do mạng nơron thường nhạy cảm hơn với 1 số tham số so với các tham số khác. Trong giới hạn, nếu tham số a là một vấn đề nhưng thay đổi b không có hiệu lực thì bạn sẽ lấy mẫu a xuyên suốt hơn tại 1 vài điểm cố định nhiều lần hơn.
  2. Tối ưu siêu tham số (hyper-parameter optimization): Có 1 số lượng lớn các hộp công cụ tối ưu siêu tham số bayesian ưu thích và 1 vài người bạn của tôi cũng đã báo cáo thành công với chúng, nhưng kinh nghiệm cá nhân của tôi là cách tiếp cận hiện đại để khám phá 1 không gian đẹp và rộng của các mô hình và các siêu tham số được sử dụng như 1 thực tập sinh. Đùa thôi (Just kidding).

 

3.6. Vắt kiệt nước (Squeeze out the juice)

Khi bạn tìm thấy các loại kiến trúc và siêu tham số tốt nhất, bạn vẫn có thể sử dụng 1 vài thủ thuật khác để vắt kiệt những giọt nước ép cuối cùng ra khỏi hệ thống:

  1. Hòa hợp (ensembles): Các mô hình hòa hợp là một cách được đảm bảo khá nhiều để đạt được 2% độ chính xác cho bất cứ điều gì. Nếu bạn có đủ khả năng tính toán trong thời gian thử nghiệm, hãy xem xét việc chưng cất hỗn hợp (ensemble) của bạn vào một mạng bằng tri thức đen tối (dark knowledge).
  2. Để nó huấn luyện (leave it training): Tôi đã thấy mọi người cố gắng dừng việc huấn luyện mô hình khi độ lỗi xác thực dường như đang chững lại. Theo kinh nghiệm của tôi, mạng tiếp tục huấn luyện trong thời gian dài không chủ ý. Một lần tôi vô tình rời khỏi việc huấn luyện mô hình trong suốt kỳ nghỉ đông và khi tôi trở lại vào tháng 1, đó là SOTA ("trạng thái nghệ thuật - state of the art").

 

4. Kết luận (Conclusion)

Khi bạn thực hiện đến đây, bạn sẽ có tất cả các thành phần để thành công: Bạn có hiểu biết sâu sắc về công nghệ, bộ dữ liệu và vấn đề, bạn đã thiết lập toàn bộ cơ sở hạ tầng huấn luyện/đánh giá và đạt được độ tin cậy cao về độ chính xác của nó, và bạn đã khám phá các mô hình ngày càng phức tạp hơn, đạt được những cải tiến về hiệu suất theo cách bạn đã dự đoán từng bước một. Bây giờ bạn đã sẵn sàng để đọc rất nhiều bài báo, thử 1 số lượng lớn các thử nghiệm và nhận kết quả SOTA của bạn. Chúc may mắn!

 

 

Happy coding!!!

01h02AM, 01/05/2019

Our Team