하지만 문제는 신경망의 출력을 [0, 1] 범위 사이로 강제하는 방법입니다. 신경망의 마지막 층에 많이 사용하는 밀집 층은 입력에 대해 행렬 곱셈(matMul)과 편향 덧셈(biasAdd) 연산을 수행합니다. matMul이나 biasAdd 연산은 본질적으로 그 결괏값이 [0, 1] 사이가 되는지 보장하지 않습니다. matMul과 biasAdd의 결괏값에 시그모이드 같은 비선형 압축 함수를 추가하면 간단하게 [0, 1] 범위로 만들 수 있습니다.
코드 3-5에 있는 또 다른 새로운 점은 'adam' 옵티마이저입니다. 이 옵티마이저는 이전 예제에서 사용한 'sgd' 옵티마이저와 다릅니다. adam이 sgd와 어떻게 다를까요? 이전 장의 2.2.2절을 떠올려 보면 sgd 옵티마이저는 역전파를 통해 구한 그레이디언트를 항상 고정 숫자(학습률 × -1)와 곱하여 모델의 가중치 업데이트를 계산합니다. 이 방식에는 단점이 조금 있습니다. 학습률이 작으면 손실 최솟점으로 느리게 수렴합니다. 손실 (초)평면이 어떤 특별한 성질을 가질 때 가중치 공간을 지그재그로 이동합니다. adam 옵티마이저는 sgd의 이런 단점을 해결하기 위해 스마트한 방법으로 (초기 훈련 반복에서) 그레이디언트에 따라 변하는 계수를 곱합니다. 또한, 모델의 가중치 파라미터마다 다른 곱셈 계수를 사용합니다. 결과적으로 adam은 다양한 딥러닝 모델에서 sgd에 비해 일반적으로 학습률 선택에 덜 의존적이고 수렴이 잘됩니다. 이 때문에 인기가 높은 옵티마이저입니다. TensorFlow.js 라이브러리는 (rmsprop과 같은) 인기 있는 다양한 옵티마이저를 제공합니다. INFO BOX 3.1에 있는 표에서 이런 옵티마이저를 간략히 소개합니다.