손실함수(Loss Function)

손실함수는 비용함수(Cost Function)라고도 합니다. 손실에는 그만큼의 비용이 발생한다는 개념에서 말입니다. 손실함수가 왜 필요한지부터 파악하기 위해 다음과 같은 데이터가 있다고 합시다.

t = [0, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 0]

총 10개 값으로, 어떤 입력값이 0~9중 어떤 값인지를 나타내는 확률값 입니다. 첫번째 값이 0일때의 확률이고, 두번째가 1일때의 확률입니다. 즉, 위의 값은 3일때와 4일때의 확률이 각각 50%인 셈입니다.

위의 데이터는 항상 옳은 경우의 실제값라고 합시다. 이제.. 아래의 데이터는 계산된, 즉 예측된 데이터입니다.

y = [0.01, 0.01, 0.1, 0.3, 0.33, 0.04, 0.02, 0.05, 0.01, 0.1]

위의 예측 데이터 역시 0~9중 어떤 값일 거라는 계산된 확률값으로써 0일 확률은 1%, 2일 확률은 10%, 3일 확률은 30%라고 예측하고 있습니다. 그럼, 이 예측값와 실제값에 대한 오차는 어떻게 계산할 수 있을까요? 바로 이 오차가 손실함수의 값이 됩니다.

손실함수는 흔히 평균제곱오차(Mean Squared Error, MSE)와 교차 엔트로피 오차(Cross Entropy Error, CEE)가 사용됩니다.

평균 제곱 오차(Mean Squared Error, MSE)

MSE의 공식은 다음과 같습니다.

    $$E=\frac{1}{2}{\displaystyle\sum_{i=1}^{n} {(y_{i}-t_{i})^{2}}}$$

앞서 언급한 실제값 t와 예측값 y에 대한 평균제곱오차의 손실값은 아래의 파이선 코드를 통해 얻을 수 있습니다.

import numpy as np

def MSE(y, t):
    return 0.5 * np.sum((y-t)**2)

t = np.array([0, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 0])
y = np.array([0.01, 0.01, 0.1, 0.3, 0.33, 0.04, 0.02, 0.05, 0.01, 0.1])

print(MSE(t,y))

위의 출력값으로써 손실값은 0.04685 입니다. 그럼 동일한 t에 대해 상대적으로 잘못 예측한 상황의 y값을 아래처럼 얻었다고 가정합시다.

y = [0.3, 0.01, 0.1, 0.01, 0.04, 0.02, 0.05, 0.33, 0.01, 0.1]

위에 대한 손실값은 0.33685 입니다. 즉 손실값이 예상했던 것처럼 상대적으로 큽니다.

교차 엔트로피 오차(Cross Entropy Error, CEE)

CEE의 수식은 다음과 같습니다.

    $$E=-\displaystyle\sum_{i=1}^{n} {t_{i}\log{y_i}}$$

위이 식에서 log는 밑이 e인 자연로그입니다. CEE의 이해를 위해 자연로그에 대한 그래프를 시각화해 보는 코드는 다음과 같습니다.

import numpy as np
import matplotlib.pyplot as plt
 
x = np.arange(0, 1, 0.01)
y = np.log(x)
 
plt.plot(x, y)
plt.show()

그래프는 다음과 같습니다.

가로축은 정답일 확률이고, y 축은 손실값에 -1을 곱한 값입니다. 가로축값이 1, 즉 정답이 확률이 100%일때 손실값은 0이 되고, 정답이 확률이 낮아 질수록 손실값은 무한대로 커지게 됩니다.

이제 앞서 언급한 실제값 t와 예측값 y에 대한 교차 엔트로피 오차, CEE를 얻는 파이선 코드는 다음과 같습니다.

import numpy as np

def CEE(y, t):
    delta = 1e-10
    return -np.sum(t*np.log(y+delta))

