cchyun
cchyun Reinforce NLP Developer

자연어처리를 위한 행렬연산

자연어처리를 위한 행렬연산

이 포스트는 자연어처리에서 사용되는 기본적인 행렬연산에 대해서 이해하는데 목적이 있습니다. 행렬연산에 대해서 알고 계시는 분은 다음 포스트를 참고하세요.

미리 확인해야할 포스트

개발 환경은 google colab을 사용하며 개발 언어는 pytorch를 사용합니다. 이 포스트를 작성할 시점의 pytorch 버전은 1.7.1 입니다.

소스코드

1. 환경

Google colab에서 필요한 환경을 설정하는 과정입니다.

우선 필요한 library를 import 합니다.

1
2
3
4
5
6
import argparse
import random

import matplotlib.pyplot as plt
import numpy as np
import torch

다음은 필요한 환경을 설정합니다.

1
2
3
4
5
6
7
8
9
10
# 환경 설정
args = {
    # random seed value
    "seed": 1234,
    # CPU 또는 GPU 사용여부 결정
    "device": torch.device("cuda" if torch.cuda.is_available() else "cpu")
}
args = argparse.Namespace(**args)

print(args)

위 코드의 실행 결과는 아래와 같습니다.

1
Namespace(device=device(type='cpu'), seed=1234)

다음은 random seed를 설정해줍니다.

1
2
3
4
5
# random seed 설정
random.seed(args.seed)
np.random.seed(args.seed)
torch.manual_seed(args.seed)
torch.cuda.manual_seed_all(args.seed)

1. 행렬 A

우선 아래와 같이 실습을 위한 5행 4열의 행렬을 선언합니다. 5행 4열의 행렬의 모양을 $5 \times 4$로 표현합니다.

1
2
3
4
# A 행렬 선언
A = np.random.randint(-9, 10, (5, 4)) / 10
A = torch.tensor(A)
print(A)

위 코드의 실행 결과는 아래와 같습니다. 랜덤 행렬 A가 생성되었습니다.

1
2
3
4
5
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

2. Element Wise Sum

행렬 간의 더하기를 해 보겠습니다. 전제조건은 두 행렬의 모양이 같아야 합니다. 아래 수식과 같이 $m \times n$ 행렬 A와 $m \times n$ 행렬 B의 합은 행렬의 같은 위치의 성분들을 더한 것과 같습니다.

행렬의 합은 교환법칙, 결합법칙이 성립합니다.

  • $A + B = B + A$
  • $(A + B) + C = A + (B + C)$

행렬의 더하기를 이제 실습으로 확인해 보겠습니다. 우선 A와 더하기를 할 행렬 B를 선언 후 더합니다. 행렬 B도 행렬 A와 같이 $5 \times 4$ 행렬이어야 합니다. 두 행렬의 더히기를 코드로 표현할 때는 $A + B$와 같이 ‘$+$’ 기호를 사용합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 합
B = np.random.randint(-9, 10, (5, 4)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A + B)

위 코드의 실행 결과는 아래와 같습니다. 행렬의 각 성분들끼리 더해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[ 0.8000,  0.0000,  0.6000, -0.1000],
        [-0.7000,  0.7000,  0.2000, -0.7000],
        [ 0.9000,  0.6000, -0.6000,  0.5000],
        [-0.7000, -0.5000, -0.8000,  0.1000],
        [-0.7000,  0.4000, -0.6000,  0.9000]], dtype=torch.float64)

tensor([[ 1.4000, -0.3000,  0.9000,  0.5000],
        [ 0.1000,  0.7000,  0.4000, -0.4000],
        [ 1.6000,  0.2000,  0.1000,  0.5000],
        [-0.1000,  0.4000, -0.1000,  0.4000],
        [-1.1000, -0.3000, -0.9000,  0.3000]], dtype=torch.float64)
2.1. Broadcasting $(m \times n) + (m \times 1)$

다음은 행렬 덧셈에서의 브로드캐스팅에 대해서 알아보겠습니다. 브로드캐스팅은 행령을 더할 때 행 또는 열의 개수가 1일때 자동으로 복사된 성분으로 계산해주는 기능을 말합니다. 자세한 내용은 numpy Broadcasting을 참고하세요.

