23-1학기에 backpropagation을 numpy로 구현했었는데
1년 지났다고 기억이 안 난다....
일단 그때 봤던 자료들을 다시 살펴봤는데 그때는 이해가 됐는데 왜 지금은 이해가 안되는걸까
다시 차분하게 정리해봐야지
2 layer라고 가정하고 numpy로 구현하기
실제로는 class로 짰는데
필요한 부분만 함수로 정리해봤다
def init_parameter(input_dim,num_hiddens,num_classes):
limit_w1 = np.sqrt(6/(input_dim + num_hiddens))
limit_w2 = np.sqrt(6/(num_hiddens + num_classes))
w1 = np.random.uniform(-limit_w1,limit_w1,(input_dim,num_hiddens))
w2 = np.random.uniform(-limit_w2,limit_w2,(num_hiddens,num_classes))
b1 = np.zeros((1,num_hiddens))
b2 = np.zeros((1,num_classes))
🧐 이때 bias도 0으로 초기화 해준다
그 이유는 ..?
-> 초기에는 가중치에 신경을 쓰는게 맞아서라고 직관적으로 이해해보자
-> 그리고 대칭성 유지 bias를 0으로 유지
def forward(X):
z1 = X.dot(w1) + b1
a1 = sigmoid(z1)
z2 = a1.dot(w2) + b2
y = softmax(z2) # 마지막은softmax로
return z1,a1,z2,y
그냥 계산을 쭉 하면 되는 것
문제의 backpropagation
수식으로 보는거랑 코드로 보는거랑 이해가 잘 안된달까
def backpropagation(X,Y,z1,a1,z2,y):
#일단 제일 마지막 gradient
dz2 = -(Y-y)
dw2 = a1.dot(dz2) /X.shape[0] # 배치 크기로 나눠준다
db2 = np.sum(dz2,axis = 0,keepdims = True)/X.shape[0]
da1 = w2.T.dot(dz2)
dz1 = da1 * (a1*(1-a1))
dw1 = X.T.dot(dz1) / X.shape[0] # 배치 크기로 나눠준다
db1 =np.sum(dz1,axis = 0,keepdims = True)/X.shape[0]
return dw1,db1,dw2,db2
🧐 X.shape[0]를 나누는 이유
미니 배치 학습을 위해 전체 데이터를 여러 작은 배치로 나누워서 학습을 진행함
즉, 배치 크기로 나누어 줌으로써 각 배치에서 평균 기울기를 계산 할 수 있음 -> 배치 크기에 상관없이 모델이 매번 일관된 크기의 업데이트 가능
🧐 bias 업데이트
손실함수 L에 대한 바이어스 b에 대한 기울기는
∂L/∂b
바이어스는 모든 입력 데이터에 동일하게 적용
따라서, 바이어스에 대한 기울기는 각 데이터 포인트에서 발생한 오차의 총합으로 나타낼 수 있음
바이어스는 입력 데이터와 상관없이 뉴런의 출력에 항상 일정하게 더해지는 값이다. 따라서, 바이어스를 얼마나 조정해야 할지를 결정하는 데 필요한 정보는 단순히 모든 데이터 포인트에서 발생한 오차의 총합
lr = 0.01
z1,a1,z2,y = forward(X_batch)
dw1,db1,dw2,db2 = backward(X_batch, Y_batch, z1,a1,z2,y)
# 파라미터 업데이트
W1 -= lr * grads['dW1']
b1 -= lr * grads['db1']
W2 -= lr * grads['dW2']
b2 -= lr * grads['db2']
추가로 이야기 나온 num_hiddens는 어떻게 정해야하나...?
적절한 Hidden layer의 노드 개수에 대해서도 한 번 찾아봐야겠다
backpropagation을 완전히 이해하지는 못했는데 그래도 80%는 이해한 거 같다