t = np.array([0, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 0])
y0 = [0, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 0]
y1 = [0.01, 0.01, 0.1, 0.3, 0.33, 0.04, 0.02, 0.05, 0.01, 0.1]
y2 = np.array([0.3, 0.01, 0.1, 0.01, 0.04, 0.02, 0.05, 0.33, 0.01, 0.1])

print(CEE(t,y0)) # 0.6931471803599453
print(CEE(t,y1)) # 8.265472039806522
print(CEE(t,y2)) # 21.21844021456322

보시는 것처럼 실제값에서 예측값이 멀어질 수록 손실값이 커지는 것을 알 수 있습니다.

FingerEyes-Xr에서 문자열로 공간 데이터 추가하기

로컬 파일이나 URL Request를 통해, 또는 문자열 그대로.. 여튼, 아래와 같이 문자열로 구성된 데이터가 있다고 하자.

[
	{
		"주소":"전라남도 무안군 무안읍 면성1길 78",
		"인구":"100",
		"_상태":"OK",
		"WKT": "POINT(151985.4391669556 266232.22030393773)"
	},
	{
		"주소":"전라남도 무안군 무안읍 성남리 779-2",
		"인구":"50",
		"_상태":"OK",
		"WKT": "POINT(152027.07037272514 265628.6982788675)"
	},
	{
		"주소":"전라남도 무안군 무안읍 무안로 513-8",
		"인구":"77",
		"_상태":"OK",
		"WKT": "POINT(152432.06457469938 266037.0198316685)"
	}
]

위의 문자열에서 고려해야할 유일한 규칙은 좌표 데이터를 구성하기 위해서 WKT 필드가 활용(대소문자 구분)된다는 점이다. 그외의 필드는 모두 속성 필드로 해석된다. 이 JSON 문자열을 지도의 구성 단위인 레이어로 추가하기 위한 코드는 다음과 같다.

var json = 
    '[ \
        { \
            "주소": "전라남도 무안군 무안읍 면성1길 78", \
            "인구": "100", \
            "_상태": "OK", \
            "WKT": "POINT(151985.4391669556 266232.22030393773)" \
        }, \
        { \
            "주소": "전라남도 무안군 무안읍 성남리 779-2", \
            "인구": "50", \
            "_상태": "OK", \
            "WKT": "POINT(152027.07037272514 265628.6982788675)" \
        }, \
        { \
            "주소": "전라남도 무안군 무안읍 무안로 513-8", \
            "인구": "77", \
            "_상태": "OK", \
            "WKT": "POINT(152432.06457469938 266037.0198316685)" \
        } \
    ]';

var lyr = new Xr.layers.FeatureJSONLayer("레이어 이름", { EPSG: 4326, dataset: json });
map.layers().add(lyr);
map.update();

지리정보시스템(GIS)를 활용한 통계지도

지리정보시스템(GIS)의 중요한 기능 중 하나는 데이터에 대한 효과적인 시각화입니다. 그 중 위치에 기반하여 통계 데이터를 주제도와 그래프의 형태로 시각화 할 수 있습니다. 이러한 통계지도 작성을 위해 통계 데이터로써 아래와 같이 엑셀 등과 같은 프로그램에서 저장된 파일 사용합니다.

위의 데이터를 보면 행정구역의 이름에 대한 다양한 통계값들이 나열되어 있어 있는 형태입니다. 이를 컴마(,)로 값을 분리한 형식인 CSV로 저장합니다. 이처럼 위와 같은 CSV 형식의 데이터 파일의 남자와 여자에 대한 인구수를 합해 주제도로 표현한 통계지도는 다음과 같습니다.

또한 아래는 동일한 데이터 파일에 대해서 여자와 남자의 인구수를 축으로 하여 파이차트로 표현한 통계지도입니다.

위의 통계지도는 넥스젠(NexGen)이라는 GIS 솔루션을 통해 작성된 것인데요. 넥스젠에서 통계지도 기능에 대한 활용 동영상은 아래 글을 참고하시기 바랍니다.

