Main Content

벡터화

벡터화 사용

MATLAB®은 행렬 및 벡터 연산에 최적화되어 있습니다. MATLAB 행렬과 벡터 연산을 사용하도록 루프 기반의, 스칼라 위주의 코드를 수정하는 과정을 벡터화라고 합니다. 다음과 같은 이유에서 코드를 벡터화할 가치가 있습니다.

  • 모양: 벡터화된 수학 코드는 교재에서 볼 수 있는 수학 표현식과 비슷한 모양으로, 이해하기가 더 쉽습니다.

  • 오류 발생 가능성 감소: 루프가 없는, 벡터화된 코드는 길이가 짧은 경우가 많습니다. 코드의 라인 수가 적으면 프로그래밍 오류가 발생할 가능성이 줄어듭니다.

  • 성능: 벡터화된 코드는 같은 작업을 수행하는 루프가 포함된 코드보다 훨씬 더 빠르게 실행됩니다.

일반 계산에 대해 코드 벡터화하기

다음 코드는 0부터 10까지의 범위에 속하는 1,001개 값의 사인값을 계산합니다.

i = 0;
for t = 0:.01:10
    i = i + 1;
    y(i) = sin(t);
end

다음은 동일한 코드가 벡터화된 형태입니다.

t = 0:.01:10;
y = sin(t);

두 번째 코드 예제는 일반적으로 첫 번째 코드 예제보다 더 빠르게 실행되며, MATLAB을 보다 효율적으로 사용하는 방법입니다. 위의 코드가 포함된 스크립트를 만들어 시스템에서 실행 속도를 테스트하고, tictoc 함수를 사용하여 실행 시간을 측정해 보십시오.

특정 작업에 대해 코드 벡터화하기

다음 코드는 매 5번째 요소에서 벡터의 누적합을 계산합니다.

x = 1:10000;
ylength = (length(x) - mod(length(x),5))/5;
y(1:ylength) = 0;
for n= 5:5:length(x)
    y(n/5) = sum(x(1:n));
end 

벡터화를 하면 훨씬 더 간결한 MATLAB 프로세스를 작성할 수 있습니다. 다음 코드는 그것의 한 예를 보여줍니다.

x = 1:10000;
xsums = cumsum(x);
y = xsums(5:5:length(x)); 

배열 연산

배열 연산자는 데이터 세트의 모든 요소에 대해 동일한 연산을 수행합니다. 이러한 연산 유형은 반복적인 계산에 유용합니다. 예를 들어, 원뿔의 지름(D)과 높이(H)를 기록하여 다양한 원뿔의 부피(V)를 수집한다고 가정하겠습니다. 원뿔 하나에 대한 정보만 수집하는 경우 이 단일 원뿔의 부피는 다음과 같이 계산할 수 있습니다.

V = 1/12*pi*(D^2)*H;

이제, 10,000개의 원뿔에 대한 정보를 수집해 보십시오. 벡터 DH에는 각각 10,000개의 요소가 포함되어 있으며, 지금은 10,000개의 부피값을 계산해야 되는 상황입니다. 대부분의 프로그래밍 언어에서는 다음 MATLAB 코드와 비슷한 루프를 설정해야 합니다.

for n = 1:10000
   V(n) = 1/12*pi*(D(n)^2)*H(n);
end

MATLAB에서는 스칼라의 경우와 유사한 구문을 사용하여 벡터의 각 요소에 대해 계산을 수행할 수 있습니다.

% Vectorized Calculation
V = 1/12*pi*(D.^2).*H;

참고

연산자 *, /, ^ 앞에 마침표(.)를 추가하면 이러한 연산자가 배열 연산자로 변환됩니다.

배열 연산자를 사용하면 차원이 서로 다른 행렬을 결합할 수도 있습니다. 이렇게 크기가 1인 차원을 자동으로 확장하는 기능은 그리드 생성, 행렬과 벡터 연산 등을 벡터화하는 데 유용합니다.

행렬 A가 테스트 점수를 나타내고, 각 행이 서로 다른 학급을 나타낸다고 가정하겠습니다. 각 학급의 평균 점수와 학생 개인별 점수 간의 차이를 계산하려고 한다고 생각해 봅시다. 루프를 사용할 경우 연산은 다음과 같습니다.

A = [97 89 84; 95 82 92; 64 80 99;76 77 67;...
 88 59 74; 78 66 87; 55 93 85];

mA = mean(A);
B = zeros(size(A));
for n = 1:size(A,2)
    B(:,n) = A(:,n) - mA(n);
end

이를 보다 직접적으로 계산하는 방법은 루프를 실행할 필요가 없고 속도가 훨씬 빠른 A - mean(A)를 사용하는 것입니다.

