로지스틱 회귀(Logistic Regression)

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
import matplotlib.pyplot as plt

아래와 같은 데이터가 있다고 생각해보겠습니다. 독립변수 x는 0-9까지의 값이 입력되어 있고 종속변수 y는 연속된 수치 형태의 값이 아닌 0,1의 이항변수로 되어 있습니다. 데이터를 통해 보니 0-4까지는 0, 5-9까지는 1을 리턴하는 형태입니다. 예를 들어서 체력 테스트를 하는데 턱걸이 0-4까지는 0(불합격), 5-9까지는 합격(1) 이라고 가정해보겠습니다.

이러한 문제를 선형회귀의 형태로 분석해본다면 어떻게될까요? 분석이 가능할까요?
가능해보기도 합니다. 먼저 데이터를 시각화 해보겠습니다.

x = [[1],[2],[3],[4],[0],[5],[6],[7],[8],[9]]
y = [0,0,0,0,0,1,1,1,1,1]
line = np.linspace(0,1,num=10)
yline = np.linspace(0.5,0.5,num=10)

plt.scatter(x,y)
plt.plot(line)
plt.plot(yline)

데이터를 시각화 해보면 위와 같은 형태의 그래프가 표시됩니다. 그리고 이러한 데이터라면 선형회귀로도 가능할듯 보입니다. 0-4, 5-9의 데이터를 중간에 주황색 선을 0.5를 기준으로 입력되는 데이터가 0.5 보다 크면 1을 출력하고 작으면 0을 출력하게 하면 될것같습니다.
이러한 기본 컨셉을 가지고 선형회귀를 통해서 해당 내용을 구현해보겠습니다.

from sklearn.linear_model import LinearRegression
linear = LinearRegression().fit(x,y)
predict = linear.predict([[50]])
print(predict)
# 7.39393939

위와 같은 모델을 구현하고 테스트 해보니 0-9까지의 턱걸이 갯수로는 예측이 비교적 잘되는듯 보입니다. 하지만 어떤 사람이 와서 턱걸이를 50회 혹은 그 이상을 하게되면 예측치가 합격(1)/불합격(0)을 벗어서나 7이라는 숫자가 나오게됩니다. 더 큰 숫자를 입력하면 예측치가 더 높게 나오겠죠.

이렇게되면 이 모델은 사용할 수가 없습니다.
만약 이럴 때에 턱걸이를 아무리 많이 해도 합격과 불합격으로만 결과치를 표시하고 싶다면 어떻게 하는게 좋을까요?

이럴 때에 사용하는 것이 로지스틱 회귀입니다. 같은 데이터로 sklearn의 로지스틱회귀(LogisticRegression) 패키지를 사용해서 모델을 만들어보면 50이라는 큰 숫자를 입력해도 합격을 의미하는 1이라는 값을 보여주게됩니다.

from sklearn.linear_model import LogisticRegression
logistic = LogisticRegression().fit(x,y)
predict = logistic.predict([[50]])
print(predict)
# 1

이러한 것을 가능하게 해주는 것은 Hypothesis가 기존의 선형회귀 모델과 다른 특별한 형태를 가지기 때문입니다. 이것을 Sigmoid라고 부릅니다.

이 함수에서 보면 z의 값은 선형회귀의 Wx+b의 값, 즉 선형회귀에서의 출력값을 사용합니다. 수식에 따라서 출력은 0-1 사이의 실수 값을 리턴하게 되고 0.5를 기준으로 작으면 0을 크면 1을 출력해줄 수 있게됩니다.

그렇다면 최적화는 어떻게 하게될까? 선형회귀에서는 MSE(Mean Square Error)를 사용하지만 로지스틱에서는 아래와 같은 Cross-Entropy를 사용합니다.

https://towardsdatascience.com/cross-entropy-for-classification-d98e7f974451

위의 식은 두개의 식을 하나로 합친 것입니다.

결정트리(Decision Tree)

결정트리는 결론에 이르기 위해서 “예/아니오”의 답변을 요구하는 질문을 반복하면서 학습을 진행합니다. 마치 스무고개 질문과 비슷하죠. 최소한의 적은 질문으로 어떤 문제를 해결하기 위해서는 질문을 잘해야 하는것처럼 결정트리의 중요한 부분도 “어떻게 질문하는가?” 하는 것 중요합니다.

https://kr.akinator.com/

위의 프로그램은 “akinator”라는 게임입니다. 사용자는 아키네이터의 질문에 따라서 답을 고르게 됩니다. 이런 방법으로 몇차례 질문과 답을 주고 받으면 내가 머리속으로 그려낸 인물에 대해서 정확하게 예측합니다. 이것은 이미 이 프로그램을 통해 게임을 진행했던 사람들의 많은 데이터가 축적되어 있기 때문에 가능한 일입니다. 제가 테스트 했던 인물은 축구선수 손흥민이 이었는데 손흥민선수는 이미 222,308회 선택되어 졌기 때문에 많은 데이터가 축적되어 있었습니다. 그렇기 때문에 몇번 안돼서 아키네이터가 답을 맞췄습니다.

결정트리가 마치 이와 같습니다. 결정트리도 데이터셋에서 질문을 통해서 분류(classification) 작업을 수행합니다. 맨 마지막에 마지막 답이 남을 때까지 데이터를 분류합니다. 이렇게 남는 맨 마지막 노드를 리프(lefa) 노드라고 합니다. 그리고 각 노드들은 엣지(edge)로 연결되어 있습니다.

분류에도 여러 알고리즘들이 있는데 그렇다명 언제 결정트리(Decision Tree)를 사용하면 좋을까요?
예를 들어서 고객이 은행에 대출요청을 했고 은행이 고객에 대한 수락 요청을 거부했을 경우 고객에게 어떤 이유로 대출이 불가한지 설명해야 한다면 바로 이 의사결정트리가 유용한 알고리즘이 될 수있습니다.

해당 알고리즘이 가장 높은 성능을 발휘한다는 보장은 없지만 그래도 어떤 결정에 대해서 설명이 가능하다는 것은 중요한 이유가됩니다. 바로 한눈에 보고 이해할 수 있다는 이러한 정점이 있기 때문에 어떤 결정에 합리적인 판단 기준으로 제시하기 위해 해당 알고리즘을 사용합니다.

의사결정나무은 예측 변수를 기반으로 결과를 분류하거나 예측하는데 이러한 의사결정의 규칙(Decision Rule)을 나타내는 과정이 마치 나무구조와 같다고 해서 붙여진 이름입니다. 해당 알고리즘을 활용하면 분류(classification)나 예측(prediction)도 수행이 가능합니다.

아래의 예제는 붓꽃 데이터를 Decision-Tree 알고리즘을 통해 구성한 결과입니다.

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# DecicionTreeClassifier 생성
tree_iris = DecisionTreeClassifier()

# 붓꽃 데이터를 로딩하고, 학습과 테스트 데이터 세트로 분리
iris_data = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=11)

# DecisionTreeClassifier 학습
tree_iris.fit(X_train, y_train)
from sklearn.tree import export_graphviz
export_graphviz(tree_iris, out_file='tree-iris.dot', class_names=['Setosa','Versicolour','Virginica'], feature_names=iris_data.feature_names, impurity=False, filled=True)

