NumPy나 PyTorch, Tensorflow의 텐서들도
파이썬의 list 또는 tubple 에서의 indexing과 slicing이 거의 그대로 사용됨.
2023.07.12 - [Python] - [Python] list (sequence type) : summary
[Python] list (sequence type) : summary
list는 ordered mutable collection으로, collection을 위한 python data type들 중 가장 많이 사용된다. C에서의 array와 같이 가장 기본적인 collection임. 단, heterogeneous item을 가질 수 있으며, 여러 methods를 가지는
ds31x.tistory.com
단, multi-dimension에서 square bracket(대가로) 안에서 comma로 구분하여 각 축에 대한 indexing과 slicing을 할 수 있다는 차이가 있음.
- square bracket안에서 comma로 구분되는 객체들은 index로 사용되며,
- 해당 객체들의 갯수는 indexing과 slicing의 대상이 되는 source tensor객체의 ndim과 같거나 작아야 함.

1. Simple Indexing and Slicing
1-1. NumPy의 ndarray
- 가장 기본이 되는 indexing 을 지원함.
arr[x,y,z]와arr[x][y][z]둘 다 사용가능함.- 후자를 index chaining이라고 부르기도 함. NumPy등에선 전자의 사용을 권함.
a = np.arange(0,12).reshape(3,4)
print(a)
print('==================')
print(f'a[0] is "{a[0]}"') # 첫번째 행.
print(f'a[0,2] is "{a[0,2]}"') # 2
print(f'a[0][2] is "{a[0][2]}"') # 2
print('------------------')
print(f'a[1,2:] is "{a[1,2:]}"') # slicing의 활용.
print(f'a[1,::2] is "{a[1,::2]}"')
print(f'a[1,::-2] is "{a[1,::-2]}"')
print(f'a[1,::-1] is "{a[1,::-1]}"')
print(f'a[1,3:0:-1] is "{a[1,3:0:-1]}"')
1-2. PyTorch의 tensor
- negative step이 동작하지 않음.
a = np.arange(0,12).reshape(3,4)
a_torch = torch.tensor(a)
print(a_torch)
print(f'a_torch[0] is "{a_torch[0]}"') # 첫번째 행.
print(f'a_torch[0,2] is "{a_torch[0,2]}"') # 2
print(f'a_torch[0][2] is "{a_torch[0][2]}"') # 2
print(f'a_torch[1,2:] is "{a_torch[1,2:]}"')
print(f'a_torch[1,::2] is "{a_torch[1,::2]}"')
# print(f'a_torch[1,::-2] is "{a_torch[1,::-2]}"')
# print(f'a_torch[1,::-1] is "{a_torch[1,::-1]}"')
# print(f'a_torch[1,3:0:-1] is "{a_torch[1,3:0:-1]}"')
- 아래의 3개 statement는 동작하지 않음: negative step
negative step 대신에 역순 처리는 flip 메서드 또는 torch.flip 함수를 이용하면 됨: 새로운 복사본을 만들고 특정 축만 지정하여 사용가능함.
1-3. Tensorflow의 constant (텐서)
- 텐서 인스턴스가 immutable이라는 것 이외에는 numpy와 유사함.
a = np.arange(0,12).reshape(3,4)
a_tf = tf.constant(a)
print(a_tf)
print(f'a_tf[0] is "{a_tf[0]}"') # 첫번째 행.
print(f'a_tf[0,2] is "{a_tf[0,2]}"') # 2
print(f'a_tf[0][2] is "{a_tf[0][2]}"') # 2
print(f'a_tf[1,2:] is "{a_tf[1,2:]}"')
print(f'a_tf[1,::2] is "{a_tf[1,::2]}"')
# print(f'a_tf[1,::-2] is "{a_tf[1,::-2]}"')
# print(f'a_tf[1,::-1] is "{a_tf[1,::-1]}"')
# print(f'a_tf[1,3:0:-1] is "{a_tf[1,3:0:-1]}"')
2. Fancy Indexing ***
단순한 scalar index나 slicing가 달리,
Fancy indexing 은
- index들의 tensor(index tensor)들 을 square bracket 내에 기재하여,
- 여러 elements를 한번에 선택함.
다음 URL을 자세히 읽어볼 것: https://dsaint31.tistory.com/374
[NumPy] Fancy Indexing & Combined Indexing
IndexingNumPy에서 indexing은 4+1 가지 방식을 따름.scalar를 이용한 indexing ( simple indexing ) : array[0]slicingboolean mask : array[array > 1]fancy indexing : vectorized indexing. index들을 element로 가지는 array를 넘겨줌.combined
dsaint31.tistory.com
PyTorch나 TensorFlow에선
Advanced Indexing 또는
Tensor based Indexing 이라는 용어로 불림.
Fancy Indexing은 NumPy에서 주로 사용되는 용어임.
Fancy Indexing에서
index array는 행 or 열 (정확히는 각 axis에서의) index를 나타내는 integer 로 이루어진 "index tensor 인스턴스를 사용"함.
제한점은 square bracket 안에 주어지는 index tensor 객체의 수는
source가 되는 tensor객체의 ndim과 같거나 작은 수 여야 함.
NumPy의 경우 np.array 가 아닌 list를 사용해도 똑같은 동작을 보장하나,
PyTorch에서는 list를 사용할 경우 동작이 차이가 있다.
반드시,
각자의 tensor를 나타내는 클래스의 객체를
index tensor로 사용하길 권한다.
즉,
- NumPy에서는 ndarray 객체를 index로 직접 사용한다
- 유일하게 NumPy만이 Python의 list 객체를 사용해도 완벽하게 동일하게 동작함.
- Fancy Indexing의 경우 index 객체의 shape로 결과 객체의 shape가 결정됨.
- 즉, source가 1d-array라고해도, index로 넘겨진 객체가 2d-array이면, 결과 객체도 index로 넘겨진 객체와 동일한 shape의 2d-array가 됨.
- PyTorch에서는 tensor 객체를 index로 직접 사용하는게 좋다
- NumPy의 ndarray객체도 잘 동작하나,
- Python의 list객체를 사용할 경우, nested list 객체에서 동작이 다르다.
- integer index 값만으로 구성된 list (= list의 item이 int로 구성됨,1차원)는 같은 값을 가지는 tensor 객체의 경우와 같은 동작을 보이지만, nested list는 그렇지 않음 (axis-0가 unpacking되어 들어가는 동작을 PyTorch에선 보임).
tensorflow의 경우,
tf.gather 와 tf.gather_nd 를 통해
fancy indexing 와 유사한 기능을 제공하나
직접적으로 fancy indexing을 지원하지 않음.
2-1. 제한사항과 동작 방식
- 사용되는 index tensor의 개수는 원본 tensor의 차원 수(number of dimensions, ndim)보다 많으면 동작하지 않음.
- 예시: "3차원 원본 tensor"에 "index tensor 2개를 사용"하는 경우
- index tensor들이 2개이므로 원본 텐서의 첫 2 차원(dimension-0, dimension-1)을 결정함
- 남은 차원(dimension-2)은 결과 텐서에 그대로 유지됨.
- 예시: "3차원 원본 tensor"에 "index tensor 2개를 사용"하는 경우
- 구체적 예: 원본 텐서가 2×3×4이고 크기가 3×2인 인덱스 텐서 2개를 사용할 경우
- index tensor가 2개이므로 원본의 첫 2개 차원을 결정.
- 각 위치에 원본의 세 번째 차원에 해당하는 길이 4의 1차원 텐서가 배치됨.
- 결과적으로 3×2×4 크기의 텐서가 생성됨 (2-2를 참고).
예제 코드: 3×5×6인 원본텐서에 대해 2×3×4인 인덱스 텐서 2개를 사용.
import torch
# 원본 텐서: 3 x 5 x 6
x = torch.arange(3 * 5 * 6).reshape(3, 5, 6)
# Fancy indexing에 사용할 index 텐서 2개: shape = (2, 3, 4)
# 예시로 일부 값만 임의 생성
i = torch.tensor([
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[1, 1, 1, 1],
[1, 1, 1, 1],
[2, 2, 2, 2]]
])
j = torch.tensor([
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[1, 1, 1, 1],
[1, 1, 1, 1],
[2, 2, 2, 2]]
])
# Fancy indexing: i, j의 shape = (2,3,4)가 leading axis가 됨
out = x[i, j]
print(out.shape) # torch.Size([2, 3, 4, 6])
print(out) # Fancy indexing 결과
개별 접근한 결과는 다음을 참고:

