본문 바로가기
파이썬 라이브러리를 활용한 머신러닝

Neural Networks

by 지식광부키우기 2019. 11. 14.

 

인공 신경망 (Neural Networks)

 

뇌 구조 (Biological Neural Networks)는 지능형 기계를 구축하는데 영감을 주었습니다. -> 인공신경망 (Artificial Neural Networks)

 

그림1

 

신경망이라 알려진 알고리즘들은 최근 '딥러닝 (deep learning)'이란 이름으로 다시 주목받고 있습니다. 

 

여기서는 복잡한 딥러닝 알고리즘의 출발점이며 비교적 간단하게 분류와 회귀에 쓸 수 있는 다중 퍼셉트론 (MLP)을 다루겠습니다.

 

다중 퍼셉트론은 피드포워드 신경망 (feed-forward 신경망), 또는 그냥 신경망이라고도 합니다. 

 

 

다층 퍼셉트론 (Multi-layer Perceptrons)

 

다층 퍼셉트론 (Multi-layer Perceptrons)

 

입력 층, 은닉 층 (0에서 많이), 출력 층이 있습니다.

 

MLP는 여러 단계를 걸쳐 결정을 만들어내는 선형 모델의 일반화된 모습이라고 볼 수 있습니다. 

 

다층 $f^{1}, f^{2}, ..., f^{l}$은 체인처럼 연결되어 형성됩니다. 

 

$f(\mathbf{x}) = f^{l}(...(f^{2}(f^{1}(\mathbf{x}))))$

 

 

display(mglearn.plots.plot_logistic_regression_graph())

그림1

 

 

display(mglearn.plots.plot_single_hidden_layer_graph())

그림2

 

 

mglearn.plots.plot_two_hidden_layer_graph()

그림3

 

 

신경망 모델

 

입력 층 : 왼쪽 노드는 입력 특성을 나타냅니다. 

 

은닉 층 : MLP에서는 가중치 합을 만드는 과정이 여러 번 반복되며, 먼저 중간 단계를 구성하는 은닉 유닛을 계산하고 이를 이용하여  최종 결과를 산출하기 위해 다시 가중치 합을 계산합니다.

 

출력 층 : 오른쪽 노드는 입력의 가중치 합, 출력을 나타냅니다. 

 

연결선은 학습된 계수 (learned parameters)를 표현합니다. 

 

그림4

 

 

비선형 함수

 

여러 개의 가중치 합을 계산하는 것은 수학적으로 보면 하나의 가중치 합을 계산하는 것과 같습니다. -> 뉴랄 네트워크는 단순히 선형 모델입니다.

 

이 모델을 선형 모델보다 강력하게 만들려면 또 다른 기교가 필요합니다.

 

각 은닉 유닛의  가중치 합을 계산한 후 그 결과에 비선형 활성화 함수를 적용합니다.

 

rectifying nonlinear unit (relu) $g(x) = max(0, x)$ : 0이하를 잘라버립니다.

 

hyperbolic tangent (tanh) $g(x) = \frac{e^{x} - e^{-x}}{e^{x} + e^{-x}}$ : 낮은 입력값에 대해서는 -1로 수렴하고 큰 입력값에 대해서는 +1로 수렴합니다. 

 

 

line = np.linspace(-3, 3, 100)
plt.plot(line, np.tanh(line), label="tanh")
plt.plot(line, np.maximum(line, 0), label="relu")
plt.legend(loc="best")
plt.xlabel("x")
plt.ylabel("relu(x), tanh(x)")

그림5

 

마지막 은닉 층의 출력은 출력 층을 위한 입력이 됩니다. 

 

Linear Units (for regression, $y \in R$)

 

$\hat{y} = \mathbf{w}^{T}\mathbf{h} + b$

 

Sigmoid Units (for binary classification, $y \in \left \{ 0,1 \right \}$)

 

$\hat{y} = P(y=1 \mid \mathbf{x} ; \mathbf{\theta}) = \sigma(z) = \sigma(\mathbf{w}^{T}\mathbf{h} + b)$

 

