반응형

Tensorflow.org의 링크 내용을 정리함.


텐서보드에 이미지 데이터 나타내기

    TensorFlow Summary API를 사용하면, 텐서와 임의의 이미지를 쉽게 로그에 기록하고 텐서보드에서 볼 수 있다. 이는 입력 데이터를 샘플링하고 조사하거나 레이어 가중치(layer weights)와 생성된 텐서(generated tensors)를 시각화하는데 엄청나게 도움이 된다.

    이 글에서 이미지인 텐서를 시각화하기 위해 Image Summary API를 사용하는 방법과 어떻게 임의의 이미지를 가지고 와서 이를 텐서로 변환하고 텐서보드에 시각화하는지를 본다. 간단하지만 모델이 어떻게 수행하고 있는지 이해하는데 도움이 되는 Image Summary를 사용하는 실제 예제를 사용하여 진행한다.


Setup

Source>>

    from datetime import datetime
    import io
    import itertools
    from packaging import version
    from six.moves import range

    import tensorflow as tf
    from tensorflow import keras

    import matplotlib.pyplot as plt
    import numpy as np
    import sklearn.metrics

    print("TensorFlow version: ", tf.__version__)
    assert version.parse(tf.__version__).release[0] >= 2, \
        "This notebook requires TensorFlow 2.0 or above."

Result>>

    TensorFlow version:  2.1.0


Fashion-MNIST 데이터셋 다운로드

    Fashion-MNIST 데이터셋의 이미지를 분류하기 위한 간단한 신경망을 만들어 보자. 이 데이터셋은 28 x 28 회색(grayscale) 이미지가 10가지 패션 상품 분류로 각 분류마다 7,000장씩 총 70,000장으로 구성되어 있다.

우선, 데이터셋을 다운로드 한다.

Source>>

    # Download the data. The data is already divided into train and test.
    # The labels are integers representing classes.
    fashion_mnist = keras.datasets.fashion_mnist
    (train_images, train_labels), (test_images, test_labels) = \
        fashion_mnist.load_data()

    # Names of the integer classes, i.e., 0 -> T-short/top, 1 -> Trouser, etc.
    class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 
        'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

Result>>

    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
    32768/29515 [=================================] - 0s 0us/step
    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
    26427392/26421880 [==============================] - 0s 0us/step
    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
    8192/5148 [===============================================] - 0s 0us/step
    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
    4423680/4422102 [==============================] - 0s 0us/step


단일 이미지 시각화

    Image Summary API가 어떻게 동작하는지 이해하기 위해, 훈련셋의 첫번째 훈련 이미지를 텐서보드에 로그로 남긴다.

    그 전에, 훈련 데이터의 모양을 검사한다.

Source>>

    print("Shape: ", train_images[0].shape)
    print("Label: ", train_labels[0], "->", class_names[train_labels[0]])

Result>>

    Shape:  (28, 28)
    Label:  9 -> Ankle boot

    데이터셋의 각 이미지의 모양은 높이와 너비를 나타내는 (28, 28) 모양의 rank-2 텐서이다.

    그러나, tf.summary.image()는 (batch_size, height, width, channels)를 포함하는 rank-4 텐서를 인자로 받는다. 따라서 텐서의 모양을 알맞게 변경한다.

Source>>

    # Reshape the image for the Summary API.
    img = np.reshape(train_images[0], (-1, 28, 28, 1))

    이제 이미지를 로그로 기록하고 텐서보드에서 보자.
    여기서 로그 디렉토리는 logs/image/train_data로 한다.

Source>>

    # Sets up a timestamped log directory.
    logdir = "logs/image/train_data/" + datetime.now().strftime("%Y%m%d-%H%M%S")
    # Creates a file writer for the log directory.
    file_writer = tf.summary.create_file_writer(logdir)

    # Using the file writer, log the reshaped image.
    with file_writer.as_default():
        tf.summary.image("Training data", img, step=0)

    이미지를 검사하기 위해 텐서보드를 사용하자.

