본 포스팅은 다음 과정을 정리 한 글입니다.
Custom and Distributed Training with TensorFlow
지난 시간 리뷰
지난 시간에는 텐서가 어떻게 작동하는지 살펴보았습니다.
이제 파이썬의 몇 가지 강력한 함수를 살펴보고 이를 사용하여 텐서를 조작하는 방법에 대해 살펴보겠습니다.
TensorFlow는 두 가지 유형의 코드 실행이 존재합니다.
모든 데이터와 작업이 세션 내에서 실행되기 전에 그래프에 로드되는 그래프 기반 모드
모든 코드가 한 줄씩 실행되는 즉시 실행 모드
이 두 가지를 지원합니다.
이번에는 즉시 실행 모드인 Eager execution만 살펴보겠습니다.
우선, 텐서를 검사하기 전에 텐서를 평가해야 합니다. 이것은 매우 간단하며 다음 몇 개의 슬라이드에서 살펴보겠습니다.
다음 python에서 제공하는 Broadcasting을 살펴보겠습니다.
Broadcasting의 예를 보면, 10x10 행렬의 모든 요소에 1을 추가하려는 경우 반복하여 100번의 작업을 수행하는 대신, 행렬에 1의 배율을 추가하면 나머지는 Broadcasting이 수행하게 됩니다.
세 번째로 Python 연산자가 텐서에서 작동하면 연산자 오버 로딩을 수행할 수 있습니다.
마지막으로 NumPy 호환성이 있으며 이것은 데이터 사이언스 세계에서 매우 중요합니다.
TensorFlow는 NumPy작업과 호환되므로 NumPy 스케일이 유용해야 합니다. 따라서 이들 중 일부를 자세히 살펴보겠습니다.
먼저 텐서 평가를 살펴보겠습니다.
이제 숫자 2로 초기화된 x라는 변수를 만듭니다.
그런 다음 TensorFlow 제곱 연산을 사용하여 이 숫자를 제곱할 수 있습니다.
텐서 객체가 print문과 같은 Python 표현식을 사용할 때마다 텐서가 즉시 계산됩니다. 이것은 텐서 객체가 입력을 받아 계산을 실행한다는 것을 의미합니다.
이 예제에서 x = 2이고 tf.square는 x를 취하고 x_squared라는 변수 안에 저장된 텐서 객체를 반환합니다.
Eager모드를 끄면 텐서 객체와 x 제곱은 이 시점에서 실제로 값 4를 갖지 않습니다. 이 값 4를 얻으려면 실제로 tf.square 함수를 TensorFlow Session내에서 실행하도록 명시적으로 지시해야 합니다.
그러나 Eager모드가 켜져 있고 기본적으로 설정이 되어 있기 때문에 tf.square는 두 개를 가져와 값 4개를 포함하는 텐서 객체를 반환합니다. 따라서 다음 print 문은 eager모드가 켜져 있기 때문에 숫자 4를 표시합니다. 그렇지 않으면 eager 모드가 꺼진 상태에서 print문은 이름, 모양, 데이터 유형 및 나머지와 같은 텐서 객체의 세부 사항을 표시하지만 아직 숫자 4를 값으로 저장하지 않았기 때문에 4를 출력하지 않습니다.
다음은 Broadcast입니다. Broadcast가 왜 유용한지 살펴보겠습니다.
먼저 크기가 2x2인 텐서를 만들어 변수 a에 저장할 수 있습니다.
2x2행렬에 a에 숫자 1인 스칼라(1차원) 값을 더하려고 하면 어떻게 될까요?
TensorFlow는 이 스칼라 1을 자동으로 2x2텐서로 변환하여 a에 저장된 텐서의 차원과 같게 만듭니다.
이 경우 2x2 텐서의 모든 위치는 동일한 값 1을 갖게 됩니다.
그런 다음 tf.add는 이 두 행렬에 대해 각 요소 별로 더하기 연산을 수행합니다.
이를 브로드캐스팅 (Broadcasting)이라고 하며, 다른 차원의 두 텐서를 더하거나 빼는 것은 더 적은 차원의 텐서 차원과 일치하도록 복제되는 방식으로 처리됩니다.
브로드캐스팅은 더 높은 차원으로 확장될 수도 있습니다.
TensorFlow는 일반 Python 연산자 오버로드도 지원합니다.
예를 들어 tf.constant를 이와 같이 1, 2, 3, 4 값을 포함하는 2x2로 선언하면 제곱과 같은 Python 연산자를 사용할 수 있습니다.
텐서의 내용을 제곱하려면 ** 2 인 Python 구문을 사용할 수 있습니다.
그러면 행렬 a의 각 요소를 제곱 한 다음 해당 값을 2x2 텐서에 저장합니다.
또한 텐서는 numpy와 상호운용이 가능하며 그 반대의 경우도 마찬가지입니다.
여기에서 numpy를 사용하여 두 개의 텐서를 곱할 수 있음을 알 수 있습니다.
이것은 numpy 연산이기 때문에 우선 피연산자가 numpy 값으로 평가되고 계산이 수행됩니다.
즉, np.multiply는 ndarrays, 목록 및 스칼라와 같은 값과 같은 배열을 받아들이지만 TensorFlow 객체는 받지 않도록 설계되었습니다. 따라서 먼저 TensorFlow는 텐서 객체 a와 b를 ndarray로 변환 한 다음에 해당 ndarray를 np.multiply 함수에 전달합니다.
여기서 결과 숫자 15는 일반 Python 정수입니다.
또 중요한 것은 numpy 및 numpy 구문과의 상호 운용성입니다.
이것이 얼마나 잘 작동하는지에 대한 예를 보겠습니다.
먼저 numpy함수 np.ones를 사용하여 3x3 배열을 가지는 ndarray를 만들었습니다.
이 결과는 모두 값 1을 가지는 3x3 numpy배열입니다.
이제 tf.multiply를 사용하여 해당 배열에 3을 곱하고 결과를 텐서 객체로 반환할 수 있습니다.
ndarray 데이터 유형에서 텐서 데이터 유형으로 사전 변환할 필요가 없는 것입니다.
TensorFlow는 이 과정을 자동으로 처리합니다. 그리고 그 곱셈의 결과를 출력할 때, 모두 3이라는 값을 가지는 numpy배열이 아닌 3x3 텐서가 생성되었습니다.
또한 tensor.numpy() 함수를 통해 다시 numpy 배열로 변환을 할 수 있습니다.
이제 일부 tf.Variable에 몇 가지 작업을 적용해 보겠습니다.
여기에서 연산자 오버 로딩이 tf.Variable에도 적용됨을 알 수 있습니다.
여기에서 tf.Variable을 사용하여 Tensor를 만들고 값 0을 저장할 수 있습니다.
그런 다음 이 tf.Variable에 정수 1을 더 할 수 있습니다.
TensorFlow는 더하기 연산자로 추가되는 두 항목의 데이터 유형을 확인합니다. 1이 텐서 객체가 아니라는 것을 알기 때문에 값 1을 저장하는 텐서 객체를 만든 다음, 0을 가지는 텐서 객체에 더합니다.
더하기 연산자를 사용하여 0을 가지는 TensorFlow 변수에 숫자 1을 더하면 v + 1의 결과가 값 1을 가지는 텐서 객체가 되는 것을 볼 수 있습니다.
두 번째 예제에서는 텐서 내장 함수인 assign_add를 사용하여 동일한 작업을 수행합니다.
작업이 완료되면 이 시점에서 변수 상태는 'UnreadVariable(읽지 않음)' 이 되는 것을 알 수 있습니다. 약간 혼란스러울 수 있는데요, 읽지 않음 변수 속성은 그래프 모드 실행과의 하위 호환성을 위해 존재합니다.
굳이 자세히 알 필요는 없습니다. 읽지 않음 변수 상태는 TensorFlow에서 변수가 세션에서 실행되었는지 여부를 추적하는 데 사용됩니다.
여기서 알아야 할 중요한 것은 부호 추가를 통해 행을 실행하고 1의 값을 전달하면 1의 값이 즉시 표시되고 numpy가 1의 값을 가지는 것입니다.
numpy = 1.0은 변수에 저장된 값이 1이라는 것이지, numpy 버전이 1이라는 의미가 아닙니다.
v.read_value().numpy()를 호출하여 변수를 가져오면 변수의 값을 읽고 읽지 않음 변수는 변수 ID로 업데이트됩니다.
읽기 값만 호출하면 첫 번째 예제에서와 같이 텐서에서 출력물을 얻을 수 있습니다.
numpy를 추가하면 변수를 평가하고 값 출력을 제공합니다.
TensorFlow 레이어 클래스에서 상속하는 사용자 지정 클래스를 정의하면 이러한 텐서가 저장하는 값을 볼 수 있도록 클래스 변수를 평가할 수도 있습니다.
이 경우 MyLayer 클래스를 정의했습니다.
init 함수에서는 self.my_var 및 self.my_other_var_list 클래스 변수를 정의했습니다.
self.my_var은 값 100을 가지는 텐서 객체입니다.
self.my_other_var_list는 각각 0에서 1까지의 값을 가지는 텐서 객체의 리스트입니다.
MyLayer 유형의 개체를 만들어 변수 m에 저장할 수 있습니다.
MyLayer 객체는 "variables"라는 함수를 상속하므로 클래스의 텐서 객체를 반복할 수 있습니다.
여기에서 m.variables가 리스트 내에서 호출되고 사용되는 것을 볼 수 있습니다.
각 텐서 객체는 100, 0, 1의 값을 가지며 이 변수들이 검색됩니다.
각 텐서 객체에서 .numpy()함수를 호출하여 첫 번째 변수(self.my_var)의 텐서 객체 값인 100, 두 번째 변수(self.my_other_var_list)의 텐서 객체 값인 0, 1이 출력됩니다.
변수를 클래스화 해야하지만 m.variables는 세 번의 반복을 거쳐 세 개의 텐서 객체를 검색합니다. 그 이유는 두 번째 변수가 두 개의 텐서 객체의 목록이기 때문입니다. m.variables 함수는 목록을 풀고 내용을 한 번에 하나씩 검색합니다.
numpy 스타일 캐스팅을 사용하여 데이터 유형을 변경할 수도 있습니다.
예를 들어 1, 2, 3을 포함하는 tf.constant를 만들면 텐서의 데이터 유형이 int32가 됩니다.
그러나 tf.cast를 사용하여 numpy구문으로 dtype을 float32로 지정할 수 있으며 텐서 내의 데이터는 해당 유형이 됩니다.
지금까지 텐서 기본 사항에 대해서 살펴보았습니다.
텐서는 무엇이고 어떻게 작동하는지에 대해 살펴보았습니다.
Python에서 덮어쓰기에 사용되는 데이터 구조가 될 수 있다는 것과 연산자 오버 로딩, 브로드 캐스팅하는 것과 같은 다양한 Python 구조를 사용하는 방법도 살펴보았습니다.
다음 시간에는 기계학습의 몇 가지 수학과 미분 및 기울기와 같은 것이 텐서와 어떻게 작동할 수 있는지 살펴보겠습니다.
이것들은 Optimizer의 기본 기술을 형성합니다. Gradient Tape API를 사용하는 방법을 알아보겠습니다.
감사합니다.
댓글