Vectorization

Vectorization으로 for loop를 없애기!

 

- Logistic Regression 에서는 $z = w^{T}x + b$ 를 계산해야 함

$$ w = \begin{bmatrix}  : \\ : \\ :   \end{bmatrix} ,\ x =\begin{bmatrix}  : \\ : \\ :\end{bmatrix} $$

     - $w \in R^{n_{x}}$, $x \in R^{n_{x}}$

 

- non-vectorized implementation

z = 0
for i in range(n_x):
	z += w[i] * x[i]
z += b

- vectorized implementation --> 훨씬 빠름

z = np.dot(w, x) + b

 

- GPU, CPU 모두 SIMD라는 병렬처리가 가능 (GPU가 훨씬 좋을 뿐)

 

 

More Vectorization Examples

[Neural Network programming guideline]

Whenever possible, avoid explicit for-loops

 

예를 들어 벡터 $u = Av$ 내적을 for-loop으로 구하고자 하면 $i$, $j$ 에서 두 개의 for-loop을 겹쳐야 함

u = np.dot(A, v) 로 vectorization 하는 것이 효율적이다

 

say you need to apply the exponential operation (지수 연산) on every element of a matrix/vector

 

$$w = \begin{bmatrix}  v_{1} \\ \vdots \\ v_{n}   \end{bmatrix}\ u = \begin{bmatrix}  e^{v_{1}} \\ e^{v_{2}} \\ \vdots \\ e^{v_{n}} \end{bmatrix}$$

 

- non-vectorized implementation

u = np.zeros((n, 1)) # initialize
for i in range(n):
	u[i]= math.exp(v[i])

 

- vectorized implementation

import numpy as np

u = np.exp(v)

 

그 외에 np.log(v), np.abs(v), np.maximum(v, 0) ... 다양하게 있음

 

 

[Logistic Regression Derivatives]

 

 

vector dw를 생성하여 feature 개수만큼 도는 for-loop를 제거

 

Vectorizing Logistic Regression

logistic regression을 vectorize 하기

 

- logistic regression 의 forward propagation을 먼저 살펴본다

$$ z^{(1)} = w^{T}x^{(1)} + b$$

$$ a^{(1)} = \sigma (z^{(1)}) $$

 

$$ z^{(2)} = w^{T}x^{(2)} + b$$

$$ a^{(2)} = \sigma (z^{(2)}) $$

 

$$ z^{(3)} = w^{T}x^{(3)} + b$$

$$ a^{(3)} = \sigma (z^{(3)}) $$

...

 

$m$번 반복

 

$$X = \begin {bmatrix}| & | &  & | \\ x^{(1)} & x^{(2)} & ... & x^{(m)}\\ | & | &   & |\end{bmatrix} $$

 

$X$는 $(n_{x}, m)$의 vector

 

- 우선 $1 \times M$의 row vector 를 구성한다

 

$$\begin{bmatrix} z^{(1)} & z^{(2)}  & \ldots & z^{(m)} \end{bmatrix} = w^{T}X + \begin{bmatrix}b & b  &  \ldots &b \end{bmatrix}  $$

     - $ \begin{bmatrix}b & b  &  \ldots &b \end{bmatrix}$은 $1 \times m$ 의 row vector

 

$$ w^{T} \begin{bmatrix}| & | &  & | \\ x^{(1)} & x^{(2)} & ... & x^{(m)}\\ | & | &   & |\end{bmatrix} $$

     - $w^{T}$는 row vector

 

$$\begin{bmatrix} w^{T}x^{(1)}+b & w^{T}x^{(2)}+b  & \ldots & w^{T}x^{(n)}+b \end{bmatrix}$$

     - $1 \times m$의 row vector 가 생성됨

     - 즉, $\begin{bmatrix} z^{(1)} & z^{(2)}   & \ldots & z^{(m)}  \end{bmatrix}$

 

- 즉 학습셋을 수평으로 쌓는다면, $X$를 구할 수 있고

- $Z$도 수평으로 쌓을 수 있음

- $ w^{T}X + \begin{bmatrix}b & b  &  \ldots &b \end{bmatrix}  $ 는 z=np.dot(w.T, x) + b 로 나타낼 수 있음

     - 만약 b가 real number(실수)라면, 자동으로 $1 \times m$의 row vector 로 확장해서 계산됨

     -----> "broadcasting"

 

- $z$들로 이루어진 $Z$를 구한다고 하면, $a$는? 

     - $\begin{bmatrix} a^{(1)} & a^{(2)}   & \ldots & a^{(m)}  \end{bmatrix}$, 즉 $A$ 를 한번에 구하기

     - $Z$를 입력으로 받아 효율적으로 $A$를 출력하는 sigmoid function 을 프로그래밍할 것임

 

 

Vectorizing Logistic Regression's Gradient Output

[Vectorizing Logistic Regression]

$dz^{(1)} = a^{(1)} - y^{(1)}$, $dz^{(2)} = a^{(2)} - y^{(2)}$ ...

 

