텐서플로우를 임포트하거나, GPU 메모리가 정상적으로 할당되지만 Conv2D 같은 레이어를 사용하는 경우

이런 에러를 있는데, 뜨는 이유는 크게 다음과 같다.


1. CUDA PATH 등록되어 있는지 확인

2. CUDA 있는 파일에 cuDNN 제대로 복붙되었는지 확인
--> 두가지가 아니라고 확신이 들더라도, 2번과 같은 경우에서 전체를 드래그하여 복붙하지말고 폴더에 해당하는 cudnn.h 같은 파일을 일일히 하나씩 드래그 해보세요. 전체 복붙이 문제가 수도 있습니다.

3. CUDA 버전이 NVIDIA 그래픽 카드나 텐서플로우 버전과 호환이 되는지 확인
--> 물론 이게 문제라면 그전에 문제가 뜰 것.

Abstract

Despite the breakthroughs in accuracy and speed of single image super-resolution using faster and deeper convolutional neural networks, one central problem remains largely unsolved: how do we recover the finer texture details when we super-resolve at large upscaling factors? The behavior of optimization-based super-resolution methods is principally driven by the choice of the objective function. Recent work has largely focused on minimizing the mean squared reconstruction error. The resulting estimates have high peak signal-to-noise ratios, but they are often lacking high-frequency details and are perceptually unsatisfying in the sense that they fail to match the fidelity expected at the higher resolution. In this paper, we present SRGAN, a generative adversarial network (GAN) for image super-resolution (SR). To our knowledge, it is the first framework capable of inferring photo-realistic natural images for 4× upscaling factors. To achieve this, we propose a perceptual loss function which consists of an adversarial loss and a content loss. The adversarial loss pushes our solution to the natural image manifold using a discriminator network that is trained to differentiate between the super-resolved images and original photo-realistic images. In addition, we use a content loss motivated by perceptual similarity instead of similarity in pixel space. Our deep residual network is able to recover photo-realistic textures from heavily downsampled images on public benchmarks. An extensive mean-opinion-score (MOS) test shows hugely significant gains in perceptual quality using SRGAN. The MOS scores obtained with SRGAN are closer to those of the original high-resolution images than to those obtained with any state-of-the-art method.


깊은 cnn을 활용하여 SR의 도메인에서 좋은 결과를 만들어내고 있지만, 큰 upscaling factor를 사용할 때 디테일한 부분을 어떻게 잡아낼 것인가에 대한 문제가 해결되지 않고 있다. 

최적화에 기반한 SR은 목적함수가 핵심이다.

최근 연구는 mean squared reconstruction error(per-pixel error같음)을 최소화 시키는 방향으로 나아가고 있다.

이를 통해 얻어진 결과는 노이즈는 잘 잡아내지만 디테일한 부분이 부족하다는 게 매우 만족스럽지 못하다.

이 논문은 GAN을 활용한 SRGAN을 소개한다.

4배의 upscaling factor를 사용하여 진짜 같은 이미지를 만들어내는 것에선 첫 번째 프레임워크이다.

이를 위해 우리는 adversarial, content loss로 우리어진 perceptual loss 함수를 제안한다.

adversarial loss는 진짜 이미지와 sr 이미지의 차이를 학습한 discriminator를 사용하여 자연스러운 이미지를 만들도록 도와준다.

content loss는 픽셀 공간의 유사성이 아닌 feature map(perceltual loss의 특징)의 유사성을 고려한다.

우리가 사용한 깊은 resnet은 다운샘플된 이미지를 진짜 같은 이미지로 복원할 수 있게한다.

SRGAN을 사용함에 따라 MOS에서 유의한 향상을 보였다. SRGAN과 MOS를 사용하면 더 좋은 결과를 만들어낼 수 있다.


요약

  • 이 논문은 진짜 같은 Super-Resolution을 생성하는게 목적이며, 기존에 SR은 여러 장의 이미지를 활용했다는 것에 반해 GAN을 이용하여 한장의 이미지로 결과물을 생성한다.
  • 기존의 per-pixel loss는 perceptual difference를 잡아내지 못하기 때문에, 이 논문 또한 perceptual loss를 활용한다.
  • Per-pixel MSE는 평균을 사용하기 때문에 디테일이 사라지게 되는 원인이다. 하지만 GAN을 사용하면 이를 보완할 수 있다. GAN은 sampling을 활용하기 때문인데, 이때 sampling된 이미지는 우리가 볼때는 괜찮지만 score 측면에서는 떨어지는 결과를 준다.
  • loss를 보면 VGG 네트워크를 사용했다고 나오는데, 이는 GAN 내부에 존재하는 것이 아니라 generator가 뽑아낸 target 값을 단순하게 imagenet weight가 load된 VGG를 사용하여 feature map을 도출하고, 이를 통해 loss를 계산하는 것이다.여기서 VGG는 따로 학습되지 않는다.
  • 마지막으로 밑 그림의 결과 사진을 보면 목도리나 손 같은 부분이 기존 결과보다 더 디테일한 것을 볼 수 있는데, 정답과는 상관이 없기 때문에 점수는 떨어진다.

 

