juuuding

[Neural Networks and Deep Learning] Basic of Neural Network Programming 본문

인공지능/코세라 딥러닝 특화과정

[Neural Networks and Deep Learning] Basic of Neural Network Programming

jiuuu 2024. 7. 13. 15:06

 Binary classification

 

 예를 들어 64x64x3 픽셀 값을 가진 사진을 고양이인지(1) 아닌지(0) 분류하는 모델을 만든다고 하자. 이 모델은 결과 값이 0 or 1 두가지로 나뉘기 때문에 "이진 분류(binary classification)"이라고 한다. 이때 input인 X에는 사진의 픽셀을 모두 펼친 값이 들어간다. (64*64*3,1). 이러한 shape을 가진 m개의 사진들이 모델의 훈련 예제 역할로서 X를 구성하게 되는데, 따라서 X의 shape은 (64*64*3, m)이 된다. 훈련을 위해서 각 훈련 예제들에 해당하는 label 값 Y도 필요한데, 한 예제 당 한 값씩 가지면 되므로 Y의 shape은 (1,m)이다. 

 

 

 Logstic regression

 

 로지스틱 회귀는 주로 이진 분류를 할 때 사용하는 통계적 모델이다. sigmoid 함수를 사용하여 0-1 사이 값을 출력하며, 이 값은 특정 클래스에 속할 확률을 뜻한다. 손실 함수로는 cross-entropy 손실 함수를 사용하여 학습한다. 

로지스틱 회귀 식

 

 훈련 예제인 x가 주어지면 매개변수들로 계산을 한 후 sigmoid 함수에 넣어 결과 값을 도출한다. 여기서 나온 결과 값 yhat은 y가 1일 확률을 의미하고 yhat이 0.5 이상이면 1, 0.5 미만이면 0으로 x의 클래스를 예측한다. 참고로 x와 w의 shape은 (nx,1), b의 shape은 (1,1)이다. W.T·x은 내적 연산이므로 차원이 맞아야 계산이 가능하다.(전치하여 차원이 맞춰짐)

 로지스틱 회귀의 비용 함수는 위 식의 L에 표현된 cross-entropy 함수다. 여기서 cross-entropy 비용 함수 대신 squared error func을 사용한다면 local min이 많아 global min을 찾기 어려워 최적화가 힘들어진다. cross-entropy 비용 함수를 모든 훈련 예제에 적용하여 비용을 계산한 식은 다음과 같다. 

 

 

 모든 예제의 손실 값(L)을 구하고 이를 합하여 평균 값을 계산하면 전체 예제에 대한 비용(J)을 계산할 수 있다.

 

 

 Gradient Descent

 

 모델을 학습할 때는 "경사 하강법( Gradient Descent)"를 사용한다. 이것은 비용 함수 J를 최소화 하는 (w,b)를 찾는 과정이다. 경사 하강 식은 다음과 같다. 

 

 

위 식에서 alpha는 learning rate를 나타내며 이 값은 학습 속도를 조절하는 역할을 한다. 그리고 식에 음수 기호를 넣는 이유는 기울기의 반대 방향으로 변수가 업데이트 되어야 비용 함수가 최소화되기 때문이다. 

 

 

 Logistic regression Gradient descent

 

 로지스틱 회귀 경사 하강을 하기 위해 dw, db을 먼저 구해야한다. 이 값들을 구하기 위해서는 chain rule을 사용해야 한다. 

 

1. da (dyhat) 계산

 

2. dz 계산

dz = (dL/da) * (da/dz) = da * (da/dz)

참고로 da/dz는 시그모이드 함수에 대한 미분 값인데, 이 값은 "σ(z)(1−σ(z)"로 정의된다. 이 식을 가지고 chain rule을 하여 dz를 계산하면 다음과 같은 식이 나온다.

 

3. dw 계산

 dw = (dL/dz) * (dz/dw) = dz * (dz/dw)

 z=wx+b이므로 dz/dw는 x이다. 

따라서 dw = dz * x 이다.

 

4. db 계산

 db = (dL/dz) * (dz/db) = dz * (dz/db)

z=wx+b이므로  dz/db는 1이다.

따라서 db = dz * 1 이다. 

 

