내용
import tensorflow as tf from tensorflow import keras import numpy as np import pandas as pd from scipy import stats from sklearn import preprocessing import matplotlib.pyplot as plt
텐서(Tensor)
텐서란?
텐서는 숫자로 구성된 데이터의 컨테이너입니다. 즉, 숫자를 담는 그릇입니다. 가장 많이 접할 수 있는 2D 텐서인 행렬이 텐서의 예입니다. 결과적으로 텐서는 행렬을 임의의 수의 차원으로 일반화한 것으로 텐서의 맥락에서 차원(dimension)은 종종 축(axis)이라고 할 수 있습니다.
tensorflow에서 텐서는 tf.constant()
함수에 의해 생성됩니다. 생성된 텐서의 값은 동일한 객체에서 변경, 수정 될 수 없습니다.
생성된 텐서의 타입은 tf.dtypes.DType
에서 확인 할 수 있습니다.
- 스칼라는 0차원 텐서이므로 축은 없습니다.(rank 0)
- 벡터는 1차원 텐서로서 1개의 축을 가집니다.(rank 1)
- 행렬은 2차원 텐서이며 2개 축을 가집니다.(rank 2)
- 3차원 텐서는 3개의 축을 가집니다.(rank 3)
- 계속 확장할 수 있습니다.
rank0=tf.constant(4) rank0
<tf.Tensor: shape=(), dtype=int32, numpy=4 >
rank1=tf.constant([2,3]) rank1
<tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 3], dtype=int32) >
rank2=tf.constant([[1,2],[3,4],[5,6]]) rank2
<tf.Tensor: shape=(3, 2), dtype=int32, numpy= array([[1, 2], [3, 4], [5, 6]], dtype=int32) >
rank3 = tf.constant([[[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],[25, 26, 27, 28, 29]],]) rank3
<tf.Tensor: shape=(3, 2, 5), dtype=int32, numpy= array([[[ 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], [25, 26, 27, 28, 29]]], dtype=int32) >
주요속성
- 축(axis)
- 텐서의 특정 차원
- 축 수(순위, rank)
- 예를 들어 3D 텐서에는 3개의 축이 있고 행렬에는 2개의 축이 있습니다.
tf.rank()
속성으로 확인- 모양(shape)
- 텐서가 각 축을 따라 얼마나 많은 차원을 가지고 있는지를 설명하는 정수 튜플입니다.
-
shape
속성으로 확인 - 데이터 유형(일반적으로 Python 라이브러리에서는 dtype이라고 함)
- 텐서에 포함된 데이터 유형입니다.
- float32 , uint8, float64 등이 될 수 있습니다. 드문 경우지만 char 텐서를 볼 수 있습니다.
- 문자열 텐서는 Numpy(또는 대부분의 다른 라이브러리)에 존재하지 않습니다. 왜냐하면 텐서는 사전 할당된 연속 메모리 세그먼트에 존재하는 것으로 문자열은 가변 길이이므로 메모리 할당을 사전에 지정할 수 없으므로 이 구현을 사용할 수 없습니다.
- 데이터 총수
tf.size()
로 확인 할 수 있습니다.
a=tf.zeros([2,2,5,5]) a
<tf.Tensor: shape=(2, 2, 5, 5), dtype=float32, numpy= array([[[[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]], [[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]]], [[[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]], [[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]]]], dtype=float32) >
pd.DataFrame([a.dtype, tf.rank(a).numpy(), a.shape, tf.size(a).numpy()],index=['type','rank','shape','total'])
0 | |
---|---|
type | <dtype: 'float32'> |
rank | 4 |
shape | (2, 2, 5, 5) |
total | 100 |
텐서 a는 다음 그림과 같이 나타낼 수 있습니다.
위 객체 a의 각 축의 의미는 다음과 같습니다. 이러한 축 배열의 의미는 텐서 인덱스의 일반적인 순서입니다.
Batch | Width | Height | Features | |
---|---|---|---|---|
a의 축 인덱스 | 2 | 2 | 5 | 5 |
위와 같이 축의 인덱스는 전역에서 로컬로 정렬되는 경우입니다. 배치 축이 먼저 오고 그 다음에 공간 차원과 각 위치의 특성이 마지막에 옵니다.
딥러닝 등에서 필수적인 dataset인 MNIST의 데이터를 예로 소개하면 다음과 같습니다.
keras.datasets으로부터 호출한 이 데이터는 특성(feature, 설명변수)와 라벨(label, 반응변수)로 구분되어 있으며 이를 다시 학습데이터(training data)와 검증 데이터(test data)로 구분되어 있습니다.
다음 코드에서 feature와 label을 나타냅니다.
- xtr: training data - training feature
- ytr: training data - training label
- xte: test data - test feature
- yte: test data - test label
from tensorflow.keras.datasets import mnist (xtr, ytr), (xte, yte) = mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11493376/11490434 [==============================] - 0s 0us/step 11501568/11490434 [==============================] - 0s 0us/step
print(f'축의 수 :{xtr.ndim}, 형태: {xtr.shape}, 자료형: {xtr.dtype}')
축의 수 :3, 형태: (60000, 28, 28), 자료형: uint8
여기에 있는 것은 8비트 정수의 3D 텐서입니다. 보다 정확하게는 28 × 8 정수로 구성된 60,000개(batch) 행렬의 배열입니다. 이러한 각 행렬은 0에서 255 사이의 계수를 갖는 회색조 이미지입니다. 위 데이터 셋의 4번째 데이터를 나타내면 다음과 같습니다.
img=xtr[4] plt.figure(dpi=50) plt.imshow(img, cmap=plt.cm.binary) plt.show()![png](tf_tensor_files/tf_tensor_13_0.png)
Numpy에서 텐서 조작
객체에서 특정 요소를 선택하는 것을 슬라이싱(slicing)이라고 합니다. 슬라이싱은 각 축의 부분을 지정합니다. 즉, 인덱스 선택은 대괄호와 콜론을 사용하여 다음과 같이 일반화 할 수 있습니다.
- 객체[시작인덱스:(마지막 인덱스+1):간격]
- 인덱스는 0을 포함하는 양의 정수로서 0부터 시작
- 마지막은 포함되지 않음
- 선택요소가 한 개일 경우는 정수만을 입력
- 객체[:]와 같이 수치 없이 콜론만을 입력하는 경우 그 축에 대해 객체의 모든 요소를 선택
- 예를 들어 위 객체 xtr에서 1축의 2번째와 나머지 축의 모든 차원을 선택한다면 다음과 같습니다.
xtr[1] = xtr[1,:,:] = xtr[1, 0:28, 0:28] - 음의 정수일 경우는 객체의 역순을 의미 즉, 객체[-1]은 객체의 마지막 요소를 의미
10~100번째 부분을 선택한다면 다음과 같습니다.
x1=xtr[10:101] x1.shape
(91, 28, 28)
x1=xtr[10:101, :,:] x1.shape
(91, 28, 28)
x1=xtr[10:101, 0:28, 0:28] x1.shape
(91, 28, 28)
ytr[len(ytr)-1] # 훈련데이터의 마지막 라벨
8
ytr[-1]
8
np.array()
또는 tensor객체.numpy()
메서드를 사용하여 텐서를 numpy 배열로 변환할 수 있습니다.
x=tf.constant([[1,2],[3,4],[5,6]], dtype=tf.float16) x
<tf.Tensor: shape=(3, 2), dtype=float16, numpy= array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16) >
x.numpy()
array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16)
np.array(x)
array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16)
차원의 변경
객체의 형태 변경을 위해서는 tf.reshape()
함수를 적용합니다.
다음 코드에서 열 차원으로 -1을 전달하였습니다. 이는 지정한 다른 차원을 기준으로 자동으로 정렬한다는 의미입니다. 다른 차원을 지정하지 않고 -1만을 전달하는 경우 참고할 축이 없으므로 1차원 텐서로 변경됩니다.
x=tf.constant([1, 2, 3]) x.shape
TensorShape([3])
tf.reshape(x, [3,1])
<tf.Tensor: shape=(3, 1), dtype=int32, numpy= array([[1], [2], [3]], dtype=int32) >
x1=tf.reshape(x, [-1,1]) x1
< tf.Tensor: shape=(3, 1), dtype=int32, numpy= array([[1], [2], [3]], dtype=int32) >
tf.reshape(x1, [-1])
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 2, 3], dtype=int32) >
다음은 3차원 텐서를 2차원 텐서로 변경한 경우입니다.
y=np.random.randint(1, 20, size=(2, 2, 3)) y=tf.constant(y) y
<tf.Tensor: shape=(2, 2, 3), dtype=int64, numpy= array([[[13, 18, 9], [ 1, 5, 12]], [[ 5, 8, 17], [ 8, 12, 2]]])>
tf.reshape(y, [-1, 2,6])
< tf.Tensor: shape=(1, 2, 6), dtype=int64, numpy= array([[[13, 18, 9, 1, 5, 12], [ 5, 8, 17, 8, 12, 2]]])>
tf.reshape(y, [2,6])
<tf.Tensor: shape=(2, 6), dtype=int64, numpy= array([[13, 18, 9, 1, 5, 12], [ 5, 8, 17, 8, 12, 2]])>
위 객체 y의 차원은 2 × 2 × 3 으로 위와 같이 차원 인덱스 [0] 을 조정함으로서 2차원으로 축소할 수 있습니다. 또한 다음과 같은 방법을 적용할 수 있습니다. (2×2)×3 또는 (2×2×3)와 같이 인수로 전달 함으로서 차원을 축소할 수 있습니다.
tf.reshape(y, [2*2, 3])
< tf.Tensor: shape=(4, 3), dtype=int64, numpy= array([[13, 18, 9], [ 1, 5, 12], [ 5, 8, 17], [ 8, 12, 2]])>
tf.reshape(y, [2*2*3])
< tf.Tensor: shape=(12,), dtype=int64, numpy=array([13, 18, 9, 1, 5, 12, 5, 8, 17, 8, 12, 2])>
tf.reshape(y, [-1])
<tf.Tensor: shape=(12,), dtype=int64, numpy=array([13, 18, 9, 1, 5, 12, 5, 8, 17, 8, 12, 2]) >
텐서의 형태 변경을 위해 tf.reshape()
을 적용하는 경우 변경 전과 후의 총 요소 수는 같아야 합니다. 원시(raw) 객체의 각 축의 수를 보존하면서 축을 교환하기 위해 tf.transpose()
함수를 적용합니다.
- tf.transpose(x, perm=[])
- 축의 순서를 변경하기 위해 인수를 perm에 전달합니다.
- 예를 들어 2 × 2 × 3는 축인덱스 0, 1, 2에 대응하는 차원이 각각 2, 2, 3임을 의미합니다. 이 객체를 perm=(1,2,0) 지정하여 transpose를 적용하는 경우 차원은 2 × 3 × 2로 전환됩니다.
tf.transpose(y, [1,2,0])
<tf.Tensor: shape=(2, 3, 2), dtype=int64, numpy= array([[[13, 5], [18, 8], [ 9, 17]], [[ 1, 8], [ 5, 12], [12, 2]]]) >
tf.transpose(y, [1, 0, 2])
< tf.Tensor: shape=(2, 2, 3), dtype=int64, numpy= array([[[13, 18, 9], [ 5, 8, 17]], [[ 1, 5, 12], [ 8, 12, 2]]]) >
데이터 배치의 개념 (The notion of data batches)
일반적으로 딥 러닝에서 접하게 되는 모든 데이터 텐서의 첫 번째 축(인덱싱이 0에서 시작하기 때문에 축 0)은 샘플 축(샘플 차원)이 됩니다. MNIST 예에서 샘플은 숫자 이미지입니다. 또한 딥 러닝 모델은 전체 데이터 세트를 한 번에 처리하지 않습니다. 오히려 데이터를 작은 배치(batch)로 나눕니다. 구체적으로 다음은 배치 크기가 128인 MNIST 숫자의 배치입니다.
batch=xtr[:128] batch.shape
(128, 28, 28)
배치 텐서를 고려할 때 첫 번째 축(축 0)을 기준으로 구분합니다. 이러한 축을 배치(batch) 축 또는 배치 차원이라고 합니다. 이것은 Keras 및 기타 딥 러닝 라이브러리를 사용할 때 자주 접하게 되는 용어입니다.
실제로 조작할 데이터는 다음의 범주 중에 하나일 것입니다. 다음에서 samples는 features(변수들)이 모두 포함되어 있는 인스턴스로서 batch로 고려할 수 있습니다. 예를 들어 2차원 텐스 즉, 행렬의 경우 하나의 행이 sample이며 batch가 됩니다.
- 벡터 데이터 - 모양의 2D 텐서(samples, features)
- 시계열 데이터 또는 시퀀스 데이터 - 모양의 3D 텐서(samples, timesteps, features)
- 이미지 - 4D 텐서의 모양 (samples, height, width, channels) 또는 (samples, channels, height, width)
- 비디오 — 5D 텐서의 (samples, frames, height, width, channels) 또는 (samples, frames, channels, height, width)
댓글
댓글 쓰기