밑바닥부터 시작하는 딥러닝 교재의 코드를 보면(https://github.com/kchcoo/WegraLee-deep-learning-from-scratch /common/layers.py)
SoftmaxWithLoss 레이어의 역전파에서 마지막으로 dx를 반환할 때 batch_size로 나눠주잖아
SoftmaxWithLoss 레이어는 엄밀히 말하면 Softmax 레이어랑 Categorical Cross-Entropy Loss 레이어를 결합한 거고
Categorical Cross-Entropy Loss를
이렇게 정의하면 dL/dyhat_i = -1/B * (y_ij / yhat_ij)니까
역전파 때 오차를 batch_size로 나누는 과정은 두 레이어 중에 Cross-Entropy Loss 레이어 과정에서 일어나는 거고, Softmax 레이어랑은 관계 없다고 생각했거든
근데 numpy로 트랜스포머를 구현한 프로젝트(https://github.com/AkiRusProd/numpy-transformer /transformer/activations.py)를 보니까 softmax 함수의 역전파를 이렇게 구현해 놨더라고
class Softmax():
def __init__(self) -> None:
self.axis = -1
def forward(self, x):
self.x = x
e_x = np.exp(x - np.max(x, axis = self.axis, keepdims=True))
self.softmax = e_x / np.sum(e_x, axis = self.axis, keepdims=True)
return self.softmax
def backward(self, grad = None):
batch_size = self.x.shape[0]
softmax = self.forward(self.x)
J = softmax[..., np.newaxis] * np.tile(np.identity(softmax.shape[-1], dtype = self.x.dtype), (softmax.shape[0], *tuple(np.ones(softmax.ndim, dtype = np.int8).tolist()))) - (softmax[..., np.newaxis, :].transpose(*tuple(np.arange(0, softmax.ndim - 1, 1, dtype=np.int8).tolist()), -1, -2) @ softmax[..., np.newaxis, :]) #np.matmul(softmax[:, :, None], softmax[:, None, :])
input_grad = grad[..., np.newaxis, :] @ J
return input_grad.reshape(self.x.shape) / batch_size
마지막에 batch_size로 나누는데 결국 어느 쪽 레이어에서 batch_size로 나누는 게 맞는 거야?
아님 애초에 두 레이어 모두 batch_size로 나누든 말든 별로 상관 없는 거야?
batch_size로 안나눠주면 batch_size가 커질수록 계산된 gradient 스케일이 커져서 학습이 마구 요동치겠지 softmax 계산이랑은 상관 없음
ㄱㅅ