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