분류를 수행하고 그 결과를 ‘tree_iris.dot’이라는 파일로 기록합니다.

digraph Tree {
node [shape=box, style="filled", color="black"] ;
0 [label="petal length (cm) <= 2.45\nsamples = 120\nvalue = [41, 40, 39]\nclass = Setosa", fillcolor="#fffdfd"] ;
1 [label="samples = 41\nvalue = [41, 0, 0]\nclass = Setosa", fillcolor="#e58139"] ;
0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ...

해당 파일을 읽어보면 위와 같이 코드의 형태로 표시됩니다. 이렇게 만들어진 코드를 바로 이해해도 되겠지만 무리가 있죠. 파이썬의 graphviz라는 패키지를 이용하면 이러한 데이터를 훌륭히 시각화 할 수 있습니다.

# 아래와 같이 패키지를 설치합니다.
conda install python-graphviz

패키지를 설치하고 tree-iris.dot 파일을 읽어보겠습니다.

import graphviz

with open('./tree-iris.dot') as f:
    dot_graph = f.read()
display(graphviz.Source(dot_graph))

위의 그림을 살펴보면 결정트리를 IF(petal length <= 2.45) THEN Setosa ELSE IF(petal width <= 1.55) and IF(petal length <= 5.25) THEN vesicolour … 와 같은 방법으로 표현할 수 있습니다. 그렇기 때문에 어떤 결과를 얻었을 때에 규칙을 통해서 데이터를 얻었다고 이야기 할 수 있는 것입니다.

가장 위에 있는 노드는 Root-Node 라고 하고 Parent-Child Node의 연결이 마치 나무의 구조와 같다고 해서 의사결정나무라고도 부릅니다. 그리고 맨 마지막에 있는 것이 Teminal-Node라고 부릅니다. 각 노드의 연결선을 엣지(Edge) 라고 하고 Yes(True)/No(False)를 의미합니다.

루트노드는 가장 첫번째 질문으로 매우 중요합니다. 이 질문은 가능한 많은 데이터를 분류 할 수 있는 질문이어야 합니다. 위의 경우는 Petal Length <= 2.45를 기준으로 총 120개의 샘플 데이터를 41:79로 분류했습니다. 그리고 41은 전부 “Setosa”라는 종류라는 것을 얻었습니다. 그리고 나머지 79개의 데이터를 다시 Petal Width <= 1.55라는 조건을 통해서 38:41의 데이터로 분류해 냅니다. 이런 과정을 반복하면서 데이터를 마지막가지 분류하게됩니다.

이 결정트리를 그동안 사용했던 fitness.csv를 통해서 적용해 보겠습니다.

dataset = pd.read_csv('./fitness.csv')
dataset.loc[ dataset['age'] < 40, 'ACODE']= 1
dataset.loc[ (dataset['age'] >= 40) & (dataset['age'] <50), 'ACODE']= 2
dataset.loc[ (dataset['age'] >= 50) & (dataset['age'] <60), 'ACODE']= 3

x_data = dataset[dataset.columns[1:-1]].values
y_data = dataset[dataset.columns[-1]].values

fit_tree = DecisionTreeClassifier()
fit_tree.fit(x_data, y_data)

from sklearn.tree import export_graphviz
export_graphviz(fit_tree, out_file='tree-fit.dot', class_names=['30','40','50'], feature_names=dataset.columns[1:-1], impurity=False, filled=True)

with open('./tree-fit.dot') as f:
    dot_graph = f.read()
display(graphviz.Source(dot_graph))

위에서 보시는것처럼 IF maxpulse <= 174 AND oxygen <= 46.723 THEN 50 라는 예측결과를 얻었습니다. 이런 방법으로 조건의 만족 여부를 따라서 이동하다보면 결국 class 값을 얻을 수 있습니다.

위 예제에서는 이지분리(Binary Split)을 사용했지만 알고리즘에 따라서 다지분리(Multi Split)도 가능합니다.

로지스틱 회귀(Logistic Regression) 다중분류

이전까지는 age, weight, oxygen, runtime, runpulse, rstpulse 데이터를 통해서 maxpulse를 예측하는 예제를 살펴봤습니다. maxpulse는 연속된 값을 가지고 있는 수치-연속형 데이터입니다.

dataset = pd.read_csv('./fitness.csv')
dataset.head()

데이터의 특징은 아래와 같습니다.

dataset.info()

RangeIndex: 31 entries, 0 to 30
Data columns (total 7 columns):
# Column Non-Null Count Dtype
— —— ————– —–
0 age 31 non-null int64
1 weight 31 non-null float64
2 oxygen 31 non-null float64
3 runtime 31 non-null float64
4 runpulse 31 non-null int64
5 rstpulse 31 non-null int64
6 maxpulse 31 non-null int64
dtypes: float64(3), int64(4)
memory usage: 1.8 KB

앞으로 하려고 하는 부분은 weight, oxygen, runtime, runpulse, rstpulse, maxpulse 칼럼 정보를 통해서 이 사람이 어느 나이대인가를 예측하는 분류의 문제를 해결해보도록 하겠습니다.

그러나 아쉽게도 예제 데이터는 범주형 컬럼은 없고 모두 수치형-연속형 데이터들입니다. 그렇기 때문에 범주형-순서형 데이터 컬럼을 하나 만들어줘야합니다. 아래와 같은 범주형 데이터 컬럼을 하나 만들겠습니다.
나이가 40대 이하이면 1, 40~50 사이에 있으면 2, 50~60 사이에 있으면 3 이렇게 넣어보겠습니다.

범주형 : 몇개의 범주로 나눠진 자료로 명목형, 순서형 데이터가 있음
* 명목형 : 성별, 성공여부, 혈액형 등 단순히 분류된 자료
* 순서형 : 개개의 값들이 이산적이며 그들 사이에 순서 관계가 존재하는 자료

수치형 : 이산형과 연속형으로 이뤄진 자료로 이산형과 연속적인 형태의 자료가 있음
* 이산형 : 이산적인 값을 갖는 데이터로 출산 횟수 등을 의미
* 연속형 : 연속적인 값을 갖는 데이터로 키나 몸무게 등을 의미

dataset.loc[ dataset['age'] < 40, 'ACODE']= 1
dataset.loc[ (dataset['age'] >= 40) & (dataset['age'] <50), 'ACODE']= 2
dataset.loc[ (dataset['age'] >= 50) & (dataset['age'] <60), 'ACODE']= 3

훈련에 사용하는 컬럼은 나이를 제외한 나머지 컬럼들입니다. 어떤 내용들이 있는지 살펴보면 아래와 같습니다.

해당 데이터를 3차원 평면에 나타내보면 아래와 같습니다. 그래프를 보면 3개의 포인터가 다른 색으로 분류되어 있습니다. 데이터가 많지 않고 5차원 데이터를 3차원으로 축소한 형태이기 때문에 각 분류가 정확하지 않은것 같습니다. 학습 데이터가 좋은 형태로 되어 있다면 아마도 그래프의 각 요소들이 잘 구분되어 있을테지만 아쉽게도 예제 데이터는 그렇지 않은듯합니다.

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(1, figsize=(8, 6))
ax = Axes3D(fig, elev=-150, azim=110)
X_reduced = PCA(n_components=3).fit_transform(dataset[dataset.columns[:-1]])
ax.scatter(X_reduced[:, 0], X_reduced[:, 1], X_reduced[:, 2], c=y,
           cmap=plt.cm.Set1, edgecolor='k', s=40)
ax.set_title("First three PCA directions")
ax.set_xlabel("1st eigenvector")
ax.w_xaxis.set_ticklabels([])
ax.set_ylabel("2nd eigenvector")
ax.w_yaxis.set_ticklabels([])
ax.set_zlabel("3rd eigenvector")
ax.w_zaxis.set_ticklabels([])

plt.show()

이제 sklearn 패키지의 분류문제를 풀수 있는 LogisticRegression를 사용해보겠습니다. 사용하는 방법은 LinearRegression과 같이 간단하게 사용할 수 있습니다. x 데이터와 y 데이터는 위에서 설명해드린 데이터를 사용합니다.

그런다음 LogisticRegression에서 옵션을 선택하고 해당 모델을 학습합니다. sklearn은 효율적인 학습을 위해 몇가지 효율적인 학습 알고리즘을 제시합니다. 특히 feature의 수, 학습 데이터의 양, 분류할 문제의 수 등을 따라서 필요한 알고리즘을 선택합니다.

이번 예제는 3개의 분류 문제(multiclass problems)를 풀어야 하기 때문에 ‘newton-cg’ 알고리즘을 사용하도록 하겠습니다. 이 외에도 여러 알고리즘과 파라메터가 있습니다. sklearn 공식 사이트에서 해당 내용들을 확이해보시기 바랍니다.

https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
x = dataset[dataset.columns[1:-1]].values
y = dataset['ACODE'].values

from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(solver='newton-cg', max_iter=100).fit(x, y)

predict = clf.predict(x)
accuracy=(predict.reshape(-1,1) == y.reshape(-1,1)).sum()/len(y)
print(accuracy)
# 0.8387096774193549 정확도

알고리즘을 통해 확인해보니 0.8387 의 정확도를 보여줍니다. 학습용 데이터가 작기 때문에 테스트용 데이터를 만들 수 없었던 이유로 정확도 계산을 한다고 하기도 좀 애매하지만 더 많은 데이터를 통해서 훈련과 테스트를 해보시길 권해드립니다.

그리고 학습이 완료된 후에 기울기와 절편 정보를 출력해보면 아래와 같습니다.

print('W:{}, b:{}'.format(clf.coef_.T, clf.intercept_.T))
W:[[ 0.46533128 -0.20477993 -0.26055162]
 [ 0.60397447 -0.08600717 -0.5179662 ]
 [-0.13946355  0.42710757 -0.28764394]
 [ 0.21778426 -0.00474545 -0.2130358 ]
 [ 0.00648806  0.03361824 -0.04010692]
 [ 0.05094977 -0.07150677  0.02055985]], b:[-116.45602409   30.69276337   85.76326072]

그리고 이러한 데이터를 통해서 직접 행렬곱을 해봐도 같은 결과가 나오는 것을 확인할 수 있습니다.

y_hat = np.matmul(x,clf.coef_.T)+clf.intercept_
y_hat.argmax(axis=1)+1

회귀분석(다중회귀)

이전 글에는 하나의 독립변수를 사용한 예측을 테스트했습니다. 이번에는 2개 이상의 독립변수를 포함하는 다중회귀분석에 대해서 동일한 데이터셋을 활용해서 알아보겠습니다.

dataset = pd.read_csv('./fitness.csv')
dataset[0:10]

지난 예제에서 runpulse를 통해서 maxpulse를 예측하는 회귀분석을 수행했습니다. 여기서 runpulse는 독립변수로서 종속변수인 maxpulse에 영향을 주는 변수입니다. 이번에도 maxpulse를 예측할 것인데 독립변수가 age, weight, oxygen, runtime, runpulse, rstpulse 이렇게 6개입니다.

먼저 예측할 maxpulse를 제외한 데이터셋을 만들어줍니다. 이것을 dataset2라고 정의하겠습니다. dataset2은 6개의 column과 31개의 row를 가진 [31 × 6] 행렬이 됩니다. 이것은 이전 시간에 하나의 독립변수를 통해서 예측을 하기 위해서 작성했던 수식인 y = Xw+b를 확장한 개념입니다.

이것을 풀어서 쓰게 되면 y = X1w1 + X2w2 + X3w3 + X4w4 + X5w5 + X6w6 + b가 되는 것이고 이런 y가 31개가 있다는 의미가 됩니다. 그러니까 각각의 요소에 w1~w6개의 값을 곱해주는 [31 × 6] × [6 × 1] + b의 행렬곱 연산이 되고 이는 [1 × 6] × [6 × 1] + b 이런 벡터의 내적의 합이 31개가 된다는 동일한 의미입니다.

dataset2 = dataset[dataset.columns[0:-1]]
print(dataset2.values)

이제 이전에 단순회귀에서 했던것처럼 sklearn 패키지의 LinearRegression을 임포트 하고 기울기와 절편을 계산합니다. fit()에 두개의 인자값을 주는데 하나는 6개의 독립변수들의 값이 있는 2차원 행렬이고 또 하나는 실제 계산 값이 들어있는 데이터셋입니다. 여기서는 알고자 하는 값인 maxpulse 값이 되겠습니다.

from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(dataset2.values, dataset['maxpulse'].values.reshape(-1,1))
print('W:{}, b:{}'.format(lr.coef_, lr.intercept_))
#W:[[-0.00794636  0.09986232  0.57982346  1.18233886  0.87556793  0.05439718]], b:[-25.0634534]

간단히 계산이 완료되었고 예상했던것처럼 알고자 했던 각 독립변수의 가중치 값인 6개와 절편 값을 얻었습니다. 이 모델이 어느정도 결과값과 일치하는지 차이를 보기 위해서 실제값과 예측값을 그래프로 시각화해보겠습니다.

plt.plot(lr.predict(dataset2.values),'*')
plt.plot(dataset['maxpulse'],'x')

실제값 “x”와 예측값 “*”를 비교해보니 일부 오차가 큰 것들도 있지만 제법 합리적으로 예측을 한것같습니다. 그리고 이 값은 아래와 같이 행렬 곱으로 계산할 수도 있습니다. 여기서 x는 독립 변수들의 값이고 y는 LinearRegression을 통해서 얻은 w, b 값입니다. 이 값들을 활용해서 y = Xw + b 형식으로 풀어낸 값과 동일합니다.

x = dataset2.values
y = lr.coef_.reshape(-1,1)
predict = np.matmul(x,y) + lr.intercept_

회귀분석(단순회귀)

선형모델은 100여년 전부터 사용되어 왔고 현재도 널리 사용되는 모델입니다. 이 모델은 입력 특성에 대한 선형함수 모델을 만들어서 예측을 수행하는 모델입니다. 간단히 직선의 방정식을 생각하면 됩니다. 기울기 w와 y축과 만나는 절편 b 값을 알아내는 것이 해당 모델의 핵심이라고 할 수 있습니다.

예를 들어 아래와 같은 fitness.csv 데이터셋이 있다고 가정해봅니다. 해당 데이터는 [‘age’, ‘weight’, ‘oxygen’, ‘runtime’, ‘runpulse’, ‘rstpulse’,’maxpulse’]의 7개의 Feature로 되어 있습니다. pandas 패키지로 파일을 읽어서 상위 10개의 데이터를 표시해봅니다.

dataset = pd.read_csv('./fitness.csv')
dataset[0:10]

각각의 column 혹은 feature 정보는 관계가 있을 수도 혹은 별다른 관계가 없을 수도 있습니다. 예를 들어서 위의 feature 정보를 통해서 아래와 같이 3개의 산점도(scatter)를 그려보면 1번은 두개의 변수간에 상관관계가 약하다고 할 수 있지만 아래의 두개 즉, runpulse-maxpulse, oxygen-runtime은 어느정도 상관관계가 있다고 할 수 있습니다. 이것을 방향에 따라서 양의 상관관계, 음의 상관관계라고 할 수 있습니다.

2번째 그래프와 같이 runpulse과 maxpulse의 관계를 통해서 본다면 runpulse 값이 증가할 수록 maxpulse 값도 함께 증가하는 것을 알 수 있습니다. 그렇기 때문에 이런 관계를 통해서 어떤 규칙성을 만들면 runpulse가 주어졌을 때 maxpulse를 어느정도는 예측할 수 있습니다. 이것을 통계적인 용어로 보면 독립변수인 runpulse의 변화에 따른 종속변수인 maxpulse의 상관관계를 파악하는 것이라고 할 수 있습니다.

fig, axes = plt.subplots(3,1,figsize=(10,12))
axes[0].set_title('runtime - maxpulse')
axes[0].scatter(dataset['runtime'],dataset['maxpulse'])

axes[1].set_title('runpulse - maxpulse')
axes[1].scatter(dataset['runpulse'],dataset['maxpulse'])

axes[2].set_title('oxygen - runtime')
axes[2].scatter(dataset['oxygen'],dataset['runtime'])

회귀분석을 위해서는 특정 독립 변수 값에 해당하는 종속변수의 값이 정규분포를 이뤄야합니다. 그래야 어떤 규칙성을 발견할 수 있습니다. 또 종속변수들의 값은 서로 독립적이어야 하는데 만일 그렇지 않으면 어떤 부분이 종속변수에 영향을 미치는지 발견하기가 어렵습니다. 이에 대한 연장으로 독립변수가 여러 개인 경우 변수간에 영향을 주지 않아야 합니다.

이런 이유로 회귀분석을 생각해 보면 아래의 표와 같이 독립변수의 수, 척도, 관계 등에 의해 회귀분석의 종류를 나눌 수 있습니다.

이훈영의 연구방법론, p.400

아래의 runpulse-maxpulse 그래프를 다시 살펴보면 데이터의 어떤 규칙성을 발견할 수 있습니다. 이제 이 데이터를 특징을 가장 잘 나타내는 직선을 긋는다고 생각해보겠습니다.

사람은 직관에 의해서 선을 한번에 그을 수 있습니다. 비록 정확하진 않더라도 정답과 비슷한 모양으로 선을 그을 수 있지만 컴퓨터는 그렇게 할 수 없습니다. 선형회귀는 바로 컴퓨터가 점들을 잘 표현하는 직선(Y = Xw + b)을 그리는 과정이라고 할 수 있습니다. 여기서 궁금한 것이 바로 w와 b 값입니다.

그중에서 가장 많이 사용하는 알고리즘을 sklearn에서 구현한 LinearRegression을 통해서 runpulse-maxpulse 회귀분석을 구현해보겠습니다. 구현은 생각보다 간단합니다. sklearn 패키지는 이런 복잡한 작업들을 단 2줄에 해결 할 수 있도록 편리한 함수를 제공해줍니다.

from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(dataset['runpulse'].values.reshape(-1,1),dataset['maxpulse'].values.reshape(-1,1))
print('W:{}, b:{}'.format(lr.coef_, lr.intercept_))
# W:[[0.83109283]], b:[32.78331594]

간단히 우리가 구하고자 하는 w, b 값을 구했습니다. 이제 두개의 미지수를 찾았으니 어떤 X 입력 값에 대해서 Y(여기서는 maxpulse)를 쉽게 예측할 수 있습니다. 실제로 데이터 예측을 해보겠습니다. fitness.csv 데이터가 많다면 훈련용 셋과 테스트 셋을 나눠서 정확도를 보겠지만 원본 데이터의 갯수가 30개라서 테스트용 데이터를 나누지 않고 위의 값 중에서 없는 runpulse 값을 찾고 이때에 maxpulse를 예측해보겠습니다.

dataset.loc[(dataset['runpulse']>=178) & (dataset['runpulse']<=180)]

위와 같은 조건으로 검색해보니 179가 없는 것을 확인했습니다. 179를 입력했을 때에 180과 185 사이의 값으로 예측된다면 예측 모델이 비교적 정확하다고 할 수 있겠습니다.

y_hat = lr.predict([[179]])
#array([[181.54893295]])

예측 결과 181.54 정도가 나와서 비교적 합리적인 값을 도출해냈습니다. 이렇게 해도 되고 y=Xw+b의 식에 값을 대입해도 동일한 y 값을 얻을 수 있습니다.

plt.plot(dataset['runpulse'],lr.predict(dataset['maxpulse'].values.reshape(-1,1)),'*')
plt.plot(dataset['runpulse'],dataset['maxpulse'],'x')

최근접이웃 알고리즘(K-NN)

k-NN(Nearest Neighbors) 알고리즘은 가장 간단한 머신러닝 알고리즘입니다. 훈련용 데이터셋을 통해서 모델을 만들고 새로운 데이터가 입력될 때는 훈련 데이터셋에서 가장 가까운 “최근접 이웃”을 찾습니다.

KNN 알고리즘의 좋은 설명을 해주는 사이트의 허민석님의 강의를 올려드리니 참고해보시면 좋겠습니다. 이 외에도 알고리즘을 설명하는 많은 강의 사이트가 있으니 개념이 궁금하신 분들은 찾아보시길 추천해드립니다.

이번 글에서는 개념에 대한 설명보다는 바로 예제 코드를 살펴보도록 하겠습니다. 해당 알고리즘은 sklearn에서 이미 잘 구현했기 때문에 사용자가 별도의 알고리즘을 구현한 필요가 없습니다. 사용자는 단 몇줄의 코드만으로 해당 알고리즘을 사용할 수 있습니다. 예제는 sklearn에서 제공하는 KNeighborsClassifier를 사용하도록 하겠습니다. 사용하는 데이터 역시 sklearn에서 제공하는 load_iris() 데이터를 사용해보겠습니다.

Introduction to Machine Learning with Python, KNeighborsClassifier

sklearn에서 제공하는 다양한 데이터셋이 있습니다. 그중에 이번 예제는 붗꽃 데이터를 사용해보겠습니다. 붗꽃 데이터는 꽃받침과 꽃잎의 넓이와 길이 정보와 붗꽃의 종류(setosa, versicolor, virginica) 데이터의 형식으로 되어 있습니다. 4개의 feature 정보와 label 컬럼이 있고 데이터의 수는 150개 정도이기 때문에 분류 문제를 테스트해보기에 적절한 예제입니다.

from sklearn.datasets import load_iris
iris = load_iris()
iris.keys()
#dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])

feature 정보는 아래와 같이 sepal(꽃받침) length, sepal width, petal(꽃잎) length, petal width의 네개 컬럼 정보가 들어있습니다.

iris.feature_names
#['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

해당 데이터를 pandas의 데이터프레임으로 데이터 타입을 바꾸면 좀 더 편하게 데이터의 내용을 확인 할 수 있고 pandas에서 제공하는 다양한 함수를 사용할 수 있습니다.

import pandas as pd
df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
df.head()
df.describe()

sklearn의 KNeighborsClassifier 패키지를 임포트합니다. 그리고 데이터를 훈령용 세트와 테스트용 세트를 8:2로 분리해서 x_train, y_train, x_test, y_test 형태로 데이터를 생성합니다. 데이터 세트를 분리한 후에 shape을 보면 120:30의 형태로 데이터가 나뉜 것을 확인할 수 있습니다.

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, shuffle=False, random_state=701)
print(x_train.shape, x_test.shape)
#(120, 4) (30, 4)

