Giá trị cực kỳ nhỏ hoặc NaN xuất hiện trong mạng nơ-ron huấn luyện


328

Tôi đang cố gắng triển khai kiến ​​trúc mạng thần kinh trong Haskell và sử dụng nó trên MNIST.

Tôi đang sử dụng hmatrixgói cho đại số tuyến tính. Khung đào tạo của tôi được xây dựng bằng cách sử dụng pipesgói.

Mã của tôi biên dịch và không bị lỗi. Nhưng vấn đề là, một số kết hợp nhất định của kích thước lớp (giả sử, 1000), kích thước lô nhỏ và tốc độ học tập sẽ làm tăng NaNgiá trị trong các phép tính. Sau một số kiểm tra, tôi thấy rằng các giá trị cực kỳ nhỏ (thứ tự 1e-100) cuối cùng xuất hiện trong các kích hoạt. Nhưng, ngay cả khi điều đó không xảy ra, việc đào tạo vẫn không hoạt động. Không có cải thiện về độ mất hoặc độ chính xác của nó.

Tôi đã kiểm tra và kiểm tra lại mã của mình, và tôi không biết gốc rễ của vấn đề có thể là gì.

Đây là đào tạo backpropagation, tính toán các delta cho mỗi lớp:

backward lf n (out,tar) das = do
    let δout = tr (derivate lf (tar, out)) -- dE/dy
        deltas = scanr (\(l, a') δ ->
                         let w = weights l
                         in (tr a') * (w <> δ)) δout (zip (tail $ toList n) das)
    return (deltas)

lflà hàm mất mát, nlà mạng ( weightma trận và biasvectơ cho mỗi lớp), outtarlà đầu ra thực tế của mạng và đầu ra target(mong muốn), và daslà các dẫn xuất kích hoạt của mỗi lớp.

Trong chế độ hàng loạt out, tarlà ma trận (hàng là vectơ đầu ra) và daslà danh sách các ma trận.

Đây là tính toán gradient thực tế:

  grad lf (n, (i,t)) = do
    -- Forward propagation: compute layers outputs and activation derivatives
    let (as, as') = unzip $ runLayers n i
        (out) = last as
    (ds) <- backward lf n (out, t) (init as') -- Compute deltas with backpropagation
    let r  = fromIntegral $ rows i -- Size of minibatch
    let gs = zipWith (\δ a -> tr (δ <> a)) ds (i:init as) -- Gradients for weights
    return $ GradBatch ((recip r .*) <$> gs, (recip r .*) <$> squeeze <$> ds)

Ở đây, lfncũng giống như trên, ilà đầu vào và tlà đầu ra đích (cả ở dạng lô, dưới dạng ma trận).

squeezebiến ma trận thành vectơ bằng cách tính tổng trên mỗi hàng. Đó là dsdanh sách các ma trận các delta, trong đó mỗi cột tương ứng với các delta cho một hàng của minibatch. Vì vậy, các gradient cho độ lệch là trung bình của các delta trên tất cả các minibatch. Điều tương tự đối với gs, tương ứng với độ dốc của các trọng số.

Đây là mã cập nhật thực tế:

move lr (n, (i,t)) (GradBatch (gs, ds)) = do
    -- Update function
    let update = (\(FC w b af) g δ -> FC (w + (lr).*g) (b + (lr).*δ) af)
        n' = Network.fromList $ zipWith3 update (Network.toList n) gs ds
    return (n', (i,t))

lrlà tỷ lệ học tập. FClà hàm tạo lớp, và aflà hàm kích hoạt cho lớp đó.

Thuật toán gradient descent đảm bảo chuyển giá trị âm cho tỷ lệ học tập. Mã thực tế cho sự giảm dần gradient chỉ đơn giản là một vòng lặp xung quanh thành phần của gradmove, với điều kiện dừng được tham số hóa.

Cuối cùng, đây là mã cho hàm mất lỗi bình phương trung bình:

mse :: (Floating a) => LossFunction a a
mse = let f (y,y') = let gamma = y'-y in gamma**2 / 2
          f' (y,y') = (y'-y)
      in  Evaluator f f'

Evaluator chỉ gói một hàm mất mát và đạo hàm của nó (để tính toán delta của lớp đầu ra).

Phần còn lại của mã có trên GitHub: NeuralNetwork .

Vì vậy, nếu ai đó có cái nhìn sâu sắc về vấn đề, hoặc thậm chí chỉ cần kiểm tra sự tỉnh táo rằng tôi đang triển khai chính xác thuật toán, tôi sẽ rất biết ơn.


17
Cảm ơn, tôi sẽ xem xét điều đó. Nhưng tôi không nghĩ đây là hành vi bình thường. Theo như tôi biết, các triển khai khác của những gì tôi đang cố gắng thực hiện (mạng nơ-ron được kết nối đầy đủ truyền tải đơn giản), bằng Haskell hoặc các ngôn ngữ khác, dường như không làm được điều đó.
Charles Langlois

17
@Charles: Bạn đã thực sự thử các mạng và tập dữ liệu của riêng mình với các cách triển khai khác chưa? Theo kinh nghiệm của riêng tôi, BP sẽ dễ dàng đi ngược chiều khi NN không phù hợp với vấn đề. Nếu bạn nghi ngờ về việc triển khai BP của mình, bạn có thể so sánh đầu ra của nó với kết quả của một phép tính gradient ngây thơ (tất nhiên là trên một NN cỡ đồ chơi) - khó sai hơn BP.
shinobi

5
MNIST thường không phải là một vấn đề phân loại sao? Tại sao bạn sử dụng MES? Bạn có nên sử dụng softmax crossentropy (tính từ logits) không?
mdaoust

6
@CharlesLanglois, Đó có thể không phải là vấn đề của bạn (tôi không thể đọc mã) nhưng "lỗi hình vuông trung bình" không phải là lồi đối với vấn đề phân loại, điều này có thể giải thích cho việc bị mắc kẹt. "logits" chỉ là một cách ưa thích để nói tỷ lệ cược đăng nhập: Sử dụng ce = x_j - log(sum_i(exp(x)))phép tính từ đây để bạn không lấy nhật ký của cấp số nhân (thường tạo ra NaN)
mdaoust

6
Chúc mừng bạn đã là câu hỏi được bình chọn cao nhất (tính đến ngày 20 tháng 1) mà không có câu trả lời nào được ủng hộ hoặc được chấp nhận!
hongsy

Câu trả lời:


2

Bạn có biết về gradient "biến mất" và "bùng nổ" trong lan truyền ngược không? Tôi không quá quen thuộc với Haskell vì vậy tôi không thể dễ dàng biết chính xác backprop của bạn đang làm gì, nhưng có vẻ như bạn đang sử dụng đường cong logistic làm chức năng kích hoạt.

Nếu bạn nhìn vào biểu đồ của hàm này, bạn sẽ thấy rằng gradient của hàm này gần bằng 0 ở các đầu (vì các giá trị đầu vào trở nên rất lớn hoặc rất nhỏ, độ dốc của đường cong gần như bằng phẳng), vì vậy nhân hoặc chia bởi điều này trong quá trình nhân giống ngược sẽ dẫn đến một số lượng rất lớn hoặc rất nhỏ. Làm điều này lặp đi lặp lại khi bạn đi qua nhiều lớp làm cho các kích hoạt gần bằng không hoặc vô cùng. Vì backprop cập nhật trọng lượng của bạn bằng cách thực hiện điều này trong quá trình luyện tập, bạn sẽ có rất nhiều số không hoặc số vô hạn trong mạng của mình.

Giải pháp: có vô số phương pháp mà bạn có thể tìm kiếm để giải quyết vấn đề gradient biến mất, nhưng một điều dễ dàng để thử là thay đổi loại chức năng kích hoạt bạn đang sử dụng thành chức năng không bão hòa. ReLU là một lựa chọn phổ biến vì nó giảm thiểu vấn đề cụ thể này (nhưng có thể giới thiệu những người khác).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.