아래 수식과 같이 $m \times n$ 행렬 A과 $m \times 1$ 행렬 B의 합은 행렬 B의 열을 n번 복사해서 $m \times n$ 행렬로 변경후 행렬의 같은 위치의 성분들을 더한 것과 같습니다.

행렬의 브로드캐스팅 더하기를 이제 실습으로 확인해 보겠습니다. 우선 A와 더하기를 할 행렬 B를 선언한 후 더하기를 실행합니다. 이때 행렬 B는 행렬 A와 다르게 $5 \times 1$ 행렬로 선언합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 Boradcasting 합
B = np.random.randint(-9, 10, (5, 1)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A + B)

위 코드의 실행 결과는 아래와 같습니다. 행렬 B가 브로드캐스팅 후 각 성분들끼리 더해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[ 0.3000],
        [-0.4000],
        [ 0.8000],
        [ 0.7000],
        [-0.1000]], dtype=torch.float64)

tensor([[ 0.9000,  0.0000,  0.6000,  0.9000],
        [ 0.4000, -0.4000, -0.2000, -0.1000],
        [ 1.5000,  0.4000,  1.5000,  0.8000],
        [ 1.3000,  1.6000,  1.4000,  1.0000],
        [-0.5000, -0.8000, -0.4000, -0.7000]], dtype=torch.float64)

결과 검증을 위해 pytorch의 repeat 함수를 이용해 행렬 B를 A와 같은 모양으로 열을 복사한 B1 행렬을 이용한 합을 구해봅니다.

1
2
3
4
5
6
# B 행렬 열복사 후 합
B1 = B.repeat(1, 4)

print(B1)
print()
print(A + B1)

위 코드의 실행 결과는 아래와 같습니다. 위 브로드캐스팅 더하기와 결과가 같은것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
tensor([[ 0.3000,  0.3000,  0.3000,  0.3000],
        [-0.4000, -0.4000, -0.4000, -0.4000],
        [ 0.8000,  0.8000,  0.8000,  0.8000],
        [ 0.7000,  0.7000,  0.7000,  0.7000],
        [-0.1000, -0.1000, -0.1000, -0.1000]], dtype=torch.float64)

tensor([[ 0.9000,  0.0000,  0.6000,  0.9000],
        [ 0.4000, -0.4000, -0.2000, -0.1000],
        [ 1.5000,  0.4000,  1.5000,  0.8000],
        [ 1.3000,  1.6000,  1.4000,  1.0000],
        [-0.5000, -0.8000, -0.4000, -0.7000]], dtype=torch.float64)
2.2. Broadcasting $(m \times n) + (1 \times n)$

두번째 경우는 아래 수식과 같이 $m \times n$ 행렬 A과 $1 \times n$ 행렬 B의 합은 행렬 B의 행을 m번 복사해서 $m \times n$ 행렬로 변경후 행렬의 같은 위치의 성분들을 더한 것과 같습니다.

이제 실습으로 확인해 보겠습니다. 우선 A와 더하기를 할 행렬 B를 선언한 후 더하기를 실행합니다. 행렬 B를 행렬 A와 다르게 $1 \times 4$ 행렬로 선언합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 Boradcasting 합
B = np.random.randint(-9, 10, (1, 4)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A + B)

위 코드의 실행 결과는 아래와 같습니다. 행렬 B가 브로드캐스팅 후 각 성분들끼리 더해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[0.4000, 0.2000, 0.6000, 0.7000]], dtype=torch.float64)

tensor([[ 1.0000, -0.1000,  0.9000,  1.3000],
        [ 1.2000,  0.2000,  0.8000,  1.0000],
        [ 1.1000, -0.2000,  1.3000,  0.7000],
        [ 1.0000,  1.1000,  1.3000,  1.0000],
        [ 0.0000, -0.5000,  0.3000,  0.1000]], dtype=torch.float64)

결과 검증을 위해 pytorch의 repeat 함수를 이용해 행렬 B를 A와 같은 모양으로 행을 복사한 B1 행렬을 이용한 합을 구해봅니다.

1
2
3
4
5
6
# B 행렬 행복사 후 합
B1 = B.repeat(5, 1)

print(B1)
print()
print(A + B1)

위 코드의 실행 결과는 아래와 같습니다. 위 브로드캐스팅 더하기와 결과가 같은것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
tensor([[0.4000, 0.2000, 0.6000, 0.7000],
        [0.4000, 0.2000, 0.6000, 0.7000],
        [0.4000, 0.2000, 0.6000, 0.7000],
        [0.4000, 0.2000, 0.6000, 0.7000],
        [0.4000, 0.2000, 0.6000, 0.7000]], dtype=torch.float64)

