In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

즉시 실행 기초

Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 공식 영문 문서의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 tensorflow/docs 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 docs-ko@tensorflow.org로 메일을 보내주시기 바랍니다.

이 노트북은 텐서플로를 사용하기 위한 입문 튜토리얼입니다. 다음 내용을 다룹니다 :

  • 필요한 패키지 임포트
  • 텐서(Tensor) 생성 및 사용
  • GPU 가속기 사용
  • 데이터 세트

텐서플로 임포트

시작하기 위해서 텐서플로 모듈을 임포트하고 즉시 실행(eager execution)을 활성화합니다. 즉시 실행 활성화로 텐서플로를 조금 더 대화형 프론트엔드(frontend)에 가깝게 만들어 줍니다. 세부사항은 나중에 이야기할 것입니다.


In [ ]:
import tensorflow.compat.v1 as tf

텐서

텐서는 다차원 배열입니다. 넘파이(NumPy) ndarray 객체와 비슷하며, Tensor 객체는 데이터 타입과 크기를 가지고 있습니다. 또한 텐서는 GPU 같은 가속기 메모리에 상주할 수 있습니다. 텐서플로는 텐서를 생성하고 이용하는 풍부한 연산 라이브러리(tf.add, tf.matmul, tf.linalg.inv etc.)를 제공합니다. 이러한 연산자는 자동적으로 순수 파이썬 타입을 변환합니다. 예를 들어:


In [ ]:
print(tf.add(1, 2))
print(tf.add([1, 2], [3, 4]))
print(tf.square(5))
print(tf.reduce_sum([1, 2, 3]))
print(tf.encode_base64("hello world"))

# 연산자의 오버로딩(overloding) 또한 지원합니다.
print(tf.square(2) + tf.square(3))

각각의 텐서는 크기와 데이터 타입을 가지고 있습니다.


In [ ]:
x = tf.matmul([[1]], [[2, 3]])
print(x.shape)
print(x.dtype)

넘파이 배열과 텐서플로 텐서의 가장 확연한 차이는 다음과 같습니다:

  1. 텐서는 가속기 메모리(GPU, TPU와 같은)에서 사용할 수 있습니다.
  2. 텐서는 불변성(immutable)을 가집니다.

넘파이 호환성

텐서와 넘파이 배열 사이의 변환은 다소 간단합니다.

  • 텐서플로 연산은 자동적으로 넘파이 배열을 텐서로 변환합니다.
  • 넘파이 연산은 자동적으로 텐서를 넘파이 배열로 변환합니다.

텐서는 .numpy() 메서드(method)를 호출하여 넘파이 배열로 변환할 수 있습니다. 가능한 경우, 텐서와 배열은 메모리 표현을 공유하기 때문에 이러한 변환은 일반적으로 간단(저렴)합니다. 그러나 텐서는 GPU 메모리에 저장될 수 있고, 넘파이 배열은 항상 호스트 메모리에 저장되므로, 이러한 변환이 항상 가능한 것은 아닙니다. 따라서 GPU에서 호스트 메모리로의 복사가 필요합니다.


In [ ]:
import numpy as np

ndarray = np.ones([3, 3])

print("텐서플로 연산은 자동적으로 넘파이 배열을 텐서로 변환합니다.")
tensor = tf.multiply(ndarray, 42)
print(tensor)


print("그리고 넘파이 연산은 자동적으로 텐서를 넘파이 배열로 변환합니다.")
print(np.add(tensor, 1))

print(".numpy() 메서드는 텐서를 넘파이 배열로 변환합니다.")
print(tensor.numpy())

GPU 가속기

대부분의 텐서플로 연산은 GPU를 사용하여 가속화할 수 있습니다. 어떠한 주석(annotation)도 없이, 텐서플로는 연산을 위해 자동적으로 CPU 또는 GPU를 사용할 것인지를 정합니다(그리고 필요시 텐서를 CPU 와 GPU에 복사합니다.) 연산에 의해 생성된 텐서는 전형적으로 연산이 실행된 장치의 메모리에 의해 실행됩니다. 예를 들어:


In [ ]:
x = tf.random_uniform([3, 3])

print("GPU 사용이 가능한가 : "),
print(tf.test.is_gpu_available())

print("텐서가 GPU #0에 있는가 :  "),
print(x.device.endswith('GPU:0'))

장치 이름