Softmax Units (for multi-class classification, $y \in \left \{ 1, 2, ..., c \right \}$)

 

$\mathbf{z} = (z_{1}, ..., z_{k}) = \mathbf{W}^{T}\mathbf{h} + \mathbf{b}$

 

$\hat{y_{k}} = softmax(\mathbf{z})_{k} = \frac{exp(z_{i})}{\sum_{j}exp(z_{j})}$

 

$\sum_{k}\hat{y_{k}} = 1$

 

 

최적화

 

훈련 데이터 셋

 

$D = \left \{ (\mathbf{x_{1}}, y_{1}), (\mathbf{x_{2}}, y_{2}), ..., (\mathbf{x_{n}}, y_{n})  \right \}$

 

$\mathbf{x_{i}} = (x_{i1}, ..., x_{id}) \in R^{d}$는 입력 변수 d의 i번째 입력 벡터(vector)이고 

 

$y_{i}$는 대응하는 출력 변수의 레이블입니다. 

 

The model : $\hat{y} = f(\mathbf{x} ; \mathbf{\theta})$

 

The cost function (to be minimized, usually non-convex)

 

$\jmath (\mathbf{\theta}) = \frac{1}{n} \sum_{(\mathbf{x_{i}}, y_{i}) \in D} L(y_{i}, \hat{y_{i}})$

 

훈련에서는, any gradient-based optimization algorithms이 사용 가능합니다. (backpropagation)

 

The loss function $L(y_{i}, \hat{y_i})$

 

회귀에서는 squared error를 사용합니다. 

 

$L(y_{i}, \hat{y_i})$ = $L(y_{i}, \hat{y_i})$ = $(\hat{y_{i}} - y_{i})^{2}$

 

이진 분류에서는 binary cross-entropy를 사용합니다.

 

$L(y_{i}, \hat{y_i})$ = $[-y_{i} log\hat{y_{i}} - (1 - y_{i})log(1 - \hat{y_{i}})]$

 

다중 분류에서는 categorical cross-entropy를 사용합니다.

 

$L(y_{i}, \hat{y_i}) = - \sum_{k=1}^{c} y_{ik} log \hat{y_{ik}}$

 

 

하이퍼파라미터

 

Model Architecture

 

hidden_layer_size : tuple, length = n_layers - 2, default (100, )

 

activation : {'identity', 'logistic', 'tanh', 'relu'}, default 'relu'

 

입력 변수가 굉장히 크거나 작은 값이라 매우 노이지하다면 -> tanh와 sigmoid가 덜 노이즈에 민감합니다. 

 

 

Optimization (Training)

 

alpha : L2 규제 (by default)

 

solver : {'lbfgs', 'sgd', 'adam'}, defaut 'adam'

 

보다 많은 좋은 옵션들도 있습니다. batch_size, learning_rate, max_iter, early_stopping, ...

 

기본 solver인 'adam'은 상대적으로 많은 데이터셋 (thousands of training data points or more) 에 꽤 잘 작동 합니다. 트레이닝 시간이나 평가 스코어가 유리합니다. 

 

그러나 작은 데이터셋의 경우 'lbfgs'가 더 빠르고 성능도 좋습니다. 

 

 

Two Moon 데이터셋 예시

 

은닉 층과 은닉 유닛을 다르게 해보겠습니다. 

 

 

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

X, y = make_moons(n_samples=100, noise=0.25, random_state=3)

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,
                                                    random_state=42)

mlp = MLPClassifier(solver='lbfgs', random_state=0).fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.title("hidden_layer_size = (100,)")

그림6

 

 

mlp = MLPClassifier(solver='lbfgs', random_state=0, hidden_layer_sizes=[10])
mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.title("hidden_layer_size = (10,)")

그림7

 

 

# using two hidden layers, with 10 units each
mlp = MLPClassifier(solver='lbfgs', random_state=0,
                    hidden_layer_sizes=[10, 10])
mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.title("hidden_layer_size = (10,10,)")

