04.2 손실 함수
사람들에게 “지금 얼마나 행복하나요?”라고 물으면 뭐라고 대답할까요? “아주 행복하죠”나 “그리 행복한 거 같진 않아요”라는 막연한 답이 돌아오는 게 보통입니다. 그런 가운데 누군가가 “현재 내 행복 지수는 10.23입니다”라고 대답한다면 질문한 사람이 당황해버리겠죠. 하나의 지표를 가지고 행복을 수치적으로 판단했다는 것이니까요. 그런 사람이 정말 있다면 그 사람은 자신의 ‘행복 지표’를 기준으로 인생을 살아가게 될지도 모릅니다.
자, 이 ‘행복 지표’ 이야기는 하나의 비유지만, 실은 신경망 학습에서도 이와 같은 일을 수행합니다. 신경망 학습에서는 현재의 상태를 ‘하나의 지표’로 표현합니다. 그리고 그 지표를 가장 좋게 만들어주는 가중치 매개변수의 값을 탐색하는 것입니다. ‘행복 지표’를 가진 사람이 그 지표를 근거로 ‘최적의 인생’을 탐색하듯, 신경망도 ‘하나의 지표’를 기준으로 최적의 매개변수 값을 탐색합니다. 신경망 학습에서 사용하는 지표는 손실 함수 loss function*라고 합니다. 이 손실 함수는 임의의 함수를 사용할 수도 있지만 일반적으로는 평균 제곱 오차와 교차 엔트로피 오차를 사용합니다.
WARNING_ 손실 함수는 신경망 성능의 ‘나쁨’을 나타내는 지표로, 현재의 신경망이 훈련 데이터를 얼마나 잘 처리하지 ‘못’하느냐를 나타냅니다. ‘성능 나쁨’을 지표로 한다니 무언가 부자연스럽다고 생각할지 모르지만, 손실 함수에 마이너스만 곱하면 ‘얼마나 나쁘지 않나’, 즉 ‘얼마나 좋으냐’라는 지표로 변신하죠. 또, ‘나쁨을 최소로 하는 것’과 ‘좋음을 최대로 하는 것’은 결국 같은 것이니까, 성능의 ‘나쁨’과 ‘좋음’ 중 어느 쪽을 지표로 삼아도 본질적으로 수행하는 일은 다르지 않습니다.
4.2.1 평균 제곱 오차
가장 많이 쓰이는 손실 함수는 평균 제곱 오차 mean squared error, MSE입니다. 평균 제곱 오차는 수식으로는 다음과 같습니다.
\[E = \frac{1}{2} \sum_{k} (y_k - t_k)^2 \hfill \text{[식 4.1]}\]여기서 $y_k$는 신경망의 출력(신경망이 추정한 값), $t_k$는 정답 레이블, $k$는 데이터의 차원 수를 나타냅니다. 이를테면 “3.6 손글씨 숫자 인식” 예에서 $y_k$와 $t_k$는 다음과 같은 원소 10개짜리 데이터입니다.
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
이 배열들의 원소는 첫 번째 인덱스부터 순서대로 숫자 ‘0’, ‘1’, ‘2’, … 일 때의 값입니다. 여기에서 신경망의 출력 y는 소프트맥스 함수의 출력입니다. 소프트맥스 함수의 출력은 확률로 해석할 수 있으므로, 이 예에서는 이미지가 ‘0’일 확률은 0.1, ‘1’일 확률은 0.05, ‘2’일 확률은 0.6이라고 해석되죠. 한편 정답 레이블인 t는 정답을 의미하는 위치의 원소는 1로, 그 외에는 0으로 표기합니다. 여기에서는 숫자 ‘2’에 해당하는 원소의 값이 1이므로 정답이 ‘2’임을 알 수 있습니다.
이처럼 한 원소만 1로 하고 그 외는 0으로 나타내는 표기법을 원-핫 인코딩이라 한다고 했습니다.
자, 평균 제곱 오차는 [식 4.1]과 같이 각 원소의 출력(추정 값)과 정답 레이블(참 값)의 차($y_k - t_k$)를 제곱한 후, 그 총합을 구합니다. 그러면 이 평균 제곱 오차를 파이썬으로 구현해봅시다.
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
여기에서 인수 y와 t는 넘파이 배열입니다. 이 코드는 [식 4.1]을 그대로 구현한 것이니 설명은 생략합니다. 그러면 이 함수를 실제로 사용해보죠.
>>> # 정답은 '2'
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
>>>
>>> # 예1 : '2'일 확률이 가장 높다고 추정함 (0.6)
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> mean_squared_error(np.array(y), np.array(t))
0.097500000000000031
>>>
>>> # 예2 : '7'일 확률이 가장 높다고 추정함 (0.6)
>>> y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
>>> mean_squared_error(np.array(y), np.array(t))
0.59750000000000003
두 가지 예를 살펴봤습니다. 첫 번째의 예는 정답이 ‘2’고 신경망의 출력도 ‘2’에서 가장 높은 경우입니다. 한편, 두 번째 예에서는 정답은 똑같이 ‘2’지만, 신경망의 출력은 ‘7’에서 가장 높습니다. 이 실험의 결과로 첫 번째 예의 손실 함수 쪽 출력이 작으며 정답 레이블과의 오차도 작은 것을 알 수 있습니다. 즉, 평균 제곱 오차를 기준으로는 첫 번째 추정 결과가(오차가 더 작으니) 정답에 더 가까울 것으로 판단할 수 있습니다.
지니의 채점 마법 (손실 함수)
- 지니: “도로시, 신경망이 내놓은 예측값(
y)이 정답(t)에서 얼마나 멀리 떨어져 있는지 정밀하게 채점해서 알려주는 마법의 잣대가 바로 손실 함수(Loss Function)야. 평균 제곱 오차(MSE)는 거리의 제곱을 재는 잣대고, 교차 엔트로피 오차(CEE)는 정답 확률에 로그를 씌워 채점하는 잣대란다!”- 도로시: “아! 정답을 맞혔을 때는 오차(손실) 점수가 0에 가깝고, 엉뚱한 예측을 했을 때는 오차 점수가 엄청나게 커지는구나! 이 손실 점수가 0이 되도록 줄여나가는 것이 학습의 목표겠네!”
- 토토: “왈왈! (룰러를 물어오며) 오차가 낮을수록 마법 완성도가 높아진다 멍!”
지니가 채점자로 분하여 도로시의 예측과 정답 깃발 사이의 오차를 ‘손실 함수’ 자로 측정하고 있고, 도로시와 토토가 그 모습을 긴장하며 지켜보고 있어요.
4.2.2 교차 엔트로피 오차
또 다른 손실 함수로서 교차 엔트로피 오차 cross entropy error, CEE도 자주 이용합니다. 교차 엔트로피 오차의 수식은 다음과 같습니다.
\[E = -\sum_{k} t_k \log y_k \hfill \text{[식 4.2]}\]여기서 $\log$는 밑이 $e$인 자연로그($\log_e$)입니다. $y_k$는 신경망의 출력, $t_k$는 정답 레이블입니다. 또 $t_k$는 정답에 해당하는 인덱스의 원소만 1이고 나머지는 0입니다(원-핫 인코딩). 그래서 [식 4.2]는 실질적으로 정답일 때의 추정($t_k$가 1일 때의 $y_k$)의 자연로그를 계산하는 식이 됩니다.* 예를 들어 정답 레이블은 ‘2’가 정답이라 하고 이때의 신경망 출력이 0.6이라면 교차 엔트로피 오차는 $-\log 0.6 = 0.51$이 됩니다. 또한, 같은 조건에서 신경망 출력이 0.1이라면 $-\log 0.1 = 2.30$이 됩니다. 즉, 교차 엔트로피 오차는 정답일 때의 출력이 전체 값을 정하게 됩니다.
한편, [그림 4-3]은 자연로그의 그래프입니다.
그림 4-3 자연로그 $y = \log x$의 그래프