tensor([[ 1.0000, -0.1000,  0.9000,  1.3000],
        [ 1.2000,  0.2000,  0.8000,  1.0000],
        [ 1.1000, -0.2000,  1.3000,  0.7000],
        [ 1.0000,  1.1000,  1.3000,  1.0000],
        [ 0.0000, -0.5000,  0.3000,  0.1000]], dtype=torch.float64)
2.3. Broadcasting $(m \times n) + (1 \times 1)$

두번째 경우는 아래 수식과 같이 $m \times n$ 행렬 A과 $1 \times 1$ 행렬 B의 합은 행렬 B를 복사해서 $m \times n$ 행렬로 변경후 행렬의 같은 위치의 성분들을 더한 것과 같습니다. 이때 B는 행렬이 아닌 실수 스칼라 값이어도 동일합니다.

이제 실습으로 확인해 보겠습니다. 우선 A와 더하기를 할 행렬 B를 선언한 후 더하기를 실행합니다. 행렬 B를 행렬 A와 다르게 $1 \times 1$ 행렬로 선언합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 Boradcasting 합
B = np.random.randint(-9, 10, (1, 1)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A + B)

위 코드의 실행 결과는 아래와 같습니다. 행렬 B가 브로드캐스팅 후 각 성분들끼리 더해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[-0.9000]], dtype=torch.float64)

tensor([[-0.3000, -1.2000, -0.6000, -0.3000],
        [-0.1000, -0.9000, -0.7000, -0.6000],
        [-0.2000, -1.3000, -0.2000, -0.9000],
        [-0.3000,  0.0000, -0.2000, -0.6000],
        [-1.3000, -1.6000, -1.2000, -1.5000]], dtype=torch.float64)

결과 검증을 위해 pytorch의 repeat 함수를 이용해 행렬 B를 A와 같은 모양으로 행을 복사한 B1 행렬을 이용한 합을 구해봅니다.

1
2
3
4
5
6
# B 행렬 행과 열 복사 후 합
B1 = B.repeat(5, 4)

print(B1)
print()
print(A + B1)

위 코드의 실행 결과는 아래와 같습니다. 위 브로드캐스팅 더하기와 결과가 같은것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
tensor([[-0.9000, -0.9000, -0.9000, -0.9000],
        [-0.9000, -0.9000, -0.9000, -0.9000],
        [-0.9000, -0.9000, -0.9000, -0.9000],
        [-0.9000, -0.9000, -0.9000, -0.9000],
        [-0.9000, -0.9000, -0.9000, -0.9000]], dtype=torch.float64)

tensor([[-0.3000, -1.2000, -0.6000, -0.3000],
        [-0.1000, -0.9000, -0.7000, -0.6000],
        [-0.2000, -1.3000, -0.2000, -0.9000],
        [-0.3000,  0.0000, -0.2000, -0.6000],
        [-1.3000, -1.6000, -1.2000, -1.5000]], dtype=torch.float64)

3. Element Wise Product

행렬 간의 element wise product를 해 보겠습니다. element wise product는 기호로는 $\odot$와 같이 표현합니다. 더하기와 비슷하게 전제조건은 두 행렬의 모양이 같아야 합니다. 아래 수식과 같이 $m \times n$ 행렬 A와 $m \times n$ 행렬 B의 element wise product는 행렬의 같은 위치의 성분들을 곱한 것과 같습니다.

행렬의 element wise product는 교환법칙, 결합법칙이 성립합니다.

  • $A \odot B = B \odot A$
  • $(A \odot B) \odot C = A \odot (B \odot C)$

행렬의 element wise product를 이제 실습으로 확인해 보겠습니다. 우선 행렬 B를 선언 후 행렬 A와 element wise product를 실행합니다. 행렬 B도 행렬 A와 같이 $5 \times 4$ 행렬이어야 합니다. 두 행렬의 element wise product를 코드로 표현할 때는 $A * B$와 같이 ‘$*$’ 기호를 사용합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 element wise product
B = np.random.randint(-9, 10, (5, 4)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A * B)

