UFLDL Tutorial 3. Supervised Learning and Optimization – Vectorization
http://deeplearning.stanford.edu/tutorial/
Supervised Learning and Optimization - Vectorization
Linear regression를 이야기할 때 사용했던 집 값처럼 적은 데이터들을 사용할 때에는 코드가 그리 빠르지 않아도 잘 동작합니다. 하지만 연습문제 1A와 1B처럼 for-loop를 사용하는 경우, 커다란 데이터를 다루는 문제를 돌리려면 작업이 너무 늦어지게 됩니다. 이것은 각 샘플, 혹은 각 요소들을 순서대로 looping 하는 작업을 MATLAB이 너무 느리게 처리하기 때문입니다. 이러한 for-loop를 피하기 위해 이전에 작성했던 코드를 벡터와 행렬 연산으로 표현하여 최적화하도록 합니다. 이렇게 하면 MATLAB이 매우 빠르게 실행할 수 있습니다. 이것은 다른 언어에서도 마찬가지인데, Python이나 C/C++에서도 코드를 최적화할 때 유용하게 활용할 수 있습니다.
Example: Many matrix-vector products
종종 우리는 많은 벡터들에 대해서 행렬-벡터 곱을 한번에 실행하고 싶습니다. 앞에서는, 각 샘플에 대한 $\theta^T x^{(i)}$와 같은 계산이 이에 해당합니다. $\theta$는 2D 행렬일 수도, 혹은 벡터일수도 있습니다. 이를 위해 행렬 $X$에 우리가 가진 모든 데이터들을 넣는 방법을 생각할 수 있습니다. 이 행렬 $X$에는 각 샘플 $x^{(i)}$ 벡터를 행렬의 열로 하여 죽 이어 붙인 것입니다.
$$X = \left[\begin{array}{cccc} | & | & | & | \\ x^{(1)} & x^{(2)} & \cdots & x^{(m)}\\ | & | & | & |\end{array}\right]$$
이렇게 하면 모든 $x^{(i)}$에 대해서 $y^{(i)} = Wx^{(i)}$를 다음과 같이 한번에 계산할 수 있습니다.
$$\left[\begin{array}{cccc} | & | & | & | \\ y^{(1)} & y^{(2)} & \cdots & y^{(m)}\\ | & | & | & |\end{array}\right] = Y = W X$$
따라서 linear regression에서는 $y^{(i)} = \theta^T X$를 계산하여 $\theta^T x^{(i)}$를 계산하기 위해 위해 모든 샘플에 대해 loop를 사용하지 않아도 됩니다.
Example: normalizing many vectors
위와 같이 많은 벡터 $x^{(i)}$로 이루어진 행렬 $X$가 있고, $y^{(i)} = x^{(i)} / ||x^{(i)}||_2$와 같이 normalization을 하고 싶은 경우가 있습니다. 이럴 경우에는 MATLAB의 배열 연산을 이용할 수 있습니다.
1 2 3 |
X_norm = sqrt( sum(X.^2, 1) ); Y = bsxfun(@rdivide, X, X_norm); |
이 코드는 X
의 모든 요소를 제곱하고, 각 행에 대해서 그 합을 계산합니다. 그리고 각 요소의 제곱근을 계산하게 됩니다. 그 결과는 $||x^{(i)}||$를 요소로 갖는 1 * m 행렬입니다. bsxfun
함수는 X_norm
을 X와 차원이 갖도록 복제하거나 확장하는 함수로 생각할 수 있습니다. 이는 X
에 요소별(element-wise)로 계산을 하기 위해서 입니다. 위의 예제에서는 X
의 각 요소 $X_{ji} = x^{(i)}_j$에 X_norm
에서 동일한 열에 있는 요소로 나누는 작업을 해 줍니다. 이는 곧 $Y_{ji} = X_{ji} / X_{\text{norm}} = x^{(i)}_j / ||x^{(i)}||_2 $를 계산하게 되는 것입니다. bsxfun
은 많은 요소별 함수와 함께 사용할 수 있습니다. 이런 함수들에는 @plus, @ge, @eq가 있는데 자세한 내용은 bsxfun
docs를 참고하기 바랍니다.
Example: matrix multiplication in gradient computations
Linear regression의 그래디언트 계산은 아래와 같이 덧셈으로 이루어져 있습니다.
$$\frac{\partial J(\theta; X,y)}{\partial \theta_j} = \sum_i x_j^{(i)} (\hat{y}^{(i)} - y^{(i)})$$
이를 위해서 정해진 인덱스 $j$에 대해서 모든 $i$에 대한 합을 계산하는 경우 이는 행렬 곱 $[AB]_{jk} = \sum_i A_{ji}B_{ik}$로 계산할 수 있습니다. 만약 $y$와 $\hat{y}$가 열 벡터라면, 위의 덧셈 식은 다음과 같이 쓸 수 있습니다.
$$\frac{\partial J(\theta; X,y)}{\partial \theta_j} = \sum_i X_{ji} (\hat{y}_i - y_i) = [X (\hat{y} - y)]_j$$
이제 모든 $j$에 대한 그래디언트는 $X (\hat{y} - y)$으로 한번에 계산할 수 있습니다.
MATLAB에서는 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 |
% X(j,i) = j'th coordinate of i'th example. % y(i) = i'th value to be predicted; y is a column vector. % theta = vector of parameters y_hat = theta'*X; % so y_hat(i) = theta' * X(:,i). Note that y_hat is a *row-vector*. g = X*(y_hat' - y); |
Exercise 1A and 1B Redux
연습문제 1A, 1B의 코드로 가서 ex1a_linreg.m
과 ex1b_logreg.m
를 열어보면, vector 단위 연산을 위한 코드가 주석처리된 부분이 있습니다. 이는 minFunc
을 위해서 linear_regression.m
대신 linear_regression_vec.m
을, logistic_regression.m
대신 logistric_regression_vec.m
을 사용합니다. 이것의 주석을 없애고 벡터 버전의 각 코드를 새로 작성해봅시다.