그림8

 

 

은닉 유닛의 개수와 alpha 하이퍼파라미터를 다르게 해주겠습니다.

 

 

fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for axx, n_hidden_nodes in zip(axes, [10, 100]):
    for ax, alpha in zip(axx, [0.0001, 0.01, 0.1, 1]):
        mlp = MLPClassifier(solver='lbfgs', random_state=0,
                            hidden_layer_sizes=[n_hidden_nodes, n_hidden_nodes],
                            alpha=alpha)
        mlp.fit(X_train, y_train)
        mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3, ax=ax)
        mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train, ax=ax)
        ax.set_title("n_hidden=[{}, {}]\nalpha={:.4f}".format(
                      n_hidden_nodes, n_hidden_nodes, alpha))

그림9

 

 

같은 매개변수를 사용하지만 초기화를 다르게 하여 만들어보겠습니다.

 

 

fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for i, ax in enumerate(axes.ravel()):
    mlp = MLPClassifier(solver='lbfgs', random_state=i,
                        hidden_layer_sizes=[100, 100])
    mlp.fit(X_train, y_train)
    mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3, ax=ax)
    mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train, ax=ax)

그림10

 

 

유방암 데이터셋 예시 - MLPClassifier

 

from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()

 

유방암 데이터셋을 쓰겠습니다.

 

 

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, stratify=cancer.target, random_state=42)

 

훈련 세트와 테스트 세트로 나눠줍니다.

 

 

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

 

데이터 스케일링을 해줍니다.

 

 

from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(max_iter=1000, random_state=0)
clf.fit(X_train_scaled, y_train)

 

모델을 학습시킵니다.

 

 

from sklearn.metrics import accuracy_score
y_train_hat = clf.predict(X_train_scaled)
print('train accuracy :', accuracy_score(y_train, y_train_hat))
y_test_hat = clf.predict(X_test_scaled)
print('test accuracy :', accuracy_score(y_test, y_test_hat))

그림11

 

 

모델의 성능은 매우 좋습니다.

 

 

확장된 보스턴 데이터셋 예시 - MLPRegressor

 

import mglearn
X, y = mglearn.datasets.load_extended_boston()
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
y_train = y_train.reshape(-1, 1)
y_test = y_test.reshape(-1, 1)

 

확장된 보스턴 데이터셋을 사용합니다.

 

 

from sklearn.preprocessing import StandardScaler
scalerX = StandardScaler()
scalerX.fit(X_train)
X_train_scaled = scalerX.transform(X_train)
X_test_scaled = scalerX.transform(X_test)
scalerY = StandardScaler()
scalerY.fit(y_train)
y_train_scaled = scalerY.transform(y_train)
y_test_scaled = scalerY.transform(y_test)

 

데이터 스케일링을 해줍니다.

 

 

from sklearn.neural_network import MLPRegressor
reg = MLPRegressor(max_iter=1000, random_state=0)
reg.fit(X_train_scaled, y_train_scaled)

 

모델을 만듭니다.

 

 

from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
y_train_hat_scaled = reg.predict(X_train_scaled)
y_train_hat = scalerY.inverse_transform(y_train_hat_scaled)
print('MAE :', mean_absolute_error(y_train, y_train_hat))
print('RMSE :', mean_squared_error(y_train, y_train_hat)**0.5)
print('R_square :', r2_score(y_train, y_train_hat))
y_test_hat_scaled = reg.predict(X_test_scaled)
y_test_hat = scalerY.inverse_transform(y_test_hat_scaled)
print('MAE :', mean_absolute_error(y_test, y_test_hat))
print('RMSE :', mean_squared_error(y_test, y_test_hat)**0.5)
print('R_square :', r2_score(y_test, y_test_hat))

그림12

 

 

확장된 보스턴 데이터셋 예시 - MLPRegressor tanh

 

from sklearn.neural_network import MLPRegressor
reg = MLPRegressor(activation='tanh', max_iter=1000, random_state=0)
reg.fit(X_train_scaled, y_train_scaled)

 