위 코드의 실행 결과는 아래와 같습니다. 행렬의 각 성분들끼리 곱해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[ 0.8000, -0.9000,  0.8000,  0.0000],
        [-0.6000, -0.5000, -0.8000,  0.2000],
        [ 0.8000, -0.3000, -0.7000, -0.2000],
        [ 0.3000,  0.3000, -0.9000, -0.1000],
        [ 0.6000,  0.4000,  0.4000,  0.0000]], dtype=torch.float64)

tensor([[ 0.4800,  0.2700,  0.2400,  0.0000],
        [-0.4800, -0.0000, -0.1600,  0.0600],
        [ 0.5600,  0.1200, -0.4900, -0.0000],
        [ 0.1800,  0.2700, -0.6300, -0.0300],
        [-0.2400, -0.2800, -0.1200, -0.0000]], dtype=torch.float64)
3.1. Broadcasting $(m \times n) + (m \times 1)$

다음은 행렬 element wise product에서의 브로드캐스팅은 행열의 더하기와 차이가 앖습니다.

아래 수식과 같이 $m \times n$ 행렬 A과 $m \times 1$ 행렬 B의 합은 행렬 B의 열을 n번 복사해서 $m \times n$ 행렬로 변경후 행렬의 같은 위치의 성분들을 곱한 것과 같습니다.

이제 실습으로 확인해 보겠습니다. 우선 행렬 B를 선언한 후 행렬 A와 element wise product를 실행합니다. 이때 행렬 B는 행렬 A와 다르게 $5 \times 1$ 행렬로 선언합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 Boradcasting element wise product
B = np.random.randint(-9, 10, (5, 1)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A * B)

위 코드의 실행 결과는 아래와 같습니다. 행렬 B가 브로드캐스팅 후 각 성분들끼리 곱해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[ 0.6000],
        [-0.5000],
        [-0.2000],
        [-0.2000],
        [ 0.8000]], dtype=torch.float64)

tensor([[ 0.3600, -0.1800,  0.1800,  0.3600],
        [-0.4000, -0.0000, -0.1000, -0.1500],
        [-0.1400,  0.0800, -0.1400, -0.0000],
        [-0.1200, -0.1800, -0.1400, -0.0600],
        [-0.3200, -0.5600, -0.2400, -0.4800]], dtype=torch.float64)

결과 검증은 행렬의 더하기와 차이가 없으므로 생략합니다.

3.2. Broadcasting $(m \times n) + (1 \times n)$

두번째 경우는 아래 수식과 같이 $m \times n$ 행렬 A과 $1 \times n$ 행렬 B의 합은 행렬 B의 행을 m번 복사해서 $m \times n$ 행렬로 변경후 행렬의 같은 위치의 성분들을 곱한 것과 같습니다.

이제 실습으로 확인해 보겠습니다. 우선 행렬 B를 선언한 후 행렬 A와 element wise product를 실행합니다. 행렬 B를 행렬 A와 다르게 $1 \times 4$ 행렬로 선언합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 Boradcasting element wise product
B = np.random.randint(-9, 10, (1, 4)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A * B)

위 코드의 실행 결과는 아래와 같습니다. 행렬 B가 브로드캐스팅 후 각 성분들끼리 더해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[-0.2000,  0.9000, -0.4000, -0.7000]], dtype=torch.float64)

tensor([[-0.1200, -0.2700, -0.1200, -0.4200],
        [-0.1600,  0.0000, -0.0800, -0.2100],
        [-0.1400, -0.3600, -0.2800, -0.0000],
        [-0.1200,  0.8100, -0.2800, -0.2100],
        [ 0.0800, -0.6300,  0.1200,  0.4200]], dtype=torch.float64)

결과 검증은 행렬의 더하기와 차이가 없으므로 생략합니다.

3.3. Broadcasting $(m \times n) + (1 \times 1)$

두번째 경우는 아래 수식과 같이 $m \times n$ 행렬 A과 $1 \times 1$ 행렬 B의 합은 행렬 B를 복사해서 $m \times n$ 행렬로 변경후 행렬의 같은 위치의 성분들을 곱한 것과 같습니다. 이때 B는 행렬이 아닌 실수 스칼라 값이어도 동일합니다.