n_neighbors의 적절한 이웃 값을 찾기 위해서 데이터 셋을 테스트해봅니다. 이웃 값을 너무 적게 하면 모델의 복잡도가 올라가고 너무 많게 하면 모델의 예측력이 떨어지기 때문에 적절한 값을 찾는 것이 중요합니다.

train_accuracy = []
test_accuracy = []
neighbors_set = range(1,11)
for n_neighbors in neighbors_set:    
    clf = KNeighborsClassifier(n_neighbors=n_neighbors)
    clf.fit(x_train, y_train)
    score = clf.score(x_test, y_test)
    train_accuracy.append(clf.score(x_train, y_train))
    test_accuracy.append(clf.score(x_test, y_test))
import matplotlib.pyplot as plt

plt.plot(neighbors_set, train_accuracy, label='train')
plt.plot(neighbors_set, test_accuracy, label='test')
plt.legend()

위의 결과 값과 같이 이웃 값(n_neighbors)이 5일 경우에 가장 높은 예측 정확도를 보여줍니다. 모델을 생성한 후 예측의 정확도를 보기 위해 테스트 데이터를 사용해서 예측을 수행하고 이 값을 테스트 값과 비교해본 결과 80%의 예측 정확도를 얻었습니다.

clf = KNeighborsClassifier(n_neighbors=5)
clf.fit(x_train, y_train)
clf.predict(x_test)
# array([2, 2, 2, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1])
(clf.predict(x_test) == y_test).sum()/len(y_test) # 0.8