Tensor.device는 텐서를 구성하고 있는 호스트 장치의 풀네임을 제공합니다. 이러한 이름은 프로그램이 실행중인 호스트의 네트워크 주소 및 해당 호스트 내의 장치와 같은 많은 세부 정보를 인코딩하며, 이것은 텐서플로 프로그램의 분산 실행에 필요합니다. 텐서가 호스트의 N번째 GPU에 놓여지면 문자열은 GPU:<N>으로 끝납니다.

명시적 장치 배치

텐서플로에서 "배치(replacement)"라는 용어는 개별 연산을 실행하기 위해 장치에 할당(배치) 하는 것입니다. 앞서 언급했듯이, 명시적 지침이 없을 경우 텐서플로는 연산을 실행하기 위한 장치를 자동으로 결정하고, 필요시 텐서를 장치에 복사합니다. 그러나 텐서플로 연산은 tf.device을 사용하여 특정한 장치에 명시적으로 배치할 수 있습니다. 예를 들어:


In [ ]:
import time

def time_matmul(x):
  start = time.time()
  for loop in range(10):
    tf.matmul(x, x)

  result = time.time()-start
    
  print("10 loops: {:0.2f}ms".format(1000*result))


# CPU에서 강제실행합니다.
print("On CPU:")
with tf.device("CPU:0"):
  x = tf.random_uniform([1000, 1000])
  assert x.device.endswith("CPU:0")
  time_matmul(x)

# GPU #0가 이용가능시 GPU #0에서 강제실행합니다.
if tf.test.is_gpu_available():
  with tf.device("GPU:0"): # 또는 GPU:1, GPU:2
    x = tf.random_uniform([1000, 1000])
    assert x.device.endswith("GPU:0")
    time_matmul(x)

데이터셋

이번 섹션에서는 모델에 데이터를 제공하기 위한 파이프라인을 구축하기 위해 tf.data.Dataset API를 시연해볼 것입니다. 이는 다음을 포함합니다.

  • 데이터셋 생성.
  • 즉시 실행 활성화를 통한 데이터셋 반복

모델을 훈련시키고 평가 루프를 제공할 간단하고 재사용 가능한 모듈로부터, 복잡한 입력 파이프라인을 구축하기위해 데이터셋 API를 사용하기를 권장합니다.

만약 텐서플로 그래프에 익숙하다면 알겠지만, 데이터셋 객체를 생성하기 위한 API는 즉시 실행이 활성화 되어도 동일하게 유지됩니다. 하지만 데이터셋의 요소를 반복하는 프로세스가 약간 더 간단해집니다. 또한 tf.data.Dataset 객체를 통하여 파이썬 반복문을 사용할 수 있으며, 명시적으로 tf.data.Iterator 객체를 생성할 필요가 없습니다. 그 결과, 텐서플로 가이드의 반복자(iterator)에 관한 논의는 즉시 실행이 활성화될 때에는 신경 쓰지 않아도 됩니다.

소스 Dataset 생성

굉장히 유용한 함수중 하나인 Dataset.from_tensors, Dataset.from_tensor_slices와 같은 팩토리(factory) 함수 중 하나를 사용하거나 파일로부터 읽어들이는 객체인 TextLineDataset 또는 TFRecordDataset를 사용하여 소스 dataset을 생성하세요. 더 많은 정보를 위해서 텐서플로 가이드를 참조하세요.


In [ ]:
ds_tensors = tf.data.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6])

# CSV 파일을 생성합니다.
import tempfile
_, filename = tempfile.mkstemp()

with open(filename, 'w') as f:
  f.write("""Line 1
Line 2
Line 3
  """)

ds_file = tf.data.TextLineDataset(filename)

변환 적용

맵(map), 배치(batch), 셔플(shuffle)과 같은 변환 함수를 사용하여 데이터셋의 레코드에 적용하세요. 세부사항은 tf.data.Dataset을 위한 API 문서을 참조하세요.


In [ ]:
ds_tensors = ds_tensors.map(tf.square).shuffle(2).batch(2)

ds_file = ds_file.batch(2)

반복

즉시 실행이 활성화되면 Dataset 객체는 반복이 가능합니다. 만약 텐서플로 그래프에서 데이터셋을 사용하는게 익숙하다면, Dataset.make_one_shot_iterator() 또는 get_next()와 같은 객체를 호출할 필요가 없는다는 것에 주목하세요.


In [ ]:
print('ds_tensors 요소:')
for x in ds_tensors:
  print(x)

print('\nds_file 요소:')
for x in ds_file:
  print(x)