이 그림에서 보듯이 $x$가 1일 때 $y$는 0이 되고 $x$가 0에 가까워질수록 $y$의 값은 점점 작아집니다. [식 4.2]도 마찬가지로 정답에 해당하는 출력이 커질수록 0에 다가가다가, 그 출력이 1일 때 0이 됩니다. 반대로 정답일 때의 출력이 작아질수록 오차는 커집니다.
그럼 교차 엔트로피 오차를 구현해봅시다.
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
여기에서 y와 t는 넘파이 배열입니다. 그런데 코드 마지막을 보면 np.log를 계산할 때 아주 작은 값인 delta를 더하고 있습니다. 이는 np.log() 함수에 0을 입력하면 마이너스 무한대를 뜻하는 -inf가 되어 더 이상 계산을 진행할 수 없게 되기 때문입니다. 아주 작은 값을 더해서 절대 0이 되지 않도록, 즉 마이너스 무한대가 발생하지 않도록 한 것이죠. 그러면 이 cross_entropy_error(y, t) 함수를 써서 간단한 계산을 해봅시다. 정답은 똑같이 ‘2’입니다.
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> cross_entropy_error(np.array(y), np.array(t))
0.51082545709933802
>>>
>>> y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
>>> cross_entropy_error(np.array(y), np.array(t))
2.3025850929945458
첫 번째 예는 정답일 때의 출력이 0.6인 경우로, 이때의 교차 엔트로피 오차는 약 0.51입니다. 그다음은 정답일 때의 출력이 (더 낮은) 0.1인 경우로, 이때의 교차 엔트로피 오차는 무려 2.3입니다. 즉, 결과(오차 값)가 더 작은 첫 번째 추정이 정답일 가능성이 높다고 판단한 것으로, 앞서 평균 제곱 오차의 판단과 일치합니다.
4.2.3 미니배치 학습
기계학습 문제는 훈련 데이터를 사용해 학습합니다. 더 구체적으로 말하면 훈련 데이터에 대한 손실 함수의 값을 구하고, 그 값을 최대한 줄여주는 매개변수를 찾아냅니다. 이렇게 하려면 모든 훈련 데이터를 대상으로 손실 함수 값을 구해야 합니다. 즉, 훈련 데이터가 100개 있으면 그로부터 계산한 100개의 손실 함수 값들의 합을 지표로 삼는 것입니다.
지금까지 데이터 하나에 대한 손실 함수만 생각해봤으니, 이제 훈련 데이터 모두에 대한 손실 함수의 합을 구하는 방법을 생각해보겠습니다. 예를 들어 교차 엔트로피 오차는 [식 4.3]처럼 됩니다.
\[E = -\frac{1}{N} \sum_{n} \sum_{k} t_{nk} \log y_{nk} \hfill \text{[식 4.3]}\]이때 데이터가 $N$개라면 $t_{nk}$는 $n$번째 데이터의 $k$차원 째의 값을 의미합니다($y_{nk}$는 신경망의 출력, $t_{nk}$는 정답 레이블입니다). 수식이 좀 복잡해 보이지만 데이터 하나에 대한 손실 함수인 [식 4.2]를 단순히 $N$개의 데이터로 확장했을 뿐입니다. 다만, 마지막에 $N$으로 나누어 정규화하고 있습니다. $N$으로 나눔으로써 ‘평균 손실 함수’를 구하는 것입니다. 이렇게 평균을 구해 사용하면 훈련 데이터 개수와 관계없이 언제든 통일된 지표를 얻을 수 있습니다. 예를 들어 훈련 데이터가 1,000개든 10,000개든 상관없이 평균 손실 함수를 구할 수 있습니다.
그런데 MNIST 데이터셋은 훈련 데이터가 60,000개였습니다. 그래서 모든 데이터를 대상으로 손실 함수의 합을 구하려면 시간이 좀 걸립니다. 더 나아가 빅데이터 수준이 되면 그 수는 수백만에서 수천만도 넘는 거대한 값이 되기도 합니다. 이 많은 데이터를 대상으로 일일이 손실 함수를 계산하는 것은 현실적이지 않겠죠. 이런 경우 데이터 일부를 추려 전체의 ‘근사치’로 이용할 수 있습니다. 신경망 학습에서도 훈련 데이터로부터 일부만 골라 학습을 수행합니다. 이 일부를 미니배치 mini-batch라고 하죠. 가령 60,000장의 훈련 데이터 중에서 100장을 무작위로 뽑아 그 100장을 사용하여 학습하는 것입니다. 이러한 학습 방법을 미니배치 학습이라고 합니다.
그렇다면 미니배치 학습을 구현하는, 즉 훈련 데이터에서 지정한 수의 데이터를 무작위로 골라내는 코드를 작성해봅시다. MNIST 데이터셋을 읽어오는 코드는 다음과 같습니다.
import sys, os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = \
load_mnist(normalize=True, one_hot_label=True)
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)
3장에서도 설명했지만 load_mnist 함수는 MNIST 데이터셋을 읽어오는 함수입니다. 이 함수는 dataset/mnist.py 파일에 있습니다. 이 함수는 훈련 데이터와 시험 데이터를 읽습니다. 호출할 때 one_hot_label=True로 지정하여 원-핫 인코딩으로, 즉 정답 위치의 원소만 1이고 나머지가 0인 배열을 얻을 수 있습니다.
앞의 코드에서 MNIST 데이터를 읽은 결과 훈련 데이터는 60,000개고 784차원(원래는 $28 \times 28$)임을 알 수 있습니다. 또, 정답 레이블은 10차원 데이터입니다. 그래서 앞의 x_train, t_train의 모습은 각각 (60000, 784)와 (60000, 10)이 됩니다.
그러면 이 훈련 데이터에서 무작위로 10장만 빼내려면 어떻게 하면 될까요? 넘파이의 np.random.choice() 함수를 쓰면 다음과 같이 간단히 해결할 수 있습니다.
train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
np.random.choice()로는 지정한 범위의 수 중에서 무작위로 원하는 개수만 꺼낼 수 있습니다. 가령 np.random.choice(60000, 10)은 0에서 60000 미만의 수 중에서 무작위로 10개를 골라냅니다. 다음은 실제로 돌려본 모습입니다. 이 함수가 출력한 배열을 미니배치로 뽑아낼 데이터의 인덱스로 사용하면 되겠죠.
>>> np.random.choice(60000, 10)
array([ 8013, 14666, 58210, 23832, 52091, 10153, 8107, 19410, 27260, 21411])
이제 무작위로 선택한 이 인덱스를 사용해 미니배치를 뽑아내기만 하면 됩니다. 손실 함수도 이 미니배치로 계산합니다.
NOTE_ 텔레비전 시청률도 모든 세대의 텔레비전이 아니라 선택된 일부 가구의 텔레비전만을 대상으로 구합니다. 예를 들어 경기 지방에서 무작위로 선정한 1,000가구를 대상으로 시청률을 계측한 다음, 경기 지방 전체의 시청률로 근사하는 것이죠. 그 1,000가구의 시청률이 전체 시청률과 정확히 일치하지는 않겠지만, 전체의 대략적인 값으로 사용할 수 있습니다. 이 시청률 이야기와 마찬가지로 미니배치의 손실 함수도 일부 표본 데이터로 전체를 비슷하게 계측합니다. 즉, 전체 훈련 데이터의 대표로서 무작위로 선택한 작은 덩어리(미니배치)를 사용하는 것입니다.
4.2.4 (배치용) 교차 엔트로피 오차 구현하기
그럼, 미니배치 같은 배치 데이터를 처리할 수 있는 교차 엔트로피 오차는 어떻게 구현할까요? 다행히 조금 전에 구현한 교차 엔트로피 오차(데이터 하나를 대상으로 한 구현)를 조금만 바꿔주면 됩니다. 여기에서는 데이터가 하나인 경우와 데이터가 배치로 묶여 입력될 경우 모두를 처리할 수 있도록 구현하겠습니다.
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y)) / batch_size
이 코드에서 y는 신경망의 출력, t는 정답 레이블입니다. y가 1차원이라면, 즉 데이터 하나당 교차 엔트로피 오차를 구하는 경우는 reshape 함수로 데이터의 형상을 바꿔줍니다. 그리고 배치의 크기로 나눠 정규화하고 이미지 1장당 평균의 교차 엔트로피 오차를 계산합니다.
정답 레이블이 원-핫 인코딩이 아니라 ‘2’나 ‘7’ 등의 숫자 레이블로 주어졌을 때의 교차 엔트로피 오차를 다음과 같이 구현할 수 있습니다.
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t])) / batch_size
이 구현의 핵심은, 원-핫 인코딩일 때 t가 0인 원소는 교차 엔트로피 오차도 0이므로 그 계산은 무시해도 좋다는 것입니다. 다시 말하면 정답에 해당하는 신경망의 출력만으로 교차 엔트로피 오차를 계산할 수 있습니다. 그래서 원-핫 인코딩 시 t * np.log(y)였던 부분을 레이블 표현일 때는 np.log(y[np.arange(batch_size), t])로 구현합니다.
참고로 np.log(y[np.arange(batch_size), t])를 간단히 설명하겠습니다. np.arange(batch_size)는 0부터 batch_size - 1까지 배열을 생성합니다. 즉, batch_size가 5이면 np.arange(batch_size)는 [0, 1, 2, 3, 4]라는 넘파이 배열을 생성합니다. t에는 레이블이 [2, 7, 0, 9, 4]와 같이 저장되어 있으므로 y[np.arange(batch_size), t]는 각 데이터의 정답 레이블에 해당하는 신경망의 출력을 추출합니다(이 예에서는 y[np.arange(batch_size), t]는 [y[0,2], y[1,7], y[2,0], y[3,9], y[4,4]]인 넘파이 배열을 생성합니다).
4.2.5 왜 손실 함수를 설정하는가?
이쯤이면 손실 함수의 정의는 이해하셨을 겁니다. 그렇다면 왜 굳이 손실 함수를 사용해야 하는 걸까요? 예컨대 숫자 인식의 경우도 우리의 궁극적인 목적은 높은 ‘정확도’를 끌어내는 매개변수 값을 찾는 것입니다. 그렇다면 ‘정확도’라는 지표를 두고 중간에 손실 함수의 값을 거치는 것은 쉬운 길을 빙 돌아가는 것은 아닐까요?
이 의문은 신경망 학습에서의 ‘미분’의 역할에 주목한다면 해결됩니다. 자세한 것은 다음 절에서 설명합니다만, 신경망 학습에서는 최적의 매개변수(가중치와 편향)를 탐색할 때 손실 함수의 값을 가능한 한 작게 하는 매개변수 값을 찾습니다. 이때 매개변수의 미분(정확히는 기울기)을 계산하고, 그 미분 값을 단서로 매개변수의 값을 서서히 갱신하는 과정을 반복합니다.
가령 여기에 가상의 신경망이 있고 그 신경망의 어느 한 가중치 매개변수에 주목한다고 합시다. 이때 그 가중치 매개변수의 손실 함수의 미분이란 ‘가중치 매개변수의 값을 아주 조금 변화시켰을 때, 손실 함수가 어떻게 변하나’라는 의미입니다. 만약 이 미분 값이 음수면 그 가중치 매개변수를 양의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있습니다. 반대로, 이 미분 값이 양수면 그 가중치 매개변수를 음의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있습니다. 그러나 미분 값이 0이면 가중치 매개변수를 어느 쪽으로 움직여도 손실 함수의 값은 달라지지 않습니다. 그래서 그 가중치 매개변수의 갱신은 거기서 멈춥니다.
정확도를 지표로 삼아서는 안 되는 이유는 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문입니다. 자, 이야기가 좀 길어졌으니 지금까지의 설명을 정리하겠습니다.
신경망을 학습할 때 정확도를 지표로 삼아서는 안 된다. 정확도를 지표로 하면 매개변수의 미분이 대부분의 장소에서 0이 되기 때문이다.
정확도를 지표로 삼으면 매개변수의 미분이 대부분의 장소에서 0이 되는 이유는 무엇일까요?
구체적인 예를 보겠습니다. 한 신경망이 100장의 훈련 데이터 중 32장을 올바르게 인식한다고 합니다. 그렇다면 정확도는 32%입니다. 만약 정확도가 지표였다면 가중치 매개변수의 값을 조금 바꾼다고 해도 정확도는 그대로 32%일 겁니다. 즉, 매개변수를 약간만 조정해서는 정확도가 개선되지 않고 일정하게 유지됩니다. 혹, 정확도가 개선된다 하더라도 그 값은 32.0123%와 같은 연속적인 변화보다는 33%나 34%처럼 불연속적인 띄엄띄엄한 값으로 바뀌어버립니다.
한편, 손실 함수를 지표로 삼았다면 어떨까요? 현재의 손실 함수의 값은 0.92543… 같은 수치로 나타납니다. 그리고 매개변수의 값이 조금 변하면 그에 반응하여 손실 함수의 값도 0.93432…처럼 연속적으로 변화하는 것입니다.
정확도는 매개변수의 미소한 변화에는 거의 반응을 보이지 않고, 반응이 있더라도 그 값이 불연속적으로 갑자기 변화합니다. 이는 ‘계단 함수’를 활성화 함수로 사용하지 않는 이유와도 들어맞습니다. 만약 활성화 함수로 계단 함수를 사용하면 지금까지 설명한 것과 같은 이유로 신경망 학습이 잘 이뤄지지 않습니다. 계단 함수의 미분은 [그림 4-4]와 같이 대부분의 장소(0 이외의 곳)에서 0입니다. 그 결과, 계단 함수를 이용하면 손실 함수을 지표로 삼는 게 아무 의미가 없게 됩니다. 매개변수의 작은 변화가 주는 파장을 계단 함수가 말살하여 손실 함수의 값에는 아무런 변화가 나타나지 않기 때문입니다.
도로시의 미분 가능성 깨달음
- 도로시: “아! 정확도(
Accuracy)는 맞힌 개수라서 32장, 33장처럼 뚝뚝 끊어져 변화하는구나. 매개변수를 아무리 쥐어짜듯 미세하게 바꿔도 정확도 다이얼은 꼼짝도 안 하다가 갑자기 툭 튀는 식이니까, 미분(기울기) 값이 0이 되어 버려 가중치를 어떻게 고쳐야 할지 방향을 잃어버리는 거였어!”- 지니: “정답이야! 반면에 손실 함수(
Loss) 다이얼은0.9254에서0.9253처럼 매개변수의 아주 미세한 떨림에도 즉각적이고 매끄럽게 반응하지. 이 부드럽고 연속적인 반응 덕분에 미분 값이 살아나고, 올바른 가중치 조절 마법을 수행할 수 있게 된단다.”- 토토: “왈왈! (다이얼을 툭툭 치며) 매끄러운 다이얼을 돌려야 나침반이 작동한다 멍!”
도로시가 계단식으로만 투박하게 움직이는 정확도 다이얼(미분 불가능) 대신, 아주 미세한 변화에도 매끄럽게 요동치는 손실 함수 다이얼(미분 가능)을 사용해 미세 조정을 하고 있고, 지니와 토토가 그 원리를 확인해 주고 있어요.
그림 4-4 계단 함수와 시그모이드 함수 : 계단 함수는 대부분의 장소에서 기울기가 0이지만, 시그모이드 함수의 기울기(접선)는 0이 아니다.

계단 함수는 한순간만 변화를 일으키지만, 시그모이드 함수의 미분(접선)은 [그림 4-4]와 같이 출력(세로축의 값)이 연속적으로 변하고 곡선의 기울기도 연속적으로 변합니다. 즉, 시그모이드 함수의 미분은 어느 장소라도 0이 되지는 않습니다. 이는 신경망 학습에서 중요한 성질로, 기울기가 0이 되지 않는 덕분에 신경망이 올바르게 학습할 수 있는 것입니다.
* 옮긴이_ 비용 함수(cost function)라고도 합니다. * 옮긴이_ 정답이 아닌 나머지 모두는 $t_k$가 0이므로 $\log y_k$와 곱해도 0이 되어 결과에 영향을 주지 않습니다.