다만 이 알고리즘을 사용할 경우 이웃을 어떻게 적절히 정의할 것인가에 대한 고민이 필요합니다. 만약 이웃을 적게 사용한다면 모델의 복잡도가 높아지고 많이 사용하면 복잡도는 낮아집니다. 위의 이미지에서 가장 오른쪽에 이미지의 경계면이 가장 부드럽지만 이렇게되면 모델이 지나치게 일반화 되어서 예측하는 값의 정확도가 낮아질 염려가 있습니다.

k-NN 알고리즘의 특징은 이해하기 매우 쉬운 모델이라는 점입니다. 사용이 비교적 간단하지 좋은 성능을 발휘하기 때문에 어떤 높은 난이도의 문제를 해결하기 전에 시도해봄직한 모델이라고 할 수 있습니다. 그러나 쉬운 모델이지만 훈련용 세트가 커지만 예측이 느려지는 특징이 있기 때문에 널리 사용되진 않습니다.

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class ANN(nn.Module):
    
    def __init__(self, D_in, H, D_out):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(D_in, H),
            nn.ReLU(),
            nn.Linear(H, D_out),
        )
    
    def forward(self, x):
        x = x.float()
        return self.layers(x)

# input dim, hidden size, ont-hot
model = ANN(tx_train.size(dim=1),5,torch.unique(ty_train).size(dim=0))
model

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters())