Reference

Ledig, C., Theis, L., Huszár, F., Caballero, J., Cunningham, A., Acosta, A., ... & Shi, W. (2017). Photo-realistic single image super-resolution using a generative adversarial network. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 4681-4690).

 

https://www.youtube.com/watch?v=kVEOCfBy9uY&list=PLQY2H8rRoyvzIuB8rZXs7pfyjiSUs8Vza&index=2


tf.data

이 동영상은 tf.data에 관한 동영상입니다. 본 영상은 여러 언어적 관점에서 다루지만 글은 Python view에서 나오는 내용만 적었습니다.

dataset = tf.data.TFRecordDataset(files)
dataset = dataset.shuffle(buffer_size = X)
dataset = dataset.map(lambda record: parse(record))
dataset = dataset.batch(batch_size = Y)

for element in datset:
	...

기존 tf 1.x와 다르게 매우 간단하게 정리되었습니다. eager execution이 본격적으로 사용되는 것처럼 위에서도 for-loop를 사용하여 데이터를 전달받을 수 있습니다. 

기존의  tf 1.x에서는 iterator를 초기화 한 후 사용해야 하는 등 좀 더 복잡한 과정을 거쳤었습니다.

전체적으로는 다음의 프로세스를 가진다. 'tfrecord -> shuffle -> map -> batch'

C++의 요소를 넣어 performance 측면에서 이득을 취했고, 그 결과 다시 다음의 코드를 만나볼 수 있다.

dataset = tf.data.TFRecordDataset(files) 
dataset = dataset.shuffle(buffer_size = X) 
dataset = dataset.map(lambda record: parse(record), num_parallel_cells =…) 
dataset = dataset.batch(batch_size = Y) 
dataset = dataset.prefetch(buffer_size=…)

for element in datset: …

 

supporting non-tensor types

텐서 타입이 아닌 타입을 다루기 위해서는 다음 이미지에 나오는 코드로 변환하여 tf.data를 사용할 수 있게 된다.

map이나 tensor_slice와 같은 함수는 기본적으로 tf.data Structure을 거쳐서 output 또는 input으로 반환되는 것을 볼 수 있다.

 

Static Optimizations

이 부분은 자세히 설명해주지는 않았지만, 대충 최적화를 위한 함수들이 있는 곳이라고 생각하면 될 것 같다. 다음과 같이 여러개의 함수가 존재한다.

  • map + batch fusion
  • map + filter fusion
  • map + map fusion
  • map parallelization
  • map vectorization
  • noop elimination
  • shuffle + repeat fusion

그리고 다음과 같이 사용한다.

(이 다음으로 C++ 측면에서 내부적으로 어떻게 흘러가는지 보여주지만 글에서는 생략한다.)

Dynamic optimization

아래 퀴즈에서 시작한다. 얼마나 많은 시간이 소요되겠는가?

모든 작업이 순차적으로 이루어지기 때문에 그렇다고 한다. 
그렇다면 다음 작업은 얼마나 걸리겠는가에 대한 퀴즈를 또 한번 낸다.

첫 번째 질문과의 차이는 num_parallel_calls의 차이이다. background에서 서로 다른 커널에서 수행되는 것 같은데, 감이 잘 안온다. 아무튼 여러가지의 스레드를 만들어서 동시작업을 행한다고 생각하면 편하다. 
하지만 맨 처음 element를 처리하는데 걸리는 시간은 첫 번째 문제와 같다는 것.

num_parallel_calls의 수가 늘어날 수록 output latency도 줄어든다. 하지만 그 수가 늘어난다고 해서 매우 많은 수의 thread를 만든다는 의미는 아니고, 여러 작업의 schedule을 동시에 처리해서 그 시간을 줄인다는 의미인 것 같다.