넥스젠(NexGen)의 통계지도 기능

활성화 함수(Activation Function)

활성화함수는 입력값이 특정 뉴런에서 처리되어 결과값을 생성할때 적용되는 함수입니다. 활성화 함수로 이 글에서는 3가지를 언급하는데 첫째는 계단함수, 둘째는 시그모이드 함수, 셋째는 ReLU 함수입니다. 각 활성화 함수의 수식과 그래프를 살펴보면 다음과 같습니다.

시그모이드 함수(Sigmoid Function)

    $$h(x)=\frac{1}{1+e^{-x}}$$

위의 식을 그래프로 시각화하기 위한 코드는 아래와 같습니다.

import numpy as np
import matplotlib.pylab as plt

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.arange(-10.0, 10, 0.1)
y = sigmoid(x)

plt.plot(x, y)
plt.show()

결과 그래프는 아래와 같습니다.

계단함수(Step Function)

    $$h(x)=\begin{cases}     0  & \quad (x \leq 0)\\     1  & \quad (x > 0)   \end{cases}$$

위의 식을 그래프로 시각화하기 위한 코드는 아래와 같습니다.

import numpy as np
import matplotlib.pylab as plt

def step(x):
    return np.array(x > 0, dtype=np.int)

x = np.arange(-10.0, 10, 0.1)
y = step(x)

plt.plot(x, y)
plt.show()

결과 그래프는 아래와 같습니다.

ReLU

    $$h(x)=\begin{cases}     0  & \quad (x \leq 0)\\     x  & \quad (x > 0)   \end{cases}$$

위의 식을 그래프로 시각화하기 위한 코드는 아래와 같습니다.

import numpy as np
import matplotlib.pylab as plt

def ReLU(x):
    return np.maximum(0, x)

x = np.arange(-10.0, 10, 0.1)
y = ReLU(x)

plt.plot(x, y)
plt.show()

결과 그래프는 아래와 같습니다.

Softmax

모델의 마지막 구성인 출력층에서 입력 데이터가 어떤 클래스로 분류되는지에 대한 확률값으로써 사용되는 활성화 함수로 식은 다음과 같습니다.

    $$y_{k}=\frac{\exp(a_{k})}{\displaystyle\sum_{i=1}^{n} {\exp(a_{i})}}$$

결과적으로 각 출력값들의 합은 1로써 각 출력값을 확률로 해석할 수 있습니다.

쌍곡탄젠트(Hyperbolic Tangent)

활성화 함수로 사용되는 신경망은 대표적으로 RNN입니다. RNN은 순환신경망(Recurrent Network Network)입니다. 수식은 아래와 같습니다.

    $$tanh(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}$$

그래프로 그려보면 다음과 같습니다.

신경망에서의 활성화함수는 각 뉴런계층의 값을 그 다음 뉴런계층으로 전달할때 비선형성을 부여해주게 됩니다. 즉, 활성화함수는 반드시 비선형홤수여야 하며 손실값의 최소화를 위한 경사하강법을 위해 반드시 미분 가능한 함수여야 합니다.

아나콘다(Anaconda) 설치

만약 기존에 Python이 설치되어 있다면 Uninstall(설치 디렉토리 안의 추가로 설치한 패키지는 남아 있으므로 직접 삭제 해야함)하고 관련된 시스템 속성에서 Path를 제거한다. 그리고 Anaconda 사이트에서 설치본을 다운로드 받아 설치한다. 별도의 사용자 설정없이 모두 Next로 설치를 진행하고 Path를 아래처럼 추가한다. (4개 추가했음)

폴더를 하나 만들고, 명령창에서 해당 폴더로 이동한 후 다음처럼 입력한다.

jupyter notebook

해당 폴더에서 웹으로 다양한 파이선 코드를 테스트해볼 수 있게 된다.