이미지 제네레이터와 활용하고 싶은 데이터를 포함한 데이터 제네레이터의 구현 코드입니다.

이미지는 이미지데이터 제네레이터를 통해 불러오며, 활용하고 싶은 데이터인 color는 직접 인덱스를 통해 배치 크기만큼 부르는 것을 볼 수 있습니다.

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

class DataGenerator(tf.keras.utils.Sequence):
    def __init__(self, df, batch_size = 32, target_size = (112, 112), shuffle = True):
        self.len_df = len(df)
        self.batch_size = batch_size
        self.target_size = target_size
        self.shuffle = shuffle
        self.class_col = ['black', 'blue', 'brown', 'green', 'red', 'white', 
             'dress', 'shirt', 'pants', 'shorts', 'shoes']
        self.generator = ImageDataGenerator(rescale = 1./255)
        self.df_generator = self.generator.flow_from_dataframe(dataframe=df, 
                                                          directory='',
                                                            x_col = 'image',
                                                            y_col = self.class_col,
                                                            target_size = self.target_size,
                                                            color_mode='rgb',
                                                            class_mode='other',
                                                            batch_size=self.batch_size,
                                                            seed=42)
        self.colors_df = df['color']
        self.on_epoch_end()
        
    def __len__(self):
        return int(np.floor(self.len_df) / self.batch_size)
    
    def on_epoch_end(self):
        self.indexes = np.arange(self.len_df)
        if self.shuffle:
            np.random.shuffle(self.indexes)
        
    def __getitem__(self, index):
        indexes = self.indexes[index * self.batch_size : (index + 1) * self.batch_size]
        colors = self.__data_generation(indexes)
        
        images, labels = self.df_generator.__getitem__(index)
        
        # return multi-input and output
        return [images, colors], labels
    
    def __data_generation(self, indexes):
        colors = self.colors_df[indexes].to_numpy()
        # 또는
        # colors = np.array([self.colors_df[k] for k in indexes])
        
        return colors

 

1 - https://hwiyong.tistory.com/241

구글에 검색해보니 다양한 경로에 의해 발생되는 에러인 것 같다.

내가 겪은 경우는 tensorflow-gpu 버전과 tensorflow 버전이 맞지 않아서 발생했다.

이유는 keras-tuner를 설치하면서 tensorflow를 자동으로 업데이트해버린 것 같다.

해결 방법은 역시 gpu버전을 맞추거나 tensorflow 버전을 gpu 버전에 맞추거나.. 매우 간단!

1x1 Convolution은 Network in network 논문에서 주로 다룬 개념입니다. 

Lin, M., Chen, Q., & Yan, S. (2013). Network in network. arXiv preprint arXiv:1312.4400.

 

이 글에서는 MNIST 데이터셋을 Dense 층(fully-connected layer)를 사용하지 않고 학습하는 방법을 다루겠습니다.

코딩 방법에 따라 2가지로 나뉘어서 모델을 구성할 수 있습니다. 직접 돌려보니 정확도는 약 98%정도 나왔습니다.


1. 단순하게 Global Avaerage Pooling만 사용

import tensorflow as tf

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

x_train = x_train.reshape(-1,28, 28, 1)
x_train = x_train / 255
x_test = x_test.reshape(-1, 28, 28, 1)
x_test = x_test / 255

from tensorflow.keras.layers import Input, Conv2D, GlobalAveragePooling2D, MaxPooling2D
from tensorflow.keras.models import Model


inputs = Input(shape = (28, 28, 1))
x = Conv2D(32, (3, 3), strides = (1, 1), padding = 'same', activation = 'relu')(inputs)
x = MaxPooling2D(strides = (2, 2))(x)
x = Conv2D(64, (3, 3), strides = (1, 1), padding = 'same', activation = 'relu')(x)
x = MaxPooling2D(strides = (2, 2))(x)
x = Conv2D(64, (3, 3), strides = (1, 1), padding = 'same', activation = 'relu')(x)
x = MaxPooling2D(strides = (2, 2))(x)
x = Conv2D(10, (1, 1), activation = 'softmax')(x)
x = GlobalAveragePooling2D()(x)

model = Model(inputs = inputs, outputs = x)

model.compile(optimizer = 'adam', 
              loss = 'sparse_categorical_crossentropy', 
              metrics = ['acc'])
model.fit(x_train, y_train, 
          epochs = 10, batch_size = 32)

