- 텐서(tensor)란,
수학과 물리, 그리고 딥러닝(머신러닝)에서 자주 쓰이는 개념인데, 간단하게 말하면 다차원 배열입니다.
1 이나 2, 3 과 같은 단일 수치값은 스칼라(Scalar) 라고 부르며,
[0,1,2]
이런 1D-array가 벡터(vector),
[[1,2,3],
[4,5,6]]
이런 2D-array가 행렬(matrix)이라는 이름으로 불립니다.
이러한 것들은 텐서라 부르며, 쉽게 말해 '스칼라의 묶음'입니다.
즉, 스칼라는 0차 텐서,
벡터는 1차 텐서, 행렬은 2차 텐서, 3차원 배열은 3차 텐서, 그 위로 4차 텐서, 5차 텐서... 이렇게 되는 것입니다.
- 이번 포스팅으로는 이러한 텐서의 차원과 축을 이해하는 방식을 정리하려 합니다.
이로인해 딥러닝 혹은 다차원 데이터 분석시 유용하게 사용할 수 있을 것입니다.
- 텐서는 형태(shape)가 존재합니다.
편의를 위해 파이썬 튜플 자료형으로 설명하겠습니다.
[1,2,3]
의 경우는,
행벡터로 부르며, 위 벡터는 1행 3열이죠?
수학적으로 나타내면 1x3...
튜플로는, 그냥 (3,) 입니다.
위치에 대해 알아봅시다.
저기서 원소 2의 위치는 어딜까요?
프로그래밍은 원소 자리가 0부터 시작한다는 것을 가정하면,
2의 위치는 1입니다.
[[1,2,3],
[4,5,6]]
이건 어떨까요?
2행 3열이네요.
2x3, (2,3)...
저기서 원소 3의 위치는 1행의 세번째입니다.
그러니, 프로그램 기준으로는 (0, 2)입니다.
- 위의 경우는 프로그래밍시 매우 자주 이용하고, 어찌보면 실생활에서도 많이 사용하는 형태이기에 별 어려울 것은 없습니다.
이쯤되어 보다 근본적인 내용에 대해 알아보겠습니다.
- 차원이 늘어난다는 것의 의미는 '묶음'입니다.
하나하나 늘려봅시다.
처음, 차원이 없이 원소만이 존재하는 0차원의 경우는,
1, 2, 3
이런식으로 그냥 원소만 존재하는 것이네요.
이때는 원소들이 방향도 없고 위치도 없고, 소속된 차원도 없습니다.
1,2,3이든 2,3,1이든 똑같죠...
여기에 차원을 늘려봅시다.
[1,2,3]
자, 이제 1차원 늘어났네요.
원소들로써는 자신이 속할 공간이 생기고, 그 안에 자신의 위치가 존재하는 것입니다.
[1,2,3]과, [2,3,1]은 서로 다르죠.
(3,) 으로 표현할수 있습니다.
그런데, 넓게 봅시다.
1차원 벡터 역시 하나의 원소로 볼수 있습니다.
[1,2,3], [4,5,6], [7,8,9]...
이러한 벡터가 존재한다고 할때, 이를 치환해봅시다.
[1,2,3] = v1
[4,5,6] = v2
[7,8,9] = v3
...
v1, v2, v3가 존재하는데, 처음 원소의 상태와 같군요.
v1, v2, v3에는 역시나 순서가 존재하지 않습니다.
그러면 할 일은 정해져있죠?
차원을 또 늘려봅시다.
[v1, v2, v3]
자, 여기서 보면 1차원 벡터인데,
치환된 것을 풀어보면,
[[1,2,3], [4,5,6], [7,8,9]]
이렇게 표현되며,
이를 보기 쉽게 나열하면,
[[1,2,3],
[4,5,6],
[7,8,9]]
이렇게 우리가 자주 보는 행렬의 형태가 보입니다.
위 행렬(matrix)의 형태(shape)는,
3행 3열... 3x3, (3,3)
이네요.
또 한번 늘려볼까요?
또 묶어주면 됩니다.
이번엔 치환할 것도 없이, 그냥 복사 붙여넣기를 해봅시다.
[[1,2,3],
[4,5,6],
[7,8,9]]
이 2차원 행렬이,
[[1,2,3],
[4,5,6],
[7,8,9]],
[[1,2,3],
[4,5,6],
[7,8,9]]
이렇게 2개가 존재한다고 했을 때,
이를 또 묶어주면,
[[[1,2,3],
[4,5,6],
[7,8,9]],
[[1,2,3],
[4,5,6],
[7,8,9]]]
이렇게 되는데,
3차 텐서입니다.
그래서 그냥 튜플 단위로 shape를 쓰는 것과 이해하는 것이 중요합니다.
앞으로 위와 같은 원리로 묶어주기만 하면, 100차원도 10000차원도 그다지 어려운 개념은 아닙니다.
어쨌든 위의 3차원 텐서의 형태는
(2,3,3)
입니다.
- 여기서 차원이 늘어났을 때, 형태와 위치를 읽는 법을 쉽게 알아보겠습니다.
'큰단위로 읽어들인다.'
즉, []로 묶인 바깥부터 안쪽으로 원소가 몇개 들어있는지를 읽으시면 됩니다.
[[[1,2,3,10],[4,5,6,11],[7,8,9,12]], [[1,2,3,13],[4,5,6,14],[7,8,9,15]]]
이 텐서에서, 모든 것을 묶어주는 최외곽 []는 그냥 넘어가고,
그 안에 들어있는 큰 원소(여기선 2D-array)가 2개죠?
(2,)
그리고 또 그 안에 들어있는 원소(1D-array)는 3개죠?
(2,3)
또 그 안쪽에 들어있는 원소는 4개죠?
(2,3,4)
자, 이런식으로 하나하나 튜플의 뒤로 갯수를 추가해나가면 됩니다.
- 위의 내용이 이해되셨나요?
처음엔 생소할수 있습니다.
저의 경우엔 위의 팁을 반대로 이해하는데,
(2,3,4)
라는 형태에서, 가장 뒤쪽의 것들부터 이해합니다.
가장 뒤에 것이 텐서의 가장 안쪽의 원소들을 말하고,
형태의 좌측으로 갈수록 그것들을 하나로 묶는다고 생각하시면 그게 더 보기 편할수도 있습니다.
- 자, 마지막으로 축(axis)이라는 것을 이해해봅시다.
축은, 텐서를 계산하거나 함에 있어서 기준이 되는 차원, 즉 어느 묶음으로 계산을 할지를 정하는 것입니다.
0부터 1씩 정수단위로 증가하며,
shape와 묶어서 이해하시면 됩니다.
axis = 0인 경우엔,
shape가 (3,3)인 행렬에서 0번째인 행을 기준으로 묶는다는 의미고,
axis = 1의 경우에는 1번째인 열을 기준으로 묶는다는 의미입니다.
묶는다는 것은 '치환'을 생각해보면 간단합니다.
이쯤되어 간단히 예시를 봅시다.
파이썬 코드로 작성한 것인데, 프로그래밍을 모르시더라도 쉽게 알아봅시다.
[코딩으로 텐서의 축을 이해해봅시다.]
1. 벡터 만들기
먼저, 우리가 사용할 원소들을 묶어놓는 벡터를 먼저 만들것입니다.
aN1 = 2, aN2 = 3, aN3 = 4
vec = np.arange(1, (aN1*aN2*aN3)+1) #1~24 원소가 넣어진 벡터 반환
len(vec)
# 24
이렇게, 0행 24열의 벡터가 만들어지네요.
혹시 모르시는 분들은 np를 파이썬 넘파이 라이브러리라고 이해하세요.
(혹시 그냥 행렬 공부하러 오신 분들이라도, 이참에 파이썬 프로그래밍 공부는 추천합니다. 파이썬은 배우기 쉽고, 개발이 아닌 순수 연구 분야에서도 마치 계산기처럼 사용할수도 있기에 매우 유용하죠.)
위의 코드는, 말 그대로 벡터를 만드는 코드인데,
추후 (2,3,4) 형태로 변경하기 위해, 위와 같이 작성한 것입니다.
np.arange를 하면, 1부터 시작해서 1씩 증가하는 24개의 원소를 지닌 벡터를 만들수 있습니다.
2. 텐서 변경 및 확인
vec #내부 원소 확인
#array([ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24])
t = vec.reshape([aN1,aN2,aN3]) ## 차원 변환(2,3,4)형태로 변환됨. 총 원소 개수와 맞춰주세요.
t
#array(
# [[[1,2,3,4],
# [5,6,7,8],
# [9,10,11,12]],
#
# [[13,14,15,16],
# [17,18,19,20],
# [21,22,23,24]]])
t.ndim ## t의 차원. 여기선 (2,3,4)의 내부 원소가 3개니 3차원이네요.
# 3
원소들의 묶음인 벡터를, reshape해서 (2,3,4) 형태로 만들어줬습니다.
위를 보시면 형태를 알수 있을텐데,
여기서 위에서 정리한 꿀팁을 써서 텐서를 만들어보자면,
먼저
[]
크게 하나의 텐서를 준비한 후,
[[],[]]
그 안에 2개를 추가,
[[[],[],[]], [[],[],[]]]
그리고 또 그 2개의 안에 3개를 추가...
마지막으로, 가장 안쪽에 원소 4개씩을 채워넣어주면 아주 쉽게 텐서를 작성할수 있고, 이런 원리로 쉽게 이해가 가능하죠.
3. sum을 해봅시다.
- sum은 합치는 의미이죠.
텐서에 있어서 합친다는 것은 뭘 의미할까요?
바로 내부 원소를 합친다는 것입니다.
합치는 것도 여러 기준을 둘 수 있는데, 이때 축의 기준에 대해 하나하나 알아봅시다.
3.1 axis를 두지 않았을 때...
t.sum() # 모든 원소의 합
# 300
위와 같습니다.
딱히 축이 없으므로 그냥 내부 원소들을 모두 합치면 됩니다.
참고로,
함수를 사용하지 않고, 1씩 늘어나는 원소들을 그냥 직접 더할 땐,
가우스의 방식을 사용하면 됩니다.
///참고////
가우스가 어렸을 때, 가우스의 지도 교사였던 뷔트너 선생님이 "1~100까지의 숫자들을 모두 더하면 몇이 나올까?"라는 문제를 냈는데, 제일 먼저 종이에 5050이라고 써서 제출했고, 그 이유를 "S = 1 + 2 + 3 + …… + 99 + 100, 이라면 S = 100 + 99 + …... + 3 + 2 + 1, 이다. 이것을 위 아래 같은 항끼리 더하면 2S = 101 + 101 + …… + 101 + 101 이고, 101이 100개 있는 모습이다. 따라서, 2S = 101 * 100; S = 101 * 50 = 5050 이다." 라고 하였다.
///나무위키///
3.2 axis를 0으로 두었을 때,
#array(
# [[[1,2,3,4],
# [5,6,7,8],
# [9,10,11,12]],
#
# [[13,14,15,16],
# [17,18,19,20],
# [21,22,23,24]]])
res0=t.sum(axis=0) ## axis=0 기준 합계
res0.shape
# (3, 4)
res0
# array(
# [14,16,18,20],
# [22,24,26,28],
# [30,32,34,36])
(2,3,4)
에서 가장 첫번째(0번째)를 기준으로 묶어서 계산하는 것을 말합니다.
내부 원소중 최외곽을 하나의 기준으로 묶어서, 그것을 기준으로 서로 더해주고, 다른 구조는 남겨두는 것입니다.
위에서는,
[[1,2,3,4],
[5,6,7,8],
[9,10,11,12]]
와,
[[13,14,15,16],
[17,18,19,20],
[21,22,23,24]]
을 서로 행렬 합을 해준 것입니다.
3.3 axis를 1로 두었을 때,
#array(
# [[[1,2,3,4],
# [5,6,7,8],
# [9,10,11,12]],
#
# [[13,14,15,16],
# [17,18,19,20],
# [21,22,23,24]]])
res1=t.sum(axis=1) ## axis=1 기준 합계
res1.shape
#(2, 4)
res1
#array(
# [[15,18,21,24],
# [51,54,57,60]])
이번에는 (2,3,4) 중에서 3부분을 축으로 원소를 더할 것입니다.
[1,2,3,4]와, [5,6,7,8]과 [9,10,11,12]의 원소를 더하고,
[13,14,15,16]과, [17,18,19,20]과, [21,22,23,24]를 더하시면 됩니다.
3.4 axis를 2로 두었을 때,
#array(
# [[[1,2,3,4],
# [5,6,7,8],
# [9,10,11,12]],
#
# [[13,14,15,16],
# [17,18,19,20],
# [21,22,23,24]]])
res2=t.sum(axis=2) ## axis=2 기준 합계
res2.shape
#(2, 3)
res2
#array(
# [[10,26,42],
# [58,74,90]])
이번에는 가장 안쪽의 원소들을 더하면 됩니다.
1,2,3,4를 더하고,
5,6,7,8을 더하고...
이런식으로 더하는 것이죠.
- 위와 같이 축을 정하는 것은, shape를 기준으로 보면,
어떤 것을 기준으로 묶어서 내부 원소들을 더하는가에 대한 것이고,
결과 shape로 보자면,
해당 부분이 제거되고, 다른 것이 남아있는 구조가 되는 것입니다.
// 이상입니다.
'Data Science' 카테고리의 다른 글
음파 데이터 분석 기본과 딥러닝 소리 분류기 구현 (구 블로그 글 복구) (0) | 2025.04.11 |
---|---|
음파 데이터 이론(Sampling Rate, Bit Depth) (구 블로그 글 복구) (0) | 2025.04.11 |
딥러닝 손실함수 Cross Entropy Error 정리 (구 블로그 글 복구) (0) | 2025.04.10 |
고차원 데이터에서 차원의 저주란? (딥러닝 학습시 데이터 특징을 제한하는 이유와 빅데이터 기술의 중요성) (구 블로그 글 복구) (0) | 2025.04.10 |
딥러닝 Optimizer 종류 정리(구 블로그 글 복구) (1) | 2025.04.09 |