위의 순서대로 chain rule을 적용하여 dw, db를 구하였다. 이 값으로 모델의 w,b에 경사 하강을 진행한다.

 

 

 m예제에 대한 Gradient descent

 

 위의 경우 단일 예제에 대한 dw, db 값을 구한 것이다. 실제로 모델을 학습할 때는 여러 예제가 있으므로 모든 훈련 예제를 가지고 dw, db를 구하여 w,b 값을 업데이트 해주어야 한다. 그렇기 때문에 m개의 훈련 예제에서 얻은 비용 함수 J, dw, db을 모두 합하여 평균을 계산할 것이다. 이를 코드로 표현하면 다음과 같다.

for i=1 to m
   z(i) = W.T·x(i) + b(i)
   a(i) = σ(z(i))
   J += -[ y(i)log(a(i)) + (1-y(i))log(1-a(i)) ]
   dz(i) = a(i) - y(i)
   dw += x(i)*dz(i)
   ddb += dz(i)
J/=m
dw/=m
db/=m

 

 

 Vetorization

 

 모델을 훈련할 때 최대한 반복 구문을 없애 효율적으로 훈련할 수 있게 하기 위해서 벡터화가 필요하다. 다음은 벡터화 예시 코드다. 

 

w=np.array([1,2,3])
x=np.array([4,5,6])

# 반복 구문 사용
for i in range(nx):
    z += W[i] * X[i]
# z=ad+be+cf

# 벡터화 사용
z=np.dot(W,X)
# z = ad+be+cf

 

 이처럼 벡터화를 이용하면 행렬/벡터 요소에 한방에 접근하여 계산이 가능해진다. 

 위의 "m예제에 대한 gradient descent"에서도 벡터화를 적용하여 계산을 더 빠르게 할 수 있다. 위 코드에서는 m개의 예제에 각각 한 번씩 접근하며 J, dw, db 값을 더하여 마지막에 평균을 내주었다. 벡터화를 사용하면 모든 예제에 한 번에 접근하여 각 예제에 대한 J, dw, db  값을 더해줄 수 있는데 이를 코드로 표현하면 다음과 같다.

for iter in range(1000):
    Z=np.dot(W.T,X)
    dz = A-Y
    dw = 1/m*(np.dot(X,dz.T))
    db = 1/m*(np.sum(dz))

 

 

 브로드캐스팅

 

 행렬 A의 shape이 (m,n)이고, B의 shape이 (1,n), C의 shape이 (m,1)라고 가정하자. 이때 A와 B, C 간에 연산(+-*/)을 진행하면 A와 B, A와 C 사이의 차원이 맞지 않는다. 이때 B와 C의 shape을 A에 맞추어 (m,n)으로 연장하는데, 이것을 브로드캐스팅이라고 한다. 정리하자면 numpy에서 배열 간의 연산을 수행할 때 자동으로 작은 배열의 크기를 큰 배열의 크기에 맞추는 것이다. 배열의 차원이 일치하지 않을 때, 자동으로 배열의 차원을 확장하여 동일한 크기로 만든다. 이때 실제로 데이터를 복사하여 실제 차원이 바뀌는 것이 아니고 가상으로 확장시켜 연산을 수행한다. 

 브로드캐스팅의 규칙은 다음과 같다. 

 

① 배열의 차원을 맞추기 위해 작은 배열 앞에 1을 추가한다.

 ex) (4,3)와 (3,) 의 연산 -> (3,)에 1추가 (1,3)

② 각 차원에서 크기가 1이거나 배열의 크기가 동일하면 해당 차원에서 배열이 호환된다.

 ex) (4,3), (1,3) 각 차원에서의 크기 비교 -> 4와 1(가능), 3과 3(가능)

③ 배열의 크기가 맞지 않고, 둘 중 하나가 1이 아닌 경우 브로드캐스팅이 불가능하다. 

 

A = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9],
              [10, 11, 12]])
              
B = np.array([1, 2, 3]) 

result = A + B

''' 
브로드캐스팅
B'=[[1,2,3],
    [1,2,3],
    [1,2,3],
    [1,2,3]]
'''

 

 

 

* 행렬과 벡터의 np.dot 연산이 너무 헷갈린다.. 공부해서 정리해봐야겠다.