이제 실습으로 확인해 보겠습니다. 우선 행렬 B를 선언한 후 행렬 A와 element wise product를 실행합니다. 행렬 B를 행렬 A와 다르게 $1 \times 1$ 행렬로 선언합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 Boradcasting element wise product
B = np.random.randint(-9, 10, (1, 1)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(A * B)

위 코드의 실행 결과는 아래와 같습니다. 행렬 B가 브로드캐스팅 후 각 성분들끼리 곱해진 것을 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)
tensor([[-0.8000]], dtype=torch.float64)
tensor([[-0.4800,  0.2400, -0.2400, -0.4800],
        [-0.6400, -0.0000, -0.1600, -0.2400],
        [-0.5600,  0.3200, -0.5600, -0.0000],
        [-0.4800, -0.7200, -0.5600, -0.2400],
        [ 0.3200,  0.5600,  0.2400,  0.4800]], dtype=torch.float64)

결과 검증은 행렬의 더하기와 차이가 없으므로 생략합니다.

4. Matrix multiplication

행렬 간의 matrix multiplication를 해 보겠습니다. Matrix multiplication는 기호로는 $A \times B$와 같이 표현합니다. 또는 간단하게 $AB$ 기호를 생락하기도 합니다. 전제조건은 첫 번째 행렬의 열의 개수와 두 번째 행렬의 행의 개수가 같아야 합니다. 아래 수식과 같이 $m \times n$ 행렬 A와 $n \times k$ 행렬 B의 matrix multiplication은 A의 행들과 B의 열들을 dot product 한 것과 같습니다.

결과는 첫 번째 행렬의 행 개수와 두 번째 행렬의 열 개수와 동일한 $m \times k$ 행렬입니다.

행렬의 matrix multiplication은 교환법칙은 성립하지 않고 결합법칙은 성립합니다.

  • $A \times B \ne B \times A$
  • $(A \times B) \times C = A \times (B \times C)$

위 행렬을 아래와 같이 행벡터와 열벡터로 표현할 수 있습니다.

이제 matrix multiplication을 행벡터와 열벡터 수식으로 표현하면 아래와 같습니다.

행렬의 matrix multiplication을 실습으로 확인해 보겠습니다. 우선 행렬 B를 선언 후 행렬 A와 matrix multiplication을 실행합니다. 행렬 B의 행은 행렬 A의 열과 같아야 하고 행렬 B의 열은 아무 값이나 주어도 됩니다. 다음과 같이 $4 \times 3$ B 행렬을 선언합니다. 두 행렬의 matrix multiplication은 pytorch의 matmul 함수를 이용해서 구합니다.

1
2
3
4
5
6
7
8
9
# B 행렬 matrix multiplication
B = np.random.randint(-9, 10, (4, 3)) / 10
B = torch.tensor(B)

print(A)
print()
print(B)
print()
print(torch.matmul(A, B))

위 코드의 실행 결과는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tensor([[ 0.6000, -0.3000,  0.3000,  0.6000],
        [ 0.8000,  0.0000,  0.2000,  0.3000],
        [ 0.7000, -0.4000,  0.7000,  0.0000],
        [ 0.6000,  0.9000,  0.7000,  0.3000],
        [-0.4000, -0.7000, -0.3000, -0.6000]], dtype=torch.float64)

tensor([[-0.3000, -0.1000,  0.8000],
        [ 0.0000,  0.6000,  0.2000],
        [ 0.3000, -0.6000,  0.1000],
        [ 0.2000, -0.7000, -0.1000]], dtype=torch.float64)

tensor([[ 3.0000e-02, -8.4000e-01,  3.9000e-01],
        [-1.2000e-01, -4.1000e-01,  6.3000e-01],
        [-1.3323e-17, -7.3000e-01,  5.5000e-01],
        [ 9.0000e-02, -1.5000e-01,  7.0000e-01],
        [-9.0000e-02,  2.2000e-01, -4.3000e-01]], dtype=torch.float64)

결과 검증을 위해 A의 행벡터와 B의 열벡터의 dot product를 pytorch이 dot 함수를 이용해 직접 구해보겠습니다.

1
2
3
4
5
6
7
# 직접계산
result = torch.zeros(A.size(0), B.size(-1))
for row in range(A.size(0)):
    for col in range(B.size(-1)):
        result[row][col] = torch.dot(A[row], B[:, col])

print(result)

위 코드의 실행 결과는 아래와 같습니다. pytorch의 matmul의 결과와 동일한 것을 확인할 수 있습니다.

