1. 텐서(Tensor)

  • 일반적으로 텐서는 3차원 이상을 다룰 때 표현하는 방식이지만, 여기서는 어떠한 데이터를 표현할 때, 그 값 모두를 텐서라고 부르기로 함
  • 아래에서 a, b, c, d 모두 텐서라고 지칭할 수 있

  • 랭크(rank): 텐서의 축을 나타내고, 넘파이(numpy)의 ndim(number of dimension, 차원의 수)이라는 속성값으로 구할 수 있음
  • 대괄호의 개수가 곧 랭크(축)의 값

  • 크기(shape): 텐서의 각 축을 따라 얼마나 많은 차원이 있는지를 나타내며, 파이썬의 튜플 형태

 

  - 스칼라(0차원 텐서)

  • 하나의 숫자를 담고 있는 텐서
  • 형상은 없음
x = np.array(3)
print(x)
print(x.shape)
print(np.ndim(x))

# 출력 결과
3
()
0

 

  - 벡터(1차원 텐서)

  • 숫자의 배열을 나타내는 텐서
x = np.array([1, 2, 3, 4])
print(x)
print(x.shape)
print(np.ndim(x))

# 출력 결과
[1 2 3 4]
(4,)
1

 

  - 벡터의 합

  • 같은 형상(shape)일 때, 각 원소별로 계산
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
c = a + b
print(a)
print(b)
print(c)
print(c.shape)
print(np.ndim(c))

# 출력 결과
[1 2 3 4]
[5 6 7 8]
[ 6  8 10 12]
(4,)
1

 

  - 벡터의 곱

  • \(A=(x_{1}, x_{2}, x_{3}, \cdots, x_{n})\)
    \(B=(y_{1}, y_{2}, y_{3}, \cdots, y_{n})\) 일 때,
  • 원소곱
    • 같은 형상(shape)일 때, 각 원소별로 계산
      \(A\times B=(x_{1}, x_{2}, x_{3}, \cdots, x_{n})\times (y_{1}, y_{2}, y_{3}, \cdots, y_{n})\)
      \(b\;\qquad=(x_{1}y_{1}, x_{2}y_{2}, x_{3}y_{3}, \cdots, x_{n}y_{n})\)
  • 벡터곱(product, dot)
    • 두 1차원 벡터가 있을 때 각각의 성분끼리의 곱을 모두 더하는 계산