nb_epochs = 5001
for epoch in range(nb_epochs):
    model.train()
    predict = model(tx_train)
    loss = criterion(predict, ty_train.squeeze().long())
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    if epoch % 250 == 0:
        print('{}/{}, cost:{:.5f}'.format(epoch,nb_epochs,loss.item()))

참고로 위의 코드는 pytorch를 사용해서 동일한 예측 코드를 Linear Regression으로 구현한 것입니다.

sklearn.datasets

Load_Boston 데이터셋

sklearn에서는 간단한 머신러닝 알고리즘 분석과 테스트를 위해서 작은 규모의 데이터셋(small toy datasets)을 datasets이라는 패키지에 담아두었습니다. 해당 데이터셋들은 데이터에 대한 정보와 특징들이 포함되어 있어 데이터셋을 이용하는 사용자로 하여금 해당 데이터셋이 어떤 의미인가를 쉽게 이해할 수 있도록 해주고 있습니다.

https://scikit-learn.org/stable/datasets/index.html#toy-datasets

공식 홈페이지에 들어가보시면 제공하는 데이터의 내용들을 확인 하실 수 있습니다. 간략한 정보는 아래와 같습니다.

  • load_boston : 보스턴 지역의 집값 데이터(회귀분석)
  • load_iris : 붗꽃 데이터(분류)
  • load_diabetes : 당뇨병 데이터(회귀분석)
  • load_digits : 숫자 이미지 데이터(분류)
  • load_linnerud : 20대 중반 남성의 신체정보와 운동정보(다변량분석)
  • load_wine : 와인의 특징에 따른 종류(분류)
  • load_breast_cancer : 유방암 데이터(분류)
from sklearn.datasets import load_boston
boston = load_boston()

데이터셋을 로딩하는데는 load라는 명령어와 함께 해당 데이터셋의 이름을 적어주면 간단히 로딩할 수 있습니다.
데이터를 로딩한 후에는 DESCR 을 통해서 해당 데이터셋이 어떤 특징이 있는지 살펴볼 수 있습니다.

print(boston.DESCR)
.. _boston_dataset:

Boston house prices dataset
---------------------------

**Data Set Characteristics:**  

    :Number of Instances: 506 

    :Number of Attributes: 13 numeric/categorical predictive. Median Value (attribute 14) is usually the target.

    :Attribute Information (in order):
        - CRIM     per capita crime rate by town
        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
        - INDUS    proportion of non-retail business acres per town
        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
        - NOX      nitric oxides concentration (parts per 10 million)
        - RM       average number of rooms per dwelling
        - AGE      proportion of owner-occupied units built prior to 1940
        - DIS      weighted distances to five Boston employment centres
        - RAD      index of accessibility to radial highways
        - TAX      full-value property-tax rate per $10,000
        - PTRATIO  pupil-teacher ratio by town
        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
        - LSTAT    % lower status of the population
        - MEDV     Median value of owner-occupied homes in $1000's

    :Missing Attribute Values: None

    :Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
https://archive.ics.uci.edu/ml/machine-learning-databases/housing/
...

DESCR 외에도 해당 boston 데이터셋에는 몇가지의 key 값이 더 있습니다. 해당 키값을 모두 보기 위해서는 boston.key() 명령을 사용합니다.

boston.keys()
# dict_keys(['data', 'target', 'feature_names', 'DESCR', 'filename'])
  • data : 데이터셋의 정보가 numpy.ndarray 형태로 저장
  • target : 레이블 정보
  • feature_names : 데이터셋의 컬럼정보
  • DESCR : 데이터셋 설명
  • filename : 해당 데이터셋의 저장 위치 정보 표시

