본문 바로가기
Machine Learning/Deep Learning

Manifold와 뉴럴 네트워크 학습

by devson 2024. 5. 20.

뉴럴 네트워크에서 Manifold가 어떤 연관이 있는지 찾아보면서 이해한 부분에 대해 정리해본다.

(여러 정보들을 살펴보며 나의 이해를 기준으로 정리하였기 때문에 잘못된 내용이 들어가있을 수도 있으니 너무 믿지는 말것)


Manifold

매니폴드 가설(manifold hypothesis)은 고차원의 데이터가 사실은 더 낮은 차원의 구조인 매니폴드(manifold) 위에 있다고 주장하는 가설이다.

딥러닝 모델은 학습을 통해 주어진 데이터에 대한 잠재 매니폴드(latent manifold)를 학습하여 데이터의 본질적인 특성을 이해할 수 있게된다. (manifold learning)

 

매니폴드를 설명할 때 가장 많이 나오는 스위스 롤 형태의 데이터를 예로 설명하면,

3차원으로 복잡하게 표현되어있는 데이터가 아래와 같이 2차원의 데이터로 표현될 수 있다.

https://www.cs.cmu.edu/~bapoczos/Classes/ML10715_2015Fall/slides/ManifoldLearning.pdf

 

만약 데이터 A와 데이터 B 사이를 보간(interpolation)한다면,

3차원 상의 A와 B의 위치 사이를 보간하는 것보다는

2차원의 매니폴드 상의 A와 B 사이를 보간하는 것이 해당 데이터의 본질에서 보다 적절한 방법일 것이다.

 

 

MNIST 데이터에 대해 20 사이를 보간하는 경우에 대해 얘기해보자면

  • 1)과 같이 매니폴드를 기준으로 보간하면 6이 나오는 것을 볼 수 있다.
    이는 잠재 매니폴드 상에서 2에 대한 데이터와 0 데이터 사이에 6에 대한 데이터가 존재한다고 볼 수 있다.
    (물론 매니폴드의 형태가 다르면 이렇게 나오지 않을 수 있다)
  • 하지만 2)와 같이 픽셀 값의 평균을 사용하여 보간(linear interpolation)을 하면 명확하지 않은 형태의 수가 나와 적절하게 보간되지 않았다고 볼 수 있을 것이다.

https://twitter.com/fchollet/status/1450524771976814598

 

Neural network 학습의 기하학적 의미

Neural network에서 각 layer에서 linear transformation과 non-linear transformation를 순차적으로 거치게 된다.

 

이 과정을 한 스텝 씩 살펴보기 위해 아래 영상을 참고하자.

https://www.youtube.com/watch?v=CfAL_cL3SGQ&t=264s

4분 24초 부터

 

살펴본 것 처럼 Neural network는 non-linear transformation를 통해서, 선형으로는 분리가 되지 않는 데이터도 분리가 가능해지게 된다.

입력 데이터
학습이 진행됨에 따른 데이터 non-linear transformation 양상

(출처: https://srome.github.io/Visualizing-the-Learning-of-a-Neural-Network-Geometrically)

 

실제로 아래와 같은 데이터에 대해 2진 분류하는 모델을 만들어 학습을 진행하였을 때

더보기
from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np

X, y = make_circles(n_samples=1000, factor=0.5, noise=0.1, random_state=42)
y = tf.keras.utils.to_categorical(y)

def plot_data(X, y):
    plt.scatter(X[:, 0], X[:, 1], c=np.argmax(y, axis=1), cmap=plt.cm.Spectral)
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.show()

plot_data(X, y)

 

학습이 진행됨에 따라 다음과 같이 출력 이전의 데이터가 점점 명확하게 경계가 생기는 형태로 변해가는 것을 확인할 수 있었다.

(항상 아래와 같은 데이터 형태가 나오는 것은 아니다)

학습이 진행됨에 따른 출력 이전 데이터의 양상

더보기
from tensorflow.keras import layers
from tensorflow.keras import models

# define model
def get_model():
    inputs = layers.Input(shape=(2,))
    X = layers.Dense(16, activation="relu", name="hidden_1")(inputs)
    X = layers.Dense(2, activation="relu", name="hidden_2")(X)
    outputs = layers.Dense(2, activation="softmax", name="output")(X)
    
    return models.Model(inputs, outputs)

model = get_model()
model.compile(optimizer="adam",
              loss="categorical_crossentropy",
              metrics=["accuracy"])

# train
def get_data_before_output(X):
    X = model.layers[1](X)
    X = model.layers[2](X)
    return X

for epoch in range(50):
    model.fit(X, y, epochs=1)
    if epoch % 3 == 0:
        plot_data(get_data_before_output(X), y)
        plt.show()

 

이러한 딥러닝 모델의 학습의 과정은 모델이 학습을 통해 잠재 매니폴드를 학습해가는 과정으로 볼 수 있다.

 

  • 추가) 나선 형태의 데이터를 분류 모델로 학습한 예제
더보기

 

import numpy as np
import matplotlib.pyplot as plt
from keras.utils import to_categorical

def create_spiral_data(points, classes):
    X = np.zeros((points * classes, 2))  # 데이터 포인트를 담을 배열
    y = np.zeros(points * classes, dtype='uint8')  # 레이블을 담을 배열
    for class_number in range(classes):
        ix = range(points * class_number, points * (class_number + 1))
        r = np.linspace(0.0, 1, points)  # 반경
        t = np.linspace(class_number * 4, (class_number + 1) * 4, points) + np.random.randn(points) * 0.2  # theta
        X[ix] = np.c_[r * np.sin(t), r * np.cos(t)]
        y[ix] = class_number
    return X, y

# 데이터 생성
X, y = create_spiral_data(500, 3)
y = to_categorical(y)

def plot_data(X, y):
    plt.scatter(X[:, 0], X[:, 1], c=np.argmax(y, axis=1), cmap=plt.cm.Spectral)
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.show()

plot_data(X, y)
plt.show()

from tensorflow.keras import layers, models

# define model
def get_model():
    inputs = layers.Input(shape=(2,))
    X = layers.Dense(32, activation="relu", name="hidden_1")(inputs)
    X = layers.Dense(3, activation="relu", name="hidden_2")(X)
    outputs = layers.Dense(3, activation="softmax", name="output")(X)
    
    return models.Model(inputs, outputs)

model = get_model()
model.compile(optimizer="adam",
              loss="categorical_crossentropy",
              metrics=["accuracy"])
model.summary()

# train
def get_data_before_output(X):
    X = model.layers[1](X)
    X = model.layers[2](X)
    return X
 
for epoch in range(100):
    model.fit(X, y, epochs=1)
    if epoch % 3 == 0:
        plot_data(get_data_before_output(X), y)
        plt.show()

 


 

참고

댓글