ค่า NaN ที่น้อยมากจะปรากฏในโครงข่ายประสาทเทียม


329

ฉันกำลังพยายามใช้สถาปัตยกรรมเครือข่ายประสาทเทียมใน Haskell และใช้กับ MNIST

ฉันกำลังใช้hmatrixแพ็คเกจสำหรับพีชคณิตเชิงเส้น กรอบการฝึกอบรมของฉันสร้างขึ้นโดยใช้pipesแพ็คเกจ

โค้ดของฉันคอมไพล์และไม่ผิดพลาด แต่ปัญหาคือการผสมขนาดเลเยอร์บางอย่าง (เช่น 1,000) ขนาดมินิแบทช์และอัตราการเรียนรู้ทำให้เกิดNaNค่าในการคำนวณ หลังจากการตรวจสอบบางครั้งฉันเห็นว่าในที่สุดค่าที่เล็กมาก (ลำดับของ1e-100) จะปรากฏในการเปิดใช้งาน แต่แม้ว่าจะไม่เกิดขึ้นการฝึกอบรมก็ยังไม่ได้ผล ไม่มีการปรับปรุงการสูญเสียหรือความแม่นยำ

ฉันตรวจสอบและตรวจสอบรหัสของฉันอีกครั้งและฉันก็สูญเสียว่าต้นตอของปัญหาอาจเป็นอย่างไร

นี่คือการฝึก backpropagation ซึ่งคำนวณเดลต้าสำหรับแต่ละเลเยอร์:

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)

lfคือฟังก์ชันการสูญเสียnคือเครือข่าย ( weightเมทริกซ์และbiasเวกเตอร์สำหรับแต่ละเลเยอร์) outและtarเป็นผลลัพธ์จริงของเครือข่ายและtargetเอาต์พุต (ที่ต้องการ) และdasเป็นอนุพันธ์การกระตุ้นของแต่ละเลเยอร์

ในโหมดแบทช์out, tarมีการฝึกอบรม (แถวเวกเตอร์เอาท์พุท) และdasเป็นรายการของการฝึกอบรมที่

นี่คือการคำนวณการไล่ระดับสีที่แท้จริง:

  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)

ที่นี่lfและnเหมือนกับด้านบนiคืออินพุตและtเป็นเอาต์พุตเป้าหมาย (ทั้งในรูปแบบแบตช์เป็นเมทริกซ์)

squeezeแปลงเมทริกซ์เป็นเวกเตอร์โดยการหาผลรวมของแต่ละแถว นั่นคือdsรายการเมทริกซ์ของเดลต้าโดยแต่ละคอลัมน์จะตรงกับเดลต้าสำหรับแถวของมินิแบทช์ ดังนั้นการไล่ระดับสีของอคติจึงเป็นค่าเฉลี่ยของเดลต้าในมินิแบทช์ทั้งหมด สิ่งเดียวกันสำหรับgsซึ่งสอดคล้องกับการไล่ระดับสีของน้ำหนัก

นี่คือรหัสอัปเดตจริง:

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))

lrคืออัตราการเรียนรู้ FCเป็นตัวสร้างเลเยอร์และafเป็นฟังก์ชันการเปิดใช้งานสำหรับเลเยอร์นั้น

อัลกอริทึมการไล่ระดับสีทำให้แน่ใจว่าได้ส่งค่าลบสำหรับอัตราการเรียนรู้ รหัสจริงสำหรับการไล่ระดับสีเป็นเพียงการวนรอบองค์ประกอบของgradและmoveด้วยเงื่อนไขการหยุดที่กำหนด

สุดท้ายนี่คือรหัสสำหรับฟังก์ชันการสูญเสียข้อผิดพลาดกำลังสองค่าเฉลี่ย:

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 เพียงรวมฟังก์ชันการสูญเสียและอนุพันธ์ (สำหรับการคำนวณเดลต้าของเลเยอร์เอาต์พุต)

ส่วนที่เหลือของรหัสเป็นบน GitHub: NeuralNetwork

ดังนั้นหากใครมีข้อมูลเชิงลึกเกี่ยวกับปัญหาหรือแม้แต่การตรวจสอบความมีเหตุผลว่าฉันใช้อัลกอริทึมอย่างถูกต้องฉันก็จะขอบคุณ