이렇게 입력된 데이터는 컬럼 정보와 분리되어 있기 때문에 보기에 좀 불편하고 어려울 수 있습니다. 이럴 때에 pandas 패키지를 통해 데이터프레임을 만들면 탐색적데이터분석에 좀 더 유리합니다.

import pandas as pd
df = pd.DataFrame(data=boston.data, columns = boston.feature_names)
df['TARGET'] = boston.target
df.head()

pandas의 데이터프레임으로 해당 데이터를 변경해보면 위와 같은 형태로 보여지기 때문에 데이터를 보기가 좀 더 편리합니다. 좀 더 자세한 정보를 보시고 싶으시면 df.info(), df.describe()와 같은 함수를 사용해보시면 각 컬럼의 데이터 타입과 기본적인 통계정보를 확인 할 수 있습니다. 추가로 matplotlib과 같은 시각화 함수를 사용하면 데이터를 내용을 더 쉽게 파악할 수 있습니다.

import matplotlib.pyplot as plt
plt.figure(figsize=(12,5))
plt.scatter(scaled_df['AGE'],scaled_df['DIS'])
corr = scaled_df.corr()
corr.style.background_gradient(cmap='coolwarm')

시각화로 데이터셋의 내용을 살펴본 후에 학습을 위해서 데이터를 나눠줄 필요가 있을 경우에 sklearn은 훌륭한 유틸 함수를 제공합니다. 아래와 같이 model_selection 패키지에서 train_test_split을 사용해서 학습용, 훈련용 데이터셋을 만들 수 있습니다.

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(boston.data, boston.target, test_size=0.2, shuffle=False, random_state=701)
print(x_train.shape) #404,13
print(x_test.shape) #102,13

Load_Digit 데이터셋

boston 데이터셋은 연속된 값을 가지는 회귀분석에서 많이 사용하는 예제입니다. 이번에는 분류 문제에 사용하는 데이터셋인 숫자(digit) 데이터셋을 살펴보겠습니다. 이 데이터의 shape은 (1797, 64) 입니다. 이것은 8 x 8 이미지를 1차원(64)으로 생성한 값인데 이러한 이미지가 1,797개가 있다는 의미입니다.

from sklearn.datasets import load_digits
digits = load_digits()
digits.data[0]
array([ 0.,  0.,  5., 13.,  9.,  1.,  0.,  0.,  0.,  0., 13., 15., 10.,
       15.,  5.,  0.,  0.,  3., 15.,  2.,  0., 11.,  8.,  0.,  0.,  4.,
       12.,  0.,  0.,  8.,  8.,  0.,  0.,  5.,  8.,  0.,  0.,  9.,  8.,
        0.,  0.,  4., 11.,  0.,  1., 12.,  7.,  0.,  0.,  2., 14.,  5.,
       10., 12.,  0.,  0.,  0.,  0.,  6., 13., 10.,  0.,  0.,  0.])
plt.imshow(digits.data[0].reshape(8,8))

해당 배열의 첫번째 값을 가지고 8×8배열로 만든 후에 이것을 이미지로 표시해보면 위와 같은 이미지 “0” 값이 나옵니다. 즉 이런 데이터가 1,797개가 있다는 의미입니다. 이러한 데이터를 훈련시키면 숫자가 입력될 때 이것이 어떤 숫자인지 예측할 수 있게 됩니다.

from sklearn.decomposition import PCA
pca = PCA(n_components=2) # 시각화를 위해 2차원으로 성분을 추출
dim2data = pca.fit_transform(digits.data)
plt.figure(figsize=(6,6))
plt.scatter(dim2digit[:,0], dim2digit[:, 1], c=digits.target, linewidth=1)
plt.show()

지도학습(Supervised Learning)

정의

지도학습은 가장 널리 사용되는 머신러닝 방법 중에 하나입니다. 지도학습의 특징은 입력(Input)과 출력 데이터(Label)가 존재한다는 것이죠. 기존에 존재하는 Input/Label 데이터 들을 통해서 어떤 알고리즘을 만들고 새로운 데이터가 들어왔을 때에 만들어진 알고리즘에 기반하여 출력값을 내는 것을 의미합니다.

말이 좀 복잡하지만 쉽게 말해서 지도학습이란 그 말 자체에서 처럼 “학습자(여기서는 컴퓨터와 같은 Machine)에게 데이터의 특징과 이에 대한 정확한 답변을 주고 그 특징을 학습시킨 후에 새로운 데이터를 주었을때 학습의 결과로 어떤 결과값을 예측하는 것”이라고 할 수 있겠습니다.

반대의 경우를 비지도학습(Unsupervised Learning)이라고 하는데 이런 경우는 학습자에게 데이터 외에는 아무것도 알려주지 않고 스스로 학습하는 과정을 통해 새로운 데이터가 주어졌을때 결과값을 예측하는 것입니다.

지도학습의 종류

지도학습의 종류는 크게 분류(Classification) 문제와 회귀(Regression) 문제가 있습니다. 간단히 말하면 분류는 입력 데이터가 있을 경우 미리 정의된, 가능성 있는 여러 클래스 레이블 중에 어디에 들어가는가를 예측하는 하는 것입니다. 둘 중에서 어느 그룹에 속하는 것인가를 다르는 것을 이진 분류(Binary Classification)이라고 하고 둘 이상의 그룹 중에서 어디에 들어가는 것인가를 예측하는 것이 다중 분류(Multiclass Classification)이라고 합니다.

실제로 이 분류의 문제는 상당히 많은 곳에서 사용되고 있습니다.

이진분류는 “예/아니오”의 문제를 푸는 것이라고 할 수 있겠습니다. 이 분류는 많은 부분에서 사용되고 있습니다. 이메일에서 스팸을 분류 한다거나, 신용카드 회사에서 이것이 정상적인 거래인가 비정상적인 거래인가를 예측하는 것도 이런 이진 분류의 문제입니다.

반면에 다중 분류의 경우로 가장 많이 알려진 예제는 바로 붗꽃 데이터 예제입니다. 앞으로 다뤄볼 예제이기도 합니다. 해당 데이터는 꽃 받침과 꽃 잎의 길이와 넓이 정보로 해당 붗꽃이 setosa / versicolor / virginica 세개의 품종 중에 어디에 속하는가를 예측하는 분류입니다.
이 외에도 와인의 산도나 당도를 통해서 와인의 종류를 예측하는 문제도 있습니다. 이 외에도 분류의 문제는 다양한 곳에 적용됩니다. 이미지를 통해서 개와 고양이를 분류한다던가 고객이 물건을 구입하거나 하지 않거나를 예측하기도 합니다.

반면 회귀는 연속적인 숫자를 예측하는 것입니다. 몇가지 특징을 통해서 어떤 값을 예측하는 경우를 예측해보면 우리 일상에도 다양한 분야에 적용할 수 있습니다.

예를 들어서 방의 갯수, 면적 등을 통해서 집값을 예측한다거나 교육의 수준, 나이, 주거지 등을 통해서 연간 소득을 예측한다거나 하는 것도 회귀 분석의 예입니다. 이 밖에도 농장에서 전년도 수확량, 날씨, 고용 인원을 통해서 수확량을 예측해보는 것도 회귀 분석의 예입니다.