2. 적절한 Reshape 혼합

import tensorflow as tf

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

x_train = x_train.reshape(-1,28, 28, 1)
x_train = x_train / 255
x_test = x_test.reshape(-1, 28, 28, 1)
x_test = x_test / 255

from tensorflow.keras.layers import Input, Conv2D, Reshape
from tensorflow.keras.layers import GlobalAveragePooling2D, MaxPooling2D
from tensorflow.keras.models import Model


inputs = Input(shape = (28, 28, 1))
x = Conv2D(32, (3, 3), strides = (1, 1), padding = 'same', activation = 'relu')(inputs)
x = MaxPooling2D(strides = (2, 2))(x)
x = Conv2D(64, (3, 3), strides = (1, 1), padding = 'same', activation = 'relu')(x)
x = MaxPooling2D(strides = (2, 2))(x)
x = Conv2D(64, (3, 3), strides = (1, 1), padding = 'same', activation = 'relu')(x)
x = MaxPooling2D(strides = (2, 2))(x)
x = Conv2D(128, (1, 1), padding = 'same', activation = 'relu')(x)
x = GlobalAveragePooling2D()(x)
x = Reshape((1, 1, 128))(x)
x = Conv2D(10, (1, 1), padding = 'same', activation = 'softmax')(x)
x = Reshape((10,))(x)

model = Model(inputs = inputs, outputs = x)

model.compile(optimizer = 'adam', 
              loss = 'sparse_categorical_crossentropy', 
              metrics = ['acc'])
model.fit(x_train, y_train, 
          epochs = 10, batch_size = 32)

 

위에서 소개한 2가지 방법의 차이는 거의 없습니다. 이렇게도 할 수 있구나...를 보여주기 위해서

1x1 conv에 익숙하지 않은 분들에게 도움이 되길 바랍니다. 

두 가지 방법으로 정의하여 사용할 수 있습니다.

예시로는 이 글이 작성된 2019/10/27일을 기준으로 가장 최신의 활성화 함수라고 말할 수 있는 Mish Activation을 사용하였습니다.

[해당 논문]: https://arxiv.org/abs/1908.08681


케라스의 Activation 함수에 그대로 넣어서 사용하기

import tensorflow as tf
import tensorflow.keras.backend as K

from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Input, Dense, Flatten, Activation
from tensorflow.keras.models import Model

(x_train, y_train), (x_test, y_test) = mnist.load_data()

def mish(x):
    return x * K.tanh(K.softplus(x))

inputs = Input(shape = (28, 28))
x = Flatten()(inputs)
x = Dense(50)(x)
x = Activation(mish)(x)
x = Dense(30)(x)
x = Activation(mish)(x)
x = Dense(10, activation = 'softmax')(x)

model = Model(inputs = inputs, outputs = x)
model.compile(optimizer = 'adam',
             loss = 'sparse_categorical_crossentropy')

model.fit(x_train, y_train)

위의 코드와 같이 직접 activation 함수에 쓰일 연산을 정의하여 인자로 넘겨줄 수 있습니다.


함수를 등록하여 케라스의 특징인 문자열 형태로 제공하여 쓰기

import tensorflow as tf
import tensorflow.keras.backend as K

from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Input, Dense, Flatten, Activation
from tensorflow.keras.models import Model

from tensorflow.keras.utils import get_custom_objects

(x_train, y_train), (x_test, y_test) = mnist.load_data()

class Mish(Activation):
    def __init__(self, activation, **kwargs):
        super(Mish, self).__init__(activation, **kwargs)
        self.__name__ = 'Mish'

def mish(x):
    return x * K.tanh(K.softplus(x))

get_custom_objects().update({'mish': Mish(mish)})

inputs = Input(shape = (28, 28))
x = Flatten()(inputs)
x = Dense(50)(x)
x = Activation('mish')(x)
x = Dense(30)(x)
x = Activation('mish')(x)
x = Dense(10, activation = 'softmax')(x)

model = Model(inputs = inputs, outputs = x)
model.compile(optimizer = 'adam',
             loss = 'sparse_categorical_crossentropy')

model.fit(x_train, y_train)

위와 같이 클래스로 정의한 뒤, get_custom_objects를 사용해 등록하여 사용할 수 있습니다.

경우에 따라 1번과 2번 중에 편리한 것이 있을 수 있으니 선택하여 사용하시면 될 것 같네요.

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

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


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

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

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