개발일기

기초 수학 - 선형대수학 Tensor, Vector, Norm 본문

Deep Learning, Machine Learning/기초 수학

기초 수학 - 선형대수학 Tensor, Vector, Norm

Flashback 2024. 3. 23. 20:18
728x90
반응형

텐서 데이터

  • 스칼라(Scalar): 0차원의 텐서를 스칼라라 부른다. 0차원이란 [1, 2, 3]과 같이 배열 안에 들어있지 않는 경우를 의미한다. 1차원은 [1, 2, 3], 2차원은 [[1, 2], [3, 4]]와 같이 이루어져 있다. 즉 스칼라는 0차원의 숫자 상수를 나타낸다. 1, 2, 3 과 같이 개별로 존재하는 상수를 의미한다.
  • 벡터(Vector): 1차원 텐서를 벡터라 칭한다. 쉽게 생각하면 1차원 배열이다.
  • 행렬(Matrix): 2차원 텐서를 행렬이라 한다. 쉽게 생각하면 2차원 배열이다.
  • 3-Tensor: 3차원 텐서를 나타낸다. 3차원부터 차원이 n차로 늘어나면 n-Tensor로 칭한다.
  • 텐서(Tensor): 3-Tensor에서 봤듯이 차원이 늘어날수록 4차원 텐서, 5차원 텐서... 등으로 나타낸다. 스칼라는 0차원 텐서, 벡터는 1차원 텐서, 행렬은 2차원 텐서로 표현할 수 있는 만능 단위이다. 즉, 텐서는 다차원 배열로 0차원부터 n차원까지를 아우르는 행렬과 벡터이다.

 

텐서 생성(Pytorch)

import torch

torch_tensor = torch.tensor(25) # 텐서 생성
print(torch_tensor, torch_tensor.dtype) # 정수형으로 입력하면 기본 int64로 타입이 지정됨
print(torch_tensor.shape) # 텐서 크기

torch_f_tensor = torch.tensor(25, dtype=torch.float16) # dtype으로 스칼라의 타입 지정 가능
print(torch_f_tensor)

torch_vector =  torch.tensor([1, 2]) # 벡터 생성
print(torch_vector.size())

"""
tensor(25) torch.int64
torch.Size([])
tensor(25., dtype=torch.float16)
torch.Size([2])
"""
  • tensor(): 파이토치를 사용할 경우 tensor()안에 숫자 또는 배열을 넣어주면 텐서가 생성된다.
  • dtype: 텐서의 타입을 지정한다.
  • shape: 텐서 크기를 나타낸다. 스칼라인 경우 0차원이므로 []이 나오고 벡터에 요소가 2개 들어있는 경우 [2]가 출력된다.
  • size(): shape와 동일한 기능이다.

 

텐서 생성(Tensorflow)

import tensorflow as tf

tf_scalar = tf.Variable(10) # 스칼라 생성
print(tf_scalar)
print(tf_scalar.dtype) # dtype 출력
print(tf_scalar.shape) # shape 출력

tf_vector = tf.Variable([10, 20], dtype=tf.float64) # 벡터 생성
print(tf_vector)

"""
<tf.Variable 'Variable:0' shape=() dtype=int32, numpy=10>
<dtype: 'int32'>
()
<tf.Variable 'Variable:0' shape=(2,) dtype=float64, numpy=array([10., 20.])>
"""

Variable(): 텐서플로우에서 텐서를 생성하는 메서드이다.

파이토치와 dtype, shape 등 텐서의 정보를 가져오는 메서드는 동일하거나 비슷하다. 텐서플로우는 텐서를 print()로 출력하면 여러 정보가 같이 나오는 특징을 가지고 있다.

 

전치(Transpose)

전치란 행열 요소 위치를 바꿔주는걸 의미한다.

import numpy as np

x =  np.arange(8).reshape(2, 4) # 행렬 전치
print(x)
print(x.T, '\n')