17
ขอบคุณฉันจะตรวจสอบให้ แต่ฉันไม่คิดว่านี่เป็นพฤติกรรมปกติ เท่าที่ฉันรู้การใช้งานอื่น ๆ ในสิ่งที่ฉันพยายามทำ (เครือข่ายประสาทเทียมที่เชื่อมต่ออย่างสมบูรณ์แบบ feedforward) ไม่ว่าจะเป็นภาษา Haskell หรือภาษาอื่น ๆ ดูเหมือนจะไม่ทำเช่นนั้น
Charles Langlois

17
@ ชาร์ลส์: คุณลองใช้เครือข่ายและชุดข้อมูลของคุณเองด้วยการใช้งานอื่น ๆ หรือไม่? จากประสบการณ์ของฉันเอง BP จะยุ่งเหยิงได้ง่ายเมื่อ NN ไม่เหมาะสมกับปัญหา หากคุณมีข้อสงสัยเกี่ยวกับการใช้ BP คุณสามารถเปรียบเทียบผลลัพธ์กับการคำนวณการไล่ระดับสีที่ไร้เดียงสา (แน่นอนว่าเป็น NN ขนาดของเล่น) ซึ่งเป็นวิธีที่ผิดยากกว่า BP
shinobi

5
โดยทั่วไปแล้ว MNIST ไม่ใช่ปัญหาการจำแนกประเภทหรือไม่? ทำไมคุณถึงใช้ MES? คุณควรใช้ softmax crossentropy (คำนวณจาก logits) ไม่?
mdaoust

6
@CharlesLanglois อาจไม่ใช่ปัญหาของคุณ (ฉันอ่านรหัสไม่ได้) แต่ "ข้อผิดพลาดกำลังสองค่าเฉลี่ย" ไม่นูนสำหรับปัญหาการจัดหมวดหมู่ซึ่งสามารถอธิบายการติดขัดได้ "logits" เป็นเพียงวิธีง่ายๆในการพูด log-odds: ใช้การce = x_j - log(sum_i(exp(x)))คำนวณจากตรงนี้เพื่อที่คุณจะได้ไม่ต้องใช้ log ของ exponential (ซึ่งมักจะสร้าง NaN)
mdaoust

6
ขอแสดงความยินดีที่เป็นคำถามที่ได้รับการโหวตสูงสุด (ณ ม.ค. 63) ที่ไม่มีการโหวตหรือตอบรับ!
hongsy

คำตอบ:


2

คุณรู้จักการไล่ระดับสีแบบ "หายไป" และ "ระเบิด" ในการขยายภาพย้อนหลังหรือไม่ ฉันไม่ค่อยคุ้นเคยกับ Haskell ดังนั้นฉันจึงไม่สามารถมองเห็นได้อย่างง่ายดายว่า backprop ของคุณกำลังทำอะไรอยู่ แต่ดูเหมือนว่าคุณกำลังใช้เส้นโค้งโลจิสติกเป็นฟังก์ชันการเปิดใช้งานของคุณ

หากคุณดูพล็อตของฟังก์ชันนี้คุณจะเห็นว่าการไล่ระดับสีของฟังก์ชันนี้มีค่าเกือบเป็น 0 ที่ส่วนท้าย (เนื่องจากค่าอินพุตมีขนาดใหญ่มากหรือน้อยมากความชันของเส้นโค้งจะเกือบจะแบน) ดังนั้นการคูณหรือหาร ด้วยสิ่งนี้ในระหว่างการขยายภาพย้อนกลับจะส่งผลให้มีจำนวนมากหรือน้อยมาก การทำเช่นนี้ซ้ำ ๆ ในขณะที่คุณผ่านหลายชั้นจะทำให้การเปิดใช้งานเข้าใกล้ศูนย์หรือไม่มีที่สิ้นสุด เนื่องจาก backprop อัปเดตน้ำหนักของคุณโดยทำสิ่งนี้ในระหว่างการฝึกอบรมคุณจะได้ศูนย์หรือ infinities จำนวนมากในเครือข่ายของคุณ

วิธีแก้ไข: มีวิธีการมากมายที่คุณสามารถค้นหาเพื่อแก้ปัญหาการไล่ระดับสีที่หายไป แต่สิ่งหนึ่งที่ง่ายในการลองคือการเปลี่ยนประเภทของฟังก์ชันการเปิดใช้งานที่คุณใช้เป็นฟังก์ชันที่ไม่อิ่มตัว ReLU เป็นตัวเลือกยอดนิยมเนื่องจากช่วยบรรเทาปัญหานี้โดยเฉพาะ (แต่อาจแนะนำผู้อื่น)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.