devA = A - mean(A)
devA =

    18    11     0
    16     4     8
   -15     2    15
    -3    -1   -17
     9   -19   -10
    -1   -12     3
   -24    15     1

A가 7×3 행렬이고 mean(A) 1×3 벡터인 경우에도 MATLAB은 행렬과 크기가 같은 것처럼 벡터를 묵시적으로 확장하며, 연산은 정상적인 요소별 뺄셈 연산으로 실행됩니다.

피연산자의 크기 요구 사항은 각 차원의 크기 요구 사항으로, 배열의 크기가 모두 동일하거나 배열 중 하나의 크기가 1이어야 합니다. 이 요구 사항이 충족되면 배열 중 하나의 크기가 1인 차원은 다른 배열의 상응하는 차원과 같은 크기로 확장됩니다. 자세한 내용은 기본 연산에 대해 호환되는 배열 크기 항목을 참조하십시오.

벡터화에 묵시적 확장이 유용하게 쓰이는 또 다른 경우는 다차원 데이터를 사용할 때입니다. 두 변수 xy의 함수 F를 실행한다고 가정하겠습니다.

F(x,y) = x*exp(-x2 - y2)

x 벡터와 y 벡터의 점의 모든 조합에 대해 이 함수를 실행하려면 다음과 같이 값의 그리드를 정의해야 합니다. 이 작업에서는 루프를 사용하여 점 조합을 반복해서는 안 됩니다. 대신 벡터 중 하나가 열이고 다른 하나는 행인 경우, 벡터가 x+y 또는 x-y 같은 배열 연산자와 함께 사용되면 MATLAB은 자동으로 그리드를 생성합니다. 이 예제에서 x는 21×1 벡터이고 y는 1×16 벡터이기 때문에 이 작업은 x의 두 번째 차원과 y의 첫 번째 차원을 확장하여 21×16 행렬을 생성합니다.

x = (-2:0.2:2)'; % 21-by-1
y = -1.5:0.2:1.5; % 1-by-16
F = x.*exp(-x.^2-y.^2); % 21-by-16

그리드를 명시적으로 생성하려면 meshgrid 함수와 ndgrid 함수를 사용하면 됩니다.

논리형 배열 연산

배열의 대량 처리를 논리적으로 확장한다는 것은 비교와 의사결정 작업을 벡터화하는 것입니다. MATLAB 비교 연산자는 벡터 입력값을 받고 벡터 출력값을 반환합니다.

예를 들어, 10,000개의 원뿔에서 데이터를 수집하는 동안 지름에 여러 개의 음수 값을 기록한다고 가정하겠습니다. >= 연산자를 사용하여 벡터에서 유효한 값이 무엇인지 파악할 수 있습니다.

D = [-0.2 1.0 1.5 3.0 -1.0 4.2 3.14];
D >= 0
ans =

     0     1     1     1     0     1     1
MATLAB의 논리형 인덱싱 기능을 직접 이용하여 음수가 아닌 D의 요소에 대응하는 유효한 원뿔 부피값 Vgood을 선별할 수 있습니다.
Vgood = V(D >= 0);

MATLAB에서는 함수 allany를 각각 사용하여 전체 벡터의 요소에 대해 논리적 AND 또는 OR 연산을 수행할 수 있습니다. 다음과 같이, D의 모든 값이 0보다 작은 경우 경고를 발생시킬 수 있습니다.

if all(D < 0)
   warning('All values of diameter are negative.')
   return
end

MATLAB은 호환되는 크기의 두 벡터를 비교할 수 있으므로 이를 통해 추가적인 제한을 둘 수 있습니다. 다음 코드는 V가 음수가 아니며 DH보다 큰 모든 값을 찾습니다.

V((V >= 0) & (D > H))
결과로 생성된 벡터는 입력값과 크기가 같습니다.

비교에 도움을 줄 수 있도록, MATLAB에는 오버플로, 언더플로, 정의되지 않은 연산자를 나타내는 특수값(예: Inf, NaN)이 있습니다. 논리 연산자 isinfisnan은 이러한 특수값에 대해 논리 테스트를 수행하는 데 도움이 됩니다. 예를 들어, 다음과 같이 계산에서 NaN 값을 제외시키는 것이 유용할 때가 종종 있습니다.

x = [2 -1 0 3 NaN 2 NaN 11 4 Inf];
xvalid = x(~isnan(x))
xvalid =

     2    -1     0     3     2    11     4   Inf

참고

Inf == Inf는 true를 반환하지만, NaN == NaN은 항상 false를 반환합니다.

행렬 연산

코드를 벡터화할 때는 특정 크기 또는 구조를 갖는 행렬을 생성해야 하는 경우가 종종 있습니다. 이를 위해 획일한 행렬을 만들 수 있는 기법이 있습니다. 예를 들어, 동일한 요소로 구성된 5×5 행렬이 필요할 수 있습니다.