Console>>

    > tensorboard --logdir=logs/image/train_data


    'Image' 탭은 로그로 기록된 이미지를 보여준다. 여기서는 'ankle boot'이다.

    이미지는 더 쉽게 보이기 위해 기본 사이즈로 크기가 변경되어 진다. 크기가 변경되지 않은 원래 이미지를 보려면, 좌측 상단에 'Show actual image size'를 체크한다.

    밝기(brightness)와 대비(contrast) 슬라이드가 이미지 픽셀어 어떤 영향을 미치는지 확인해보자.


여러개의 이미지 시각화하기

    여러개의 훈련 예제를 로그로 남기고 싶다면, 간단하게 tf.summary.image()에 데이터를 전달할 때, 로그로 남기기 원하는 이미지의 수를 지정하면 된다.

Source>>

    with file_writer.as_default():
        # Don't forget to reshape.
        images = np.reshape(train_images[0:25], (-1, 28, 28, 1))
        tf.summary.image("25 training data examples", images, max_outputs=25, step=0)

Consloe>>

    tensorboard --logdir=logs/image/train_data



임의의 이미지 데이터 로그 남기기

    만약 matplotlib로 만들어진 이미지 같은 텐서가 아닌 이미지를 시각화하려면 이것을 텐서로 변환하기 위한 틀에 박힌 코드가 좀 필요하다.

    아래 코드에서, matplotlib의 subplot() 함수를 사용하여 처음 25개 이미지 그리드를 로그로 남기고 이 그리드를 텐서보드에서 확인한다.

Source>>

    logdir = "logs/plots/" + datetime.now().strftime("%Y%m%d-%H%M%S")
    file_writer = tf.summary.create_file_writer(logdir)

    def plot_to_image(figure):
        """Converts the matplotlib plot specified by 'figure' to a PNG image and
        returns it. The supplied figure is closed and inaccessible after this call."""
        # Save the plot to a PNG in memory.
        buf = io.BytesIO()
        plt.savefig(buf, format='png')
        # Closing the figure prevents it from being displayed directly inside
        # the notebook.
        plt.close(figure)
        buf.seek(0)
        # Convert PNG buffer to TF image
        image = tf.image.decode_png(buf.getvalue(), channels=4)
        # Add the batch dimension
        image = tf.expand_dims(image, 0)
        return image

    def image_grid():
        """Return a 5x5 grid of the MNIST images as a matplotlib figure."""
        # Create a figure to contain the plot.
        figure = plt.figure(figsize=(10,10))
        for i in range(25):
            # Start next subplot.
            plt.subplot(5, 5, i + 1, title=class_names[train_labels[i]])
            plt.xticks([])
            plt.yticks([])
            plt.grid(False)
            plt.imshow(train_images[i], cmap=plt.cm.binary)

        return figure

    # Prepare the plot
    figure = image_grid()
    # Convert to image and log
    with file_writer.as_default():
        tf.summary.image("Training data", plot_to_image(figure), step=0)

Console>>

    > tensorboard --logdir=logs/plots



이미지 분류기 만들기

    이제 실제 예제로 종합해 보자. 어쟀든, 이 글은 예쁜 그림을 그리는 것이 아닌 ML을 하는 것이다.

    Fashion-MNIST 데이터셋에 대한 훈련을 하는 동안 모델이 얼마나 잘 동작하는지 이해하기 위해 Image Summary를 사용한다.

    우선, 간단한 모델을 만들고 옵티마이져와 손실함수를 설정하여 컴파일한다. 컴파일 단계는 또한 그 과정에서 분류기의 정확도를 로그로 남길 수도 있다.

Source>>

    model = keras.models.Sequential([
        keras.layers.Flatten(input_shape=(28, 28)),
        keras.layers.Dense(32, activation='relu'),
        keras.layers.Dense(10, activation='softmax')
    ])

    model.compile(
        optimizer='adam', 
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )


    분류기를 훈련할 때, Confusion matrix를 보는 것은 매우 유용하다. Confusion matrix는 테스트 데이터상에서 분류기가 얼마나 수행되고 있는지에 대한 자세한 정보를 제공한다.

Scikit-learn을 사용하여 Confusion matrix를 계산하는 함수를 정의하고 matplotlib를 이용해 그린다.