y = np.arange(4) # 벡터 전치
print(y) # 행벡터
print(y.T) # 열벡터

"""
[[0 1 2 3]
 [4 5 6 7]]
[[0 4]
 [1 5]
 [2 6]
 [3 7]] 

[0 1 2 3]
[0 1 2 3]
"""

 

T: 전치를 하려면 변수명 뒤에 T를 써주면 전치가 된다. 파이토치와 텐서플로우도 동일하게 변수 뒤에 T를 붙여주면 전치된다.

전치된 행렬은 행과 열의 값이 바뀐게 눈에 보이지만 벡터는 값이 동일하게 나온다. 벡터는 전치하면 행벡터에서 열벡터로 변하는데 출력할 때는 행벡터로 동일하게 출력된다.

  • 행벡터: 한개의 행을 갖는 행렬을 의미한다. (1, 4)
  • 열벡터: 한개의 열을 갖는 행렬을 의미한다. (4, 1)

 

Norm

노름은 벡터의 크기를 나타내는 것으로 크게는 L1노름, L2노름이 존재하는데 주로 사용되는 노름은 L2노름이다.

P벡터 (p1, p2)와 Q벡터 (q1, q2) 사이의 벡터 크기를 L2노름이라 하며 Q벡터와 P벡터 각 요소의 차이를 제곱하여 제곱근에 넣으면 L2노름이 된다. 각 요소의 차이를 구하는 이유는 두 지점간의 거리를 구하기 위함이다. 피타고라스 정의를 바탕으로 $ a^2 + b^2 = c^2  $ 이 두 지점간의 거리이다.(가로 제곱 + 세로 제곱 = 대각선 제곱)

수학 수식으로 보면 $ ||\mathbf{x}||_2 = \sqrt{\sum_{i=1}^{n}x_i^2} $이다. $p=1$면 L1노름, $p=2$면 L2노름인데 L2노름이 자수 사용되므로  주로 $p=2$를 생략하고 $ ||x|| $로 표시한다.

 

import numpy as np

x = np.array([3, 4]) # 2차원
x2 = np.array([5, 2, 6]) # 3차원 

x1_norm = np.linalg.norm(x) # (0, 0)로부터 (3, 4)까지의 노름값(벡터 길이)
x2_norm = np.linalg.norm(x2)

print("x1 norm: ", x1_norm)
print("x2 norm: ", x2_norm)

"""
x1 norm:  5.0
x2 norm:  8.06225774829855
"""

norm(): (0, 0)지점에서 매개변수로 들어가는 벡터까지의 노름값을 구한다.

 

2차원뿐만 아니라 3차원 이상에서도 기존 공식과 동일하게 요소값을 제곱하여 더한 후 제곱근에 넣으면 노름값이 나온다.

print( np.sqrt(5**2 + 2**2 + 6**2) ) # (5, 2, 6)
# 8.06225774829855

 

L1 Norm: L2노름과 다르게 제곱근과 요소의 제곱을 사용하지 않고 단순히 요소의 절대값들을 합한다. 수식으로는 $ ||\mathbf{x}||_1 = \sum_{i=1}^{n}|x_i| $로 표시한다. L1노름은 (0, 0)을 기점으로 삼지않아도 된다는 특징을 가지고 있다. 기존 L2노름은 (0, 0)를 기점으로 (3, 4)까지의 벡터 길이를 구했는데 L1노름은 기점을 (-1, 3)처럼 다르게 지정할 수 있다.

import numpy as np

x1 = np.array([-1 ,3]) # 기점
x2 = np.array([3, 4]) # 도착지

x_norm = np.linalg.norm((x2 - x1), ord=1) # ord를 1로 지정하면 L1노름
print("L1 norm: ", x_norm)

"""
L1 norm:  5.0
"""

ord: 어떤 노름을 구할 지 정한다. 1로 지정하면 L1노름, 2로 지정하면 L2노름 식으로 늘려나갈 수 있다.