$$ A\bullet B\Rightarrow A \times B^{T}=(x_{1}, x_{2}, x_{3}, \cdots x_{n})\begin{pmatrix}
y_{1} \\
y_{2}  \\
y_{3}  \\
\vdots \\
y_{n}  \\
\end{pmatrix} \\=(x_{1}y_{1} + x_{2}y_{2} + x_{3}y_{3} + \cdots + x_{n}y_{n}$$

# 원소곱
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
c = a*b
print(c)
print(c.shape)
print(np.ndim(c))
print("-------------------------")
# 벡터곱
x = np.array([1, 2, 0])
y = np.array([0, 2, 1])
z = np.dot(x, y)
print(z)
print(z.shape)
print(np.ndim(z))

# 출력 결과
[ 5 12 21 32]
(4,)
1
-------------------------
4
()
0

 

  - 스칼라와 벡터의 곱

# 스칼라
a = np.array(10)
# 벡터
b = np.array([1, 2, 3])
print(a*b)

# 출력 결과
[10 20 30]

 

 

2. 2차원 텐서(행렬)

  • 2차원 텐서는 행렬로 생각할 수 있음
    • (m\(\times\)n)형상의 배열

matrix = np.array([[1, 2, 3],
                   [4, 5, 6]])
print(matrix)
print(matrix.shape)
print(np.ndim(matrix))
print("-------------------------")
matrix2 = np.array([[1, 2, 3, 4]])
print(matrix2)
print(matrix2.shape)
print(np.ndim(matrix2))

# 출력 결과
[[1 2 3]
 [4 5 6]]
(2, 3)
2
-------------------------
[[1 2 3 4]]
(1, 4)
2

 

  - 행렬 원소곱

  • 같은 형상(shape)일 때 덧셈, 곱셈과 같은 연산은 원소별로 진행
A = np.array([[1, 2], [3, 4]])
B = np.array([[10, 10], [10, 10]])
print("행렬 A\n", A)
print("행렬 B\n", B)
print("A * B\n", A*B)

# 출력 결과
행렬 A
 [[1 2]
 [3 4]]
행렬 B
 [[10 10]
 [10 10]]
A * B
 [[10 20]
 [30 40]]

 

  - 행렬 점곱(내적, product)

  • 1차원 벡터와 마찬가지로 앞 행렬과 뒤 행렬의 수가 같아야함
# 2*2 행렬
M = np.array([[1, 2], [3, 4]])
# 2*3 행렬
N = np.array([[2, 3, 4], [2, 3, 4]])
# 앞 행렬의 열과 뒤 행렬의 행이 같아 행렬 점곱이 가능
print("행렬 M\n", M)
print("행렬 N\n", N)
L = np.dot(M, N)
print("행렬 L\n", L)
print(L.shape)
print(np.ndim(L))

# 출력 결과
행렬 M
 [[1 2]
 [3 4]]
행렬 N
 [[2 3 4]
 [2 3 4]]
행렬 L
 [[ 6  9 12]
 [14 21 28]]
(2, 3)
2
# 3*1 행렬
m = np.array([[1], [2], [3]])
# 3*1행렬
n = np.array([[1], [2], [3]])
# 앞 행렬의 열과 뒤 행렬의 행이 달라 행렬 점곱이 불가능
l = np.dot(m, n)

print(l)
print(l.shape)
print(np.ndim(l))

# 출력 결과
ValueError: shapes (3,1) and (3,1) not aligned: 1 (dim 1) != 3 (dim 0)

 

  - 역행렬

  • 어떤 행렬 A가 있을 때, 곱해서 단위행렬(E)를 만드는 행렬 B가 존재한다면, 행렬 B는 A의 역행렬
A = np.array([[1, 2], [3, 4]])
B = np.linalg.inv(A)
print(A)
print(B)
print(np.dot(A, B))

# 출력 결과
[[1 2]
 [3 4]]
[[-2.   1. ]
 [ 1.5 -0.5]]
 # A 행렬과 A 행렬의 역행렬을 곱하여 단위행렬이 나옴
[[1.00000000e+00 1.11022302e-16]
 [0.00000000e+00 1.00000000e+00]]

 

  - 전치행렬

  • 행과 열을 바꾼 배열의 형태

A = np.array([[1, 2, 3], [4, 5, 6]])
print("A\n", A)
print("A.shape\n", A.shape)
print("-------------------------")
A_ = A.T
print("A의 전치행렬\n", A_)
print("(A.T).shape\n", A_.shape)

# 출력 결과
A
 [[1 2 3]
 [4 5 6]]
A.shape
 (2, 3)
-------------------------
A의 전치행렬
 [[1 4]
 [2 5]
 [3 6]]
(A.T).shape
 (3, 2)

 

 

3. 3차원 텐서

  • 보통 이미지를 나타낼 때 사용되는 텐서
    • (width, height, channels)
    • 일반적으로 Numpy Array로 표현

red channel, blue channel, green channel에 있는 값들이 모여 고양이 이미지가 됨

  • 시계열 데이터 또는 시퀀스(sequence) 데이터를 표현할 때도 사용
    • (samples, timesteps, features)
    • (예시) 주식 가격 데이터셋, 시간에 따른 질병 발병 건수
X = np.array([[[5, 3, 2, 1],
               [5, 5, 3, 1],
               [6, 1, 2, 3]],
              [[1, 1, 1, 1],
               [3, 4, 7, 5],
               [1, 8, 3, 4]],
               [[10, 9, 3, 9],
                [5, 4, 3, 2],
                [7, 6, 3, 4]]])

print("X\n", X, end = '\n\n')
print("X.shape:", X.shape)
print("X.ndim:", X.ndim)

# 출력 결과
X
 [[[ 5  3  2  1]
  [ 5  5  3  1]
  [ 6  1  2  3]]

 [[ 1  1  1  1]
  [ 3  4  7  5]
  [ 1  8  3  4]]

 [[10  9  3  9]
  [ 5  4  3  2]
  [ 7  6  3  4]]]

X.shape: (3, 3, 4)
X.ndim: 3
B = np.array([[[2, 3, 4],[2, 3, 4]],
              [[1, 1, 1], [1, 1, 1]]])

print("행렬 B\n", B, end = '\n\n')
print("B의 전치행렬\n", B.T)

# 출력 결과
행렬 B
 [[[2 3 4]
  [2 3 4]]

 [[1 1 1]
  [1 1 1]]]

B의 전치행렬
 [[[2 1]
  [2 1]]

 [[3 1]
  [3 1]]

 [[4 1]
  [4 1]]]

 

  - 3차원 텐서 활용 예시(이미지)

  • MNIST Dataset
  • 28\(\times\)28 사이즈의 gray scale 이미지들로 구성
  • gray scale: 0~255의 값을 통해 밝기를 포현, 0으로 갈수록 어두워지고, 255로 갈수록 밝아짐
# %pip install keras
# %pip install tensorflow
from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.ndim)
print(train_images.shape)
print(train_images.dtype)

# 출력 결과
3
(60000, 28, 28)
uint8
temp_image = train_images[3]
plt.imshow(temp_image, cmap = 'gray')
plt.show()

 

 

4. 브로드캐스팅(broadcasting)

  • 넘파이에서 다른 형상(shape)끼리 계산 가능

  • 더 작은 형상(shape)이 형상이 더 큰 배열에 확장이 가능해야함

  • (참고) 아래의 경우도 가능

