1-1 퍼셉트론 파이선 코드 알고리듬 재검토-I

in #kr5 years ago (edited)

이미 파셉트론 파이선 코드를 소개하였으나 파이선 코드가 다른 컴퓨터 언어 즉 C/C++과 비교해 다소 추상적으로 느껴지는 면이 많아 코드를 마음대로 수정하기가 어려워 여러번 이해 증진 차 재검토해 본 내용이다.

2-19 Iris flower data를 이용한 Rosenblatt 퍼셉트론 파이선 코딩(완결)
https://steemit.com/kr/@codingart/2-2-4-iris-flower-data-rosenblatt

Iris data 는 꽃 하나 당 0∼3번에 해당하는 4개의 실수형 숫자 데이터와 마지막 4번에 해당하는 문자열 꽃 이름으로 구성된다. 4개의 실수 데이터는 각각 꽃받침 길이와 폭 그리고 꽃잎의 길이와 폭 데이터로 구성된다.

불려온 데이터는 np.where() 명령에 의해서 ‘Iris-setosa’인지 확인 후 사실이면 라벨 값 –1을 부여하고 아니면 +1 값을 부여한다. 50개만 Iris-setosa 이므로 –1 데이터 및 +1 데이터가 각각 50개씩 생성된다.

y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)

꽃받침과 꽃잎의 길이 데이터만 가지고도 setosa 와 versicolor 종을 쉽게 구별할 수 있으므로 이 2종류의 데이터를 가지고 입력 벡터 즉 X 와 y 샘플을 준비하자.

X = df.iloc[0:100, [0, 2]].values

위 파이선 명령은 sepal(꽃받침) length and petal(꽃잎) length 데이터를 준비하여 출력하는 명령이다. 파이선 편집기 내부에서 실행될 경우 셸상에 실제로 출력되는 않는데 X = 을 삭제한 후 나머지 명령을 셸에서 실행하면 출력 결과 확인이 가능하다. 즉 입력 벡터 X 는 이미 컴퓨터에 저장이 되어 있어 사용 가능한 상태이다.

퍼셉트론 파이선 코드에서 웨이트 업데이트를 통해 학습하는 과정 코딩
함수 fit(X, y)를 호출하여 입력 벡터 X 와 해당하는 라벨 값을 가지는 y를 넣어주면 내부에서 rgen.normal() 명령을 실행하여 random 하게 웨이트 값들을 생성한다.

def fit(self, X, y):
rgen = np.random.RandomState(self.random_state)
self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1])
self.errors_ = []
∙∙∙

함수 fit(self, X, y)가 호출되면 처음에 설정된 random 웨이트 값들을 사용하면서 ppn = Perceptron(eta=0.1, n_iter=10) instance 명령에 따라 10회로 정의된 학습 횟수에 의해 학습이 이루어지게 된다. learning rate=0.1 과 학습회수 n_iter=10 은 class Perceptron(object)를 호출함에 따라 object 로서 정보가 전달된다. object를 통해 넘겨받은 정보는 class 내부에서 self.eta 또는 self.n_iter 형태로 사용되는데 이들은 class Perceptron() 내부에서 파라메터로 이미 정의가 되어 있다.

    for _ in range(self.n_iter):
        errors = 0
        for xi, target in zip(X, y):

zip(X, y)를 풀면 처음부터 2개의 좌표로 이루어진 각각의 데이터 세트 xi 와 데이터 세트별 라벨 명을 숫자화 하여 target 값으로 두고 for loop 즉 이 경우에는 100개의 데이터에 대해서 연산을 실행한다. 우선 변수 update를 계산한다.

            update = self.eta * (target – self.predict(xi))