위의 코드를 보면 x1과 x2 벡터 간의 절대값을 더한 값이 L1노름값으로 출력되는것을 확인할 수 있다.

 

np.abs( (x2[0] - x1[0]) + (x2[1] - x1[1]))
# 5

abs로 절대값을 구해 계산하면 L1노름과 동일한 값이 출력된다.

 

Squared L2 Norm: L2노름에서 제곱근을 씌우지 않으면 제곱 L2노름이 된다. 수식으로는 $ ||\mathbf{x}||_2^2 = \sum_{i=1}^{n}x_i^2 $로 표시한다.

제곱 L2노름은 제곱근이 없으므로 연산이 단순하다. 하지만 (0, 0)에서 느리게 자라나는 단점을 가지고 있다. 이로인해 (0, 0) 주변에 중요한 값이 있을 때 값을 구별해내기 어렵다는 단점을 가지고 있다.

import numpy as np

x2 = np.array([3, 4])
np.dot(x2, x2) # 25

dot으로 x2의 제곱 L2노름값을 간단하게 구할 수 있다. 제곱근이 없으므로 $ 3^2 + 4^2 = 25 $으로 계산된다.

 

MAX Norm: 각 요소를 절대값으로 하여 가장 큰 값을 구하는 노름이다. 수식으로는 $ ||\mathbf{x}||_\infty = \displaystyle \max_{i}|x_i| $ 로 표시한다.

import numpy as np

x1 = np.array([5, 2, 6])

x1_max_norm = np.max(np.abs(x1)) # 최대값

print("Max Norm: ", x1_max_norm) # 6

numpy의 max메서드로 절대값으로 이뤄진 벡터의 최대값을 구하면 max norm 값이 나온다.

 

Vector

Unit Vector: 벡터의 크기가 1인 벡터를 단위 벡터라 한다. 단위 벡터가 아닌 벡터를 단위 벡터로 만드려면 벡터의 크기로 각 요소를 나누면 된다. 쉽게 말하면 L2노름이 1이 되야한다.

# Unit Vector
import numpy as np

x = np.array([3, 4])
x_norm = np.linalg.norm(x, ord=2) # L2 Norm
print("L2 Norm: ", x_norm)

unit_vector = np.divide(x, x_norm) # 벡터 크기로 나눠 단위 벡터 생성
unit_vector_norm = np.linalg.norm(unit_vector, ord=2) # 단위 벡터의 L2 Norm
print("Unit Vector: ", unit_vector)
print("Unit Vector Norm: ", unit_vector_norm)

"""
L2 Norm:  5.0
Unit Vector:  [0.6 0.8]
Unit Vector Norm:  1.0
"""

 

Basis Vector: 간단하게 보면 x축 y축 등에 대한 단위 벡터를 의미한다.

위 이미지를 보면 x축 (1, 0), y축 (0, 1)이 뻗어있다. 이 축의 단위 벡터가 바로 기저 벡터다.

 

Orthogonal Vector: 두 벡터 사이의 각도가 90도 즉, 직교를 이루는 벡터를 직교 벡터라 한다.

수학적으로 보면 두 벡터의 내적값이 0이여야 직교 벡터이다. 내적이란 벡터를 수처럼 곱하는 것을 의미한다.

# Orthogonal Vector
import numpy as np

a = np.array([2, -1])
b = np.array([1, 2])

orthogonal_vector = np.dot(a, b)
print("Orthogonal Vector: ", orthogonal_vector) # 0

numpy의 dot()메서드로 내적값을 구할 수 있다. 위와같이 a와 b벡터의 내적값이 0이면 직교 벡터라 한다.

 


https://colab.research.google.com/drive/1T1Xox2zERElQnYz2VwWAtCCWTOt-b8Z0?usp=sharing

 

기초 선형대수학.ipynb

Colaboratory notebook

colab.research.google.com

 

728x90
반응형
Comments