다음과 같이 사용한다.

import numpy as np
x = np.array([1, 4, 5, 1000, 100, 3, 10, 2])
print(x)
print('----------------------')
print('partition',np.partition(x, -3))
print('partition',np.partition(x, -1))
print(np.argpartition(x, -3))
print(x[np.argpartition(x, -3)])
print('----------------------')
print('partition', np.partition(x, 3))
print(np.argpartition(x, 3))
print(x[np.argpartition(x, 3)])
print('----------------------')
print(np.partition(x, (1, 2)))
print(np.argpartition(x, (1, 3)))
print(x[np.argpartition(x, (1, 5))])

츌력 결과

np.partition에 대해서 설명하면, np.partition(np.array(), k-th)로 사용하는데

np.partition(~, 2)와 같은 경우는 리스트에서 순서 상관없이 작은 숫자 2개를 뽑아 왼쪽으로 놓겠다는 의미

np.partition(~, -2)와 같은 경우는 리스트에서 순서 상관없이 큰 값 2개를 뽑아 오른쪽으로 놓겠다는 의미이다.

np.argpartition은 partition과 같고, index를 리턴한다.

 

+ 틀리면 댓글 부탁드립니다.

이 글은 다음 텐서플로우 유튜브 동영상을 공부하면서 끄적끄적한 글입니다.

https://www.youtube.com/watch?v=IzKXEbpT9Lg&list=PLQY2H8rRoyvzIuB8rZXs7pfyjiSUs8Vza


일단 이 동영상은 전체적으로 텐서플로우 내부의 흐름을 다루고, 이를 예를 들어 설명하고 있습니다.

 

Base API에서 사용되던 tf.cond, tf.while_loop는 Higher-level이나 Low-level API에서 따로 통용되는 함수가 존재합니다.

하지만, tf 2.0으로 넘어오면서 "Functional" ops로 변경되어 If나 While문과 같이 파이썬 문법처럼 사용할 수 있게 되었습니다.
(Eager Execution, Autograph)

첫번쨰로 Control flow v1이라고 해서 low-ops에 대해 설명합니다. 

영상에서는 tf.cond(x < y, lambda: tf.add(x, z), lambda: tf.square(y))와 몇 개의 예를 들면서 low-ops에서의 흐름을 설명합니다. 쉽게 예에 대해서 그래프의 흐름을 설명해주고 있습니다. 이를 구성하기 위해 대표적으로 Switch와 Merge의 표현을 사용하게 됩니다.

이것의 장점은 Pruning이 가능하고, dataflow만 잘 만들면된다. 하지만 분석이 어렵고 매우 복잡하다. 이를 매우 nested하다 라고 표현하고 있습니다. 

결과적으로 tf 내부적으로 어떻게 control flow가 구성되는지 tf.cond(), tf.while_loop()를 통해 설명하고 있는겁니다. 다음 사이트가 이를 잘 설명해주고 있습니다.

https://towardsdatascience.com/tensorflow-control-flow-tf-cond-903e020e722a

두번째로는 Control flow v2를 설명합니다. 

여기에서는 그래프가 좀 더 함수처럼 동작하는 것을 보여주고 있습니다. 특별한 점은 없으며 텐서플로우의 기본적인 표현입니다. 각각의 함수가 텐서 인풋과 텐서 아웃풋을 별개로 가지고 있고, 매우 simple해 졌습니다. 또한, v1에서는 true와 false가 매치되어 있다면, v2에서는 별개의 함수로 작용하는 것처럼 구성되어 있습니다. 

세번째, Gradient입니다.

tf.cond, tf_while_loop를 예로 들며, 이에 대해 내부적으로 gradient가 어떻게 만들어지는지 설명합니다. 

Control flow v2의 장점은 v1의 단점을 보완한다는 것입니다. 단순, 디버깅, 통합 등등. 하지만 이 때문에 성능적으로 좀 떨어진다는 단점이 생깁니다. 따라서 이에 대한 해결방법은 표현을 v2로 쓰되, 내부적으로 v1의 기능을 사용하여 성능과 속도를 동시에 잡는 것입니다. 이를 Lowering이라고 표현하고 있습니다. 

또한, 향후에는 control flow v1과 같은 표현을 되도록이면 제거하면 좋겠다고 말하네요. 이를 위해 세션을 제거하고, TensorArray를 재구성했다고 합니다. 모든 것을 Simple하게 바꾸면서도 efficient하게 하려는 작업인 것 같습니다.