두번째 에 대한 설명은 다음과 같음:
- out[1,2,1,1] 조회 위치 지정
- 첫 번째 차원 인덱스 1, 두 번째 차원 인덱스 2, 세 번째 차원 인덱스 1, 마지막 차원 인덱스 1 선택 의미
- 해당 값이 원래 x 텐서에서 유도되는 구조
- 사용되는 인덱스가
- i[1,2,1]의 값 2,
- j[1,2,1]의 값 2인 상황
- 원본 텐서에서 참조되는 위치가 x[2, 2, 1]인 구조
- x[2, 2, 1] 계산식이 2 * (5 * 6) + 2 * 6 + 1인 구성
- 계산 결과가 60 + 12 + 1 = 73인 도출
- 사용되는 인덱스가
- 최종적으로 out[1,2,1,1] = 73인 결론
2-2. index tensor와 결과 텐서의 형태(shape)
index tensor는 많은 경우 1차원으로 사용하나, 원하는 출력 텐서 형태로 구성하는 경우도 많음.
- 기본적으로 모든 index tensor는 동일한 shape를 가지며,
이 형태가 결과 텐서의 shape의 앞에 위치하는 axis들 (leadng axes)의 크기를 결정함. - index tensor들의 형태가 다를 경우, 이들 간에 브로드캐스팅(broadcasting)이 이루어져 최종 결과 텐서의 형태가 결정됨
- broadcasting이 안되는 경우엔 동작하지 않음.
https://dsaint31.tistory.com/359
[NumPy] Broadcasting
0. Broadcasting이란?tensor와 scalar를 연산시킬 때 scalar를 상대 tensor와 같은 shape이면서 해당 scalar의 값을 가진 tensor로 변경시키고나서 이 scalar로부터 만들어진 tensor와 상대 tensor를 동작시키는 방식
dsaint31.tistory.com
2-3. index tensor 대신 Python list 를 사용할 경우의 차이점
- 스칼라(scalar)로 구성된 리스트만 이와 같은 값의 indexing tensor와 완전히 동일하게 동작함: non-nested list를 의미.
- 중첩 리스트(nested list)
l을 사용할 경우src_tensor[l]는src_tensor[l[0], l[1], ...]과 같이 동작함: 일종의 unpacking!src_tensor[*l] - 때문에, 첫 번째 차원의 크기가 원본 텐서의 차원 수보다 많으면 동작하지 않음: indexing tensor의 경우엔 첫 번째 차원의 크기가 커도 상관없음.
다음의 예제 코드로 반드시 이해할 것: https://gist.github.com/dsaint31x/a5c5b11fc3e1d75e5a0e4fa51ef03842
dl_fancyindexing_list.ipynb
dl_fancyindexing_list.ipynb. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
다음 예는 1d tensor에서 fancy indexing을 사용하는 것을 보여줌.
x = np.array([10.,20.,30.,40.,50.])
x_torch = torch.tensor([10.,20.,30.,40.,50.])
x_tf = tf.constant([10.,20.,30.,40.,50.])
f_indices = [3, 4, 1]
print('original:')
print(x)
print('----------')
print('numpy:')
print(x[f_indices])
print('----------')
print('torch:')
print(x_torch[f_indices])
print('----------')
print('tensorflow:')
print(tf.gather(x_tf,f_indices)) #1D 에선 gahter, 2D 이상시 gather_nd
print(tf.gather_nd(x_tf, [ i for i in zip(f_indices,)])) # 굳이 쓴다면, 다음과 같이.
- tensorflow의 경우, fancy indexing을 직접적으로 지원하지 않으며
- tf.gather 와 tf.gather_nd 함수를 통해 같은 동작을 수행할 수 있음.
- f_indices가 source tensor인 x와 같은 ndim을 가지고 있는 간단한 예제임.
- f_indices를 numpy.array 객체나 torch.tensor 객체로 바꾸면, 다른 ndim으로 해도 동작함: 실제 구현에서는 많이 사용되는 방식.
다음 예는 2d tensor에서 fancy indexing을 사용하는 것을 보여줌.
x = np.arange(5*5).reshape(5,5) * 10
x_torch = torch.arange(5*5).view(size=(5,5)) * 10
x_tf = tf.constant(x)
indices_0 = [0, 1, 2]
indices_1 = [0, 1, 2]
print('original:')
print(x)
print('----------')
print('numpy:')
b = x[indices_0, indices_1]
print('b.shape =',b.shape)
print(b)
print('----------')
print('torch:')
c = x_torch[indices_0, indices_1]
print('c.shape =',c.shape)
print(c)
print('----------')
print('tensorflow:')
d = tf.gather_nd(x_tf, [ i for i in zip(indices_0, indices_1)])
print('d.shape =',d.shape)
print(d)
- tensorflow의 경우, fancy indexing을 직접적으로 지원하지 않으며
- tf.gather_nd 함수를 통해 같은 동작을 수행할 수 있음.
다음 예는 3d tensor에서 fancy indexing을 사용하는 것을 보여준다.
각각의 축에서 index array를 접근하려는 elements 에 맞게 설정함
x = np.arange(5*5*5).reshape(5,5,5) * 10
x_torch = torch.arange(5*5*5).view(size=(5,5,5)) * 10
x_tf = tf.constant(x)
indices_0 = [0, 1] # x
indices_1 = [1, 2] # y
indices_2 = [2, 0] # z
print('original:')
print(x)
print('----------')
print('numpy:')
b = x[indices_0, indices_1, indices_2]
print('b.shape=',b.shape)
print(b)
print('----------')
print('torch:')
c = x_torch[indices_0, indices_1, indices_2]
print('c.shape=',c.shape)
print(c)
print('----------')
print('tensorflow')
d = tf.gather_nd(x_tf, [ i for i in zip(indices_0, indices_1, indices_2)]) # multi-dim 에선 gater_nd 임.
print('d.shape=',d.shape)
print(d)
- tensorflow의 경우, fancy indexing을 직접적으로 지원하지 않으며
- tf.gather_nd 함수를 통해 같은 동작을 수행할 수 있음.
3. Boolean Mask **
대상이 되는 tensor와 같은 shape를 가지는 boolean mask의 tensor를 통해 복수의 특정 element를 추출할 수 있음.
- 텐서 인스턴스에 비교 연산자를 적용하여 boolean mask를 얻을 수 있음 (다음 예에서 b가 boolean mask인 tensor 인스턴스임)
- 해당 텐서 인스턴스에 관계(relational, 비교)연산자으로 구성된 expression(=condition이라고 불림)을
"index가 기재되는 square bracket 안에 넣는 방식"으로의 활용이 많음.
아래 예는 numpy의 ndarray 인스턴스에서의 활용을 보여줌.
x = np.arange(3*3*3).reshape(3,3,3) * 10
print('original:')
print(x)
print('----------')
print('boolean mask:')
b = x <= 270/2
print(b.shape)
print(b)
print('----------')
print('x <= 135')
print(x[b])
print('----------')
print(x[x<=270/2])
print('----------')
print('----------')
print('x <= 135 | x>= 200')
b1 = b | (x >= 200)
print('----------')
print('boolean mask')
print(b1)
print('----------')
print(x[b1])
print('----------')
print(x[ (x<=270/2) | (x>=200)])
- boolean mask를 사용하는 방법은 조건을 만족하는 elements를 찾는데 주로 이용됨..
- 비교 연산자를 텐서에 사용할 경우, 대상 텐서와 같은 shape의 boolean mask 를 얻게 됨.
- boolean mask를 변수에 할당한 후 처리하는 것보다 조건을 square bracket 안에 넣어주는 경우가 더 많음.
- condition을 여러 개 사용할 경우 and, or, not이 아닌 &, |, ~ 를 사용해야함.
위의 경우에서 135 이하 또는 200 이상인 elements를 선택하는 동작을
torch와 tensorflow의 tensor 인스턴스로 수행하는 것을 아래의 예에서 보여줌.
x_torch = torch.arange(3*3*3).view(size=(3,3,3)) * 10
print(x_torch[ (x_torch<=270/2) | (x_torch>=200)])
print('--------------')
x_tf = tf.constant(x)
print(x_tf[ (x_tf<= tf.cast(270/2, tf.int64)) | (x_tf>=200)])
3-1. 특정 조건에 맞는 element의 index 자체를 얻기: np.where
위의 예에서는 조건에 맞는 value에 접근하는 방법을 보여줌.
만약 해당 조건에 맞는 value들이 있는 index를 얻고자 한다면, np.where 를 사용하면 된다.
2024.03.19 - [Python] - [ML] where: numpy 의 idx찾기
[ML] where: numpy 의 idx찾기
np.where 함수numpy에서 ndarray 인스턴스에서 특정 조건을 만족하는 elements의 위치(index, idx)를 찾는 기능을numpy 모듈의 where 함수가 제공해줌.사실 where 함수는 특정 조건에 따라 값을 바꾸어주는 기능
ds31x.tistory.com
위의 예제 코드들을 수행해 본 ipynb 임. (combined indexing을 포함함)
https://gist.github.com/dsaint31x/8dae0cde657c7ffc8bd768f35591cedd
dl_tensor_indexing.ipynb
dl_tensor_indexing.ipynb. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
'Python' 카테고리의 다른 글
| [Tensor] vectorized op. (or universal func) (0) | 2024.03.19 |
|---|---|
| [ML] where: numpy 의 idx찾기 (2) | 2024.03.19 |
| [DL] Tensor: Transpose and Permute (2) | 2024.03.16 |
| [DL] Tensor 객체의 attributes: ndim, shape, dtype (0) | 2024.03.15 |
| [DL] Tensor: dtype 변경(casting) 및 shape 변경. (0) | 2024.03.15 |