1
2
3
4
5
tensor([[ 0.0300, -0.8400,  0.3900],
        [-0.1200, -0.4100,  0.6300],
        [ 0.0000, -0.7300,  0.5500],
        [ 0.0900, -0.1500,  0.7000],
        [-0.0900,  0.2200, -0.4300]])
4.1. 열벡터 연산으로의 해석

딥러닝에서 수식으로 모델을 설명할 때 열벡터로 표현하는 경우가 대부분입니다. 위 행벡터와 열벡터의 수식을 열벡터로 표현하면 아래와 같습니다. A 행렬과 B의 열벡터들의 matrix multiplication으로 표현할 수 있습니다.

행렬과 하나의 열벡터의 matrix multiplication은 다음과 같이 표현할 수 있습니다.

4.2. 행벡터 연산으로의 해석

드물지만 딥러닝에서 수식으로 모델을 설명할 때 행벡터로 표현하는 때도 있습니다. 위 행벡터와 열벡터의 수식을 행벡터로 표현하면 아래와 같습니다. A의 행벡터와 행렬 B의 matrix multiplication으로 표현할 수 있습니다.

하나의 행벡터와 행렬의 matrix multiplication은 다음과 같이 표현할 수 있습니다.

5. Dot Product

두 벡터간의 dot product에 대해서 알아보겠습니다. Dot product의 기호는 $\vec{a} \bullet \vec{b}$와 같이 표현합니다. 전제조건은 두 벡터의 차원 수가 같아야 합니다. 아래 수식과 같이 같은 위치의 성분들을 곱해서 전체를 더한 것과 같습니다. 결과는 하나의 스칼라 값입니다.

두 벡터의 dot product를 기하적으로 표현하면 다음과 같습니다. 두 벡터의 길이의 곱에 두 벡터가 이루는 내각 $\theta$에 cosine을 취한 값을 더하는 것입니다.

위 그림을 수식으로 표현하면 다음과 같습니다.

cosine 값은 아래그림과 같이 변합나다.

  • 두 벡터의 방향이 같을 때 cosine 값이 1이 되어 dot-product 값이 최대가 됩니다
  • 두 벡터의 방향이 직각을 이룰 때 cosine 값이 0이 되어 dot-product 값이 0이 됩니다. 이때를 직교(orthogonality) 한다고 표현합니다.
  • 두 벡터가 방향이 반대 방향일 때 cosine 값이 -1이 되어 dot-product 값이 최소가 됩니다.

dot-product의 결과는 두 벡터가 얼마나 유사한지를 비교할 수 있는 기준으로 사용할 수 있습니다.

벡터의 dot product를 실습으로 확인해 보겠습니다. 우선 두개의 벡터를 선언합니다.

1
2
3
4
5
6
7
8
9
10
# a, b 벡터 선언
a = np.random.randint(-9, 10, (5,)) / 10
a = torch.tensor(a)

b = np.random.randint(-9, 10, (5,)) / 10
b = torch.tensor(b)

print(a)
print()
print(b)

위 코드의 실행 결과는 아래와 같습니다. 두 개의 벡터가 생성되었습니다.

1
2
3
tensor([-0.8000,  0.5000, -0.5000,  0.1000, -0.6000], dtype=torch.float64)

tensor([ 0.8000,  0.6000, -0.9000,  0.4000,  0.7000], dtype=torch.float64)

다음은 pytorch이 dot 함수를 이용해 dot-product를 구합니다.

1
2
# dot-product
print(torch.dot(a, b))

위 코드의 실행 결과는 아래와 같습니다. dot product의 결과인 스칼라값을 확인할 수 있습니다.

1
tensor(-0.1600, dtype=torch.float64)

결과 검증을 위해 두 벡터를 element wise product 값을 구한 후 합을 구해봅니다.

1
2
3
4
5
6
# element wise product and sum
c = a * b

print(c)
print()
print(torch.sum(c))

위 코드의 실행 결과는 아래와 같습니다. 위 dot product와 결과가 같은 것을 확인할 수 있습니다.

1
2
3
tensor([-0.6400,  0.3000,  0.4500,  0.0400, -0.4200], dtype=torch.float64)

tensor(-0.2700, dtype=torch.float64)

지금까지 기본적인 행렬연산에 대해서 알아봤습니다.

comments powered by Disqus