각각의 데이터 세트 xi에 대해서 함수 predict(xi)를 부르면 그 내부에서 net_input() 함수를 불러 입력 벡터 데이터 세트 xi 와 웨이트 벡터 w_[1:]와 내적 계산을 실행하여 부호를 판별해보고 +1.0 인지 아니면 –1.0을 돌려주게 된다. 만약 이 값이 target 값과 일치하면 update 값이 0.0 이 되며 일단 학습이 끝난 것이다. 일치하지 않으면 계속 학습을 진행해야 하므로 update 값과 xi와 곱한 즉 업데이트된 벡터 값을 기존의 웨이트 벡터에 합산해 주고 그 다음 번 연산에 사용한다.
self.w_[1:] += update * xi
self.w_[0] += update
errors += int(update != 0.0)
self.errors_.append(errors)
return self
100개 단위로 update 가 0.0 이 아닌 경우를 학습 횟수별로 모조리 erros 로 합산 후 저장했다가 출력한다. 총 학습 회수 n_iter 범위 내에서 계속 웨이트 값을 업데이트 해 나가다 보면 erros 값이 0.0 에 도달하게 되는데 그렇게 되면 최종적인 웨이트 값들 즉 self.w_[0], self.w_[1], self.w_[2]이 확정된 것이다. 이 확정된 웨이트 값들은 차 후에 decision boundary를 결정하기 위해서 생선된 눈금(meshgrid)좌표 전체를 대상으로 “+1”인지 “-1”인지 결정하기 위해 사용된다. 단 이 때는 지정된 object 값들에 대해서 class Perceptron의 instance를 생성 시킨 후이므로 이미 웨이트 값들이 결정되어 있으므로 classifier.predict()만 사용하면 된다.

def predict(self, X):
    """Return class label after unit step"""
    return np.where(self.net_input(X) >= 0.0, 1, -1)

def net_input(self, X):
    """Calculate net input"""
    return np.dot(X, self.w_[1:]) + self.w_[0]

Rosenblatt 퍼셉트론 알고리듬의 핵심은 입력벡터와 업데이트된 웨이트 벡터와의 내적을 계산한 연후에 그 값의 크기와는 상관 없이 양인지 음인지 따져 보아 +1.0 아니면 –1.0 의 값을 부여하는 것이다. 즉 step 함수형태로 처리함을 뜻한다.

Linearly Separable한 영역을 계산
여기까지가 위 스캐터 플롯에서 보듯이 두 무더기로 이루어지는 Iris flower data set 100개를 대상으로 한 학습 과정이라면 두 무더기 데이터들을 상호 구별이 가능한 영역을 분할하는직선을 찾을 수 있으며 이러한 특성을 “선형적으로 구분 가능하다“ 또는 ”linearly separable“하다고 정의 한다.

선형구분선은 임의로 보고 긋는 것이 아니며 설정된 가로축과 세로축 공간에서 범위를 정해 눈금을 생성하면 눈금 좌표들이 생성되는데 이들 좌표들을 전부 학습시켜야 할 필요가 있다. 즉 학습이 되면 전체 눈금들에 대해 “+1” 인지 “-1”인지 라벨 값이 결정이 되는데 이 값들을 이용하여 contour를 작도하면 contour 레벨 값에 해당하게 되는 색상 속성이 부여된 라벨 값들이 딱 2가지뿐이므로 색상이 불연속적이 되는 경계선을 시각적으로 찾아 볼 수 있으며 이 경계선을 decision boundary 라고 한다. 즉 decision boundary는 긋는 것이 아니고 색상 라벨 값을 작도해 보면 찾아지는 것이다.

decision boundary를 찾기 위한 함수를 구성해 보자. X 는 입력 벡터이며 y는 입력 벡터에 대응하는 라벨 값이다. classifier는 미리 앞에서 선언해둔 class Perceptron을 불러 필요 시 instance를 생성하도록 한다. 해상도는 눈금 데이터 생성 시 해상도 간격을 뜻한다. 해상도 차이에 따른 다음의 그래픽 처리 결과를 참고하자.

noname01.png

def plot_decision_regions(X, y, classifier, resolution=0.01):
함수의 argument 중 classifier에 앞서 생성한 instance ppn을 넣어주면 학습된 X 와 y 데이터가 정보로 넘어 올 것이다.

# setup marker generator and color map
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))]) 
unique(y) 값이 “+1” 과 “-1” 이므로 ‘red’ 와 ‘blue’ 색상만 컬러 맵 작성에 사용된다.