tanh 비선형 함수를 사용했습니다.

 

 

from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
y_train_hat_scaled = reg.predict(X_train_scaled)
y_train_hat = scalerY.inverse_transform(y_train_hat_scaled)
print('MAE :', mean_absolute_error(y_train, y_train_hat))
print('RMSE :', mean_squared_error(y_train, y_train_hat)**0.5)
print('R_square :', r2_score(y_train, y_train_hat))
y_test_hat_scaled = reg.predict(X_test_scaled)
y_test_hat = scalerY.inverse_transform(y_test_hat_scaled)
print('MAE :', mean_absolute_error(y_test, y_test_hat))
print('RMSE :', mean_squared_error(y_test, y_test_hat)**0.5)
print('R_square :', r2_score(y_test, y_test_hat))

그림13

 

기본값인 lelu와 차이가 별로 나지 않습니다. 

 

 

결론

 

뉴랄 네트워크의 주요 하이퍼파라미터

 

hidden_layer_sizes, activation

 

regularization and optimization-related hyperparameters

 

일반적으로 평가 데이터셋에서 가장 높은 성능을 낸 모델을 선택합니다.

 

데이터 전처리가 중요합니다. (데이터 스케일링과 원-핫 인코딩)

 

 

장점 

 

대량의 데이터에 내재된 정보를 잡아내고 매우 복잡한 모델을 만들 수 있다는 것입니다. 

 

충분한 연산 시간과 데이터를 주고 매개변수를 세심하게 조정하면 신경망은 다른 머신러닝 알고리즘을 뛰어넘는 성능을 냅니다.

 

 

단점

 

크고 강력한 모델이라면 신경망은 학습이 오래 걸립니다.

 

데이터 전저리에 주의해야 합니다.

 

 

신경망의 복잡도 추정

 

신경망의 매개변수를 조정하는 일반적인 방법

 

from lower to higher complexity : 한개 또는 두개의 은닉 층으로 시작해서 가능한 확장시킵니다.

 

from higher to lower complexity : 충분히 과대적합되어서 문제를 해결할만한 큰 모델을 만듭니다. 그런 다음 훈련 데이터가 충분히 학습될 수 있다고 생각될 때 신경망 구조를 줄이거나 규제 강화를 위해 alpha 값을 증가시켜 일반화 성능을 향상시킵니다.

 

 

신경망의 모델 복잡도에 관해 도움이 될만한 측정치는 학습된 가중치 또는 계수의 수 입니다. 

 

이진 분류 데이터셋에서 특성 50개와 10개의 은닛 유닉을 가진 2개의 은닉 층이 있습니다.

 

10 * (50 + 1) = 510 파라미터가 있습니다. 입력과 첫 번째 은닉 층 사이에

 

10 * (10 + 1) = 110 파라미터가 있습니다. 첫 번째 은닉층과 두 번째 은닉층 사이에 

 

1 * (10 + 1) = 11 파라미터가 있습니다. 두 번째 은닉층과 출력층 사이에 

 

 

 

딥러닝 더 나아가기

 

MLPClassifier와 MLPREgressor은 일반적인 신경망 구조를 위한 손쉬운 인터페이스를 제공하지만 전체 신경망 종류의 일부만 만들 수 있습니다. 

 

더 복잡하거나 대규모인 모델을 만들려면 사이킷런을 넘어서 전문적인 딥러닝 라이브러리들을 살펴봐야합니다.

 

tensorflow나 pytorch 등이 있습니다.

 

위의 라이브러리들은 신경망을 만드는 데 훨씬 유연한 인터페이스를 제공하고 있어 딥러닝 연구 분야에서 빠르게 성장하고 있습니다. 또한 고성능 그래픽 처리장치 (GPU)를 사용하여 연산을 훨씬 더 빠르게 할 수 있습니다. 

 

 

파이썬 라이브러리를 활용한 머신러닝 책과 성균관대학교 강석호 교수님 수업 내용을 바탕으로 요약 작성되었습니다.

댓글