- 새로운 variable 을 지정 : 가로로 쌓은 모습 ($m$차원의 row vector)

$dZ = \begin{bmatrix} dz^{(1)} & dz^{(2)}   & \ldots & dz^{(m)}  \end{bmatrix}$

 

- $ A = \begin{bmatrix} a^{(1)} & \ldots & a^{(m)} \end{bmatrix} $,\ $ Y = \begin{bmatrix} y^{(1)} & \ldots \end{bmatrix} y^{(m)}$ 이므로

$$dZ = A - Y = \begin{bmatrix} a^{(1)} - y^{(1)} & a^{(2)} - y^{(2)} & \ldots \end{bmatrix} $$

 

- 앞서 $dw^{(1)}$, $dw^{(2)}$ 를 없애는 대신에 $dw = 0$ 를 세팅해 for-loop을 제거했다

     - 그러나 여전히 이를 이용한 for-loop 가 존재하는데, training set $m$에서 loop 하는 부분임

$$dw = 0$$

$$dw += x^{(1)}dz^{(1)}$$

$$dw += x^{(2)}dz^{(2)}$$

$$ \vdots $$

$$dw /= m$$

 

$$db = 0$$

$$db += dz^{(1)}$$

$$db += dz^{(2)}$$

$$ \vdots $$

$$db += dz^{(m)}$$

$$db /= m$$

 

이를 vectorize 해보자

- $db$의 경우, $dz^{(i)}$들을 단순히 모두 더한 다음 $m$으로 나눈다

$$db = \frac{1}{m} \sum_{i=1}^m dz^{(i)}$$

     - 파이썬에서는 $\frac{1}{m} np.sum(dz)$

 

- $dw$의 경우,

$$dw = \frac{1}{m} X dz^{T} = \frac{1}{m} \begin{bmatrix}| & | &  & | \\ x^{(1)} & x^{(2)} & ... & x^{(m)}\\ | & | &   & |\end{bmatrix} \begin{bmatrix} dz^{(1)} \\ \vdots \\ dz^{(m)} \end{bmatrix} $$

$$ = \frac{1}{m} \begin{bmatrix} x^{(1)}dz^{(1)} + \cdots + x^{(m)}dz^{(m)} \end{bmatrix}$$

 

 

[정리]

- 위 과정을 iteration 수 만큼 for-loop 해야 함 (이거는 없애기 어려움!)

 

 

Broadcasting in Python

[Broadcasting example]

- 음식별로(칼럼별로) 어떤 영양소가 얼마나 많은 칼로리를 차지하고 있는지 계산하고자 함

     - 예를 들어, 사과에서 탄수화물이 차지하는 양 $ = \frac{56.0}{56.0+1.2+1.8} \times 100$

- 이 과정을 for-loop 없이 수행할 수 있는가?

 

- 위 표를 $3\times4$의 matrix $A$로 본다

import numpy as np

A = np.array(
[56.0, 0.0, 4.4, 68.0],
[1.2, 104.0, 52.0, 8.0],
[1.8, 135.0, 99.0, 0.9]
)

cal = A.sum(axis=0) # sum vertically
percentage = 100 * A/cal.reshape(1, 4)

- 사실 cal 이 이미 $1 \times 4$ 행렬이므로 reshape 는 안 해줘도 됨

- $3 \times 4$ 행렬을 $1 \times 4$ 행렬로 나눴다. 이게 어떻게 가능한 걸까?

 

파이썬에서 자동으로 broadcasting을 해주기 때문

 

$m \times n$ 행렬과 $1 \times n$ 행렬을 더하고자 한다면

파이썬은 $1 \times n$  행렬을 $m$번 곱해서 $m \times n$ 행렬로 만든다

 

A Note on Python/Numpy Vectors

broadcasting 은 편리하지만 때로는 이상한 버그를 던지기도 함

 

import numpy as np

a = np.random.randn(5)
print(a)
print(a.shape)

>> [숫자, 숫자, 숫자, 숫자, 숫자]

>> (5, )

이는 파이썬에서 rank 1 array 라고 불리며, 행 벡터도 열 벡터도 아니다

rank 1 array 를 사용하지 마시오

 

a = np.random.randn(5, 1)
print(a)

>>[[숫자],

      [숫자],

      [숫자],

      [숫자],

      [숫자]]

이렇게 했을 때 열벡터가 생성된다 (대괄호가 두 번 겹쳐져 있는 것에 주목!)

 

팁1. assert(a.shape == (5, 1)) 와 같은 assertion을 활용하기

팁2. rank 1 array를 만들어버렸을 때 a = a.reshape((5, 1)) 로 reshape 해버리기

- reshape를 사용하는 데 주저하지 마세요

 

 

Explanation of Logistic Regression Cost Function (Optional)

[Logistic regression cost function]

[Cost on m examples]

 

QUIZ

퀴즈 통과 못해서 한번 더 치렀다.. 나는 왜이렇게 행렬이 헷갈릴까

역시 선형대수학을 안 해서겠지

복사했습니다!