이 두분석의 가장 큰 차이는 예측값이 정성적 자료 형태 즉, 범주와 순서의 형태인가 아니면 정량적 자료 형태 즉, 어떤 연속적인 수치형태를 가지는가를 생각해보면 쉽게 구분할 수 있습니다.

실제로 어떤 문제를 해결할 때에 “이것을 분류의 문제인가 혹은 회귀의 문제인가”에 대한 정의를 하지 못해서 잘못된 알고리즘을 적용하는 경우가 종종 있습니다.

만약 내가 연소득, 수확량 등의 값을 예측한다면 회귀의 문제입니다. 반면 어떤 직군, 어떤 종류에 속하는가를 예측하는 것은 분류의 문제입니다.

일반화, 과대적합, 과소적합

지도학습이 잘 이뤄진 경우는 훈련 데이터를 통해서 그 데이터셋의 특징을 정확히 파악해서 모델을 만들고 새로운 데이터가 들어왔을 때에 모델을 통해서 정확도가 높은 예측값을 출력해 낼 수 있습니다. 이런 것을 일반화(Generalization)이라고 합니다.

그러나 훈련용 데이터 셋이 너무 복잡하거나 너무 빈약한 경우에 모델이 일반화 되지 않고 단지 훈련용 데이터만 잘 반영하는 일이 발생하기도 합니다. 이런 경우에는 훈련용 데이터로는 정확한 예측을 해내지만 새로운 데이터가 들어왔을 경우에는 정확도가 낮은 값을 예측하게 됩니다. 이런 경우를 모델이 과대적합(Overfitting)되어 있다라고 이야기합니다.

반대로 모델이 너무 간단하면 이러한 모델을 통해서는 지나치게 대략적인 정보만 얻을 수 밖에 없기 때문에 이를 통한 예측 역시 어떤 의미를 찾기 어려울 수도 있습니다. 이런 경우는 과소적합(Underfitting)되어 있다라고 합니다.

다시 말하면 모델을 훈련을 많이 할 수록 예측도는 높아집니다. 그러면서 일반화는 높아지게 됩니다. 그러나 너무 훈련을 많이하게되면 모델이 데이터셋에 지나치게 민감해저 과대적합이 일어나게 됩니다. 그렇다고 모델을 훈련을 적게 한다면 과소적합이 일어납니다. 머신러닝을 통해서 훈련하게 되면 이런 일반화 곡선이 최대가 되는 점을 찾아야 합니다.

인공지능의 역사

인공지능은 최근에 시작된것 같지만 실제로는 지금으로부터 1950년 초반부터 시작된 학문입니다. 이번 글에서는 인공지능의 역사를 몇가지 중요한 사건들을 중심으로 설명해드리고자 합니다.

https://medium.com/@armand_ruiz/deep-learning-with-data-science-experience-8478cc0f81ac

1943년
이 해는 워랜 맥클록, 월터 피츠에 의해 인공신경망에 관한 최초의 연구가 이뤄진 해입니다.
이 두학자는 인간의 뇌가 뉴런의 복잡한 연결로 이뤄진 것을 착안해서 인공적으로 지능을 만들 수 있을 것이라는 것을 이론적으로 증명했습니다

위키백과 : 앨련 튜링

1950년
앨런 튜링이라는 인공지능의 역사에 중요한 인물이 있습니다.
앨런 튜링은 “계산기계와 지성”이라는 논문을 통해서 “인공지능의 개념적인 토대를 놓았다”라고 할 수 있습니다. 인공지능이라는 말을 어떻게 정의할 수 있을까요?
정의하기가 굉장히 까다롭습니다.

인공지능 = 인공+지능
인공지능은 인공+지능이라는 일상에서 많이 사용하는 어렵지 않은 단어의 연결이죠. 그러나 이 단어가 조합하게 되면 정의를 내리기가 복잡해집니다. 특히 지능이라는 단어가 더 그렇다고 할 수 있습니다.

그렇기 때문에 학자마다 각기 다른 주장을 하게되고 또 의견도 분분하게 됩니다.

예를 들어서 지식은 신에게로부터 부여 받은 것이다 라고 생각하면… 인공지능에 대한 접근은 사람이 기계에게 지능을 부여해주는 방향으로 연구가 진행됩니다.

반면에 지식은 스스로 습득해가는 것이다라고 생각하면… 기계에게 많은 데이터를 주고 이를 통해서 스스로 학습하게 하는 방향으로 연구하게됩니다.

앨런 튜링은…  지능에 대한 철학적이고 설명하기 어려운 개념들에 대한 논쟁을 벗어나서
“자신이 기계와 이야기하는지 혹은 사람과 하는지 분간할 수 없다면 그것은 기계가 지능을 가지고 있다”라고 보아야 한다는 논리를 주장을 하게 됩니다. 이것을 튜링-테스트라고 합니다.

예를 들어서 채팅창을 통해서 대화를 하는데… 나와 대화하는 것이 사람인지 아니면 기계인지 알 수 없다면 그 기계는 지능을 가지고 있다. 라고 하자는 내용입니다.

https://medium.com/rla-academy/dartmouth-workshop-the-birthplace-of-ai-34c533afe992

1956년
그로부터 몇년 후에 아주 중요한 컨퍼런스가 열립니다.
당시에 수학자이자 컴퓨터 과학자인 존 매카시는 인공지능이라는 말을 처음 사용하게되고 이 회의에서 튜링의 “생각하는 기계”를 구체화하고 논리적인 형식을 갖춘 시스템으로 이행시키는 방안을 연구하기 시작합니다. 
이 회의에 참석한 사람들은 당대의 석학들로 이때만해도 많은 과학자들은 인공지능의 미래에 대해서 낙관하고 앞으로 몇십년만 지나면 “기계가 사람이 할 수 있는 일을 하며 심지어 자신의 존재에 대해서도 자각할 것”이라는 엄청난 주장을 했습니다. 

당시에는 인공지능에 대한 관심이 대단히 높았고 또 당대의 최고 석학들이 이런 말을 했으니까 세상에 많은 사람들이 주목을 하게되었습니다.

1958년
프랭크 로젠블렛이라는 학자가 등장하면서 퍼셉트론 이론이라는 아주 중요한… 현재 딥러닝의 기본 모델을 만들었습니다. 이것을 연결주의라고 합니다.
여기서 중요한 개념이 나오는데…
인공지능은 지식을 어떻게 정의하는가에 따라서 기호주의와 연결주의로 나눌 수 있습니다.

데카르트와 같은 이성주의 철학자는 “신으로부터 부여 받은 이성을 통해서 사유한다”라고 주장했죠. 이런 철학적인 영향을 받은 컴퓨터 과학자들은 신에게 인간이 지능을 부여 받았듯… 사람이 기계에게 지식을 부여함으로 완성된다. 라고 주장했습니다.

이에 영향을 받은 연구자들은 기계에게 인간이 이해할 수 있는 어떤 기호 체계를 기계에게 지식형태로 주입하고 했습니다. 예를 들어서 연역적 추론이나 의사결정트리 같은 알고리즘입니다. 이것을 기호주의라고 하고 마빈 민스키와 같은 연구자들이 대표적인 학자들입니다. 