Source>>

    def plot_confusion_matrix(cm, class_names):
        """
        Returns a matplotlib figure containing the plotted confusion matrix.

        Args:
          cm (array, shape = [n, n]): a confusion matrix of integer classes
          class_names (array, shape = [n]): String names of the integer classes
        """
        figure = plt.figure(figsize=(8, 8))
        plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
        plt.title("Confusion matrix")
        plt.colorbar()
        tick_marks = np.arange(len(class_names))
        plt.xticks(tick_marks, class_names, rotation=45)
        plt.yticks(tick_marks, class_names)

        # Normalize the confusion matrix.
        cm = np.around(cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], decimals=2)

        # Use white text if squares are dark; otherwise black.
        threshold = cm.max() / 2.
        for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
            color = "white" if cm[i, j] > threshold else "black"
            plt.text(j, i, cm[i, j], horizontalalignment="center", color=color)

        plt.tight_layout()
        plt.ylabel('True label')
        plt.xlabel('Predicted label')
        return figure


    분류기를 훈련하고 이 과정에서 confusion matrix를 주기적으로 로그로 남긴다.

    이를 위해서는 아래 단계를 따른다.

  1. 기본적인 지표(basic metrics)를 로그로 남기기 위해 케라스 TensorBoard callback 생성
  2. Epoch의 끝마다 confusion matrix를 로그로 남기기 위해 케라스 LambdaCallback 생성
  3. Model.fit()에 생성한 콜백을 전달하여 훈련

    텐서보드를 미 실행시켜 진행 상황을 확인한다.

Source>>

    logdir = "logs/image/cfsm/" + datetime.now().strftime("%Y%m%d-%H%M%S")
    # Define the basic TensorBoard callback.
    tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir, profile_batch=100000000)
    file_writer_cm = tf.summary.create_file_writer(logdir + '/cm')

    def log_confusion_matrix(epoch, logs):
        # Use the model to predict the values from the validation dataset.
        test_pred_raw = model.predict(test_images)
        test_pred = np.argmax(test_pred_raw, axis=1)

        # Calculate the confusion matrix.
        cm = sklearn.metrics.confusion_matrix(test_labels, test_pred)
        # Log the confusion matrix as an image summary.
        figure = plot_confusion_matrix(cm, class_names=class_names)
        cm_image = plot_to_image(figure)

        # Log the confusion matrix as an image summary.
        with file_writer_cm.as_default():
            tf.summary.image("Confusion Matrix", cm_image, step=epoch)

    # Define the per-epoch callback.
    cm_callback = keras.callbacks.LambdaCallback(on_epoch_end=log_confusion_matrix)

Console>>

    > tensorboard --logdir=logs/image/cfsm

Source>>

    # Train the classifier.
    model.fit(
        train_images,
        train_labels,
        epochs=5,
        verbose=0, # Suppress chatty output
        callbacks=[tensorboard_callback, cm_callback],
        validation_data=(test_images, test_labels),
    )



    정확도가 훈련과 검증 셋 모두에서 상승하고 있는 것에 주목하자. 이는 좋은 징조이다. 하지만, 데이터의 특정 서브셋에서 어떻게 모델이 동작하는 것일까?

    로그로 기록된 confusion matrix를 시각화하기 위해 'Images 탭을 선택하고 전체 크기로 confusion matrix를 보기 위해 좌측 상단에 있는 'Show actual image size'를 체크한다.

    기본적으로 대쉬보드는 가장 마지막 단계 또는 epoch에 대한 이미지 summary를 보여준다. 이전 confusion matrix를 보기 위해 (이미지 상단에 있는) 슬라이드를 사용하자. 대각선을 따라 합쳐진 어두운 사각형 그리고 0인 하얀색으로 변하려는 경향이 있는 나머지 매트릭스로 훈련 진행에 따라 매트릭스가 얼마나 크게 변화하는지 주목하자. 이는 훈련 진행에 따라 분류기가 개선되고 있다는 의미이다.

    Confusion matrix는 이 모델에 약간 문제가 있음을 보여준다. 훌륭한 처리에도 불구하고, 'Shirt, T-Shirt, Pullover 각각이 서로 혼동되고 있다.

이 모델은 추가 작업이 필요하다. 이 부분에 흥미가 있다면, CNN으로 모델을 개선해 보면 된다. (이 글에서는 다루지 않지만, 이후 따로 다뤄 볼 예정이다.)

반응형

+ Recent posts