A = ones(5,5)*10;
또는 반복되는 값으로 구성된 행렬이 필요할 수도 있습니다.
v = 1:5;
A = repmat(v,3,1)
A =

     1     2     3     4     5
     1     2     3     4     5
     1     2     3     4     5

함수 repmat는 작은 행렬이나 벡터를 더 큰 행렬로 키울 수 있는 유연성을 제공합니다. repmat는 다음과 같이 입력 행렬을 반복하여 행렬을 만듭니다.

A = repmat(1:3,5,2)
B = repmat([1 2; 3 4],2,2)
A =

     1     2     3     1     2     3
     1     2     3     1     2     3
     1     2     3     1     2     3
     1     2     3     1     2     3
     1     2     3     1     2     3


B =

     1     2     1     2
     3     4     3     4
     1     2     1     2
     3     4     3     4

정렬, 설정, 셈 작업

여러 응용 프로그램에서, 벡터의 요소에 대해 수행되는 계산은 동일한 벡터에 있는 다른 요소에 따라 달라집니다. 예를 들어, 벡터 x는 집합을 나타낼 수 있습니다. for 또는 while 루프를 사용하지 않고 어떻게 집합 요소에 대한 반복 연산을 할 수 있는지는 명백하지 않습니다. 벡터화된 코드를 사용하면 프로세스가 훨씬 더 명확해지고 구문도 덜 복잡해집니다.

중복 요소 제거하기

벡터 내의 중복 요소를 찾는 방법에는 여러 가지가 있습니다. 그중 하나는 함수 diff를 이용하는 것입니다. 벡터 요소를 정렬한 후에, 이 벡터에 diff 함수를 사용하면 동일한 요소가 인접한 경우 0이 생성됩니다. diff(x)x보다 요소가 한 개 더 적은 벡터를 생성하기 때문에, 이 집합 내의 요소와 동일하지 않은 요소를 하나 추가해야 합니다. NaN은 항상 이 조건을 충족합니다. 마지막으로, 다음과 같이 논리형 인덱싱을 사용하여 집합 내의 고유한 요소를 선택할 수 있습니다.

x = [2 1 2 2 3 1 3 2 1 3];
x = sort(x);
difference  = diff([x,NaN]);
y = x(difference~=0)
y =

     1     2     3
또는 다음과 같이 unique 함수를 사용해도 동일한 작업을 수행할 수 있습니다.
y=unique(x);
그러나 unique 함수는 필요 이상의 기능을 제공하여 코드 실행 속도를 둔화시킬 수 있습니다. 각 코드 조각(Code Snippet)의 성능을 측정하려면 tictoc 함수를 사용하십시오.

벡터의 요소 개수 세기

단순히 x의 집합이나 부분 집합을 반환하는 대신, 벡터의 요소 개수를 셀 수 있습니다. 벡터가 정렬된 후에 find 함수를 사용하여 diff(x)에서 값이 0인 인덱스를 파악하고 요소의 값이 변경되는 위치를 표시할 수 있습니다. find 함수의 이후 인덱스 간의 차이는 특정 요소의 개수를 나타냅니다.

x = [2 1 2 2 3 1 3 2 1 3];
x = sort(x);
difference  = diff([x,max(x)+1]);
count = diff(find([1,difference]))
y = x(find(difference))
count =

     3     4     3


y =

     1     2     3
find 함수는 NaN 요소에 대한 인덱스는 반환하지 않습니다. isnan 함수와 isinf 함수를 사용하여 NaN 값과 Inf 값의 개수를 셀 수 있습니다.

count_nans = sum(isnan(x(:)));
count_infs = sum(isinf(x(:)));

벡터화에 흔히 사용되는 함수

함수설명
all

모든 배열 요소가 0이 아닌 값인지 아니면 true인지 확인

any

모든 배열 요소가 0이 아닌 값인지 확인

cumsum

누적합(Cumulative Sum)

diff

차분과 근사 도함수

find

0이 아닌 요소의 값이나 인덱스 찾기

ind2sub

선형 인덱스의 첨자

ipermute

N차원 배열의 차원 역치환(Inverse Permute)

logical

숫자형 값을 논리값(Logical)으로 변환

meshgrid

2차원 공간과 3차원 공간의 사각 그리드(Rectangular Grid)

ndgrid

N차원 공간의 사각 그리드

permute

N차원 배열의 차원 재배열

prod

배열 요소의 곱

repmat

배열의 복사본을 반복함

reshape

배열 형태 변경(reshape)

shiftdim

차원 전환

sort

배열 요소 정렬

squeeze

한원소(Singleton) 차원 제거

sub2ind

첨자를 선형 인덱스로 변환

sum

배열 요소의 합

관련 항목

외부 웹사이트