이미 사용 중인 입력 벡터 X의 최대 값에 +1을 더하고 최소 값에 대해서 각각 –1을 빼어 충분히 넓은 범위를 설정하자.

x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1  
가로축 데이터 범위에 각각 1만큼씩 양쪽으로 여유를 준다
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1  
세로축 데이터 범위에 각각 1만큼씩 아래 위 양쪽으로 여유를 준다
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                       np.arange(x2_min, x2_max, resolution))

앞서 설정한 범위 내에서 해상도를 0.01 단위로 하여 세밀한 눈금 좌표를 생성한다.

Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)

눈금 좌표들은 가로축 xx1 좌표 어레이와 세로축 xx2 좌표 어레이의 합성으로 구성되며 이들을 뜯어내 각각 한 줄짜리 어레이로 구성( flattening process) 후 Transpose 형태를 취하자. 이어서 class Perceptron을 의미하는 classifier를 불러 instance를 생성시키는 과정에서 눈금 데이터 xx1과 xx2 를 사용하여 클라스 내부에 정의된 함수 predict를 불러 학습을 시키면 눈금 좌표 전체에 개해서 라벨 값이 학습되어 매겨진다. 물론 class Perceptron을 의미하는 classifier를 불러 instance를 생성시키는 과정에서 이미 학습시킨 웨이트 값들 즉self.w_[0], self.w_[1], self.w_[2]이 사용된다.

Z = Z.reshape(xx1.shape ) 
가로축 1차원 데이터 어레이 xx1.shape 하나만을 reshape하면 원래대로 원상 복구된다
plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap) 
학습된 Z는 색상 레벨 값 데이터를 가지고 있어 레벨 별 컬러로 표현이 된다. 
alpha는 색상의 진한 정도이다
plt.xlim(xx1.min(), xx1.max()) 
plt.ylim(xx2.min(), xx2.max())

decison boundary 로 분할되는 영역 설정 및 contour 작도를 위한 함수가 설정되었으면 눈금 데이터 contour 색상 작도에 중첩하여 matplotlib 라이브러리 모듈 명령을 사용하여 Iris flower data set을 scatter 형으로 함께 작도 하자.

for idx, cl in enumerate(np.unique(y)):
unique(y)가 –1과 +1의 두 종류의 값을 가질 때에 idx는 0 에서 1 까지이며 
컬러 값 cl 도 두 종류로 붉은색과 청색이 된다. X[y == cl, 0]에서 y는 Iris flower data
의 라벨 값을 뜻하며 , 0은 첫 번째 컬럼 데이터를 X[y == cl, 1]에서 ,1은 두 번째 컬럼
데이터를 의미한다. 여기에 사용된 y 와 이어지는 y=X[∙∙∙의 y는 scatter 작도를 위      한 y 좌표를 뜻하니 혼동하지 않도록 하자.

    plt.scatter( x=X[y == cl, 0], y=X[y == cl, 1],
                alpha=0.8,  c=colors[idx],
                marker=markers[idx], label=cl, 
                edgecolor='black')

이어서 decision boundary를 포함하는 각 영역을 색상을 부여하여 작도한다.

plot_decision_regions(X, y, classifier=ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')
plt.show()

이 코드 전체를 실행하면 Rosenblatt이 뉴론에 대해서 관찰하고 모델링했던 방식으로 입력 벡터들의 웨이팅 된 합(∑,시그마)을 계산 후 부호만 판별해 보고 웨이트 업데이터 과정에 따라 학습을 완료하여 선형적으로 구분 가능한 decision bounday를 포함한 공간을 컬러풀하게 시각화하여 보여주게 된다.

noname02.png

Sort:  

짱짱맨 호출에 응답하여 보팅하였습니다.

Congratulations @codingart! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You published a post every day of the week

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

To support your work, I also upvoted your post!

Do not miss the last post from @steemitboard:

Christmas Challenge - Send a gift to to your friends

Support SteemitBoard's project! Vote for its witness and get one more award!

Coin Marketplace

STEEM 0.27
TRX 0.13
JST 0.031
BTC 61374.19
ETH 2885.69
USDT 1.00
SBD 3.55