훈련 신경망에 매우 작거나 NaN 값이 나타납니다.


329

Haskell에서 신경망 아키텍처를 구현하고 MNIST에서 사용하려고합니다.

hmatrix선형 대수 패키지를 사용하고 있습니다. 내 교육 프레임 워크는 pipes패키지를 사용하여 빌드 됩니다.

내 코드가 컴파일되고 충돌하지 않습니다. 그러나 문제는 레이어 크기 (예 : 1000), 미니 배치 크기 및 학습률의 특정 조합 NaN이 계산 값을 생성한다는 것입니다. 몇 가지 검사 후 극히 작은 값 (순서 1e-100)이 결국 활성화에 나타납니다. 그러나 그것이 일어나지 않더라도 훈련은 여전히 ​​작동하지 않습니다. 손실이나 정확성에 대한 개선은 없습니다.

내 코드를 확인하고 다시 확인했는데 문제의 원인이 무엇인지 알 수 없었습니다.

다음은 각 계층에 대한 델타를 계산하는 역 전파 훈련입니다.

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각 계층 벡터), outtar네트워크와의 실제 출력 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
감사합니다. 조사하겠습니다. 그러나 나는 이것이 정상적인 행동이라고 생각하지 않습니다. 내가 아는 한, Haskell이나 다른 언어로 내가하려는 것의 다른 구현 (간단한 피드 포워드 완전 연결 신경망)은 그렇게하지 않는 것 같습니다.
Charles Langlois

17
@Charles : 실제로 다른 구현으로 자신의 네트워크와 데이터 세트를 사용해 보았습니까? 내 경험상 NN이 문제에 적합하지 않을 때 BP는 쉽게 문제가 될 것입니다. BP 구현에 대해 의문이있는 경우 출력을 순진한 기울기 계산 (물론 장난감 크기의 NN을 통해)과 비교할 수 있습니다. 이것은 BP보다 잘못하기가 훨씬 더 어렵습니다.
shinobi

5
MNIST는 일반적으로 분류 문제가 아닙니까? MES를 사용하는 이유는 무엇입니까? 소프트 맥스 크로스 엔트로피 (로짓에서 계산)를 사용해야합니다.
mdaoust

6
@CharlesLanglois, 문제가 아닐 수도 있지만 (코드를 읽을 수 없음) "평균 제곱 오류"는 분류 문제에 대해 볼록하지 않아서 막히는 것을 설명 할 수 있습니다. "logits"는 로그 확률을 말하는 멋진 방법 일뿐입니다. 여기 에서 ce = x_j - log(sum_i(exp(x)))계산 사용 하여 지수 (종종 NaN을 생성 함)의 로그를 취하지 않도록 하십시오
mdaoust

6
찬성 또는 수락 된 답변없이 가장 많이 득표 한 질문 ('20 년 1 월 기준)이 된 것을 축하합니다 !
hongsy

답변:


2

역 전파의 "소멸"및 "폭발"그래디언트에 대해 알고 있습니까? 나는 Haskell에 너무 익숙하지 않아서 당신의 backprop가 정확히 무엇을하는지 쉽게 알 수는 없지만 활성화 함수로 로지스틱 곡선을 사용하는 것처럼 보입니다.

이 함수의 플롯을 보면이 함수의 기울기가 끝에서 거의 0 인 것을 볼 수 있습니다 (입력 값이 매우 크거나 매우 작아 지므로 곡선의 기울기는 거의 평평합니다). 따라서 곱하거나 나눕니다. 역 전파 동안 이것에 의해 매우 크거나 아주 작은 숫자가됩니다. 여러 레이어를 통과 할 때이 작업을 반복하면 활성화가 0 또는 무한대에 가까워집니다. 역 전파가 훈련 중에이를 수행하여 가중치를 업데이트하기 때문에 네트워크에 많은 0 또는 무한대가 생깁니다.

솔루션 : 소실 그라데이션 문제를 해결하기 위해 검색 할 수있는 많은 방법이 있지만 시도하기 쉬운 한 가지 방법은 사용중인 활성화 함수 유형을 비 포화 함수로 변경하는 것입니다. ReLU는이 특정 문제를 완화하기 때문에 인기있는 선택입니다 (하지만 다른 문제가 발생할 수 있음).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.