# 1차원 텐서
a = np.array(10)
b = np.array([10, 20, 30])
print(np.dot(a, b))
print(a*b)

# 서로 shape이 다르지만 브로드캐스팅을 통해 계산이 가능하도록 만들어짐
# 출력 결과
[100 200 300]
[100 200 300]
# 2차원 텐서
A = np.array([[1, 2], [3, 4]])
B = np.array([10, 20])
print("행렬 A\n", A)
print("행렬 B\n", B)
print("A * B\n", A*B)

# 출력 결과
행렬 A
 [[1 2]
 [3 4]]
행렬 B
 [10 20]
A * B
 [[10 40]
 [30 80]]
# 3차원 텐서
A = np.array([[[1, 1, 1],
               [2, 2, 2]],
               [[3, 3, 3],
                [4, 4, 4]]])
B = np.array([[10, 10, 10]])

print("행렬 A\n", A)
print("A.shape:", A.shape)
print("행렬 B\n", B)
print("B.shape:", B.shape)
print("A * B\n", A*B)

# 출력 결과
행렬 A
 [[[1 1 1]
  [2 2 2]]

 [[3 3 3]
  [4 4 4]]]
A.shape: (2, 2, 3)
행렬 B
 [[10 10 10]]
B.shape: (1, 3)
A * B
 [[[10 10 10]
  [20 20 20]]

 [[30 30 30]
  [40 40 40]]]
# 브로드캐스팅 조건에 맞지 않는 경우
# 2*3 행렬
A = np.array([[1, 2, 3], [4, 5, 6]])
# 1*2 행렬
B = np.array([10, 10])
print(A*B)

# 출력 결과
ValueError: operands could not be broadcast together with shapes (2,3) (2,)

 

 

5. 4, 5차원 텐서

  • Color Image Datasets(4차원)
    • (samples, heigth, width, channels) (Keras, Tensorflow)
    • (samples, channels, height, width) (Pytorch)
  • 동영상(5차원)
    • (samples, frames, heigth, width, channels) (Keras, Tensorflow)
    • (samples, frames, channels, height, width) (Pytorch)
    • 예시 1) (4, 300, 1920, 1080, 3) → 1920×1080 사이즈 3채널의 300프레임 수를 가진 배치가 4

 

 

6. 텐서 크기 변환

  • reshape으로 텐서의 크기 변환 가능
  • 변환 전의 원소의 개수와 변환 이후의 텐서의 개수가 같아야 함
A = np.array([[1, 2, 3], [4, 5, 6]])
print("행렬 A\n", A)
print("A.shape:", A.shape)
print("-------------------------")
# 그냥 1차원의 배열로 쭉 나열
A = A.reshape(6)
print("행렬 A\n", A)
print("A.shape:", A.shape)

# 출력 결과
행렬 A
 [[1 2 3]
 [4 5 6]]
A.shape: (2, 3)
-------------------------
행렬 A
 [1 2 3 4 5 6]
A.shape: (6,)
B = np.array([[[2, 3, 4], [2, 3, 4]],
              [[1, 1, 1], [1, 1, 1]]])
print("행렬 B\n", B)
print("B.shape:", B.shape)
print("-------------------------")
# 2*2*3 배열에서 3*4 배열로 변환
B = B.reshape(3, 4)
print("행렬 B\n", B)
print("B.shape:", B.shape)

# 출력 결과
행렬 B
 [[[2 3 4]
  [2 3 4]]

 [[1 1 1]
  [1 1 1]]]
B.shape: (2, 2, 3)
-------------------------
행렬 B
 [[2 3 4 2]
 [3 4 1 1]
 [1 1 1 1]]
B.shape: (3, 4)
  • -1을 통해 자동으로 형상을 지정 가능
  • 원소의 개수에 맞게 넘파이가 자동으로 형상을 지정
    • (2, 2, 3) → (3, -1) (o)
                     → (2, 1, 6) (o)
                     → (2, -1, -1) (x)
                     → (2, 5, -1) (x)
B = np.array([[[2, 3, 4], [2, 3, 4]],
              [[1, 1, 1], [1, 1, 1]]])
print("행렬 B\n", B)
print("B.shape:", B.shape)
print("-------------------------")
# 행만 4로 지정하고 열은 넘파이가 자동으로 계산하여 4*3 배열로 변환
B = B.reshape(4, -1)
print("행렬 B\n", B)
print("B.shape:", B.shape)

# 출력 결과
행렬 B
 [[[2 3 4]
  [2 3 4]]

 [[1 1 1]
  [1 1 1]]]
B.shape: (2, 2, 3)
-------------------------
행렬 B
 [[2 3 4]
 [2 3 4]
 [1 1 1]
 [1 1 1]]
B.shape: (4, 3)

+ Recent posts