반면에 연결주의는 베이컨과 같은 경험주의 철학에 영향을 받았습니다.
경험주의는 “경험을 통해서 지식을 얻을 수 있다”라고 주장했기 때문에 이런 학문에 영향을 받은 인공지능 학자들은 기계가 스스로 생각하고 학습할 수 있도록 만들자고 주장했고 이런 것을 구현하기 위해서 인간의 뇌에서 영감을 받아서 만든 개념이 바로 퍼셉트론의 개념입니다.

퍼셉트론 이론과 같이 우리의 뇌가 수많은 뉴런으로 연결되어 있다는 것에 착안해서 입력에 가중치를 곱해서 나오는 출력이 일정 신호 이상이면 1, 그렇지 않으면 0을 표현하는 이론입니다.
이후 딥러닝으로 발전하게 됩니다.

1969년 
마빈 민스키와 페퍼트가  『퍼셉트론즈』라는 책 출판합니다.
그 책에 보면 퍼셉트론 이론의 결정적 문제점이 있다라고 지적합니다.
M. Minsky – “퍼셉트론은 선형분리만 가능하다. 그러니까 복잡한 문제들은 해결 할 수 없다. 이걸 해결하려면 퍼셉트론을 다층으로 만들어야 XOR 같은 문제를 해결 할 수 있는데… 나도 못하고 지구상의 누구도 다층 퍼셉트론을 학습할 수 없을 것이다” 라고 주장했습니다.

그 이유는 딥러닝을 하다보면 엄청나게 많은 hidden layer 층을 쌓는데… 히든 레이어 안에서 입출력 데이터는 오차를 측정할 기준이 없기 때문에 가중치를 조정할 방법도 역시 없다라고 주장했습니다.
마빈 민스키 교수라는 당대 최고의 석학이 했던 이 말은 모든 연구자들에게 큰 영향을 주게되어서 인공신경망 이론은 거의 끝이라고 할 수 있을 깊은 암흑기에 들어가게됩니다.

1970년대
반면 기호주의적인 이론은 인공지능의 대세로 등장하면서 초기에는 많은 발전을 이루었지만 이후에 한계에 봉착합니다.
그 이유는 당시에 구현한 시스템이 해당 분야에서만 문제를 풀 수 있고 다른 분야에서는 전혀 쓸모가 없다는 것을 알게된 것이죠.

예를 들어 체스를 두는 인공지능을 만들었다면… 해당 인공지능은 그 일 외에 아무것도 할 수 없다는 것입니다.
“몇년 안에 기계가 사람의 하는 모든 일을 대체할 것이다”라고 주장했던 말을 기억했던 사람들은 인공지능에 대해서 큰 실망을 하게되고 이로인해 인공지능은 첫번째 암흑기에 들어가게 됩니다.

1980년대
이후에 인공지능은 범용적인 분야 즉, 사람과 같은 지식을 구현하기 보다는 세부적인 일들, 어떤 특정한 문제들을 해결하기 위한 시스템으로 발전해가고 이것을 전문가 시스템이라고 합니다.

즉, 전문가들의 지식과 경험을 데이터베이스화 하여 이를 통해서 문제를 해결하는 방법입니다.
이 가운데서 가장 잘알려진 시스템이 미국 스탠포드대에서 개발한 “마이신(MYCIN)”입니다.
마이신은 500가지의 룰이 준비되어 있으면서 질문에 차례로 답변하면 항생제를 처방하도록 디자인된 시스템으로 69%의 확률로 적절한 처방을 할 수 있었다고 합니다.

이것도 처음에 좋은 반응을 얻었지만…  의학 분야에 해결해야 할 문제가 점점 복잡해지고 문제의 영역이 확대되면서 이러한 시스템의 가장 중요한 부분인 “지식데이터 구축”이 어렵다는 현실적인 한계에 부딪히게 됩니다.
예를 들어서 마이신이 심장병이나, 암, 폐렴 이런 분야에는 답변을 할 수 없었다는 것이죠. 만약 그렇게 하려면 모든 분야에 룰을 만들어야 하는데… 그것이 가능한가? 그렇지 않다는 것입니다.

결국 규칙기반의 인공지능을 구글 브레인의 수장으로 있는 <제프 딘>은 “그동안 인공지능 분야에 더딘 진보를 이루며 깨달은 것은 – 인간이 컴퓨터에 직접 코딩을 하며 세계의 모든 규칙을 정의해서 컴퓨터에게 가르칠 수 없다-는 것이다.” 라고 말하며 결론적으로 그의 말은 컴퓨터가 스스로 학습하면서 배우지 않으면 안된다라는 결론에 이르렀다는 것이죠.

2006년
오랬동안 깊은 암흑의 길을 걷던 연결주의 인공지능은 당시에 마빈 민스키 교수가 했던 여러가지 문제들을 “다층퍼셉트론의 도입, 오류 역전파, 효과적인 활성함수 개발 등”으로 기존의 여러가지 불가능해보이던 문제를 극복하고 재기에 성공합니다.
이에 주도적인 역할을 한 것이 제프리 힌튼 교수입니다. 
힌튼 교수는 당시에 많은 사람이 외면했던 다층 퍼셉트론 이론을 꾸준히 연구해서 “A fast learning algorithm for deep belief nets”이라는 인공신경망의 기념비적인 논문을 발표합니다.
이 논문 이후에 학자들은 다시 인공신경망 이론에 관심을 가지기 시작합니다. 
그의 논문에 보면 Deep brief Nets이라는 용어가 등장하는데 이것이 바로 딥러닝의 시작이라고 할 수 있습니다.

위키백과, 제프리 힌튼

2012년
또 오늘날 많은 사람들에게 인공지능의 성능을 각인시켜준 엄청난 사건이 있었습니다.
그것은 인공지능을 활용한 이미지 분류 챌린지입니다.
수년전 부터 이미지 인식 오류률 26%대에서 0.1%를 줄이려고 당대의 수재들이 모여서 연구하고 노력해왔지만 그 결과는 그리 좋지 못했습니다. 그러나 CNN이라는 이미지 분류 알고리즘이 등장하면서 에러율을 10%대로 끌어 내린 엄청난 사건이 일어나면서 사람들은 이런 놀라운 결과를 만들어낸 인공신경망 알고리즘에 주목하게 됩니다.

https://devopedia.org/imagenet

2016년
이후 우리나라에서 인공지능의 붐이 불기 시작한 것은 구글 알파고가 이세돌에게 바둑 승리하면서 인공지능에 대한 관심이 더욱 높아지게됐습니다.
바둑은 체스와 달리 경우의 수가 너무나 많기 때문에 인공지능이 인간을 이길 수 없다고 예측했고 아마도 이세돌 9단이 4:1정도로 이길 것이라고 전문가들은 예측했지만 결과는 정반대로 알파고가 4:1로 승리했습니다.

https://www.kdnuggets.com/2016/03/alphago-not-solution-ai.html

이 세기의 이벤트였던 이 대결로 인공지능의 성능에 눈부신 발전에 모두 놀라게 됐습니다.

현재 인공지능은 놀라운 속도로 발전하고 있으며 이를 가능하게 해주는 것은 바로 알고리즘, 하드웨어의 발전, 빅데이터 처리 기술이라고 할 수 있습니다.