반응형
반응형

바로 앞 포스팅 '의사결정트리 학습'에서 언급했던 불순도를 계산하는 3가지 방법은 다음과 같습니다.


  1. 지니 인덱스
  2. 엔트로피
  3. 분류오류


이 3가지 불순도 계산 방법은 각각 특성을 가지고 있습니다. 아래와 같은 상황을 생각해봅니다.

어떤 데이터 세트에 여러가지 데이터가 섞여 있다고 가정합니다. 이 데이터 세트에 특정 부류(예를 들면 다각형들이 모여있는 집단에서 원으로 분류되는 부류와 같이..)에 속하는 멤버가 차지하는 비율을 가지고 불순도를 계산해 보도록 합니다. 비율은 0~100%의 범위를 가지지만 이를 0~1사이의 값으로 조정하더라도 본질적으로 차이가 없다는 것은 잘 알고 있지요?


이 비율이 0에서 1로 변해갈때 3가지 불순도 계산 방법의 추이는 어떻게 되는지 그래프로 그려보면 아래 그림과 같습니다.


 


이 그래프에서 엔트로피(scaled)로 되어 있는 회색 실선으로 나타낸 곡선은 원래 엔트로피의 0.5배한 그래프입니다. 그래프를 보면 3가지 불순도 계산 방법 모두 0 또는 1에 가까와질 수록 불순도가 낮아지고, 0.5일 때 불순도가 가장 높게 나옵니다. 이는 곧, 데이트 세트에 여러가지 부류에 속하는 데이터가 섞여 있는 경우, 특정 부류에 속하는 멤버입장에서 고려할 때 그 멤버가 차지하는 비율이 0에 가까와지거나 1에 가까와 질 때 가장 순도가 높고, 0.5일 때 불순도가 가장 높다..라고 해석 됩니다. 


분류오류의 경우 멤버가 차지하는 비율이 0.5까지 높아짐에 따라 선형적으로 불순도가 증가하는 것에 반해, 지니 인덱스와 엔트로피는 멤버가 차지하는 비율이 증가함에 따라 불순도가 빠르게 증가하다가 0.5 근방에서 최대를 보이는 패턴을 갖습니다. 특히 엔트로피의 경우가 불순도가 더 급격하게 증가하는 경향을 보입니다.


3가지 불순도의 특성에 대해서는 이 정도에서 마무리 하도록 하겠습니다.


이제 scikit-learn에서 제공하는 의사결정트리 학습 API를 이용해서 아이리스 데이터에 적용해보도록 합니다.


여태까지 우리가 작성한 코드에 아래의 모듈을 추가합니다.




그리고 if __name__ == '__main__'에서  분류기 알고리즘 적용 부분에 아래의 코드로 수정합니다.




이 코드에서 DecisionTreeClassifier()가 scikit-learn이 제공하는 의사결정트리 학습 API이며 criterion='entropy'는 엔트로피를 불순도 계산 방법으로 적용한다는 의미입니다. scikit-learn의 DecisionTreeClassifier()가 지원하는 불순도 계산 방법은 지니 인덱스와 엔트로피입니다. 지니 인덱스를 적용하고자 하면 criterion = 'gini'로 하면 됩니다.


마지막으로 plot_decision_region() 함수의 title 인자로 적절한 값을 적용합니다.


수정된 코드를 skl_tree.py로 저장합니다.


skl_tree.py의 if __name__ == '__main__' 


skl_tree.py를 실행하면 다음과 같은 결과가 나옵니다.






반응형
반응형

우리는 한번쯤 스무고개라는 놀이를 해본 경험이 있을 겁니다. 상대방이 가지고 있는 답을 20번 이내의 질문으로 알아 맞추는 것이죠. 스무고개는 정답을 맞추는 사람이 질문을 잘 던져야 답을 제대로 빨리 찾아낼 수 있습니다.


스무고개와 비슷한 원리로, 의사결정트리 분류기(decison tree classifier)는 일련의 질문에 근거하여 주어진 데이터를 분류해주는 알고리즘입니다. 아래의 그림을 보시죠.



이 그림은 휴일에 무엇을 할지 결정하기 위한 의사결정트리의 한 예입니다. 흰색 둥근 사각형으로 되어 있는 부분이 의사결정을 위한 질문이고, 회색으로 채워진 사각형은 의사결정이 된 부분입니다.


이와 비슷한 원리로 꽃잎길이와 꽃잎너비와 같이 수치로 되어 있는 데이터에 대해서도 의사결정트리를 이용해 분류가 가능하겠지요. 예를 들면 아래 그림과 같이 3-depth 질문으로 분류가 가능한 질문을 만들 수가 있을 것입니다.

 


위 그림을 해설하면 이렇습니다.

첫 번째 질문인 꽃잎너비가 0.75 이하인 녀석들만 분류를 해봤더니 setosa로 모두 분류되었고, 그렇지 않은 녀석들 중에 꽃잎길이가 4.9 이하인 녀석들만 모아보니 versicolor와 verginica의 비율이 10:4로, 꽃잎길이가 4.9 초과하는 녀석들만 모아보니 versicolor와 verginica의 비율이 2:10으로 나오더라 입니다. 마찬가지로 각기 분류된 녀석들 각각에서 꽃잎너비가 1.6이하인지, 꽃잎길이가 5.0 이하인지로 질문을 던져서 각각 분류해보니 최종적으로 맨 아래의 회색 사각형처럼 분류되더라..가 결론입니다.


만약 이 질문의 깊이가 더 깊어지면 보다 더 정확한 값들로 분류가 되겠지만, 이전 포스팅에서 말한 오버피팅이 되버리는 문제가 발생할 수 있으므로 적절한 선에서 질문을 잘라줘야 합니다.


그렇다면 이런 일련의 질문들을 어떻게 만들 수 있을까요...?

의사결정트리 학습은 트레이닝 데이터를 이용해 데이터를 최적으로 분류해주는 질문들을 학습하는 머신러닝입니다.


의사결정트리 학습에 대한 개념은 이것이 다입니다. 이제 의사결정트리에서 질문들을 만들어내기 위한 메커니즘에 대해 가볍게 살펴봅니다. 말은 가볍게라고 썼지만 결코 가볍지 않습니다.

의사결정트리 학습에서 각 노드에서 분기하기 위한 최적의 질문은 정보이득(Information Gain)이라는 값이 최대가 되도록 만들어주는 것이 핵심입니다. 어느 특정 노드에서 m개의 자식 노드로 분기되는 경우 정보이득은 아래의 식으로 정의합니다.





여기서, f는 분기를 시키기 위한 트레이닝 데이터의 특성값(예를 들어, 꽃잎 길이 또는 꽃잎 너비)이며 Dp는 부모노드에 존재하는 데이터 세트, Dj는 j번째 자식노드에 존재하는 데이터 세트입니다. I(Dp)는 Dp의 데이터 불순도(impurity), I(Dj)는 Dj의 데이터 불순도를 의미합니다. 그리고 Np와 Nj는 Dp의 데이터 개수와 Dj의 데이터의 개수입니다.


여기서 데이터 불순도란 데이터가 제대로 분류되지 않고 섞여 있는 정도를 말하는데, 이에 대해서는 아래에서 좀 더 자세히 다루도록 하겠습니다.


아무튼 정보이득 IG는 자식노드의 데이터 불순도가 작으면 작을수록 커지게 됩니다.


계산을 단순화하기 위해 보통 의사결정트리에서 분기되는 자식 노드의 개수는 2개로 하는데, 이를 이진 의사결정트리(Binary decision tree)라고 부릅니다. 특정 노드에서 분기되는 2개의 자식노드를 각각 L과 R첨자를 나타내서 표현하여 위 식을 단순화하면 아래와 같은 식이 됩니다.




아까 위에서 데이터 불순도를 언급했는데, 이진 의사결정트리에서 데이터 불순도를 측정하는 방법은 아래와 같이 3가지가 있습니다.


  1. 지니 인덱스(Gini Index)
  2. 엔트로피(entropy)
  3. 분류오류(classification error)

c개의 부류(class)로 분류되는 트레이닝 데이터가 있는 경우, 노드 t에 존재하는 데이터 중, i부류( i  c)에 속하는 데이터의 비율을 p(i\t) 표현하기로 하면,  위에서 말한 3가지 데이터 불순도 I(t)는 아래와 같은 식에 의해 계산됩니다.


지니 인덱스

엔트로피


분류오류

 
정보이득과 데이터 불순도를 이해하기 위해 아래의 예를 가지고 실제 계산을 해봅니다.




위 그림과 같이 80개의 데이터가 40개씩 두 종류의 데이터로 분류되어 있고, 이를 (40, 40)과 같이 표현해 봅니다. 의사결정트리 A는 (40, 40)을 (30, 10), (10, 30)으로 분리하는 것이고, 의사결정트리 B는 (40, 40)을 (20, 40), (20, 0)으로 분리하는 것입니다.

여기서 데이터 불순도 계산 방법으로 분류오류를 적용하고, 최종적으로 A, B의 정보이득을 각각 계산해보도록 하겠습니다.

Dp의 데이터 불순도를 계산하려면 p(i\t)를 계산해야 하겠죠. p(i\t)는 노드 t에 존재하는 데이터 중, i부류에 속하는 데이터 비율이라고 했습니다. 위의 예에서 2가지 부류만 있으므로 편의상 제1부류, 제2부류라 하겠습니다. (40, 40)은 80개의 데이터 중 40개는 제1부류에, 나머지 40개는 제2부류에 속하는 멤버이므로 p(1\t), p(2\t)는 모두 0.5로 같습니다. 따라서 위에서 서술한 분류오류에 의한 데이터 불순도 역시 제1부류, 제2부류 모두 0.5로 같습니다.


마찬가지 방법으로 DLDR의 데이터 불순도를 계산할 수 있겠지요. 계산식을 적어보면 다음과 같습니다.


의사결정트리 A의 경우 불순도 I(Dp), I(DL), I(DR) 및 정보이득 IG 계산






따라서 정보이득 계산식에 의한 정보이득 값은 다음과 같습니다.




의사결정트리 B의 경우 불순도 I(Dp), I(DL), I(DR) 및 정보이득 IG 계산 


 






따라서 정보이득계산 식에 의한 정보이득 값은 다음과 같습니다.



이제 정보이득을 계산하는 원리는 대충 이해했겠지요.


비슷한 방법으로 데이터 불순도를 계산하는 방법으로 지니 인덱스, 엔트로피를 적용하였을 경우 A, B에서 정보이득은 다음과 같이 계산됩니다.


지니 인덱스로 계산한 경우

  • A에서 정보이득 : 0.125
  • B에서 정보이득 : 약 0.16

엔트로피로 계산한 경우
  • A에서 정보이득 : 0.19
  • B에서 정보이득 : 0.31

의사결정트리에서 정보이득이 최대가 되는 것을 채택해야 하므로, 분류오류를 적용하였을 때는 A, B가 모두 같은 값이 나와서 어떤 것을 선택하더라도 무관했지만, 지니 인덱스로 계산하거나 엔트로피로 계산한 경우에는 B가 A보다 정보이득 값이 크므로 B를 선택하게 될 것입니다.

다음 포스팅에서는 3가지 데이터 불순도의 특성에 대해 가볍게 살펴보고 scikit-learn의 의사결정트리 학습에 의한 아이리스 데이터 분류를 수행하는 코드를 살펴보겠습니다.


반응형
반응형


[15편] scikit-learn SVM을 비선형 분리 모델에 적용하기  머신러닝/딥러닝 / 파이썬(Python)

2017. 3. 31. 0:51

복사http://sams.epaiai.com/220970865707

번역하기 전용뷰어 보기

SVM이 머신러닝 실무자들에게 인기가 높은 이유 중 하나는 선형으로 분류가 되지 않는 모델에 대해서도 적용할 수 있다는 장점 때문입니다.


아래는 표준 정규분포로부터 200개의 샘플을 추출하고, 추출한 샘플을 100개씩 두 개의 그룹으로 나누어, 두 그룹에 속하는 멤버들이 0보다 큰지 아닌지에 대한 논리값을 순서대로 XOR 연산하여 나온 결과 중 True는 1로, False는 -1로 라벨링한 후, 이를 좌표에 표시하는 코드입니다.




코드를 실행하면 아래와 같이 선형 분리가 되지 않는 모양으로 빨간 사각형 그룹의 점과 파란 x 그룹들의 점들이 분포하고 있는 그림을 볼 수 있습니다.



 

위 그림처럼 분포되어 있는 빨간색 그룹과 파란색 그룹을 선형으로 분류하기가 쉽지 않습니다. 엄밀히 말하면 불가능합니다. 다시 말하면, 위 그림과 같은 비선형 모델은 여태 우리가 다루었던 퍼셉트론이나 로지스틱 회귀로는 머신러닝을 수행하기가 어렵습니다. 그렇다면 위 그림처럼 분포하고 있는 두 그룹을 어떻게 하면 선형으로 분류할 수 있을까요? 아래의 그림을 봅니다.




원리는 이렇습니다.


위의 왼쪽 그림은 2개의 값 (X1, X2)로 되어 있는 데이터 샘플의 분포를 2차원 평면에 그려 놓은 것입니다. 데이터의 분포는 원형으로 되어 있어서 선형 분리가 되지 않습니다. (X1, X2) 2차원으로 분포된 데이터를 변환 Φ를 통해 1차원 높은 3차원 (Z1, Z2, Z3) 형태의 분포로 변환하게 되면 오른쪽 위 그림처럼 빨간색 그룹과 파란색 그룹을 선형(엄밀하게 말하면 2차원 초평면으로 분리가 가능함)으로 분리할 수 있게 됩니다. 이렇게 선형으로 분리한 후 다시 2차원 (X1, X2) 분포로 변환시키면 오른쪽 아래 그림처럼 빨간색 그룹과 파란색 그룹을 회색 원 모양의 경계선으로 두 그룹을 분류하게 됩니다.


이런 원리에 입각하여 정리하면 또 다시 복잡한 수식이 등장하게 되는데, 이 부분을 다 스킵하면 결국 커널 함수(kernel function)라고 하는 것이 나옵니다. 커널 함수는 두 분류의 집단에 분포하고 있는 값들에 대한 벡터 내적 계산의 효율화를 위해 도입된 것으로 그 종류가 많은데, 이에 대해서 너무 깊게 알 필요는 없을 것 같습니다.


아무튼 커널 함수 중 가장 광범위하게 사용되는 것이 Radial Basic Function kernel(RBF 커널)입니다. RBF 커널과 관련된 식이 가우스 함수(Gaussian fucntion)와 동일한 형태여서 가우시안 커널(Gaussian kernel)로도 불립니다.

참고로, RBF 커널 k는 다음과 같은 다소 복잡한 수식으로 표현됩니다.


여기서 γ는 최적화를 위한 자유 파라미터(free parameter)라고 부릅니다. 따라서 RBF 커널을 적용하는 SVM은 정규화와 관련된 값 C와 자유 파라미터 γ의 값을 지정해주어야 합니다. 이에 대해서는 이 포스팅의 마지막 부분에서 다루어 보도록 합니다.



자, 그러면 scikit-learn이 제공하는 SVM으로 RBF 커널을 적용하여 위에서 서술한 비선형 분리 모델에 대해 머신러닝을 수행한 결과를 살펴보도록 합니다.


먼저, 'scikit-learn을 이용한 SVM' 포스팅에서 구현했던 skl_svm.py의 if __name__ == '__main__'에서 아이리스 데이터를 읽어 들이는 부분을 주석처리하고 이 아래 부분에 위에서 언급한 표준 정규분포부터 200개의 데이터를 추출하고 좌표에 표시하는 코드를 추가한 후, SVM 객체를 호출하는 부분과 plot_decision_region()을 호출하는 부분을 아래와 같이 수정합니다.



 



if __name__ == '__main__': 코드는 아래와 같습니다.




이 코드를 수행하면 아래와 비슷한 결과가 나옵니다. 참고로 200개 샘플 데이터는 프로그램을 수행할 때마다 달라지므로 본 포스팅에서 제시한 결과와 다를 수 있습니다.





RBF 커널을 적용한 SVM으로 머신러닝을 수행하면 선형으로 분리가 되지 않는 데이터에 대해서도 훌륭하게 머신러닝을 수행하여 제대로 된 학습이 이루어질 수 있음을 알 수 있습니다. 


그러면 아이리스 데이터에 rbf 커널을 가진 SVM을 적용하는데, γ 값을 달리해서 적용해 보겠습니다. 

아이리스 데이터를 읽는 코드는 아래와 같습니다. 

​skl_svm_rbf.py

 





 


 

위 결과를 보면 γ 값이 작으면 트레이닝 데이터를 잘 분류하면서도 테스트 데이터에 대해서도 훌륭한 결과를 보여주지만, γ 값이 커지면 트레이닝 데이터에는 매우 잘 맞아 떨어지지만 테스트 데이터에 대해서는 결과가 썩 만족스럽게 나오지 않습니다. 따라서 이 결과는 오버피팅 된 결과이며 γ 값의 크기는 오버피팅과 관련이 되어 있음을 알 수 있습니다.


따라서 적절한 γ 값을 적용하여 오버피팅이나 언더피팅이 되지 않게 하는 것이 중요합니다.


반응형
반응형

여태까지 다루었던 scikit-learn의 퍼셉트론, 로지스틱 회귀, SVM은 경사하강법 알고리즘을 적용한 것입니다.

만약 머신러닝을 수행할 데이터가 대용량이라면 확률적 경사하강법(Stochastic Gradient Descent) 알고리즘을 적용한 버전이 여러모로 효율적일 수 있습니다.


scikit-learn의 SGDClassifier 클래스는 확률적 경사하강법 알고리즘을 적용한 퍼셉트론, 로지스틱 회귀, SVM을 활용할 수 있도록 해줍니다.


확률적 경사하강법을 적용한 퍼셉트론, 로지스틱 회귀, SVM을 이용하려면 아래와 같은 코드를 적용하면 됩니다.



이전 포스팅의 skl_svm.py에서 아래의 모듈을 추가합니다.




scikit-learn의 Perceptron(), LogisticRegression(), SVC() 대신 SGDClassifier()의 loss 인자값을 달리해서 확률적 경사하강법 알고리즘을 적용한 것으로 대체할 수 있습니다.




수정한 코드를 반영하여 skl_sgd.py로 저장합니다. 


skl_sgd.py 



확률적 경사하강법을 적용한 퍼셉트론, 로지스틱 회귀, SVM의 실행 결과는 다음과 같습니다.


확률적 경사하강법 적용 퍼셉트론 실행 결과





확률적 경사하강법 적용 로지스틱 회귀 실행 결과





확률적 경사하강법 SVM 실행 결과 


 




결과를 보면 퍼셉트론을 제외하고 로지스틱 회귀나 SVM은 영역 구분 자체가 완전히 달라졌음을 알 수 있습니다. 참고로 확률적 경사하강법을 적용한 코드는 실행할 때마다 그 결과값이 다르게 나옵니다. 


반응형
반응형

로지스틱 회귀와 함께 분류를 위한 강력한 머신러닝 알고리즘으로 널리 사용되는 것이 바로 SVM(Support Vector Machine)입니다.


SVM은 퍼셉트론의 개념을 확장하여 적용한 알고리즘인데, 퍼셉트론이 분류 오류를 최소화하는 알고리즘인 반면 SVM은 margin을 최대가 되도록 하는 알고리즘입니다.


여기서 말하는 margin은 분류를 위한 경계선과 이 경계선에 가장 가까운 트레이닝 데이터 사이의 거리를 말합니다. 이 경계선에 가장 가까운 트레이닝 데이터들을 support vector라고 부릅니다. 아래의 그림을 보시죠~

위의 왼쪽 그림을 보면 빨간색 원으로 표시된 집단과 초록색 더하기 기호로 표시된 집단을 분류하는 경계선은 다양하게 존재할 수 있습니다. SVM은 두 집단을 분류하는 경계선 중 support vector와의 거리가 가장 멀리 떨어져 있는 경계선을 찾아내는 알고리즘입니다.

이 알고리즘은 경계선에 가장 가까이 있는 support vector를 지나는 선과 거리가 최대가 되는 경계선을 구함으로써 그 목적을 달성하게 됩니다. 여기서 말한 선은 엄밀히 말하면 다차원 공간의 초평면(hyperplane)인데, 초평면의 개념을 설명하기도 복잡하고, 실제 초평면을 머릿속으로 상상하기도 힘들므로, 그냥 선으로 생각하고 사용해도 큰 무리가 없으므로 초평면을 선으로 생각하도록 하겠습니다.


SVM이 퍼셉트론의 개념으로부터 출발하고 확장된 것이므로 입력되는 트레이닝 데이터, 입력값과 곱해지는 가중치와 관련된 수식들이 등장하게 되는데, 중간단계의 복잡한 수식을 모두 스킵하면 결국 아래 수식의 최소 값을 구하는 것이 SVM의 핵심 원리가 됩니다.



여기서 j는 트레이닝 데이터의 특성값의 개수가 그 범위가 되며, i는 트레이닝 데이터의 개수가 그 범위가 됩니다. C는 로지스틱 회귀에서 설명했던 정규화와 관련된 상수와 비슷한 개념이며 ξ는 여유 변수(slack variables)라고 부르는데, 이 변수는 분류 오류가 생기는 데이터에 적절한 패널티를 부여함으로써 비선형적으로 분리되는 모델을 완화시켜 최적화된 수렴값을 가지도록 하기 위한 선형 제약(linear contraints) 값입니다.


말이 좀 어렵지만 여기서 우리가 눈여겨 볼 부분이 C입니다. C값을 변화시키면 알고리즘이 결정하는 경계선이 달라집니다. 아래의 그림을 보면서 살포시 이해를 해봅니다.




위 그림과 같이 빨간색 원으로 이루어진 집단과 초록색 더하기 기호로 이루어진 집단이 분포하고 있을 때, C 값이 크면 왼쪽 그림처럼, C 값이 작으면 오른쪽 그림처럼 경계선이 결정됩니다. 왼쪽 그림에서 경계선은 오버피팅되었다고 볼 수 있고 오른쪽 그림에서 경계선은 최적화 되었다고 볼 수 있습니다. 


대충 눈치를 챘겠지만 scikit-learn의 SVM을 적용할 때도 위에서 설명한 C값이 등장하게 됩니다.

이전 포스팅에서 구현했던 skl_logistic.py에서..


아래와 같이 SVM을 활용하기 위한 모듈을 추가합니다.




그리고 if __name__ == '__main__': 에서 로지스틱 회귀 알고리즘을 호출하는 부분을 아래의 내용으로 바꿉니다.



마지막으로 plot_decision_regions() 함수 호출부분에서 title 인자를 적절한 값으로 수정합니다.


수정된 코드를 skl_svm.py로 저장합니다.


skl_svm.py 



추가된 코드에서 C 값으로 1.0이 할당되어 있습니다. 이 C값을 조정함으로써 언더피팅과 오버피팅을 조절하게 됩니다.  

이제 코드를 수행해보면 아래와 같은 결과가 나옵니다.






로지스틱 회귀가 구분한 경계선과 비교해보면 경계선과 support vector와의 거리가 동일하게 그려져 있음을 알 수 있습니다. 결과의 품질은 로지스틱 회귀와 동일하게 나오네요. 


반응형
반응형

로지스틱 회귀(logistic regression)는 선형 또는 바이너리 분류 문제를 위한 단순하면서도 보다 강력한 분류 알고리즘입니다. 로지스틱 회귀에서 등장하는 회귀(regression)는 통계에서 말하는 회귀와는 별로 관계가 없는, 머신러닝의 분류와 관련된 알고리즘 이름으로 생각하면 됩니다.


로지스틱 회귀는 선형 분리 모델에서 훌륭하게 동작하며, 실제로 가장 많이 사용되는 분류 알고리즘 중 하나입니다. 머리가 좀 아프더라도 로지스틱 회귀를 살포시 이해하도록 해봅니다.


로지스틱 회귀를 이해하기 위한 첫 단계는 다음과 같은 약간의 확률과 관련된 지식이 필요합니다.

영어로 odds는 어떤 일이 일어날 승산을 말합니다. 특정 사건의 승산률 odds ratio는 다음과 같이 정의됩니다.



여기서 p는 어떤 특정 사건이 발생할 확률입니다. 즉 odds ratio는 어떤 특정 사건이 일어날 확률과 그 사건이 일어나지 않을 확률의 비로 정의됩니다.


다시 우리의 머신러닝 주제로 돌아갑니다. 우리가 머신러닝을 수행해서 얻은 학습 결과를 이용해 어떤 값에 대한 예측값이 실제 결과값과 동일하게 나올 확률을 p라고 하면 이 머신러닝의 odds ratio 역시 위와 같이 정의됩니다.


이제, odds ratio의 로그값을 함수값으로 가지는 함수를 정의해 봅니다.




p는 0과 1사이의 수이고,  f(p)의 범위는 실수 전체가 됩니다. 우리가 이전 포스팅에서 다루었던 순입력 함수의 리턴값을 다시 한번 상기해 봅니다.  순입력 함수의 리턴값을 z로 표기하면 아래와 같습니다.



z는 w의 값에 따라 실수 전체에 대해 매핑되는 값이며, z의 값에 따라 입력된 트레이닝 데이터 X가 어떤 집단에 속하는지 아닌지 결정하게 되는 값입니다. 따라서 위에서 정의한 f(p)의 값을 z로 매핑할 수 있습니다.




여기서, p를 z에 관한 식으로 나타내면 아래와 같은 식이 됩니다. (고등학교때 배웠던 지수와 로그의 관계식을 알면 됩니다!)




이 확률 p를 z에 관한 함수로 표현하여 다음과 같이 나타냅니다.



이 함수를 그래프로 그려보면 다음과 같은 s자 형태의 곡선으로 나타납니다. 이런 이유로 이 함수를 sigmoid 함수라고 부릅니다.  




로지스틱 회귀에서는 순입력 함수의 리턴값에 대해 가중치 업데이트 여부를 결정하는 활성 함수로 이 sigmoid 함수를 이용합니다.




즉 로지스틱 회귀는 순입력 함수의 리턴값을 sigmoid 함수에 대입하여 그 결과값을 가지고 가중치 업데이트를 할지 말지를 결정하는 것이 핵심입니다. sigmoid 함수의 리턴값은 곧 입력한 트레이닝 데이터가 특정 클래스에 속할 확률이 되므로, 로지스틱 회귀는 입력되는 트레이닝 데이터의 특정 클래스에 포함될 예측 확률에 따라 머신러닝을 수행하는 알고리즘임을 알 수 있습니다.


우리는 실생활에서 예측되는 부류(class) 그 자체 뿐만 아니라 그 부류에 속할 확률이 유용할 때가 많습니다. 일기예보에서 내일 비가 올 확률을 예측하는 것이라던지, 환자의 증상을 보고 그 환자가 특정 질병에 걸렸을 확률을 계산하는 것 등이 그 예입니다. 로지스틱 회귀는 실제로 의학 분야에서 광범위하게 활용되고 있기도 합니다.

로지스틱 회귀에서 활용되는 비용함수 J(w)와 가중치 업데이트 식은 퍼셉트론의 그것과 동일한 원리이며, 이에 대한 구체적인 내용은 본 포스팅의 목적에 어울리지 않게 너무 어려워서 패스하도록 합니다.


그러면 아이리스 데이터를 가지고 scikit-learn을 이용하여 로지스틱 회귀 알고리즘으로 머신러닝을 수행하는 코드를 봅니다.


이전 포스팅에서 설명한 skl_perceptron.py 코드에서 아래의 내용을 반영합니다.


필요 모듈 임포트하는 부분에서 아래 코드를 추가합니다.




if __name__ == '__main__': 에서 아래 코드를 찾습니다.


 



이 부분을 아래의 코드로 수정합니다.




마지막으로 plot_decision_regions() 함수 호출부분에서 title 인자를 적절하게 변경합니다.

이렇게 작성된 코드를 skl_logistic.py로 저장합니다.

skl_logistic.py

 

 

결국 scikit-learn의 퍼셉트론 코드와 로지스틱 회귀 코드는 알고리즘을 선택하고 적용하는 부분만 다릅니다.


그런데 LogisticRegression()을 보면 C=1000.0 이라는 생소한 부분이 보입니다. 이 값을 이해하려면 머신러닝에 있어 공통적으로 나타나는 문제인 오버피팅과 이를 해결하기 위한 정규화라는 개념을 알아야 합니다.


오버피팅(Overfitting)이란 말 그대로 너무 잘 맞아 떨어진다는 뜻이며, 이는 머신러닝을 위해 입력된 값에만 너무 잘 맞아 떨어지도록 계산을 해서 입력에 사용된 트레이닝 데이터 이외의 데이터들에 대해서는 잘 맞아 떨어지지 않는 경우가 발생하게 되어 미지의 데이터에 대해 그 결과를 예측하기 위한 머신러닝의 결과로는 바람직하지 않습니다.


아래의 그림을 보면 오버피팅을 이해할 수 있습니다. 물론 이 그림은 'Python Machine Learning'에서 가져온 것입니다.




위 그림에서 2번째 그림처럼 최적의 조건을 찾기 위해 사용되는 것이 정규화(regularization)라는 기법인데, 정규화를 통해 데이터 모델의 복잡성을 튜닝하여 언더피팅과 오버피팅의 트레이드 오프(trade-off)를 찾아내는 것입니다. 로지스틱 회귀에서 사용되는 정규화 기법은 가장 일반적으로 사용되는 L2 정규화인데, 위의 코드에서 생소했던 C=1000.0 부분이 L2 정규화와 관련된 인자로 이해하시면 됩니다.


L2 정규화는 로지스틱 회귀의 비용함수 J(w)를 아래와 같은 식으로 변형하여 동작합니다.



여기서 λ를 정규화 파라미터라고 부르며, C값은 λ의 역수로 정의합니다.



따라서 C값을 감소시키면 λ가 커지게 되며, 이는 정규화를 강하게 한다는 의미입니다. 아무튼 생소했던 C값이 무엇인지 대충은 알았으므로 이쯤에서 넘어 가도록 하겠습니다.


코드를 수행하면 다음과 같은 결과가 나옵니다.





결과를 보면 scikit-learn 퍼셉트론에 비해 로지스틱 회귀가 아이리스 데이터에 대해 더 정확도 높게 분류하고 있고, 머신러닝에 의해 분리된 영역이 퍼셉트론의 결과와 다르다는 것을 알 수 있습니다.


이것으로 로지스틱 회귀에 대한 내용은 마무리하겠습니다. 


반응형
반응형

[10편] 'scikit-learn을 이용해 퍼셉트론 머신러닝 수행하기'에서 scikit-learn의 퍼셉트론으로 아이리스 데이터의 70%를 가지고 머신러닝을 수행하고, 나머지 30%의 데이터로 머신러닝 결과를 테스트해보는 코드를 제시했습니다.


이번에는 70%의 데이터를 가지고 머신러닝을 수행한 결과가 실제 어떻게 데이터들을 분류하고 있는지 그래프로 나타내보고, 퍼셉트론 알고리즘의 유효성에 대해 고찰해보고자 합니다.


먼저, 아래의 코드를 작성하고 mylib 폴더에 plotdregion.py로 저장합니다.


plotdregion.py


  

이 코드는 plot_decision_region 함수의 인자 classifier로 주어진 퍼셉트론 분류기를 이용해 머신러닝을 수행한 결과가 트레이닝 데이터가 분포하고 있는 좌표계에서 어떻게 영역을 구분하는지 보여주기 위함입니다.


원리는 이렇습니다.

아이리스 트레이닝 데이터 X는 (꽃잎길이, 꽃잎너비) 2개의 값으로 되어 있으므로 x축의 값으로 꽃잎길이, y축의 값으로 꽃잎너비로 되어 있는 좌표계에 데이터를 표시할 수 있습니다.


그리고 좌표계를 바둑판으로 생각해보면, 좌표계에서 격자의 교차점도 아이리스 데이터 (꽃잎길이, 꽃잎너비)의 어떤 값이라고 생각할 수 있겠지요. 따라서 이 교차점의 좌표를 인자 classifier로 주어진 퍼셉트론 분류기로 머신러닝을 수행하여 나온 결과도 iris-setosa, iris-versicolor, iris-virginica를 의미하는 0, 1, 2 중 어느 한개의 값이 될 것이고, 0으로 되어 있는 영역은 빨간색, 1로 되어 있는 부분은 파란색, 2로 되어 있는 부분은 초록색으로 표시를 하여 머신러닝의 결과를 영역으로 표시하게 됩니다.


그럼 코드의 주요부분을 설명하겠습니다.


>>> markers = ('s', 'x', 'o', '^', 'v')

>>> colors = ('r', 'b', 'lightgreen', 'gray', 'cyan')

>>> cmap = ListedColormap(colors[:len(np.unique(y))])


markers는 matplotlib에서 정의한 점 표시 모양 5개를 튜플로 정의한 것이고, colors는 색상 5개를 튜플로 정의한 것입니다. ListedColormap은 인자로 주어진 색상을 그래프상에 표시하기 위한 객체입니다. numpy.unique(y)는 y에 있는 고유한 값을 작은 값 순으로 나열합니다. 예를 들어 [1, 0, 0, 2, 2, 0, 0, 1, 2]에는 고유한 값이 0, 1, 2밖에 없으므로 numpy.unique([1, 0, 0, 2, 2, 0, 0, 1, 2])는 [0, 1, 2]가 됩니다. 

따라서 cmap = ListedColormap(colors[:3])이 되어 cmap에는 colors[0], colors[1], colors[2]가 매핑된 ListedColormap 객체가 됩니다. 이는 곧 빨간색, 파란색, 밝은 초록색이 매핑되는 겁니다.



>>> x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1

>>> x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1

>>> xx, yy = np.meshgrid(np.arange(x1_min, x1_max, resolution),

                                np.arange(x2_min, x2_max, resolution)) 


x1_min, x1_max는 트레이닝 데이터 X의 첫 번째값인 꽃잎길이의 최소값-1, 최대값+1 입니다.

마찬가지로 x2_min, x2_max는 트레이닝 데이터 X의 두 번째값인 꽃잎너비의 최소값-1, 최대값+1 입니다. 

numpy.meshgrid()는 격자의 교차점 좌표를 편리하게 다룰 수 있도록 값을 리턴하는 함수입니다. 이해를 돕기 위해 아래의 코드를 봅니다.


>>> x = [0, 1, 2, 3, 4, 5]

>>> y = [0, 1, 2, 3, 4, 5]

>>> xx, yy = np.meshgrid(x, y)

>>> plt.plot(xx, yy, marker='.', color='k', linestyle='none')


이 코드는 x 범위 0~5까지 정수, y 범위 0~5까지 정수를 좌표로 하는 격자의 교차점의 위치를 찍어주는 코드입니다. 실행을 해보면 다음과 같은 결과가 나옵니다.



따라서 원래 코드에서 xx와 yy는 아이리스 트레이닝 데이터의 꽃잎길이, 꽃잎너비가 분포하는 좌표 격자 교차점을 resolution 간격으로 편리하게 만들어줄 수 있는 값을 가지고 있습니다.



>>> Z = classifier.predict(np.array([xx.ravel(), yy.ravel()]).T)

>>> Z = Z.reshape(xx.shape) 


xx, yy를 ravel()을 이용해 1차원 배열로 한줄로 쭉 만든 후 전치행렬로 변환하여 퍼셉트론 분류기의 predict()의 인자로 입력하여 계산된 예측값을 Z로 둡니다. Z를 reshape()을 이용해 원래 배열 모양으로 복원합니다.



>>> plt.contourf(xx, yy, Z, alpha=0.5, cmap=cmap)


Z를 xx, yy가 축인 그래프상에 cmap을 이용해 등고선을 그립니다.


이후 부분은 아이리스 트레이닝 데이터를 matplotlib의 산점도 그리기를 이용해 그래프상에 찍어주는 겁니다. 특히 테스트 데이터는 강조하는 원으로 다시 표시 해주었습니다. matplotlib의 산점도 그리기 포스팅을 참고하면 됩니다.


☞ matplotlib으로 산점도 그리기 바로가기



이제 이전 포스팅의 skl_perceptron0.py의 if __name__ == '__main__':코드 맨 마지막에 아래의 코드를 추가해서 skl_perceptron.py로 저장합니다.

 

 

여기서 numpy.vstack(x, y)는 x배열과 y배열을 수직으로 쌓는 것이고, numpy.hstack(x, y)는 x배열과 y배열을 수평으로 쌓는 것입니다.


skl_perceptron.py 



코드를 실행해보면 다음과 같은 결과를 볼 수 있습니다.






scikit-learn의 퍼셉트론 알고리즘의 아이리스 트레이닝 데이터를 이용해 머신러닝한 결과는 iris-setosa를 빨간색 영역으로, iris-versicolor를 파란색 영역으로, iris-virginica를 초록색 영역과 같이 분류합니다.


그런데 실제 데이터 분포를 보면 알 수 있듯이 x로 표시된 iris-versicolor가 빨간색 영역과 초록색 영역에서도 보이고, 초록색 원으로 표시된 것도 파란색 영역에서 찾을 수 있습니다. 따라서 퍼셉트론으로 아이리스 3가지 품종을 완벽하게 분류하지 못했다는 것을 알 수 있습니다. 테두리가 검정색인 원으로 표시한 것이 테스트 데이터입니다. 테스트 데이터 중 4개가 엉뚱한 영역에 있음을 알 수 있습니다. 


퍼셉트론은 이와 같이 선형분리가 완벽하게 되지 않는 데이터로는 머신러닝을 제대로 수행할 수 없습니다.


반응형
반응형

이미 말했듯이 퍼셉트론이나 이를 발전시킨 아달라인은 머신러닝을 위한 트레이닝 데이터가 완전히 선형 분리가 되어 있지 않으면 제대로 된 머신러닝을 수행할 수 없습니다. 하지만 우리가 접하는 대부분의 데이터들은 선형 분리가 불가능한 것들이며, 이런 이유로 머신러닝을 위한 방법으로써 퍼셉트론은 실제로 잘 활용되지는 않습니다.


그렇지만 퍼셉트론은 분류(classification)를 위한 다양한 알고리즘의 개발과 발전에 기초 개념을 제공했다는 것만으로도 매우 가치가 있는 것이죠~


다양한 분류 알고리즘 중, 해결하고자 하는 문제에 최적인 알고리즘을 선택하는 일은 경험이 필요합니다. 이 분류 알고리즘으로 트라이 해보고, 저 분류 알고리즘으로 한번 트라이 해보고, 이런 식으로 다양한 분류 알고리즘을 적용하여 가장 최적인 결과가 나오는 것을 선택하는 것이죠. 이 후, 이와 유사한 작업을 할 경우에는 최적인 알고리즘을 채택해서 적용하게 되는 것입니다. 따라서 우리가 획득하고 트레이닝 시킬 데이터의 종류에 따라 최적인 분류 알고리즘을 찾는 것이 중요합니다.


분류를 위한 머신러닝을 수행할 때 일반적으로 아래의 단계를 따릅니다.

  1. 트레이닝 데이터에서 트레이닝을 수행할 적절한 특성(feature)값(실제 머신러닝을 위해 입력되는 값) 선택하기
  2. 성능 메트릭(performance metric) 선택하기
  3. 분류기(classifier)를 선택하고 알고리즘 최적화하기
  4. 머신러닝 성능 측정하기
  5. 알고리즘 튜닝하기


위에서 설명한 각 단계의 자세한 내용은 차근차근 진행하도록 하도록 하고, 이번 포스팅에서는 우리가 이미 직접 파이썬으로 구현해보았던 퍼셉트론을 scikit-learn API를 이용해 수행해보는 것입니다.




scikit-learn은 파이썬으로 구현한 머신러닝을 위한 확장 라이브러리입니다. scikit-learn은 아래와 같이 머신러닝을 위한 다양한 API를 제공합니다.


  • Classification
  • Regression
  • Clustering
  • Dimensional reduction
  • Model selection
  • Preprocessing


이와 관련한 많은 내용을 알고 싶다면 scikit-learn 공식 사이트를 방문해서 살펴보도록 하세요~


☞ scikit-learn 공식 사이트 바로 가기


여러분의 컴퓨터에 scikit-learn이 아직 설치되어 있지 않았다면 윈도우 명령 프롬프트를 실행하고 다음과 같이 pip를 이용해 scikit-learn을 설치합니다.


pip install scikit-learn



자 그러면 아래의 코드를 작성하고 skl_perceptron0.py로 저장합니다.


skl_perceptron0.py 


>>> from sklearn import datasets

>>> from sklearn.cross_validation import train_test_split

>>> from sklearn.preprocessing import StandardScaler

>>> from sklearn.linear_model import Perceptron

>>>from sklearn.metrics import accuracy_score


scikit-learn의 필요한 모듈을 임포트합니다.

이후에 등장하는 코드는 이전 포스팅에서 퍼셉트론이나 아달라인을 잘 이해했다면, scikit-learn에서 제공하는 API를 이용하는 것 이외에는 거의 동일한 프로세스로 머신러닝을 수행하게 됩니다.



>>> iris = datasets.load_iris()


scikit-learn에는 이전 포스팅에서 학습 데이터로 다루었던 아이리스에 대한 데이터를 자체적으로 가지고 있습니다. 이 데이터는 datasets 모듈의 load_iris() 함수를 통해 읽어올 수 있는데, scikit-learn에서 정의한 특이한 클래스 형태로 저장되어 있습니다.



>>> X = iris.data[:, [2, 3]]

>>> y = iris.target   


X에 아이리스 데이터에서 꽃잎길이와 꽃잎너비 데이터를 대입합니다. 그리고 y에는 품종 데이터를 담습니다. y를 화면에 출력해보면 알겠지만 y에 저장된 아이리스 품종데이터는 0, 1, 2의 값으로만 구성되어 있습니다. 이는 iris-setosa, iris-versicolor, iris-verginica에 해당하는 값입니다. 즉, scikit-learn에서 품종 데이터를 이미 숫자로 바꾸어 저장해둔 것입니다.



>>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3,

                                                              random_state=0)


train_test_split()은 원래 트레이닝 데이터와 결과값인 X와 y를 무작위로 섞은 후 test_size로 주어진 비율만큼 각각 나눕니다. 예를 들어 test_size = 0.3으로 되어 있으면 X와 y를 무작위로 섞은 후에 70%는 X_train과 y_train으로, 나머지 30%는 X_test, y_test로 둡니다. random_state의 값은 난수 발생을 위한 seed의 인자값입니다.


아이리스 데이터를 70%는 트레이닝 데이터로, 나머지 30%는 테스트 데이터로 나누는 이유는 70%의 데이터로 머신러닝을 수행하여 학습이 마무리되면, 나머지 30%의 데이터에 대한 예측값과 실제 결과값을 비교함으로써 머신러닝이 얼마나 잘되었는지 확인하기 위함입니다.



>>> sc = StandardScaler()


scikit-learn의 preprocessing 모듈이 제공하는 StandardScaler()는 표준화를 적용하기 위한 클래스입니다. 표준화에 대해서는 이미 이전 포스팅에서 자세히 다루었습니다.



>>> sc.fit(X_train)


X_train의 평균과 표준편차를 구합니다.



>>> X_train_std = sc.transform(X_train)

>>> X_test_std = sc.transform(X_test)


X_train과 X_test를 표준화한 데이터를 각각 X_train_std, y_test_std에 대입합니다.



>>> ml = Perceptron(eta0=0.01, n_iter=40, random_state=0)

>>> ml.fit(X_train_std, y_train)


scikit-learn의 linear_model에는 머신러닝을 위한 다양한 선형모델 알고리즘들을 제공합니다. 여기에는 Perceptron 클래스도 포함되어 있습니다. learning rate을 0.01로 두고 퍼셉트론 객체를 생성합니다. X_train과 y_train을 이용해 머신러닝을 수행합니다.



>>> y_pred = ml.predict(X_test_std)


퍼셉트론으로 머신러닝을 수행한 후 X_test_std를 이용해서 이에 대한 예측값을 계산하고 y_pred로 둡니다.



>>> print('총 테스트 개수:%d, 오류개수:%d' %(len(y_test), (y_test != y_pred).sum()))


테스트 값에 대한 실제 결과값인 y_test와 머신러닝을 수행한 퍼셉트론에 의해 예측된  값인 y_pred를 비교하여 다른 값을 가진 개수를 출력합니다.



>>> print('정확도: %.2f' %accuracy_score(y_test, y_pred))


scikit-learn의 metrics 모듈에서 제공하는 accuracy_score()는 인자에 y_test, y_pred를 순서대로 입력하면 y_test와 y_pred를 비교하고 값이 같은 비율, 즉 정확도를 계산해줍니다.



이 코드를 수행하면 아래와 같은 결과가 나옵니다.


총 테스트 개수: 45, 오류개수: 4

정확도: 0.91




다음 포스팅에서 실제 아이리스 트레이닝 데이터가 어떻게 분포하는지 산점도로 그래프를 그려보고, 퍼셉트론 머신러닝에 의해 학습된 분류가 어떻게 이 데이터들을 구분하는지 그 영역을 그래프로 그려서 확인하고 그 유효성에 대해 살펴보도록 하겠습니다. 


반응형
반응형

우리가 실제로 다루는 데이터는 매우 큰 용량의 데이터가 대부분이며 , 이런 대규모 데이터에 대한 머신러닝을 이전 포스팅에서 설명한 배치 경사하강법을 이용한 아달라인을 그대로 활용하는 것은 성능면에서 문제가 있습니다.


이런 문제점을 극복하기 위한 방법으로 확률적 경사하강법(Stochastic Gradient Descent)을 적용한 아달라인이 있습니다.


이전 포스팅의 내용 중 아달라인의 가중치 업데이트 수식을 다시 상기해 봅니다.



이 수식의 계산을 위해, 

모든 트레이닝 데이터의 실제 결과값과 예측값의 차를 더한 값을 구해야 합니다.


그런데, 확률적 경사하강법에서는 아래와 같이 i번째 트레이닝 데이터에 대해서만 계산한 값을 이용합니다.


  

예를 들어 이해를 해봅니다.


100만개의 트레이닝 데이터가 있다고 가정합니다. 이를 배치 경사하강법으로 계산하려면 100만 x 100만 = 1조 번의 연산이 필요합니다. 이는 가중치를 업데이트 하는 식에서 y^(i)를 계산하기 위해 100만번의 덧셈을 해야하고, wj를 최종적으로 업데이트하기 위해 전체적으로 또 100만번의 덧셈을 수행해야 하기 때문입니다.


그런데, 확률적 경사하강법을 적용하게 되면 100만개의 모든 데이터를 활용하여 머신러닝을 수행하더라도 100만번의 연산만 필요합니다. 


확률적 경사하강법을 이용하면 배치 경사하강법의 근사치로 계산되지만 가중치를 업데이트하는 시간이 빠르기 때문에 실제로는 비용함수의 수렴값에 더 빨리 도달하게 됩니다.


그리고, 확률적 경사하강법에서 비용함수의 값을 계산할 때 값이 순환되는 것을 피하기 위해 학습을 반복할 때마다 트레이닝 데이터의 순서를 랜덤하게 섞어서 수행하는 것이 좋습니다.


또한 크라우드 소싱(crowd sourcing)으로 머신러닝을 적용하는 웹에서도 확률적 경사하강법을 활용할 수 있습니다. 어떤 사람이 웹을 통해 데이터를 제공하고, 이 데이터에 대한 예측 결과를 그 사람에게 제시하여 피드백을 받아 재학습을 시키는 것이 바로 그 예입니다. 이에 대해서는 이후 포스팅에서 다루어 보도록 하겠습니다.


그러면 실제 코드를 보면서 이해해봅니다. 아래 코드를 작성하고 mylib 폴더에 adalinesgd.py로 저장합니다.


adalinesgd.py 


이전 포스팅에서 소개한 표준화를 적용한 배치 아달라인의 AdalineGD 클래스와 여기서 구현한 AdalineSGD 클래스의 다른 부분만 설명합니다.

주요 변경 부분은 AdalineSGD 클래스의 fit() 함수의 로직과 _suffle() 함수가 추가된 것입니다.


>>> from numpy.random import seed


난수 발생기 초기화를 위해 numpy.random 모듈의 seed 함수를 임포트합니다.



>>> if random_state:

       seed(random_state)


random_state의 값이 있으면 이 값으로 난수 발생기를 초기화합니다.




>>> if self.shuffle:

       X, y = self._shuffle(X, y)


self.shuffle이 True로 설정되어 있으면, self._shuffle() 함수를 이용해 트레이닝 데이터 X와 y를 랜덤하게 섞습니다.



>>> cost = []
       for xi, target in zip(X, y):
          output = self.net_input(xi)
          error = target - output                
          self.w_[1:] += self.eta * xi.dot(error)
          self.w_[0] += self.eta * error                
          cost.append(0.5 * error**2)


       avg_cost = sum(cost)/len(y)

       self.cost_.append(avg_cost)


이 부분이 가중치를 업데이트하는 아래 식을 구현한 것입니다.



모든 트레이닝 데이터에 대해 비용함수의 값을 더해주고 for구문이 완료되면 평균값을 구하여 최종적인 비용함수 값으로 취합니다. 



>>> def _shuffle(self, X, y):

       r = np.random.permutation(len(y))

       return X[r], y[r]


numpy.random.permutation은 주어진 인자 미만의 정수(0을 포함)로 순열을 만드는 함수입니다. 따라서 r의 값은 0~len(y) 미만까지 정수를 랜덤하게 섞은 결과이므로, X[r], y[r]은 X와 y를 랜덤하게 섞은 numpy 배열이 됩니다.


나머지 부분은 이전 포스팅의 코드와 동일합니다.


자, 그러면 아래의 파일을 작성하고 std_adalinesgd_iris.py로 저장합니다. 보면 알겠지만, AdalineSGD를 임포트한 것과 AdalineSGD 클래스에서 random_state=1 로 설정한 것 빼고는 이전 포스팅의 std_adaline_iris.py와 로직이 동일합니다. 


std_adalinesgd_iris.py 

 


이 코드를 실행하면 아래와 같은 결과가 나옵니다. 



  


위 그래프를 보면, 비용함수의 값이 빠르게 줄어드는 것을 볼 수 있습니다. 이유는 한번의 반복문에서 가중치를 업데이트하는 빈도수가 더 많이 일어나기 때문이지요. 학습 반복회수가 늘어나면 결국 배치 경사하강법을 적용한 아달라인과 비슷해짐을 알 수 있습니다.


이로써 단순 인공신경망인 퍼셉트론과 아달라인에 대해 간략하게 살펴보았습니다.

물론 수식들이 나오고 내용도 이해하기 난해하지만 이제 어려운 부분들은 모두 끝났습니다. 앞으로 다루게 될 내용들은 이 개념들의 확장들이고, Scikit-Learn과 같은 라이브러리들은 사용자 친화적인 API들이므로 알고리즘 내부를 알 필요없이 다양한 머신러닝을 적용할 수 있습니다.


그렇더라도 퍼셉트론과 아달라인의 개념을 이해하게 되면 scikit-learn에서 제공하는 API의 활용도 더 편리하게 사용할 수 있고, 나아가 텐서플로우(TensorFlow)와 같은 오픈소스로 공개된 러닝 시스템에서도 여태까지 배운 개념이 적용되기도 합니다.


다음에는 머신러닝과 관련된 사용자 친화적인 scikit-learn API를 이용하여 머신러닝을 수행하는 다양한 알고리즘과 관련된 내용을 소개하도록 하겠습니다. 




반응형
반응형
[7편] 단순 인공신경망 아달라인을 파이썬으로 구현하기에서는 단순한 단층 인공신경망인 아달라인을 구현해 보았습니다. 하지만 매우 작은 값의 learning rate을 적용해야 비용함수가 특정값에 수렴하게 되어 머신러닝이 유효하게 되지만, 이는 매우 많은 수의 반복 학습이 필요하게 되어 머신러닝을 수행하는데 있어 성능상의 문제가 발생한다고 했습니다.


하지만 통계학에서 말하는 표준화(Standardization)를 이용해 트레이닝 데이터를 표준화된 값으로 변환하여 머신러닝을 수행하게 되면 비교적 큰 값의 learning rate에도 머신러닝이 유효하게 동작합니다.


우리는 고등학교 수학시간에 정규분포라는 통계학 용어를 접해보았을 것입니다. 정규분포라는 것이 통계학에 있어서는 매우 중요한데, 자연상에 존재하는 다양한 값들의 분포는 일반적으로 정규분포를 따른다고 알려져 있습니다.


예를 들어 지구상에 존재하는 사람의 키에 따른 사람의 수를 그래프로 그려보면 평균키를 기준으로 좌우로 대칭인 곡선으로 나타나는데, 평균에서 멀어지면 급격하게 그 수가 감소하는 양상으로 나타납니다. 이는 비단 사람의 키 뿐만 아니라 사람의 수명이라든가, 어느 한 종류 군집의 특징적인 값들의 분포들이 정규분포를 따른다고 봐도 됩니다.



위 그림은 정규분포 곡선을 나타낸 것인데 정규분포곡선은 평균값을 기준으로 좌우 대칭인 종모양의 곡선이며, 이 곡선의 폭을 결정하는 것이 표준편차입니다. 표준편차는 값들의 분포가 평균값을 기준으로 얼마나 흩어져 있느냐를 말해주는 수치입니다.


그런데 모든 정규분포 곡선은 어떤 변환을 이용하면 평균값이 0, 표준편차가 1인 정규분포 곡선으로 바꿀 수 있습니다. 평균값이 0이고 표준편차가 1인 정규분포를 표준 정규분포라고 하며, 표준 정규분포로 변환하는 작업을 표준화(Standardization)라고 부릅니다.


이전 포스팅에서 사용한 아이리스 품종 데이터는 Iris-setosa, versicolor, verginica 각각 50개씩 총 150개입니다. 만약 이 데이터가 매우 많고 이들의 꽃잎 크기나 꽃받침 크기 분포를 그려보면 아마도 정규분포 곡선 모양이 될 것입니다.


일반적으로 데이터를 표준화하여 처리하게 되면 일관성이 생길뿐만 아니라 처리 성능까지 향상되는 결과를 가져올 수 있습니다. 어떤 데이터 집합 x가 있으면 표준화 된 데이터 X는 아래의 식에 의해 구해집니다.



여기서 μ는 데이터 집합 x의 평균이며 σ는 데이터 집합 x의 표준편차입니다.

 

이제, 아이리스 품종 데이터를 표준화하여 아달라인을 적용하는 코드를 작성해 보겠습니다.

이전 포스팅의 adaline_iris.py 에서 if __name__ == '__main__' 을 아래와 같이 수정합니다.


std_adaline_iris.py


코드에서 아이리스 품종 데이터를 읽어들이는 부분도 동일합니다. 바뀐 부분만 살펴봅니다.


>>> X_std = np.copy(X)


X를 하나 복사하여 X_std로 둡니다.

 


>>> X_std[:, 0] = (X[:, 0] - X[:, 0].mean()) / X[:, 0].std()

>>> X_std[:, 1] = (X[:, 1] - X[:, 1].mean()) / X[:, 1].std()


이 부분이 바로 아이리스 데이터에서 꽃받침 길이와 꽃잎 길이의 표준화한 값을 X_std에 할당하는 부분입니다. numpy의 mean()은 numpy 배열에 있는 값들의 평균을, numpy의 std()는 numpy 배열에 있는 값들의 표준편차를 구해주는 함수입니다.



>>> adal = AdalineGD(eta=0.01, n_iter=15).fit(X_std, y)


learning rate을 0.01로, 반복회수를 15로 두고, X_std를 아달라인으로 머신러닝을 수행합니다.



>>> plt.plot(range(1, len(adal.cost_) + 1), adal.cost_, marker='o')


이전 포스팅에서는 y축의 값으로 log 스케일로 그렸지만, 지금은 그냥 그대로 그려봅니다.

코드를 수행하면 아래와 같은 그래프가 화면에 그려집니다.




표준화를 이용하면 learning rate을 0.01로 두어도 비용함수의 값이 발산하지 않고 특정한 값에 수렴하고 있음을 알 수 있습니다.


여기까지 살펴본 아달라인 내용은 우리가 가지고 있는 모든 트레이닝 데이터를 한꺼번에 일괄 적용하여 학습을 수행하는 것이었습니다. 이를 배치 경사하강법(Batch Gradient Descent)을 이용한 아달라인이라 부릅니다. 배치 경사하강법을 이용하는 경우, 데이터의 개수가 매우 많아지고, 데이터 입력값의 종류도 다양하다면 머신러닝을 효율적으로 수행할 수 없고 시간도 매우 오래 걸리게 됩니다.


다음 포스팅에서는 이를 극복하기 위한 확률적 경사하강법(Stochastic Gradient Descent)을 이용하는 아달라인에 대해 다루도록 하겠습니다. 


반응형
반응형

이번 포스팅에서는 퍼셉트론을 구현한 코드를 조금 수정하여 아달라인 코드를 파이썬으로 구현해 봅니다. 아래의 코드를 작성하고 mylib 폴더에 adaline.py로 저장합니다.


adaline.py 


기본인자의 디폴트 값, thresholds를 0.0으로 고정한 부분을 제외하면 퍼셉트론을 구현한 Perceptron 클래스와 아달라인을 구현한 AdalineGD 클래스에서 다른 부분은 아래의 fit() 내에서 구현한 부분 밖에 없습니다.


>>> for i in range(self.n_iter):

          output = self.net_input(X)

          errors = (y - output)

          self.w_[1:] += self.eta * X.T.dot(errors)

          self.w_[0] += self.eta * errors.sum()

          cost = (errors**2).sum() / 2

          self.cost_.append(cost)


이 부분이 아달라인에서 가중치를 업데이트 하는 아래 식을 구현한 것입니다.



adline.py에서 리스트로 정의된 self.cost_ 는 비용함수 J(w)가 어떤 값으로 수렴하는지 확인하기 위해 매 반복마다 계산되는 비용함수값을 저장하기 위한 변수입니다.


X.T는 행렬 X의 전치행렬을 의미합니다.



행렬 X와 행렬 Y의 행렬 곱셈이 가능하려면 X의 행의 숫자와 Y의 열의 숫자가 같아야 하는데, X.T로 X의 행과 열을 바꾸어야 가중치가 저장된 errors와 곱셈이 가능하기 때문입니다.


이것이 다입니다. 퍼셉트론의 코드를 다 이해하고, 아달라인의 가중치 업데이트 식을 알고 있다면 아달라인 코드는 이해하기가 쉽습니다.


이제 퍼셉트론에서 다루었던 아이리스 품종 데이터를 이용해서 아달라인으로 머신러닝을 수행하는 아래의 코드를 작성하여 adaline_iris.py로 저장합니다.


adaline_iris.py


if __name__ == '__main__': 에 있는 코드는 퍼셉트론에서와 마찬가지로 아이리스 품종 데이터 100개를 읽어와서 하나는 learning rate을 0.01로 두고 아달라인을 수행하여 얻은 비용함수 J(w)의 값 변화와 또 하나는 learning rate을 0.0001로 두고 아달라인을 수행하여 얻은 비용함수 J(w)의 값 변화를 matplotlib을 이용해 화면에 그려주는 코드입니다.


matplotlib에 대한 내용은 예전에 따로 시리즈로 포스팅했으니 아래 링크를 참조하기 바랍니다.


☞ Matplotlib을 이용한 데이터 시각화 프로그래밍 시리즈 바로가기



코드를 수행하면 다음과 같은 그래프가 화면에 나타납니다.




왼쪽 그래프를 보면 알수 있듯이 learning rate을 0.01로 수행한 아달라인의 비용함수 J(w) 값은 계속 커집니다. 즉 이 경우에는 J(w) 값이 수렴하지 않고 발산해버립니다.


반면, learning rate의 값을 0.0001로 수행한 아달라인의 비용함수 J(w)값은 계속 작아져서 어느 특정한 값에 수렴하게 된다는 것을 알 수 있습니다.


따라서 learning rate을 매우 작게 해야 아달라인의 J(w) 값이 작아지고 우리가 원하는 머신러닝의 결과를 얻을 수 있습니다.


이렇게 되는 이유는 아래의 그림으로 설명이 가능합니다.




이전 포스팅에서 아달라인에서 스텝 크기 △w는 아래와 같이 정의된다고 하였습니다.



여기서 η가 learning rate 입니다.


아달라인이 제대로 수행되려면 위 그림의 왼쪽 그림과 같이 초기 가중치로부터 곡선을 따라 아래로 차근차근 내려가야 하는데, 이렇게 되려면 △w의 값이 작아져야 합니다. 만약 △w의 값이 크게 되면 위 그림의 오른쪽 그림과 같이 초기 가중치가 위치한 부분에서 곡선의 최소값을 건너뛴 위치에서 다시 스텝을 계산하게 되고 이런 식으로 스텝의 크기가 갈수록 커져버려서 비용함수 J(w)의 값은 발산하게 됩니다.


learning rate의 값이 매우 작으면 J(w)의 값은 수렴하지만, 의미 있는 값으로 수렴하는 것을 확인하려면 매우 많은 반복 회수가 필요합니다.


다음에는 정규분포에서 사용되는 표준화(standardization)를 이용해 이런 단점을 극복하는 아달라인 알고리즘을 적용해보도록 합니다. 



반응형
반응형

아달라인(Adaline)


단층 인공신경망인 퍼셉트론이 발표된지 몇 년 후인 1960년에 Bernard Widrow와 Tedd Hoff는 퍼셉트론 알고리즘을 향상시킨 새로운 인공신경망 알고리즘에 대한 논문을 발표합니다.


이 인공신경망을 Adaptive Linear Neuron, 줄여서 Adaline(아달라인)이라 불렀습니다. 

아달라인은 분류(classification)를 위한 보다 발전된 머신러닝 알고리즘인 회귀(regression), 로지스틱 회귀(logistic regression), SVM(Support Vector Machine)에 대한 알고리즘의 토대를 마련하게 됩니다.


퍼셉트론과 알다라인의 차이점은 바로 가중치 업데이트를 위한 활성 함수(Activation Function)가 다른 것입니다. 아래 그림은 이전 포스팅에서 설명했던 퍼셉트론 알고리즘을 도식화 한 것입니다.




아래 그림은 아달라인 알고리즘을 도식화 한 것입니다. 




위의 두 그림을 보면 알 수 있듯이,


퍼셉트론에서는 활성 함수가 순입력 함수의 리턴값을 임계값을 기준으로 1 또는 -1로 리턴한 값과, 실제 결과값을 임계값을 기준으로 1 또는 -1로 리턴한 값을 비교하여 가중치를 업데이트 하도록 하거나 결과를 출력하는 기능을 하였습니다.


그런데 아달라인에서는 순입력 함수의 리턴값과 실제 결과값을 비교하여 이 오차가 최소가 되도록 가중치를 업데이트 하는 기능을 활성 함수가 수행하게 됩니다. 이를 위해 아달라인을 발표한 논문에서는 최소제곱법을 이용한 비용함수(cost function) J(w)를 아래와 같이 정의하였고, J(w) 값이 최소가 되도록 가중치를 업데이트 하는 것이 핵심입니다.



여기서 y(i)는 i번째  트레이닝 데이터에서 주어진 실제 결과값이고 y^(i)은 i번째 트레이닝 데이터에 대해 수행한 순입력 함수의 리턴값입니다. 따라서 실제로 이 식은 아래와 같은 식이 되는 것이죠~


                                                     


가중치 w는 n개로 이루어진 변수이지만, 이를 그냥 한개의 변수로 단순화시켜 생각해보면 J(w)는 결국 w에 대한 2차 함수가 되며 이 값이 최소가 되는 w를 찾으면 됩니다. 2차항의 계수가 양수인 2차 함수의 최소값은 중학교 수학정도만 알면 누구나 쉽게 구할수 있지만, w는 n개이기 때문에 2차함수의 최소값을 구하는 공식을 바로 적용할 수는 없습니다.

여기서는 경사하강법(gradient descent)이라 불리는 최적화 알고리즘을 이용하여 J(w)가 최소가 되는 가중치를 찾게 됩니다.


경사하강법은 어떤 곡선에서 기계적으로 최소값을 찾아가는 방법인데, 곡선상의 어떤 한지점에서 경사방향으로 한걸음 내려가고, 내려간 그 지점에서 또 다시 경사방향으로 한걸음 내려가고 하는 식으로 진행해서, 결국은 최소지점까지 도달하는 방법입니다.


아래 그림에서 보듯이, 경사하강법은 곡선을 따라 일정 크기(이를 스텝 크기라고 함)만큼 접선의 기울기 방향으로 내려가면서 그 값이 최소값인지 판단하는 것인데, 아달라인에서 각 스텝마다 그 다음 스텝 크기의 결정은 그 지점에서 접선의 기울기와 learning rate으로 결정됩니다. 아래 그래프에서 스텝 크기는 화살표 길이의 w성분값 입니다.




아달라인은 이런 경사하강법을 이용하는데, 스텝의 크기 △w는 아래와 같이 정의합니다.



여기서 η는 learning rate이고 △J(w)는 w에서 J(w)의 접선의 기울기, 즉 J(w)의 미분값입니다. 위의 설명에 따라 초기 가중치 w의 값에서 시작하여 아래의 식에 의해 스텝을 달리하며 w를 업데이트 합니다.



n개의 트레이닝 데이터에 대해 아달라인을 수행하고, 이 식에 의해 가중치가 업데이트 됨에 따라 J(w)가 어떤 값에 수렴하게 되면 이는 제대로 머신러닝이 이루어졌다는 것을 의미합니다. 만약 어떤 값에 수렴하지 못하고 발산하게 되면 learning rate을 조정해봄으로써 J(w)가 수렴하게 될 수도 있습니다. 따라서 아달라인에서는 J(w)가 수렴하도록 하는 learning rate을 찾는 것이 중요한데, 보통 매우 작은 값을 적용하면 됩니다.


위의 가중치 업데이트 식에서, J(w)를 w에 대해 편미분하면 아래와 같은 식으로 됩니다.



여기서 xj(i)는 i번째 트레이팅 데이터의 j번째 입력값입니다. 혹시나 이 수식이 어떻게 유도되는지 궁금해 하시는 분들이 있을까봐, 간단하게 설명하자면....(물론 수학이라는 소리만 들어도 두드러기 생기는 분들이 많을 것이라 생각합니다...) △wj 의 값을 아래와 같이 유도하면 됩니다. 아래 수식에서 편의상 i번째 트레이닝 데이터를 의미하는 첨자 i는 생략했습니다.


고등학교 때 배웠던 합성함수의 미분은 아래와 같습니다.




그리고 cost function J(w)는 아래와 같고,


이 식을 합성함수의 미분 공식을 고려하여, wj에 대해서 편미분하면,


                                                          


                                                          


                                                         



따라서,

                                                           


그러므로,


                                                          



위 유도식은 우리가 머신러닝을 진행함에 있어 전혀 몰라도 상관 없으니 그냥 참고만 하시기 바랍니다~



다음 포스팅에서는 퍼셉트론 알고리즘을 구현한 파이썬 코드를 약간 수정하여 아달라인을 구현해 도록 합니다. 

공감 


반응형
반응형

이번에는 아래의 링크에서 얻을 수 있는 150개의 아이리스 데이터를 단순 인공신경망인 단층 퍼셉트론으로 머신러닝을 수행해 보도록 하겠습니다.


https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data


위의 링크를 클릭해보면 아래와 같은 csv 형태로 저장된 150개의 데이터를 볼 수 있습니다.


5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa

...

7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
...

6.2,3.4,5.4,2.3,Iris-virginica
5.9,3.0,5.1,1.8,Iris-virginica 


이 데이터는 4개의 특성값과 이 특성값에 따른 품종으로 되어 있는데, 첫번째부터 차례대로 꽃받침길이, 꽃받침너비, 꽃잎길이, 꽃잎너비, 아이리스 품종을 나타냅니다. 데이터는 각각 iris-setosa에 대한 데이터가 50개, iris-versicolor에 대한 데이터가 50개, iris-virginica에 대한 데이터가 50개로 구성되어 있습니다.


참고로 아이리스(iris)는 우리말로 붓꽃이라 부르며 꽃받침길이와 너비, 꽃잎길이와 너비로 품종을 구분하나 봅니다.




이제 위의 데이터를 아래의 표와 같이 보기 편하게 정리를 해봅니다.



우리가 해볼 것은 이전 포스팅에서 소개한 퍼셉트론 알고리즘을 이용해 꽃받침길이/너비, 꽃잎길이/너비를 이용해 아이리스 품종을 구분할 수 있도록 머신러닝을 수행하는 것입니다.


아시다시피 퍼셉트론은 바이너리 결과를 가지므로 3개의 품종을 동시에 구분할 수 있는 머신러닝을 수행할 수는 없습니다. 따라서 1~100까지의 데이터에서 꽃받침길이와 꽃잎길이 데이터만을 이용해 Iris-Setosa와 Iris-Versicolor의 2개 품종을 구분할 수 있도록 학습시키도록 합니다.


아래의 코드를 작성하고 perceptron_iris.py로 저장합니다. 참고로 코드의 맨 윗줄 %matplotlib inline은 Jupyter 환경에서 코딩하는 경우, maplotlib 결과를 임베드시켜 보여주는 코드이므로  Jupyter 환경이 아닌 경우, 삭제하면 됩니다.

 

perceptron_iris.py 


>>> import pandas as pd

>>> import matplotlib

>>> import matplotlib.pyplot as plt

>>> from matplotlib import style

>>> from mylib.perceptron import Perceptron


필요한 모듈을 임포트 합니다.


>>> krfont = {'family':'NanumGothic', 'weight':'bold', 'size':10}
>>> matplotlib.rc('font', **krfont)

>>> matplotlib.rcParams['axes.unicode_minus'] = False


이 부분은 matplotlib에서 한글을 표현하기 위한 것으로 윈도우 환경에서 구동하는 경우 제거해도 됩니다.



이전 포스팅의 perceptron_and.py와 비교하여 if __name__ == '__main__' 에서 변경된 부분입니다.


>>> df = pd.read_csv('iris.data', header=None)


csv 형식으로 저장된 'iris.data'를 pandas의 read_csv() 함수를 이용해서 읽고, pandas의 DataFrame 객체로 변환합니다. 아이리스 데이터 링크에 걸려 있는 파일을 로컬로 다운로드 받아서 수행하는 경우 위 코드와 같이 하면 되지만 'iris.data' 부분에 아이리스 데이터를 다운로드 받을 수 있는 URL을 그대로 적어도 무방합니다.


참고로 pandas는 데이터 처리와 관련된 다양한 라이브러리를 제공하는 파이썬 확장 모듈입니다. pandas에 대한 내용은 너무 방대하므로 여기서는 다루지 않습니다.



>>> y = df.iloc[0:100, 4].values

>>> y = np.where(y=='Iris-setosa', -1, 1)


아이리스 데이터를 저장한 DataFrame에서 0~99라인까지 5번째 컬럼의 데이터 값을 numpy 배열로 리턴받아 y에 대입합니다. 따라서 아이리스 데이터 파일에서 100개의 데이터를 추출하여 5번째 컬럼의 데이터 값을 numpy 배열로 리턴한 것이죠. 앞에서 보인 표를 보면 5번째 컬럼은 아이리스 품종을 나타내는 문자열 값입니다.(x0은 아이리스 데이터에 있는 값이 아님을 유의하세요~) 


y에 저장된 품종을 나타내는 문자열이 'Iris-setosa'인 경우 -1, 그렇지 않은 경우 1로 바꾼 numpy 배열을 y에 다시 대입합니다. 앞에서 언급했듯이 아이리스 데이터는 1~50까지가 Iris-setosa이며, 51~100까지가 Iris-versicolor 입니다. 따라서 y의 0~49번 인덱스에 해당하는 값은 -1로 50~99번 인덱스에 해당하는 값은 1로 되어 있는 numpy 배열이 됩니다.



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


아이리스 데이터를 저장한 DataFrame에서 0~99라인까지 1번째, 3번째 컬럼의 데이터 값을 numpy 배열로 리턴받아 이를 X에 대입합니다. 아이리스 데이터의 1번째 값은 꽃받침길이이며, 3번째 값은 꽃잎길이입니다. 즉 이 코드는 꽃받침길이, 꽃잎길이에 따른 아이리스 품종을 머신러닝으로 학습하는 내용이 될 것입니다.



>>> plt.scatter(X[:50, 0], X[:50, 1], color='r', marker='o', label='setosa')

>>> plt.scatter(X[50:100, 0], X[50:100, 1], color='b', marker='x', label='versicolor')

>>> plt.xlabel('꽃잎 길이(cm)')

>>> plt.ylabel('꽃받침 길이(cm)')

>>> plt.legend(loc=4)

>>> plt.show()


이 코드는 X에 저장된 아이리스 데이터의 꽃받침길이, 꽃잎길이에 대한 데이터의 상관관계를 파악하기 위해 Matplotlib의 산점도를 이용해 화면에 그려보는 코드입니다. Matplotlib의 산점도 그리기는 아래의 제 블로그 포스팅을 참고하세요.


☞ 데이터 시각화 프로그래밍 - 산점도 그리기 바로가기

 


>>> ppn = Perceptron(eta=0.1)

>>> ppn.fit(X, y)

>>> print(ppn.errors_)


이 부분은 이전 코드와 변동 없습니다.


코드 설명을 보면 알 수 있듯이, 이전 포스팅에서 AND 연산을 학습한 것과 비교하면 학습하려는 데이터 세트를 구성하는 방법을 제외하고는 거의 동일하다는 것을 알 수 있습니다.


프로그램을 실행하면 다음과 같은 결과가 화면에 나올 것입니다.




[ 0.2   1.4   0.94]
[ 0.    0.8   1.32]
[-0.2  0.2  1.7]
[-0.2   0.32  2.12]
[-0.4  -0.7   1.84]
[-0.4  -0.7   1.84]
[-0.4  -0.7   1.84]
[-0.4  -0.7   1.84]
[-0.4  -0.7   1.84]
[-0.4  -0.7   1.84]
[1, 3, 3, 2, 1, 0, 0, 0, 0, 0]


위 실행결과를 보면 알 수 있듯이 6번째 학습후에 가중치의 값이 최종적으로 결정된다는 것을 알 수 있습니다.

50개의 Iris-setosa 데이터, 50개의 Iris-versicolor 데이터로 수행한 머신러닝의 결과는 다음과 같이 요약할 수 있습니다.


*** 머신러닝 결과 *** 

-0.4 + (-0.7) x (꽃받침길이) + 1.84 x (꽃잎길이) 의 값이 0보다 작거나 같으면 Iris-setosa, 이 값이 0보다 크면 Iris-versicolor로 분류함



여태까지 언급한 퍼셉트론은 입력값과 가중치, 그리고 하나의 노드(또는 뉴런)와 출력값을 가지는 단층 퍼셉트론이며 퍼셉트론에 대한 내용은 이정도에서 마무리 하도록 합니다.  


참고로 단층 퍼셉트론은 선형 분리가 가능한 데이터에 대해서만 유효하며,  비선형적으로 분리되는 데이터에 대해서는 가중치 값을 계산하지 못하는 경우가 대부분입니다. 예를 들어 단층 퍼셉트론으로 AND 연산에 대한 학습은 가능하였지만 XOR 연산에 대한 학습은 불가능하다고 증명되었습니다. 단층 퍼셉트론의 많은 약점을 보완하기 위해 다층 퍼셉트론 개념이 나왔는데 이에 대해서는 시간이 되는대로 차후에 살펴보도록 합니다. 


반응형
반응형

이번 포스팅에서는 [3편]에서 보인 퍼셉트론을 파이썬으로 구현한 perceptron.py 코드에 대해 설명을 합니다.



perceptron.py 



>>> import numpy as np


numpy 모듈을 임포트합니다.



>>> class Perceptron()


퍼셉트론을 구현하는 클래스를 정의하고, 주요 알고리즘은 클래스 멤버 함수로 구현할 것입니다.



>>> def __init__(self, thresholds=0.0, eta=0.01, n_iter=10)


Perceptron 클래스의 생성자를 정의합니다. 생성자의 기본인자로 임계값, learning rate, 학습 횟수를 지정하는 thresholds, eta, n_iter의 디폴트 값을 각각 0.0, 0.01, 10으로 지정하고 있습니다.



>>> def fit(self, X, y)


트레이닝 데이터 X와 실제 결과값 y를 인자로 받아 머신러닝을 수행하는 함수입니다. 머신러닝 분야에서 일반적으로 트레이닝 데이터를 대문자 X로, 실제 결과값을 소문자 y로 표현합니다. 여기서도 이 관습을 따라 X, y를 각각 트레이닝 데이터, 실제 결과값을 의미하는 인자로 정의한 겁니다.



>>> self.w_ = np.zeros(1+X.shape[1])


가중치를 numpy 배열로 정의합니다. X.shape[1]은 트레이닝 데이터의 입력값 개수를 의미합니다. 예를 들어 X가 4 x 2 배열인 경우, X.shape의 값은 (4, 2)가 되며 X.shape[1]의 값은 2가 됩니다. 이 경우 self.w_는 np.zeros(3)이 될 것이고, 실제 값은 numpy 배열 [0. 0. 0.]이 됩니다.



>>> self.errors_ = []


머신러닝 반복 회수에 따라 퍼셉트론의 예측값과 실제 결과값이 다른 오류 회수를 저장하기 위한 변수입니다.



>>> for _ in range(self.n_iter):

          errors = 0

          for xi, target in zip(X, y):

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

             self.w_[1:] += update * xi

             self.w_[0] += update

             errors += int(update != 0.0)

          self.errors_.append(errors)

          print(self.w_)


self.n_iter로 지정한 숫자만큼 반복합니다. for 다음에 적힌 _은 아무런 의미가 없는 변수이며 단순히 for문을 특정 회수만큼 반복만 하고자 할 때 관습적으로 사용하는 변수라고 생각하면 됩니다.


초기 오류 횟수를 0으로 정의하고, 트레이닝 데이터 세트 X와 결과값 y를 하나씩 꺼집어내서 xi, target 변수에 대입합니다. xi는 하나의 트레이닝 데이터의 모든 입력값 x1~xn 을 의미합니다. 참고로 x0는 1로 정해져 있습니다.

update는 아래의 식에 대한 값입니다.



여기서 실제 결과값과 예측값에 대한 활성 함수 리턴값이 같게 되면 update는 0이 됩니다. 따라서 트레이닝 데이터 xi의 값에 곱해지는 가중치에 update * xi의 값을 단순하게 더함으로써 가중치를 업데이트 할 수 있는데, 이는 결과값과 예측값의 활성 함수 리턴값이 같아질 경우 0을 더하는 꼴이라 가중치의 변화가 없을 것이고, 결과값과 예측값의 활성 함수 리턴값이 다를 경우 0이 아닌 유효한 값이 더해져서 가중치가 업데이트 될 것이기 때문입니다. 마찬가지로 w0의 값에 해당하는 self.w_[0]에는 x0가 1이므로 update만 단순하게 더하면 됩니다. 여기까지가 아래의 식을 계산한 것이 됩니다.



update의 값이 0이 아닌 경우, errors의 값을 1증가시키고 다음 트레이닝 데이터로 넘어갑니다. 모든 트레이닝 데이터에 대해 1회 학습이 끝나면 self.errors_에 발생한 오류 횟수를 추가한 후, 가중치를 화면에 출력하고 다음 학습을 또 다시 반복합니다.



>>> def net_input(self, X):

          return np.dot(X, self.w_[1:]) + self.w_[0]


numpy.dot(x, y)는 벡터 x, y의 내적 또는 행렬 x, y의 곱을 리턴합니다. 따라서 net_input(self, X)는 트레이닝 데이터 X의 각 입력값과 그에 따른 가중치를 곱한 총합, 즉 이전 포스팅에서 설명한 아래의 순입력 함수 결과값을 구현한 것입니다.




여기서 잠깐!

앞으로 이어질 머신러닝과 딥러닝에 대한 수식을 연산하거나 설명할 때 가장 많이 등장하는 개념이 벡터나 행렬입니다. 우리는 벡터와 행렬에 대해 고등학교 수학에서 배웠습니다. 그리고 이공계 대학을 나왔다면 선형대수학이라는 과목에서 관련된 내용을 모두 배웠으리라 생각합니다. 물론 이 포스팅을 읽고 있는 많은 분들이 벡터나 행렬, 선형대수학에 익숙하지 않은 사람들도 있겠지요~


그래서, 행렬에 대해 간단하게 짚고 넘어가도록 하겠습니다.


행렬이란 수들의 집합을 행과 열방향으로 나열해 놓은 것을 말합니다. 아래는 숫자 1, 2, 3, 4를 이용한 행렬의 한 예시를 보인 것입니다.



여기서 1, 2나 3, 4가 나열된 방향을 행이라 하고, 1, 3이나 2, 4가 나열된 방향을 열이라 합니다.



따라서 위에서 보인 행렬은 행이 2개, 열이 2개인 행렬이며, 이를 간단히 행과 열의 개수를 순서대로 적어 2 x 2(차원) 행렬이라 부릅니다. 만약 어떤 행렬 A가 행의 개수가 m개이고 열의 개수가 n개인 m x n 행렬이라면 다음과 같이 표현할 수 있습니다.



여기서, a11, a12 등을 행렬 A의 성분이라 부릅니다.


행렬이 어떤 것인지에 대해 대충 이해가 되었다면, 이제 행렬간 연산에 대해 알아봅니다. 행렬에서 가장 많이 사용되는 연산은 +, -, x 입니다.


먼저, 행렬 A, B가 있다고 할 때 두 행렬간 덧셈과 뺄셈이 가능하려면 두 행렬의 차원이 동일해야 합니다. A가 2 x 2행렬이면, B도 2 2 행렬이어야 더하기 또는 빼기가 가능하다는 것입니다.


행렬의 덧셈과 뺄셈은 두 행렬에서 일치하는 성분끼리 더하거나 빼면 됩니다. 행렬 A, B가 아래와 같다고 하고,



두 행렬 A, B의 덧셈은 다음과 같습니다.





행렬의 뺄셈도 마찬가지로 하면 되고요,~ 뭐, 다 아는 내용이니까 복습하는겸 해서 가볍게 보면 됩니다.


이제는 두 행렬의 곱셈에 대해서 살펴봅니다. 행렬간 곱셈은 더하기 빼기와는 좀 다르게 동작합니다. 행렬 A, B가 있다고 하고, 이 두 행렬의 곱 AB가 가능하려면 A와 B의 차원에 관계없이 A의 열의 개수와 B의 행의 개수가 동일하면 됩니다.




위의 두 행렬 A, B를 보면, A의 열의 개수와 B의 행의 개수가 n개로 동일하므로 A와 B의 곱 AB는 가능합니다. 하지만 B의 열의 개수와 A의 행의 개수는 다르므로 두 행렬의 곱 BA는 가능하지 않습니다. 이점 유의하세요~~


두 행렬의 곱이 가능한 조건이 충족되면 AB는 다음과 같이 정의 됩니다.




아,, 이거 간단한 수식으로 표현하려다 보니 복잡해보이네요.. 아래 그림을 보시죠..(말로 설명하면 쉽게 될걸 글로 하면 이렇게 어렵습니다..)




A의 1행과 B의 1열을 선택하고 A의 1행에 있는 성분과 B의 1열에 있는 성분끼리 곱하여 모두 더한 값을 결과 행렬의 (1, 1) 성분으로 합니다. 마찬가지로 A의 1행과 B의 2열을 성분끼리 곱한 후 모두 더한 값을 결과 행렬의 (1, 2) 성분으로 합니다. 이런 식으로 A의 모든 행과 B의 모든 열을 곱하여 결과 행렬의 해당되는 자리에 대입하면 됩니다.


따라서 A와 B의 곱 AB는 m x k 행렬이 됩니다.


이제, [3편] 퍼셉트론의 입력값과 가중치를 행렬로 표현해보면 다음과 같이 됩니다. (보통 벡터나 행렬은 굵은 글씨체로 나타냅니다.)


 



이런 식으로 두면 x와 y 행렬은 각각 1 x n 행렬이 되어 곱셈이 되지 않겠지요. 그래서 w를 n x 1 행렬로 바꾸어서 아래와 같이 곱을 정의하면 됩니다.



​파이썬은 이런 형식의 벡터나 행렬연산을 아주 쉽게 해주는 훌륭한 도구가 있는데, 그게 바로 numpy 배열입니다. 결국 numpy는 다양한 차원의 행렬을 numpy 배열로 표현해서 numpy가 제공하는 다양한 메쏘드를 이용하면 번거로운 계산작업을 손쉽게 할 수 있습니다. 

아무튼 입력값이나 가중치는 행렬로 취급하면 계산이 편리해지므로 행렬의 연산방법과 numpy에 대해 익숙해지면 좋겠지요~




>>> def predict(self, X):

          return np.where(self.net_input(X) > self.thresholds, 1, -1)


self.net_input(X)의 값, 순입력 함수 결과값이 임계값인 self.thresholds의 값보다 크면 1, 그렇지 않으면 -1을 리턴하는 코드입니다. 따라서 이 함수는 활성 함수를 구현한 것입니다.


이제 AND 연산에 대해 퍼셉트론을 수행하는 perceptron_and.py 코드입니다.


perceptron_and.py 



>>> from mylib.perceptron import Perceptron


mylib 폴더에 있는 perceptron.py에 있는 Perceptron 클래스를 임포트합니다.



>>> if __name__ == '__main__':

          X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])

          y = np.array([-1, -1, -1, 1])


          ppn = Perceptron(eta=0.1)

          ppn.fit(X, y)

          print(ppn.error_)


X는 AND 연산에 대한 트레이닝 데이터를 정의한 것입니다. 트레이닝 데이터에 대한 실제 결과값에 대한 활성 함수 리턴값은 임계값 0보다 크면 1, 그렇지 않으면 -1이므로 y를 이 값들로 바로 정의합니다.


learning rate 에타값을 0.1로 두고 Perceptron 객체를 생성하고 이 객체의 fit() 멤버 함수를 호출함으로써 퍼셉트론 알고리즘을 구동합니다. perceptron_and.py를 구동하면 아래와 같은 학습 결과가 나옵니다.


[ 0.2  0.2  0.2]
[ 0.   0.4  0.2]
[-0.2  0.4  0.2]
[-0.2  0.4  0.4]
[-0.4  0.4  0.2]
[-0.4  0.4  0.2]
[-0.4  0.4  0.2]
[-0.4  0.4  0.2]
[-0.4  0.4  0.2]
[-0.4  0.4  0.2]

[1, 3, 3, 2, 1, 0, 0, 0, 0, 0]



이 결과를 보면 5번째 이후로 가중치의 업데이트가 발생하지 않기 때문에, 6번째 학습에서 AND 연산의 최종 학습이 마무리 되었다고 판단할 수 있습니다. 


반응형
반응형

이제 [2편]에서 설명했던 퍼셉트론 알고리즘을 파이썬으로 구현해 봅니다.



먼저, 논리곱 AND 연산에 대해 퍼셉트론 알고리즘으로 머신러닝을 수행하는 예를 보면서 이해를 해보도록 합니다.

참을 1, 거짓을 0으로 정의할 때, 논리곱 AND 연산은 다음과 같이 정의됩니다.


0 AND 0 = 0

0 AND 1 = 0

1 AND 0 = 0

1 AND 1 = 1


퍼셉트론 알고리즘을 이용해 AND 연산을 학습시키는 것이 우리의 목표입니다.

AND 연산을 위해 필요한 트레이닝 데이터는 위에서 보인 4가지이며, 트레이닝 데이터의 특성값은 0과 1 2개입니다. 이를 이전 포스팅에서 정의한 표를 이용해 나타내보면 다음과 같습니다.



퍼셉트론 알고리즘에서 보통 0 또는 매우 작은 값을 가중치의 초기값으로 둔다고 했는데, 우리의 알고리즘에서는 초기 가중치의 값을 모두 0으로 둡니다.


learning rate η는 0.1로, 임계값은 0으로 정의하고, 순입력 함수 결과값이 임계값 0보다 크면 1, 그렇지 않으면 -1로 정의해봅니다.




자, 이전 포스팅에서 소개된 알고리즘 원리에 따라 퍼셉트론 학습을 수행해봅니다.


1. 트레이닝 데이터1에 대해 순입력 함수 리턴값을 계산합니다.

w0 x0 + w1 x1 + w2 x2 = 0.0 x1 + 0.0 x 0 + 0.0 x 0 = 0


2. 순입력 함수의 리턴값과 임계값 0을 비교하면 0보다 크지 않으므로 활성 함수 리턴값은 -1이 됩니다. 이는 실제 결과값에 대한 활성 함수 리턴값과 일치하므로 가중치 업데이트 없이 다음 트레이닝 데이터 2로 넘어갑니다.


3. 트레이닝 데이터2~3까지는 예측된 값의 활성 함수 리턴값과 실제 결과값의 활성 함수 리턴값이 일치하므로 가중치 업데이트 없이 트레이닝 데이터4로 넘어갑니다.


4. 트레이닝 데이터4에 대해 1과 같이 계산한 퍼셉트론 예측값의 활성 함수 리턴값은 -1로 나왔지만 실제 결과값의 활성 함수 리턴값은 1이므로 예측값과 결과값이 다릅니다. 따라서 아래의 식에 의해 가중치를 다시 계산합니다.



w0 = w0 + 0.1(1-(-1))1 = 0 + 0.2 = 0.2

w1 w1 + 0.1(1-(-1))1 = 0 + 0.2 = 0.2

w2 w2 + 0.1(1-(-1))1 = 0 + 0.2 = 0.2


5. 트레이닝 데이터4에서 가중치가 업데이트 되었으므로 새롭게 업데이트 된 가중치를 가지고 트레이닝 데이터1부터 1의 과정을 다시 수행합니다.


이런 식으로 트레이닝 데이터1~4에 대한 예측값의 활성 함수 리턴값과 실제 결과값의 활성 함수 리턴값이 같아지도록 가중치를 계속 업데이트해보면

w0 = -0.4, w1 = 0.4, w2 = 0.2 일 때, 모든 트레이닝 데이터에 대한 예측값과 결과값의 활성 함수 리턴값이 같아집니다.


이와 같은 퍼셉트론 알고리즘은 다음과 같은 파이썬 코드로 구현됩니다. 참고로 퍼셉트론 알고리즘을 구현한 아래의 코드는 Sebastian Raschka의 'Python Machine Learning'책에 있는 코드입니다. 아래의 코드를 작성하고 perceptron.py로 저장합니다.

 

perceptron.py 



나의 경우 mylib이라는 이름의 폴더를 만들고 여기에 perceptron.py를 저장해두었습니다. 여러분들의 작업 폴더가 c:/mywork 이라면 하위 폴더로 c:/mywork/mylib을 만들고 mylib에 여러분들이 작성한 라이브러리들을 저장해두면 관리하기가 쉽습니다.


아래의 코드를 작성하고, perceptron_and.py라는 이름으로 작업 폴더에 저장합니다.


perceptron_and.py



다음 포스팅에서는 perceptron.py와 perceptron_and.py 코드에 대해 가볍게 설명하고 머신러닝을 위한 훌륭한 샘플 데이터인 아이리스(iris; 우리말로 붓꽃)의 품종 분류에 대한 트레이닝 데이터를 이용해 머신러닝을 수행하는 것에 대해 살펴보겠습니다. 


반응형
반응형

 [출처] [2편] 퍼셉트론(Perceptron) - 인공신경망의 기초개념|작성자 옥수별


퍼셉트론(Perceptron)

​인공지능(AI)은 우리 사람의 뇌를 흉내내는 인공신경망과 다양한 머신러닝 알고리즘을 통해 구현됩니다. 이전 포스팅에서 잠시 언급했던 딥러닝 알고리즘이 현재 가장 널리 사용되는 인공지능을 위한 알고리즘이죠. 이런 딥러닝도 수십년전에 개념이 정립되었던 초기 인공신경망으로부터 발전된 것이라 볼 수 있습니다.


여기서 소개할 퍼셉트론과 퍼셉트론을 좀 더 발전시킨 아달라인은 이해하기가 다소 까다로울수 있지만 앞으로 계속 등장할 머신러닝의 다양한 내용을 위한 기초 개념이 되므로 이해하고 넘어가는 것이 좋습니다.


우리 사람의 뇌는 신경계를 구성하는 주된 세포인 뉴런(neuron)을 약 1000억개 정도 가지고 있으며뉴런들은 시냅스라는 구조를 통해 전기화학적 신호를 주고 받음으로써 다양한 정보를 받아들이고그 정보를 저장하는 기능을 수행합니다.

아래 그림은 하나의 뉴런에서 신호를 입력 받고 그에 대한 결과 신호를 출력하는 개념을 도식화 한 것입니다.


​우리 사람의 뇌속에는 위 그림과 같은 뉴런이 1000억개 가까이 서로가 다층적으로 복잡하게 연결되어 있는 것이죠. 이런 뇌 구조로 뇌활동이 일어나는 것이며, 인간이 수행하는 모든 일(생각하고, 배우고, 창조하고, 기억하고, 느끼고,, 아파하고,, 등등 모든 것)에 관여하고 있습니다.

1943년 신경과학자인 Warren S. McCulloch과 논리학자인 Walter Pitts는 하나의 사람 뇌 신경세포를 하나의 이진(Binary)출력을 가지는 단순 논리 게이트로 설명했습니다.

위 그림에서 여러 개의 입력 신호가 가지돌기(Dendrite)에 도착하면 신경세포 내에서 이들을 하나의 신호로 통합하고통합된 신호 값이 어떤 임계값을 초과하면 하나의 단일 신호가 생성되며이 신호가 축삭돌기(Axon)를 통해 다른 신경세포로 전달하는 것으로 이해했습니다이렇게 단순화 된 원리로 동작하는 뇌 세포를 McCulloch-Pitts뉴런(MCP 뉴런)이라 부릅니다.

 

1957년 코넬 항공 연구소에 근무하던 Frank Rosenblatt은 MCP 뉴런 모델을 기초로 퍼셉트론(Perceptron) 학습 규칙이라는 개념을 고안하게 되는데, Rosenblatt은 하나의 MCP 뉴런이 출력신호를 발생할지 안할지 결정하기 위해, MCP 뉴런으로 들어오는 각 입력값에 곱해지는 가중치 값을 자동적으로 학습하는 알고리즘을 제안했습니다.

이 알고리즘은 머신러닝의 지도학습이나 뷴류(classification)의 맥락에서 볼 때, 하나의 샘플이 어떤 클래스에 속해 있는지 예측하는데 사용될 수 있습니다.


아래 그림은 Rosenblatt이 제안한 퍼셉트론 알고리즘 개념을 도식화 한 것입니다.




여기서 x0~xn은 퍼셉트론 알고리즘으로 입력되는 값이며, w0~wn은 각각 x0~xn에 곱해지는 가중치입니다. 입력값은 보통 분류를 위한 데이터의 특성(feature)을 나타내는 값으로 이루어져 있으며, 이 특성값 x0~xn에 가중치 w0~wn을 곱한 값을 모두 더하여 하나의 값으로 만듭니다. 이 값을 만드는 함수를 순입력 함수(net input 함수)라고 부릅니다. 순입력 함수의 결과값을 특정 임계값과 비교를 하고, 순입력 함수 결과값이 이 임계값보다 크면 1, 그렇지 않으면 -1로 출력하는 함수를 정의합니다. 이 함수를 활성 함수(Activation function)라고 부릅니다.


퍼셉트론은 다수의 트레이닝 데이터를 이용하여 일종의 지도 학습을 수행하는 알고리즘입니다. 트레이닝 데이터에는 데이터의 특성값에 대응되는 실제 결과값을 가지고 있어야 합니다. 입력되는 특성값 x0~xn에 대한 실제 결과값을 y라고 한다면 이 y를 활성 함수에 의해 -1 또는 1로 변환합니다. 이렇게 변환한 값과 퍼셉트론 알고리즘에 의해 예측된 값이 다르면 이 두 개의 값이 같아질 때까지 특정식에 의해 가중치 w0~wn을 업데이트 합니다.


위 그림을 보다 단순하게 도식화하면 다음 그림과 같이 입력층, 중간층, 출력층과 같이 나타낼 수 있습니다.


여기서 중간층을 노드 또는 뉴런이라 부르며, 입력층은 다른 노드의 출력값이 입력값으로 입력되는 것이고, 출력층은 이 노드의 출력값이 다른 노드로 전달되는 층이라고 생각하면 됩니다.


이와 같이 중간층이 하나의 노드로 구성되어 중간층과 출력층의 구분이 없는 구조를 단순 또는 단층 퍼셉트론이라 부르며, 중간층을 구성하는 노드가 여러 개이고, 이러한 중간층이 다수로 구성되어 있는 구조를 다층 퍼셉트론이라 부릅니다. 


아래는 다층 퍼셉트론을 응용한 인공신경망 구조의 한 예시입니다.


이러한 다층 인공신경망을 학습하는 알고리즘을 딥 러닝(Deep Learning)이라고 말합니다.


이것이 퍼셉트론의 핵심입니다. 그러면 좀 더 자세하게 살펴보도록 하겠습니다. 


MCP 뉴런과 Rosenblatt의 퍼셉트론 모델은 사람 뇌의 단일 뉴런이 작동하는 방법을 흉내내기 위해 환원 접근법(redcutionist approach)을 이용합니다. 이는 초기 가중치를 임의의 값으로 정의하고 예측값의 활성 함수 리턴값과 실제 결과값의 활성 함수 리턴값이 동일하게 나올 때까지 가중치의 값을 계속 수정하는 방법입니다.


Rosenblatt의 초기 퍼셉트론 알고리즘을 요약하면 다음과 같습니다.

  1. 입력되는 특성값에 곱해지는 가중치 w 값들을 모두 0 또는 작은 값으로 무작위 할당함
  2. 임계값을 정의함(보통 0으로 정의합니다.)
  3. 트레이닝 데이터 샘플 x를 순입력 함수를 이용해 가중치와 각각 곱한 후 그 총합을 구함
  4. 활성 함수를 이용해 트레이닝 데이터 샘플에 대한 예측값을 -1 또는 1로 결과가 나오게 함
  5. 트레이닝 데이터 샘플의 실제 결과값에 대한 활성 함수 리턴값과 4에서 나온 예측값을 비교함
  6. 예측값과 결과값이 다르면 모든 가중치 w 업데이트하고 3의 과정부터 다시 시작함
  7. 예측값과 결과값이 동일하게 나오면 패스


자, 아래와 같이 n개의 특성값을 가진 m개의 트레이닝 데이터가 있다고 가정합니다.



이 트레이닝 데이터로 퍼셉트론 알고리즘을 이용해 머신러닝을 수행하려고 합니다. Rosenblatt의 퍼셉트론 알고리즘에서 x0는 실제 트레이닝 데이터의 특성값과는 무관하며 x0의 초기값으로 보통 1로 둡니다. 퍼셉트론 알고리즘에서 x0를 바이어스(bias)라 부릅니다. 따라서 실제 트레이닝 데이터의 특성값은 x1부터 시작한다고 보면 됩니다.


이제 우리가 1로 정한 x0의 값과 n개의 트레이닝 데이터의 각 특성값 x1~xn과 이 특성값에 곱할 가중치 w1~wn의 값을 0과 1사이의 임의의 값으로 랜덤하게 할당한 후 위 표를 아래와 같이 다시 구성해 봅니다.


트레이닝 데이터1~m까지 각 특성값에 대해 퍼셉트론 알고리즘을 구동합니다. 퍼셉트론 알고리즘에서 가중치를 업데이트 하는 식은 다음과 같이 정의됩니다.



여기서 wj 는 트레이닝 데이터의 j번째 특성값 xj와 곱하는 가중치이며, y는 트레이닝 데이터의 실제 결과값에 대한 활성 함수 리턴값, y^은 예측값에 대한 활성 함수 리턴값입니다. (y - y^) 앞에 곱한 그리스 문자 η는 학습률(learning rate)이라 하며, Rosenblatt의 퍼셉트론에서는 learning rate을 매우 작은 값으로 할당합니다.


퍼셉트론 알고리즘은 다음과 같은 방식으로 구동합니다.


  1. ​w0~wn의 값을 0.0, 임계값 0.0, 에타값은 0.1로 둡니다.
  2. 트레이닝 데이터1의 특성값들에 대한 예측값의 활성 함수 리턴값을 계산합니다.
  3. 2에서 계산된 값이 이 실제 결과값의 활성 함수 리턴값과 같으면 가중치 업데이트 없이 다음 트레이닝 데이터에 대해 2번 과정으로 넘어갑니다.
  4. 예측값의 활성 함수 리턴값이 실제 결과값의 활성 함수 리턴값과 다르면 위 식에 의해 w0~wn의 가중치를 업데이트 합니다. 
  5. 트레이닝 데이터2~m에 대해서 2~4를 반복합니다.


위와 같은 절차로 트레이닝 데이터1~m까지 모든 예측값이 실제 결과값과 동일해질 때까지 반복합니다. 트레이닝 데이터1~m까지 예측값에 대한 활성 함수 리턴값이 실제 결과값의 활성 함수 리턴값과 동일하면 퍼셉트론 학습은 종료됩니다.


퍼셉트론에 대한 개략적인 개념을 소개했으니, 다음 포스팅에서는 퍼셉트론 알고리즘을 파이썬으로 구현해보고, 실제 몇 개의 데이터 세트를 이용해 머신러닝을 수행해보도록 하겠습니다. 


반응형
반응형

 [출처] [1편] 인공지능과 머신러닝|작성자 옥수별


2016년 3월, 구글의 자회사 딥마인드가 만든 알파고가 세계적인 바둑기사 이세돌을 4승1패로 이긴 이후, 우리나라뿐만 아니라 전세계적으로 인공지능(AI, Artificial Intelligence)에 대한 관심이 증폭되었습니다.


사실, 인공지능이라는 개념은 1956년 소수의 수학자와 과학자들이 모인 다트머스 회의에서 처음으로 탄생했습니다. 하지만 여러가지 이유로 인해 인공지능은 한동안 관심사에서 멀어지게 되고 등한시되었습니다.


그러다가 IBM의 인공지능 딥블루가 1997년 체스경기에서 인간 챔피언을 이겨버렸고, 이후 컴퓨팅 기술의 발전과 HW의 고사양화, 빅데이터 시대를 맞이하면서 인공지능은 진화하게 됩니다. 다시말하면, 1990년대 중반까지 인공지능이 선험적인 경험과 지식을 활용하는 형태였다면, 2000년대 이후에는 기계 스스로 대량의 데이터를 통해 스스로 지식을 찾아내는 방식으로 진화하게 된 것입니다.


기계 스스로 대량의 데이터로부터 지식이나 패턴을 찾아내어 학습하는 것을 우리는 '머신러닝(Machine Learning)'이라 부르고, 우리말로는 '기계학습'이라 부릅니다. 그리고 머신러닝 연구의 한부분인 인공신경망 분야에 '딥러닝(Deep Learning)'이라는 새로운 방식으로 인해 대전환점을 맞이하게 됩니다.




2017년 IT 키워드중 하나가 바로 인공지능이며, 인공지능의 기반이 되는 머신러닝이 이제 대세가 되는 시대가 된것 같습니다.


이에 따라 이번 포스팅 시리즈는 파이썬을 이용하여 머신러닝을 구현하는데 필요한 내용을 정리하고 학습하도록 하겠습니다. 포스팅에서 다룰 내용은 대부분이 각종 사이트에서 발췌하여 제가 응용한 것들이며, 참고 서적으로는 Sebastian Raschka가 지은 'Python Machine Learning'이라는 책입니다.


또 다시 파이썬 예찬론을 펼치게 되는데요, 파이썬은 언어자체의 문법이 깔끔하고 영어와 유사하며 온갖 용도로 사용되는 가장 인기있는 언어중 하나입니다. 특히, 데이터의 처리와 분석에 관련된 다양한 라이브러리들이 있어 데이터 과학자들 사이에 입지가 튼튼하며, 인공지능을 구현하는데 인기도 면에서 2위를 달리는 프로그래밍 언어입니다. 이쯤에서 파이썬 예찬론은 그만하겠습니다.


파이썬에 대해 완전 초보이신 분들은 제가 정말 쉽게 쉽게 쓴, '초보자를 위한 파이썬200(정보문화사)'로 학습하시면 도움이 될 것 같습니다~^^


아무튼 이번 포스팅에서는, 머신러닝에 대한 대략적인 기초 지식을 살펴보고, 본격적인 내용에 대해 포스팅하도록 하겠습니다. 아마 OpenCV 강좌처럼 장대한(?) 여정이 될 것 같군요~


머신러닝은 주어진 데이터를 바탕으로 새로운 질문에 대해 예측하는 것이 최종 목적이며, 이러한 머신러닝을 학습방법에 따라 구분하면 다음과 같은 것들이 있습니다. 이들에 대한 자세한 내용은 강좌를 진행하면서 서술하겠습니다.


  • 지도학습(Supervised Learning)
    • 컴퓨터에 학습을 시킬 때 사람이 개입하여 정답을 달아주는 방식으로 학습을 진행합니다. 예를 들어 손으로 쓴 숫자를 인식하기 위해 컴퓨터에 학습을 시킬 때 최초 일정량의 데이터에 대해서 사람이 이 녀석인 1이야, 저 녀석은 8이야 하는 식으로 각 숫자에 대해 라벨을 달아주어 컴퓨터에 학습을 시킵니다. 제 블로그의 OpenCV 강좌 중 머신러닝 기초2 - KNN을 이용하여 손글씨 인식하기가 지도학습을 이용한 머신러닝의 한 예입니다.
    • 지도학습에는 다음과 같은 것들이 있습니다.
      • 분류(Classification)
      • 회귀(Regression)
  • 비지도학습(Unsupervised Learning)
    • 사람의 개입없이 컴퓨터 스스로가 라벨링 되어 있지 않은 데이터에 대해 학습을 하는 방식입니다. 이 세상에 존재하는 대부분의 데이터는 라벨이 붙어 있지 않습니다. 따라서 비지도학습은 머신러닝이 나아갈 방향입니다.
    • 비지도학습에는 다음과 같은 것들이 있습니다.
      • 군집화(Clustering)
      • 분포추정(Underlying Probability Density Estimation)
  • 강화학습(Reinforcement Learning)
    • 강화학습은 기계 혹은 컴퓨터로 하여금 현태 상태에서 어떤 행동을 취하는 것이 최적의 것인가를 학습하는 것입니다. 하나의 행동을 취할때 마다 외부에서 보상(Reward)이 주어지며, 이 보상이 최대화하는 방향으로 학습이 진행됩니다. 외부에서 주어지는 보상은 행동을 취한 후 바로 주어질 수도 있고, 조금 있다가 줄 수도 있습니다.  


머신러닝을 구현하기 위한 알고리즘에는 다음과 같은 것들이 있으며, 자세한 내용은 강좌를 진행하면서 서술하겠습니다.

  • 경사하강법(Gradient Descent)
  • 회귀기법
    • 선형 회귀(Linear Regression)
    • 로지스틱 회귀(Logistic Regression)
  • 확률기반
    • 나이브 베이즈 분류기(Naive Bayes Classifier; NBC)
    • 은닉 마르코프 모델(Hidden Markov Model; HMM)
  • 기하기반
    • k-Means Clustering(kMC)
    • k-Nearest Neighbors(kNN)
    • Support Vector Machine(SVM)
  • 인공신경망
    • Perceptron
    • Multi Layer Perceptron(MLP)
    • Deep Neural Network


이번 머신러닝 포스팅 시리즈에 소개되는 대부분의 내용은 Sebastian Raschka의 Python Machine Learning이라는 책을 참고하였으며, 다양한 인터넷 사이트의 내용들을 참고하였습니다.



포스팅에서 다루는 파이썬 버전은 3.5.x 이므로 2.7.x 버전 사용자는 참고하시기 바랍니다. 그리고 앞으로 게시될 머신러닝 포스팅 시리즈에 등장하는 많은 수의 소스코드는 기본적으로 Python Machine Learning에 있는 소스코드를 토대로 작성되었으며, 필요시 제가 수정한 부분도 있다는 사실을 미리 알려둡니다.


먼저, 윈도우 명령 프롬프트를 구동하고 아래와 같이 필요한 패키지부터 설치합니다.


pip install numpy

pip install scipy

pip install matplotlib

pip install scikit-learn

pip install pandas​


이제 준비는 끝났으므로 다음 포스팅부터 본격적으로 공부를 시작해보도록 하겠습니다. 


반응형
반응형

>>>t = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]


>>>x = np.array(t)


>>>type(x)


>>>x.shape

(3, 4)


>>>x.ravel()

array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])


>>>x.ravel().shape

(12,)


>>>x.shape = (6, 2)


>>>x

array([[1, 2],

[3, 4],

[5, 6],

[7, 8],

[9, 10],

[11, 12]])


>>>x.transpose()

array([[1, 3, 5, 7, 9, 11],

[2, 4, 6, 8, 10, 12]])


>>>x.resize((2, 6))

>>>x

array([[1, 2, 3, 4, 5, 6],

[7, 8, 9, 10, 11, 12]

반응형

'Program > Python' 카테고리의 다른 글

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 0: invalid start byte  (0) 2017.12.18
python iloc 와 loc 의 차이  (0) 2017.12.17
numpy 수학함수  (0) 2017.12.02
numpy 함수  (0) 2017.12.02
numpy meshgrid  (0) 2017.12.02
반응형


# 파이프라인을 가진 스트리밍 워크플로우

'''
위스콘신 유방암 데이터
이 데이터는 악성(malignant)과 양성(benign)의 암세포로 구성된 569개 샘플을 포함.
~2 열까지는 각 샘플의 ID숫자와 이들의 진단 결과값을 저장
3 ~ 32 열은 30개의 실제 값으로 이루어진 피처
'''

import pandas as pd
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data', header=None)

from sklearn.preprocessing import LabelEncoder
X = df.loc[:, 2:].values
y = df.loc[:, 1].values
print('df: ', df)
print('X: ', X)
print('y: ', y)
le = LabelEncoder()
y = le.fit_transform(y)
print('y-transform: ', y)

print(le.transform(['M', 'B']))

from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=1)
print('X_train: ', X_train)
print('y_train: ', y_train)
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
pipe_lr = Pipeline([('scl', StandardScaler()),
('pca', PCA(n_components=2)),
('clf', LogisticRegression(random_state=1))])
pipe_lr.fit(X_train, y_train)
print('Test Accuracy: %.3f' % pipe_lr.score(X_test, y_test))


# k-fold 교차검증을 사용하여 모델 성능 평가
# - 일반화 성능을 만족시키는 최적의 하이퍼파라미터 값을 찾기 위한 모델 튜닝에 사용
import numpy as np
from sklearn.cross_validation import StratifiedKFold
kfold = StratifiedKFold(y=y_train, n_folds=10, random_state=1)
#print('kfold: ', kfold)
scores = []
for k, (train, test) in enumerate(kfold):
# print('train: ', train, 'test: ', test)
pipe_lr.fit(X_train[train], y_train[train])
score = pipe_lr.score(X_train[test], y_train[test])
scores.append(score)
print('Fold: %s, Class dist.: %s, Acc: %.3f' % (k+1, np.bincount(y_train[train]), score))
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))


from sklearn.cross_validation import cross_val_score
scores = cross_val_score(estimator=pipe_lr, X=X_train, y=y_train, cv=10, n_jobs=1)
print('CV accuracy scores: %s' % scores)
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

# 검증 곡선을 사용해서 높은 바이어스나 높은 분산에 대한 이슈를 어떻게 해소할 수 있는지 확인.
import matplotlib.pyplot as plt
from sklearn.learning_curve import learning_curve
pipe_lr = Pipeline([
('scl', StandardScaler()),
('clf', LogisticRegression(penalty='l2', random_state=0))
])
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_lr,
X=X_train,
y=y_train,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=10,
n_jobs=1)
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

plt.plot(train_sizes, train_mean, color='blue', marker='o', markersize=5, label='training accuracy')
plt.fill_between(train_sizes, train_mean + train_std, train_mean - train_std, alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean, color='green', linestyle='--', marker='s', markersize=5, label='validation accuracy')
plt.fill_between(train_sizes, test_mean + test_std, test_mean - test_std, alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training samples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.0])
plt.show()

# 검증곡선으로 오버피팅과 언더피팅 다루기
'''
검증곡선은 학습곡선과 관련되어 있지만 훈련 정확도와 테스트 정확도를 샘플 크기의 함수로 플롯하는 대신,
로지스틱 회귀의 역정규화 파라피터 C와 같은 모델 파라미터 값들을 다양하게 한다.
'''
from sklearn.learning_curve import validation_curve
param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]
train_scores, test_scores = validation_curve(
estimator=pipe_lr,
X=X_train,
y=y_train,
param_name='clf__C',
param_range=param_range,
cv=10
)
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
plt.plot(param_range, train_mean, color='blue', marker='o', markersize=5, label='training accuracy')
plt.fill_between(param_range, train_mean + train_std, train_mean - train_std, alpha=0.15, color='blue')
plt.plot(param_range, test_mean, color='green', linestyle='--', marker='s', markersize=5, label='validation accuracy')
plt.fill_between(param_range, test_mean + test_std, test_mean - test_std, alpha=0.15, color='green')
plt.grid()
plt.xscale('log')
plt.legend(loc='lower right')
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.0])
plt.show()


반응형
반응형
'''
차원축소를 위한 피처 선택의 방법 중 피처 추출이 있다.
원래보다 낮은 차원의 새로운 피처 부분공간으로 변환시킴으로써 데이터의 정보를 요약할 수 있도록 도와주는
세 가지 기초 기법에 대해 학습한다.
* 비지도적 데이터 압축을 위한 주성분 분석(PCA)
* 최대화 분류 분리를 위한 지도적 차원축소 기법으로써의 선형 분리 분석(LDA)
* 커널(kernel) 주성분 분석을 활용한 비선형 차원축소
'''
# 주성분 분석을 활요한 비지도적 차원축소

# 전체 분산과 설명 분산
import pandas as pd
df_wine = pd.read_csv('wine.data', header=None)

from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
# 표준화
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.fit_transform(X_test)

# 공분산 행렬 만들기
# 아이겐페어(eigenpairs) Numpy linalg.eig 함수 사용
import numpy as np
cov_mat = np.cov(X_train_std.T) # 공분산행렬 계산 (같은 피처를 행으로 만들어서)
eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)
print('\nEigenvalues \n%s' % eigen_vals)
print('\nEigenvectors \n%s' % eigen_vecs)

# 아이겐밸류의 분산 비율을 구한다.
tot = sum(eigen_vals)
var_exp = [(i / tot) for i in sorted(eigen_vals, reverse=True)] # 내림차순 정렬
cum_var_exp = np.cumsum(var_exp) # 설명 분산의 누적 합 계산
import matplotlib.pyplot as plt
plt.bar(range(1,14), var_exp, alpha=0.5, align='center', label='individual explained variance')
plt.step(range(1,14), cum_var_exp, where='mid', label='cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal components')
plt.legend(loc='best')
plt.show()

# 피처 변환
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:, i]) for i in range(len(eigen_vals))]
eigen_pairs.sort(reverse=True) # 내림차순 정렬
print('eigen_pairs:\n', eigen_pairs)
w = np.hstack((eigen_pairs[0][1][:, np.newaxis], eigen_pairs[1][1][:, np.newaxis]))
print('Matrix W:\n', w)

# 투영행렬을 사용해서 샘플 x(1x13차원의 행 벡터) PCA 부분공간 x`로 변환할 수 있다.
print('X_train_std[0]:\n', X_train_std[0].dot(w))
# 124 x 13차원의 훈련데이터를 행렬의 내적곱 계산을 사용해서 두 개의 주성분으로 변환
X_train_pca = X_train_std.dot(w)

colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
plt.scatter(X_train_pca[y_train==l, 0], X_train_pca[y_train==l, 1], c=c, label=l, marker=m)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='upper left')
plt.show()


## 사이킷런에서 주성분 분석
from matplotlib.colors import ListedColormap

def plot_decision_regions(X, y, classifier, resolution=0.02):
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])

x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))

Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())

for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y==cl, 0], y=X[y==cl, 1], alpha=0.8, c=cmap(idx), marker=markers[idx], label=cl)

from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
lr = LogisticRegression()
X_train_pca = pca.fit_transform(X_train_std)
X_test_pca = pca.transform(X_test_std)
lr.fit(X_train_pca, y_train)
plot_decision_regions(X_train_pca, y_train, classifier=lr)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.legend(loc='lower left')
plt.show()

plot_decision_regions(X_test_pca, y_test, classifier=lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.show()

pca = PCA(n_components=None)
X_train_pca = pca.fit_transform(X_train_std)
print('pca.explained_variance_ratio_:\n', pca.explained_variance_ratio_)

# 세 개의 평균 벡터 결과 도출
np.set_printoptions(precision=4)
mean_vecs = []

for label in range(1,4):
mean_vecs.append(np.mean(X_train_std[y_train==label], axis=0))
print('MV %s: %s\n' %(label, mean_vecs[label-1]))

d = 13 # number of features
S_W = np.zeros((d, d))
for label, mv in zip(range(1,4), mean_vecs):
class_scatter = np.zeros((d, d))
for row in X[y==label]:
row, mv = row.reshape(d, 1), mv.reshape(d, 1)
class_scatter += (row-mv).dot((row-mv).T)
S_W += class_scatter
print('Within-class scatter matrix: %sx%s' % (S_W.shape[0], S_W.shape[1]))

print('Class label distribution: %s' % np.bincount(y_train)[1:])

d = 13
S_W = np.zeros((d, d))
for label, mv in zip(range(1,4), mean_vecs):
class_scatter = np.cov(X_train_std[y_train==label].T)
S_W += class_scatter
print('Scaled within-class scatter matrix: %sx%s' % (S_W.shape[0], S_W.shape[1]))

mean_overall = np.mean(X_train_std, axis=0)
d = 13
S_B = np.zeros((d,d))
for i, mean_vec in enumerate(mean_vecs):
n = X[y==i+1, :].shape[0]
mean_vec = mean_vec.reshape(d, 1)
mean_overall = mean_overall.reshape(d, 1)
S_B += n * (mean_vec - mean_overall).dot((mean_vec - mean_overall).T)
print('Between-class scatter matrix: %sx%s' % (S_B.shape[0], S_B.shape[1]))


반응형
반응형


import pandas as pd
from io import StringIO

csv_data = '''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
0.0,11.0,12.0,
'''
df = pd.read_csv(StringIO(csv_data))
print('df:\n ', df)
print('df.isnull().sum():\n ', df.isnull().sum())

# 결측값을 가진 샘플이나 피처 제거
print('df.dropna():\n ', df.dropna()) # 결측값을 가진 행을 제거하는 방법
print('df.dropna(axis=1):\n ', df.dropna(axis=1)) # 결측값을 가진 열을 제거하는 방법
print("df.dropna(how='all'):\n", df.dropna(how='all')) # 모든 열이 결측값을 가진 행만 제거
print('df.dropna(thresh=4):\n', df.dropna(thresh=4)) # 최소 4개의 결측치가 아닌 값을 가지지 못하는 행 제거
print("df.dropna(subset=['C']:\n", df.dropna(subset=['C'])) # C열에서 결측치가 있는 행을 제거

# 결측값의 보정
# 전체 피처열의 평균값으로 결측값을 간단히 대체하는 평균보정법
from sklearn.preprocessing import Imputer
imr = Imputer(missing_values='NaN', strategy='mean', axis=0)
imr = imr.fit(df)
imputed_data = imr.transform(df.values)
print('imputed_data:\n', imputed_data)


import pandas as pd
df = pd.DataFrame([
['green', 'M', 10.1, 'class1'],
['red', 'L', 13.5, 'class2'],
['blue', 'XL', 15.3, 'class1']
])
df.columns = ['color', 'size', 'price', 'classlabel']
print('df:\n', df)

# 순위형 피처 매핑
# ex) XL = L + 1 = M + 2
size_mapping = {'XL': 3, 'L': 2, 'M': 1}
df['size'] = df['size'].map(size_mapping)
print('df:\n', df)

# 역매핑
inv_size_mapping = {v: k for k, v in size_mapping.items()}
print('inv_size_mapping:\n', inv_size_mapping)


# 분류 레이블 인코딩
import numpy as np
class_mapping = {label:idx for idx, label in enumerate(np.unique(df['classlabel']))}
print('class_mapping:\n', class_mapping)

df['classlabel'] = df['classlabel'].map(class_mapping)
print('df:\n', df)
# 역매핑
inv_class_mapping = {v: k for k, v in class_mapping.items()}
df['classlabel'] = df['classlabel'].map(inv_class_mapping)
print('df:\n', df)

from sklearn.preprocessing import LabelEncoder
class_le = LabelEncoder()
y = class_le.fit_transform(df['classlabel'].values)
print('y:\n', y)

print('class_le.inverse_transform(y):\n', class_le.inverse_transform(y))


# 명목형 피처에 원핫 인코딩 수행
X = df[['color', 'size', 'price']].values
color_le = LabelEncoder()
X[:, 0] = color_le.fit_transform(X[:, 0])
print('X:\n', X)


# one hot encoding
from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder(categorical_features=[0])
print('ohe.fit_transform(X).toarray():\n', ohe.fit_transform(X).toarray())

print('df:\n', df)
print("pd.get_dummies(df[['price', 'color', 'size']]):\n", pd.get_dummies(df[['price', 'color', 'size']]))


# 데이터를 훈련용과 테스트용으로 분할하기
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None)
df_wine.columns = ['Class label', 'Alcohol',
'Malic acid', 'Ash',
'Alcalinity of ash', 'Magnesium',
'Total phenols', 'Flavanoids',
'Nonflavanoid phenols', 'Proanthocyanins',
'Color intensity', 'Hue',
'OD280/OD315 of diluted wines', 'Proline']
print('Class labels', np.unique(df_wine['Class label']))
print('df_wine.head():\n', df_wine.head())

# 테스트용과 훈련용으로 임의 분할하기 위한 편리한 방법 한 가지는 사이킷런 cross_validation 서브 모듈의 train_test_split 함수
from sklearn.cross_validation import train_test_split
X, y = df_wine.iloc[:, 1].values, df_wine.iloc[:, 0].values
print('X:\n', df_wine.iloc[:, 1].values) # Alcohol
print('y:\n', df_wine.iloc[:, 0].values) # Class label
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
print('X_train:\n', X_train)
print('X_test:\n', X_test)

# 최소-최대 스케일링 프로시저
from sklearn.preprocessing import MinMaxScaler
mms = MinMaxScaler()
X_train = np.array(X_train).reshape((len(X_train), 1))
X_test = np.array(X_test).reshape((len(X_test), 1))
X_train_norm = mms.fit_transform(X_train)
X_test_norm = mms.transform(X_test)
print('X_train_norm:\n', X_train_norm)
print('X_test_norm:\n', X_test_norm)

# 표준화 프로시저
from sklearn.preprocessing import StandardScaler
stdsc = StandardScaler()
X_train_std = stdsc.fit_transform(X_train)
X_test_std = stdsc.transform(X_test)
print('X_train:\n', X_train)
print('X_train_std:\n', X_train_std)


############ 오버피팅을 줄일 수 있는 일반적인 방법

from sklearn.linear_model import LogisticRegression
LogisticRegression(penalty='l1')
print("LogistincRegression(penality='l1'):\n", LogisticRegression(penalty='l1'))
lr = LogisticRegression(penalty='l1', C=0.1)
lr.fit(X_train_std, y_train)
print('Training accuracy:', lr.score(X_train_std, y_train))
print('Test accuracy:', lr.score(X_test_std, y_test))
lr.intercept_
print('lr.intercept_: ', lr.intercept_)
lr.coef_
print('lr.coef_: ', lr.coef_)


# 여러가지 정규화 강도를 위한 다양한 피처들의 가중계수인 정규화의 경로를 플롯
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.subplot(111)
colors = ['blue', 'green', 'red', 'cyan',
'magenta', 'yellow', 'black',
'pink', 'lightgreen', 'lightblue',
'gray', 'indigo', 'orange']
weights, params = [], []
for c in np.arange(-4, 6):
lr = LogisticRegression(penalty='l1', C=10**np.float32(c), random_state=0)
lr.fit(X_train_std, y_train)
weights.append(lr.coef_[1])
params.append(10**np.float32(c))

weights = np.array(weights)
for column, color in zip(range(weights.shape[1]), colors):
plt.plot(params, weights[:, column], label=df_wine.columns[column+1], color=color)

plt.axhline(0, color='black', linestyle='--', linewidth=3)
plt.xlim([10**(-5), 10**5])
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.xscale('log')
plt.legend(loc='upper left')
ax.legend(loc='upper center', bbox_to_anchor=(1.38, 1.03), ncol=1, fancybox=True)
plt.show()
# 연속형 피처 선택 알고리즘
'''
SBS(Sequential Backward Selection) : 계산상 효율을 위해 초기 피처 부분 공간의 차원을 축소시킬 때
분류기 성능이 무너지는 것을 최소화하는 것을 목표로 한다.
'''
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None)
df_wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash',
'Alcalinity of ash', 'Magnesium', 'Total phenols', 'Flavanoids',
'Nonflavanoid phenols', 'Proanthocyanins', 'Color intensity', 'Hue',
'OD280/OD315 of diluted wines', 'Proline']

from sklearn.cross_validation import train_test_split
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values

from sklearn.base import clone
from itertools import combinations
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import accuracy_score
class SBS():
def __init__(self, estimator, k_features, scoring=accuracy_score,
test_size=0.25, random_state=1):
self.scoring = scoring
self.estimator = clone(estimator)
self.k_features = k_features
self.test_size = test_size
self.random_state = random_state

def fit(self, X, y):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=self.test_size,
random_state=self.random_state)
dim = X_train.shape[1]
#print('dim:\n', dim)
self.indices_ = tuple(range(dim))
#print('self.indices:\n', self.indices_)
self.subsets_ = [self.indices_]
#print('self.subsets:\n', self.subsets_)
score = self._calc_score(X_train, y_train, X_test, y_test, self.indices_)
#print('score:\n', score)
self.scores_ = [score]

while dim > self.k_features:
scores = []
subsets = []

for p in combinations(self.indices_, r=dim-1):
score = self._calc_score(X_train, y_train, X_test, y_test, p)
scores.append(score)
subsets.append(p)
best = np.argmax(scores)
self.indices_ = subsets[best]
self.subsets_.append(self.indices_)
dim -= 1

self.scores_.append(scores[best])
#print('self.scores_:\n', self.scores_)
self.k_score_ = self.scores_[-1]

return self

def transform(self, X):
return X[:, self.indices_]

def _calc_score(self, X_train, y_train, X_test, y_test, indices):
self.estimator.fit(X_train[:, indices], y_train)
y_pred = self.estimator.predict(X_test[:, indices])
score = self.scoring(y_test, y_pred)
return score

from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
knn = KNeighborsClassifier(n_neighbors=2)
sbs = SBS(knn, k_features=1)
sbs.fit(X_train_std, y_train)
#print('sbs.subsets_:\n', sbs.subsets_)
k_feat = [len(k) for k in sbs.subsets_]
plt.plot(k_feat, sbs.scores_, marker='o')
plt.ylim([0.7, 1.1])
plt.ylabel('Accuracy')
plt.xlabel('Number of features')
plt.grid()
plt.show()

k5 = list(sbs.subsets_[8])
#print('k5:\n', k5)
print('df_wine.columns[1:][k5]:\n', df_wine.columns[1:][k5])

knn.fit(X_train_std, y_train)
print('Training accuracy:', knn.score(X_train_std, y_train))
print('Test accuracy:', knn.score(X_test_std, y_test))

knn.fit(X_train_std[:, k5], y_train)
print('Training accuracy:', knn.score(X_train_std[:, k5], y_train))
print('Test accuracy:', knn.score(X_test_std[:, k5], y_test))

#### 랜덤 포레스트를 활용한 피처 중요도의 평가
'''
랜덤 포레스트를 사용하면 데이터가 선형적으로 분리 가능한지 여부와 상관없이
포레스트 내의 모든 의사결정나무로부터 계산된 평균 불순도 감소분으로 피처의 중요도를 측정할 수 있다.
나무 기반의 모델은 표준화나 정규화가 필요하지 않다는 것을 기억하자.
'''
from sklearn.ensemble import RandomForestClassifier
feat_labels = df_wine.columns[1:]
forest = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)
forest.fit(X_train, y_train)
importances = forest.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(X_train.shape[1]):
print("%2d) %-*s %f" % (f+1, 30, feat_labels[f], importances[indices[f]]))

plt.title('Feature Importances')
plt.bar(range(X_train.shape[1]), importances[indices], color='lightblue', align='center')
plt.xticks(range(X_train.shape[1]), feat_labels, rotation=90)
plt.xlim([-1, X_train.shape[1]])
plt.tight_layout()
plt.show()

X_selected = forest.transform(X_train, threshold=0.15)
print('X_selected.shape:\n', X_selected.shape)


반응형
반응형

Chapter3. 사이킷런을 사용한 머신러닝 분류기.


사이킷런을 이용한 퍼셉트론 훈련.


from sklearn import datasets
import numpy as np
iris = datasets.load_iris()
#print(iris)
X = iris.data[:, [2, 3]] # petal length, petal width
y = iris.target

print('Class labels: ', np.unique(y))

# 훈련용과 테스트용으로 분리
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

from sklearn.linear_model import Perceptron
ppn = Perceptron(max_iter=40, eta0=0.1, random_state=0)
ppn.fit(X_train_std, y_train)

y_pred = ppn.predict(X_test_std)
print('y_pred: ', y_pred)
print('y_test: ', y_test)
print('Misclassified samples : %d' % (y_test != y_pred).sum()) # 4 / 45(test_data) = 0.089

# 테스트 데이터에 대한 퍼셉트론의 분류 정확도
from sklearn.metrics import accuracy_score
print('Accuracy: %.2f' % accuracy_score(y_test, y_pred)) # y_test : 진분류, y_pred : 예측 분류

# 테스트 데이터로부터의 샘플인 것을 강조하기 위해 테스트 데이터에는 작은 원을 더하도록 변경.
from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt

def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])

# plot the decision surface
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())

#plot all samples
X_test, y_test = X[test_idx, :], y[test_idx]
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, c=cmap(idx), marker=markers[idx], label=cl)

if test_idx:
X_test, y_test = X[test_idx, :], y[test_idx]
plt.scatter(X_test[:, 0], X_test[:, 1], c='', alpha=1.0, linewidth=1, marker='o', s=55, label='test set')

X_combined_std = np.vstack((X_train_std, X_test_std))
y_combined = np.hstack((y_train, y_test))
#print('X_combined_std: ', X_combined_std)
#print('y_combined: ', y_combined)
plot_decision_regions(X=X_combined_std, y=y_combined, classifier=ppn, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()


# 시그모이드
import matplotlib.pyplot as plt
import numpy as np

def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))

z = np.arange(-7, 7, 0.1)
#print('z: ', z)
phi_z = sigmoid(z)
#print('phi_z: ', phi_z)
plt.plot(z, phi_z)
plt.axvline(0.0, color='k') # x축 중앙선
plt.axhspan(0.0, 1.0, facecolor='1.0', alpha=1.0, ls='dotted')
plt.axhline(y=0.5, ls='dotted', color='k') # y축 중앙선
plt.yticks([0.0, 0.5, 1.0])
plt.ylim(-0.1, 1.1)
plt.xlabel('z')
plt.ylabel('$\phi (z)$')
plt.show()


# 사이킷런으로 로지스틱 회귀 모델 훈련하기
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression(C=1000.0, random_state=0)
lr.fit(X_train_std, y_train)

plot_decision_regions(X_combined_std, y_combined, classifier=lr, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()


'''오버피팅(overfitting) - 훈련 데이터에 대한 모델의 성능은 좋으나 경험하지 못한 데이터(테스트 데이터)
대해서는 잘 일반화되지 못하는 것.
'''

# 역정규화 파라미터 C에 대해 여러 가지 값을 갖는 10개의 로지스틱 회귀 모델을 피팅했다.
# 파라미터 C를 감소시키면(, 정규화 강도를 증가시키면) 가중계수는 수축한다.
weights, params = [], []
for c in np.arange(-5, 5):
lr = LogisticRegression(C=10**np.float32(c), random_state=0)
lr.fit(X_train_std, y_train)
weights.append(lr.coef_[1])
params.append(10**np.float32(c))

weights = np.array(weights)

plt.plot(params, weights[:, 0], label='petal length')
plt.plot(params, weights[:, 1], label='petal width', linestyle='--')
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.legend(loc='upper left')
plt.xscale('log')
plt.show()

# 서포트 벡터 머신의 마진 분류 최대화
# SVM 모델 훈련
from sklearn.svm import SVC
svm = SVC(kernel='linear', C=1.0, random_state=0)
svm.fit(X_train_std, y_train)

plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105, 150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()


# 퍼셉트론, 로지스틱 회귀, 서포트 벡터 머신의 확률적 그래디언트 디센트 버전을 초기 설정
from sklearn.linear_model import SGDClassifier
ppn = SGDClassifier(loss='perceptron')
lr = SGDClassifier(loss='log')
svm = SGDClassifier(loss='hinge')

np.random.seed(0)
X_xor = np.random.randn(200, 2)
#print('X_xor: ', X_xor)
y_xor = np.logical_xor(X_xor[:, 0] > 0, X_xor[:, 1] > 0)
#print('y_xor: ', y_xor)
y_xor = np.where(y_xor, 1, -1)

#print('X_xor[y_xor==1, 0]: ', X_xor[y_xor==1, 0])
#print('X_xor[y_xor==1, 1]: ', X_xor[y_xor==1, 1])
plt.scatter(X_xor[y_xor==1, 0], X_xor[y_xor==1, 1], c='b', marker='x', label='1')
plt.scatter(X_xor[y_xor==-1, 0], X_xor[y_xor==-1, 1], c='r', marker='s', label='-1')
plt.ylim(-3.0)
plt.legend()
plt.show()


svm = SVC(kernel='rbf', random_state=0, gamma=0.10, C=10.0)
svm.fit(X_xor, y_xor)

plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.show()


svm = SVC(kernel='rbf', random_state=0, gamma=0.2, C=1.0)
svm.fit(X_train_std, y_train)

plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105, 150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

svm = SVC(kernel='rbf', random_state=0, gamma=100.0, C=1.0)
svm.fit(X_train_std, y_train)

plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105, 150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

# 의사결정나무 학습
import matplotlib.pyplot as plt
import numpy as np
def gini(p):
return (p)*(1 - (p)) + (1 - p)*(1 - (1-p))

def entropy(p):
return - p*np.log2(p) - (1 - p)*np.log2((1 - p))

def error(p):
return 1 - np.max([p, 1 - p])

x = np.arange(0.0, 1.0, 0.01)
ent = [entropy(p) if p != 0 else None for p in x]
sc_ent = [e*0.5 if e else None for e in ent]
err = [error(i) for i in x]
fig = plt.figure()
ax = plt.subplot(111)
for i, lab, ls, c, in zip([ent, sc_ent, gini(x), err],
['Entropy', 'Entropy (scaled)', 'Gini Impurity', 'Misclassification Error'],
['-', '-', '--', '-.'],
['black', 'lightgray', 'red', 'green', 'cyan']):
line = ax.plot(x, i, label=lab, linestyle=ls, lw=2, color=c)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.15), ncol=3, fancybox=True, shadow=False)
ax.axhline(y=0.5, linewidth=1, color='k', linestyle='--')
ax.axhline(y=1.0, linewidth=1, color='k', linestyle='--')
plt.ylim([0, 1.1])
plt.xlabel('p(i=1)')
plt.ylabel('Impurity Index')
plt.show()

# 의사결정나무 만들기
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='entropy', max_depth=3, random_state=0)
tree.fit(X_train, y_train)
X_combined = np.vstack((X_train, X_test))
y_combined = np.hstack((y_train, y_test))
print('X_train: ', X_train)
print('y_train: ', y_train)
plot_decision_regions(X_combined, y_combined, classifier=tree, test_idx=range(105, 150))
plt.xlabel('petal length [cm]')
plt.ylabel('petal width [cm]')
plt.legend(loc='upper left')
plt.show()

from sklearn.tree import export_graphviz
export_graphviz(tree, out_file='tree.dot', feature_names=['petal length', 'petal width'])


from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(criterion='entropy', n_estimators=10, random_state=1, n_jobs=2)
forest.fit(X_train, y_train)

plot_decision_regions(X_combined, y_combined, classifier=forest, test_idx=range(105,150))
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.legend(loc='upper left')
plt.show()

# 유클리디안(Euclidean) 거리 메트릭을 사용하여 사이킷런에서 KNN 모델 구현
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5, p=2, metric='minkowski')
knn.fit(X_train_std, y_train)

plot_decision_regions(X_combined_std, y_combined, classifier=knn, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.show()


반응형
반응형
import numpy as np

class Perceptron(object):
def __init__(self, eta=0.01, n_iter=10):
self.eta = eta
self.n_iter = n_iter

def fit(self, X, y):
self.w_ = np.zeros(1 + X.shape[1])
#print('X.shape: ', X.shape)
#print('X.shape[1]: ', X.shape[1])
#print('self.w_ :', self.w_)
#print('zip(X, y): ', list(zip(X, y)))
self.errors_ = []
for _ in range(self.n_iter):
errors = 0
for xi, target in zip(X, y):
#print('xi :', xi)
#print('target :', target)
update = self.eta * (target - self.predict(xi))
#print('self.predict(xi) :', self.predict(xi))
#print('update :', update)
self.w_[1:] += update * xi
self.w_[0] += update
#print('self.w_[:] : ', self.w_[:])
errors += int(update != 0.0)
self.errors_.append(errors)
print('self.errors_: ', self.errors_)
return self

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

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

import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/'
'machine-learning-databases/iris/iris.data', header=None)
df.tail()

####%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

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

# extract sepal length and petal length
X = df.iloc[0:100, [0, 2]].values
#print('X: ', X)
# plot data
plt.scatter(X[:50, 0], X[:50, 1],
color='red', marker='o', label='setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
color='blue', marker='x', label='versicolor')

plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

plt.tight_layout()
#plt.savefig('./images/02_06.png', dpi=300)
plt.show()



ppn = Perceptron(eta=0.1, n_iter=10)
ppn.fit(X, y)
#print('X: ', X)
#print('y: ', y)
plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Number of misclassifications')
plt.show()


from matplotlib.colors import ListedColormap

def plot_decision_regions(X, y, classifier, resolution=0.02):
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])

x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))
#print('xx1: ', xx1)
#print('xx1.ravel(): ', xx1.ravel())
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())

for idx, cl in enumerate(np.unique(y)):
#print('X[y == cl, 0]: ', X[y == cl, 0])
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, c=cmap(idx),
marker=markers[idx], label=cl)

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


X_std = np.copy(X)
X_std[:, 0] = (X[:, 0] - X[:, 0].mean()) / X[:, 0].std()
X_std[:, 1] = (X[:, 1] - X[:, 1].mean()) / X[:, 1].std()


class AdalineGD(object):
def __init__(self, eta=0.01, n_iter=50):
self.eta = eta
self.n_iter = n_iter

def fit(self, X, y):
self.w_ = np.zeros(1 + X.shape[1])
self.cost_ = []

for i in range(self.n_iter):
output = self.net_input(X)
errors = (y - output)
# print(errors)
self.w_[1:] += self.eta * X.T.dot(errors)
self.w_[0] += self.eta * errors.sum()
cost = (errors ** 2).sum() / 2.0
self.cost_.append(cost)

return self

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

def activation(self, X):
return self.net_input(X)

def predict(self, X):
return np.where(self.activation(X) >= 0.0, 1, -1)


fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8, 4))

ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')

ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')

plt.tight_layout()
# plt.savefig('./adaline_1.png', dpi=300)
plt.show()

ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, y)
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.show()

plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.show()


from numpy.random import seed

class AdalineSGD(object):
def __init__(self, eta=0.01, n_iter=10, shuffle=True, random_state=None):
self.eta = eta
self.n_iter = n_iter
self.w_initialized = False
self.shuffle = shuffle
if random_state:
seed(random_state)

def fit(self, X, y):
self._initialize_weights(X.shape[1])
self.cost_ = []
for i in range(self.n_iter):
if self.shuffle:
X, y = self._shuffle(X, y)
cost = []
for xi, target in zip(X, y):
cost.append(self._update_weights(xi, target))
avg_cost = sum(cost)/len(y)
self.cost_.append(avg_cost)
return self

def partial_fit(self, X, y):
if not self.w_initialized:
self._initialize_weights(X.shape[1])
if y.ravel().shape[0] > 1: #ravel -> [1, 2, 3], [4, 5, 6] -> [1, 2, 3, 4, 5, 6]
for xi, target in zip(X, y):
self._update_weights(xi, target)
else:
self._update_weights(X, y)
return self

def _shuffle(self, X, y):
r = np.random.permutation(len(y)) # 순서를 임의로 바꾸거나 임의의 순열을 반환한다.
return X[r], y[r]

def _initialize_weights(self, m):
self.w_ = np.zeros(1 + m)
self.w_initialized = True

def _update_weights(self, xi, target):
output = self.net_input(xi)
error = (target - output)
self.w_[1:] += self.eta * xi.dot(error)
self.w_[0] += self.eta * error
cost = 0.5 * error ** 2
return cost

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

def activation(self, X):
return self.net_input(X)

def predict(self, X):
return np.where(self.activation(X) >= 0.0, 1, -1)


ada = AdalineSGD(n_iter=15, eta=0.01, random_state=1)
print('X_std: ', X_std)
print('y: ', y)
ada.fit(X_std, y)
#ada.partial_fit(X_std[0, :], y[0])
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Stochastic Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.show()
plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Averages Cost')
plt.show()


반응형
반응형

https://docs.scipy.org/doc/numpy/reference/routines.math.html

반응형

'Program > Python' 카테고리의 다른 글

python iloc 와 loc 의 차이  (0) 2017.12.17
ravel(), transpose(), resize()  (0) 2017.12.13
numpy 함수  (0) 2017.12.02
numpy meshgrid  (0) 2017.12.02
window에서 패키지 설치 에러 해결하기  (0) 2017.11.21
반응형
NumPy에서 배열 생성을 위한 주요 함수들은 다음과 같다.
함수 이름설명
array()입력 데이터(리스트, 튜플, 배열, 순차형 데이터 등)를 이용하여 배열을 생성한다.
생성시 적절한 데이터의 타입을 추정한다.
예시) arr = np.array([1, 2, 3])
arange()내장 range와 유사하지만 리스트 대신 ndarray를 반환한다.

arr = np.arange(10) # 0부터 9까지의 정수 10개를 생성한다.

np.arange(9).reshape((3, 3)) # 3행 3열의 numpy.ndarray를 반환한다.(0부터 시작)

# 1.0부터 4.5까지 0.5씩 증가시키기
x = np.arange( 1.0, 4.1, 0.5 )
ones()주어진 dtype과 주어진 모양을 가지는 배열을 생성하고, 값을 모두 1로 초기화 시킨다.
zeros((행수, 열수))요소의 모든 내용이 0인 배열을 생성해준다.
예시) zeros((2,2)) : 요소의 값이 모두 0인 2 * 2의 배열을 생성한다.
empty()배열을 생성하되, ones나 zeros처럼 값을 초기화 하지는 않는다.


NumPy가 지원하는 자료형 목록은 다음과 같다.
종류Type Code설명
int8, unit8i1, u1부호가 있는 8비트 정수형과 부호가 없는 8비트 정수형
int16, unit16i2, u2부호가 있는 16비트 정수형과 부호가 없는 16비트 정수형
int32, unit32i4, u4부호가 있는 32비트 정수형과 부호가 없는 32비트 정수형
int64, unit64i8, u8부호가 있는 64비트 정수형과 부호가 없는 64비트 정수형
float16f2반정밀도 부동 소수점
float32f4 또는 f단정밀도 부동 소수점, C 언어의 float와 호환
float64f8 또는 d배정밀도 부동 소수점, C 언어의 double와 호환, 파이썬의 float 객체와 호환 


단항 유니버설 함수는 하나 이상의 스칼라 값을 받아서 스칼라 결과 값을 반환해주는 함수를 말한다.
함수설명
abs, fabs각 원소의 절대값(정수, 실수, 복소수)을 구한다. 복소수가 아닌 경우 빠른 연산을 위하여 fabs를 사용한다.
sqrt(배열)각 원소의 제곱근을 계산한다. arr ** 0.5와 동일한 결과이다.
square각 원소의 제곱을 계산한다. arr ** 2와 동일한 결과이다.
exp각 원소에서 지수 e*x를 계산한다.
print(np.exp(1)) #2.71828182846
log, log10, log2, log1p각각 자연 로그, 로그 10, 로그 2, 로그(1+x)
sign각 원소의 부호를 계산한다. 1(양수), 0(영), -1(음수)
ceil각 원소의 소수 자리를 올린다.
floor각 원소의 소수 자리를 버린다.
rint각 원소의 소수 자리를 반올림한다.
modf각 원소의 몫과 나머지를 각각의 배열로 반환한다.
isnan각각의 원소가 숫자인지 아닌지를(NaN, Not A Number) 나타내는 Boolean 배열을 반환한다.
isfinite, isinf배열의 각 원소가 유한한지(non-inf, non-Nan), 무한인지를 나타내는 Boolean 배열을 반환한다.
cos, cosh, sin, sinh, tan, tanh일반 삼각 함수와 쌍곡 삼각 함수
arccos, arccosh, arcsin, arcsinh, arctan, arctanh역 삼각 함수
logical_not각 원소의 논리 부정(not) 값을 계산한다.


이항 유니버설 함수는 2개의 인자를 취해서 단일 배열을 반환해주는 함수를 말한다.
함수설명
add(배열a, 배열b)두 배열에서 같은 위치의 요소끼리 더한다.
subtract(배열a, 배열b)첫 번째 배열의 요소에서 두 번째 배열의 원소를 뺀다.
multiply(배열a, 배열b)배열의 요소끼리 곱한다.
divide(배열a, 배열b), floor_divide첫 번째 배열의 요소에서 두 번째 배열의 원소를 나눈다.
power첫 번째 배열의 요소에 두 번째 배열의 요소를 제곱한다.
maximum, fmax두 원소 중에서 큰 값을 반환한다. fmax는 NaN을 무시한다.
minimum, fmin두 원소 중에서 작은 값을 반환한다. fmin은 NaN을 무시한다.
mod첫 번째 배열의 요소에 두 번째 배열의 원소를 나눈 나머지를 구한다.
copysign첫 번째 배열의 요소의 기호를 두 번째 배열의 요소의 기호로 바꾼다.
greater, greater_equal, less, less_equal, euqal, not_equal각각 두 원소 간의 >, >=, < , <=, ==, != 비교 연산 결과를 Boolean 배열로 반환한다.
logical-and, logical_or, logical_xor각각 두 원소 간의 논리 연산 &, |, ^ 결과를 반환한다.


기본 배열 통계 메소드는 다음과 같은 항목들이 있다.
메소드설명
sum배열 전체 혹은 특정 축에 대한 모든 원소의 합을 계산한다.
크기가 0인 배열에 대한 연산 결과는 0이다.
mean산술 평균을 구한다.
크기가 0인 배열에 대한 연산 결과는 NaN이다
std, var각각 표준 편차와 분산을 구한다.
선택적으로 자유도를 줄 수 있으면 분모의 기본 값은 n이다.
min, max최소 값, 최대 값
argmin, argmax최소 원소의 색인 값, 최대 원소의 색인 값
cumsum누산 메소드라고 하는데, 각 원소의 누적 합을 구해준다.(cumulative sum)
cumprod각 원소의 누적 곱



자주 쓰는 선형 대수 함수는 다음과 같은 항목들이 있다.
함수설명
numpy.diag정사각 행렬의 대가/비대각 원소를 1차원 배열로 반환하거나 1차원 배열을 대각선 원소로 하고, 나머지는 0으로 채운 단위 행렬을 반환한다.
numpy.dot()행렬과 행력 및 행렬과 벡터 간의 곱셈을 연산한다.
numpy.trace행렬의 대각원 원소의 합을 계산한다.
numpy.linalg.det행렬식을 게산한다.
numpy.linalg.eig정사각 행렬의 고유 값과 고유 벡터를 계산한다.
numpy.linalg.inv정사각 행렬의 역행렬을 계산한다.
numpy.linalg.pinv행렬의 무어=펜로즈 유사역원 역행렬을 계산한다.
numpy.linalg.qrQR 분해를 계산한다.
numpy.linalg.svd특이값 분해(SVD)를 계산한다.
numpy.linalg.solveA가 정사각 행렬일 때 Ax = b를 만족하는 x를 구한다.
numpy.linalg.lstsqy = xb를 만족하는 최소 제곱해를 구한다.


다음은 numpy.random 함수에 포함되어 있는 일부분이다.
함수설명
seed()난수 발생기의 시드를 지정한다.
예시) np.random.seed(1000)
permutation순서를 임의로 바꾸거나 임의의 순열을 반환한다.
shuffle리스트나 배열의 순서를 뒤섞는다.
rand균등 분포에서 표본을 추출한다.
randint주어진 최소/치대 범위 안에서 임의의 난수를 추출한다.
randn()표준 편차가 1이고 평균 값이 0인 정규 분포(matlab과 같은 방식)에서 표본을 추출한다.
예시 : arr = np.random.randn( 10 )
binomial이항 분포에서 표본을 추출한다.
normal정규 분포(가우시안)에서 표본을 추출한다.
beta베타 분포에서 표본을 추출한다.
chisquare카이 제곱 분포에서 표본을 추출한다.
gamma감마 분포에서 표본을 추출한다.
uniform균등(0, 1) 분포에서 표본을 추출한다.
standard_normal()정규 분포 샘플 데이터를 생성한다.
예시) y = np.random.standard_normal( 20 )



numpy에서 제공하는 집합 함수는 다음과 같다.
함수설명
unique(x)배열 x에서 중복된 원소를 제거한 후 정렬하여 반환한다.
intersect1d(x, y)배열 x와 y에 공통적으로 존재하는 원소를 정렬하여 반환한다.
union1d(x, y)두 배열의 합집합을 반환한다.
in1d(x, y)x의 원소 중 y의 원소를 포함하는 지를 나타내는 불리언 배열을 반환한다.
setdiff1d(x, y)x와 y의 차집합을 반환한다.
setxor1d(x, y)한 배열에는 포함되지만, 두 배열 모두에는 포함되지 않는 원소들의 집합인 대칭 차집합을 반환한다.


반응형

'Program > Python' 카테고리의 다른 글

ravel(), transpose(), resize()  (0) 2017.12.13
numpy 수학함수  (0) 2017.12.02
numpy meshgrid  (0) 2017.12.02
window에서 패키지 설치 에러 해결하기  (0) 2017.11.21
python 가상환경  (0) 2017.11.21
반응형
import numpy as np

samples = np.arange(-10, 10, 0.2)
print('sample: ', samples)

xs, ys = np.meshgrid(samples, samples)
print('xs: ', xs)
print('ys: ', ys)

sample:  [ -1.00000000e+01  -9.80000000e+00  -9.60000000e+00  -9.40000000e+00

  -9.20000000e+00  -9.00000000e+00  -8.80000000e+00  -8.60000000e+00

  -8.40000000e+00  -8.20000000e+00  -8.00000000e+00  -7.80000000e+00

  -7.60000000e+00  -7.40000000e+00  -7.20000000e+00  -7.00000000e+00

  -6.80000000e+00  -6.60000000e+00  -6.40000000e+00  -6.20000000e+00

  -6.00000000e+00  -5.80000000e+00  -5.60000000e+00  -5.40000000e+00

  -5.20000000e+00  -5.00000000e+00  -4.80000000e+00  -4.60000000e+00

  -4.40000000e+00  -4.20000000e+00  -4.00000000e+00  -3.80000000e+00

  -3.60000000e+00  -3.40000000e+00  -3.20000000e+00  -3.00000000e+00

  -2.80000000e+00  -2.60000000e+00  -2.40000000e+00  -2.20000000e+00

  -2.00000000e+00  -1.80000000e+00  -1.60000000e+00  -1.40000000e+00

  -1.20000000e+00  -1.00000000e+00  -8.00000000e-01  -6.00000000e-01

  -4.00000000e-01  -2.00000000e-01  -3.55271368e-14   2.00000000e-01

   4.00000000e-01   6.00000000e-01   8.00000000e-01   1.00000000e+00

   1.20000000e+00   1.40000000e+00   1.60000000e+00   1.80000000e+00

   2.00000000e+00   2.20000000e+00   2.40000000e+00   2.60000000e+00

   2.80000000e+00   3.00000000e+00   3.20000000e+00   3.40000000e+00

   3.60000000e+00   3.80000000e+00   4.00000000e+00   4.20000000e+00

   4.40000000e+00   4.60000000e+00   4.80000000e+00   5.00000000e+00

   5.20000000e+00   5.40000000e+00   5.60000000e+00   5.80000000e+00

   6.00000000e+00   6.20000000e+00   6.40000000e+00   6.60000000e+00

   6.80000000e+00   7.00000000e+00   7.20000000e+00   7.40000000e+00

   7.60000000e+00   7.80000000e+00   8.00000000e+00   8.20000000e+00

   8.40000000e+00   8.60000000e+00   8.80000000e+00   9.00000000e+00

   9.20000000e+00   9.40000000e+00   9.60000000e+00   9.80000000e+00]

xs:  [[-10.   -9.8  -9.6 ...,   9.4   9.6   9.8]

 [-10.   -9.8  -9.6 ...,   9.4   9.6   9.8]

 [-10.   -9.8  -9.6 ...,   9.4   9.6   9.8]

 ..., 

 [-10.   -9.8  -9.6 ...,   9.4   9.6   9.8]

 [-10.   -9.8  -9.6 ...,   9.4   9.6   9.8]

 [-10.   -9.8  -9.6 ...,   9.4   9.6   9.8]]

ys:  [[-10.  -10.  -10.  ..., -10.  -10.  -10. ]

 [ -9.8  -9.8  -9.8 ...,  -9.8  -9.8  -9.8]

 [ -9.6  -9.6  -9.6 ...,  -9.6  -9.6  -9.6]

 ..., 

 [  9.4   9.4   9.4 ...,   9.4   9.4   9.4]

 [  9.6   9.6   9.6 ...,   9.6   9.6   9.6]

 [  9.8   9.8   9.8 ...,   9.8   9.8   9.8]]


이것이 meshgrid


반응형

'Program > Python' 카테고리의 다른 글

numpy 수학함수  (0) 2017.12.02
numpy 함수  (0) 2017.12.02
window에서 패키지 설치 에러 해결하기  (0) 2017.11.21
python 가상환경  (0) 2017.11.21
파이썬 내장함수  (0) 2017.11.20
반응형

Windows에서 pip로 패키지를 설치할 때 에러가 발생하는 경우가 많습니다. 예를 들어 다음은 notebook 패키지를 설치할 때 의존 패키지 중 MarkupSafe에서 에러가 난 상황입니다. 에러 메시지를 잘 보면 "Running setup.py install for MarkupSafe ..." 부분이 error로 표시됩니다.

C:Usersdojang>pip install notebook
...생략...
  Running setup.py install for simplegeneric ... done
  Running setup.py install for pandocfilters ... done
  Running setup.py install for MarkupSafe ... error
Exception:
Traceback (most recent call last):
...생략...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 68: invalid start byte

이 문제를 해결하려면 MarkupSafe 패키지를 Windows 전용으로 만든 whl 파일을 설치해야 합니다. 먼저 웹 브라우저를 실행하고 다음 주소로 이동합니다.

  • Python Extension Packages for Windows
    http://www.lfd.uci.edu/~gohlke/pythonlibs/

웹 사이트가 열리면 Ctrl+F를 눌러서 방금 에러가 발생했던 패키지인 MarkupSafe를 검색합니다. 그다음에 MarkupSafe가 나오면 링크를 클릭합니다.

▼ 그림 48-4 Unofficial Windows Binaries for Python Extension Packages에서 패키지 검색
그림 48 4 Unofficial Windows Binaries for Python Extension Packages에서 패키지 검색

링크를 클릭하면 MarkupSafe 패키지 항목으로 이동합니다. 여기에 MarkupSafe 패키지의 whl 파일들이 있는데 자신의 파이썬 버전과 비트수에 맞는 whl 파일을 다운로드합니다.

▼ 그림 48-5 Unofficial Windows Binaries for Python Extension Packages에서 whl 파일 받기
그림 48 5 Unofficial Windows Binaries for Python Extension Packages에서 whl 파일 받기

whl 파일은 패키지이름-패키지버전-파이썬버전-ABI태그-플랫폼태그.whl 형식으로 되어 있습니다. 예를 들어 cp36과 cp36m은 파이썬 3.6이라는 뜻이고, win32는 32비트 Windows, win_amd64는 64비트 Windows라는 뜻입니다(이후 새 파이썬 버전이 나오면 해당 버전에 맞춰서 새 whl 파일이 올라옵니다).

현재 설치된 파이썬의 버전을 확인하려면 명령 프롬프트(PowerShell)에서 python -V를 입력합니다.

C:Usersdojang>python -V
Python 3.6.0

그리고 파이썬 비트수를 확인하려면 파이썬 셸에서 다음과 같이 실행합니다.

파이썬이 32비트일 경우

>>> import platform
>>> platform.architecture()
('32bit', 'WindowsPE')

파이썬이 64비트일 경우

>>> import platform
>>> platform.architecture()
('64bit', 'WindowsPE')

여기서는 파이썬 버전 3.6에 32비트라고 가정하고 MarkupSafe-1.0-cp36-cp36m-win32.whl을 받아서 설치하겠습니다(MarkupSafe의 버전은 시간이 지나면 달라질 수 있습니다). 받은 whl 파일을 설치하기 전에 앞에서 설치할 때 에러가 발생한 MarkupSafe 패키지를 삭제합니다.

C:Usersdojang>pip uninstall -y MarkupSafe
Uninstalling MarkupSafe-1.0:
  Successfully uninstalled MarkupSafe-1.0

이제 pip로 whl 파일을 설치 해줍니다(파일을 다운로드한 경로는 C:₩Users₩dojang₩Downloads라고 가정).

C:Usersdojang>pip install C:UsersdojangDownloadsMarkupSafe-1.0-cp36-cp36m-win32.whl
Processing c:\usersdojang\downloadsmarkupsafe-1.0-cp36-cp36m-win32.whl
Installing collected packages: MarkupSafe
Successfully installed MarkupSafe-1.0

여기서 끝이 아닙니다. 처음에 notebook 패키지를 설치하던 중간에 에러가 발생했으므로 notebook 패키지가 완전히 설치되지 않았습니다. 따라서 다음과 같이 notebook 패키지를 다시 설치해줍니다.

C:Usersdojang>pip install notebook
Collecting notebook
...생략...
Installing collected packages: jinja2, mistune, nbconvert, notebook
Successfully installed jinja2-2.9.6 mistune-0.7.4 nbconvert-5.1.1 notebook-5.0.0

whl 파일로 MarkupSafe 패키지를 설치해 놓았으므로 notebook 패키지를 설치할 때 에러가 발생하지 않습니다. 이런 방식으로 Windows에서 pip로 패키지를 설치할 때 에러가 발생하는 문제를 해결할 수 있습니다.



반응형

'Program > Python' 카테고리의 다른 글

numpy 함수  (0) 2017.12.02
numpy meshgrid  (0) 2017.12.02
python 가상환경  (0) 2017.11.21
파이썬 내장함수  (0) 2017.11.20
비트 연산자 사용하기  (0) 2017.11.20
반응형

파이썬을 사용하다 보면 pip로 패키지를 설치하게 되는데 이 패키지들은 파이썬 설치 폴더(디렉터리)의Lib/site-packages 안에 저장됩니다. 그래서 pip로 설치한 패키지는 모든 파이썬 스크립트에서 사용할 수 있게 됩니다. 평소에는 이런 방식이 큰 문제가 없지만 프로젝트 여러 개를 개발할 때는 패키지의 버전 문제가 발생합니다.

예를 들어 프로젝트 A에서는 패키지X 1.5를 사용해야 하고, 프로젝트 B에서는 패키지X 2.0을 사용해야 하는 경우가 생깁니다. 이 패키지X 1.5와 2.0은 호환이 되지 않는다면 개발하기가 상당히 불편해집니다.

이런 문제를 해결하기 위해 파이썬에서는 가상 환경(virtual environment)을 제공하는데, 가상 환경은 독립된 공간을 만들어주는 기능입니다. 가상 환경에서 pip로 패키지를 설치하면 가상 환경 폴더(디렉터리)의 Lib/site-packages 안에 패키지를 저장해줍니다. 즉, 프로젝트 A와 B 각각 가상 환경을 만들어서 프로젝트 A에는 패키지X 1.5를 설치하고, 프로젝트 B에는 패키지X 2.0을 설치합니다. 이렇게 하면 파이썬 스크립트를 실행할 때도 현재 가상 환경에 설치된 패키지를 사용하므로 버전 문제가 발생하지 않습니다.

▼ 그림 48-3 파이썬 가상 환경
그림 48 3 파이썬 가상 환경

특히 가상 환경에는 파이썬 실행 파일(인터프리터) 자체도 포함되므로 각 가상 환경 별로 다른 버전의 파이썬 인터프리터가 들어갈 수 있습니다. 즉, 스크립트를 실행할 때는 원래 설치된 파이썬 인터프리터가 아닌 가상 환경 안의 파이썬 인터프리터를 사용합니다.

그럼 가상 환경을 만드는 방법을 알아보겠습니다. 가상 환경은 venv 모듈에 가상 환경 이름을 지정해서 만듭니다.

venv는 파이썬 3.3이상부터 사용 가능

  • python -m venv 가상환경이름

여기서는 C:₩project 폴더 아래에 가상 환경을 만들겠습니다. 다음과 같이 명령 프롬프트에서 example 가상 환경을 만들고 example 폴더 안으로 이동합니다. 그다음에 Scripts 폴더 안의 activate.bat 파일을 실행하면 가상 환경이 활성화됩니다.

Windows 명령 프롬프트

C:project>python -m venv example
C:project>cd example
C:projectexample>Scriptsactivate.bat
(example) C:projectexample>

Windows PowerShell에서는 Activate.ps1 파일을 실행합니다.

Windows PowerShell

PS C:project> python -m venv example
PS C:project> cd example
PS C:projectexample> .ScriptsActivate.ps1
(example) PS C:projectexample>

프롬프트 앞을 보면 (example)과 같이 가상 환경 이름이 표시됩니다. 이 상태에서 pip로 패키지를 설치하면 C:₩project₩example₩Lib₩site-packages 안에 패키지가 저장됩니다. 또한, 이 상태에서 스크립트 파일을 실행하면 현재 가상 환경 안에 있는 파이썬 인터프리터와 패키지를 사용합니다.

(example) C:projectexample>pip install numpy
(example) C:projectexample>dir B Libsite-packages
easy_install.py
numpy
numpy-1.12.1.dist-info
pip
pip-9.0.1.dist-info
pkg_resources
setuptools
setuptools-28.8.0.dist-info
__pycache__

가상 환경에 설치된 패키지는 목록을 저장해 두었다가 나중에 다시 설치할 수 있습니다. 다음과 같이 pip freeze로 패키지 목록과 버전 정보를 requirements.txt 파일에 저장합니다(git 등으로 버전 관리를 할 때 저장소에 설치된 패키지를 모두 추가하지 않고, requirements.txt 파일만 관리하면 됩니다).

(example) C:projectexample>pip freeze > requirements.txt

requirements.txt 파일의 내용대로 패키지를 설치하려면 pip install에서 -r 또는 --requirement 옵션을 사용합니다.

(example) C:projectexample>pip install -r requirements.txt

만약 가상 환경 별로 파이썬 인터프리터 버전을 다르게 만들고 싶다면 해당 버전의 파이썬 인터프리터로 venv 모듈을 실행하면 됩니다. 다음은 파이썬 3.4를 사용하는 가상 환경을 만듭니다(파이썬 3.4를 설치했다고 가정).

C:project>C:Python34python.exe -m venv example3

이렇게 하면 venv 모듈을 실행한 파이썬 실행 파일(인터프리터)이 가상 환경 안에 들어갑니다.

이번에는 macOS와 리눅스에서 가상 환경을 만드는 방법입니다. 다음과 같이 python3으로 venv 모듈을 실행여 가상 환경을 만들고, source로 bin 디렉터리 안의 activate 파일을 적용하여 가상 환경을 활성화합니다.

macOS, 리눅스

~$ python3 -m venv example
~$ cd example
~/example$ source bin/activate
(example) ~/example$

가상 환경을 사용할 때 주의할 점이 있는데, 가상 환경을 만들고 나서 폴더(디렉터리)를 다른 곳으로 이동시키면 활성화가 안 됩니다. 왜냐하면 가상 환경을 활성화하는 activate.batActivate.ps1activate 파일 안에 현재 가상 환경 폴더의 경로가 내장되어 있기 때문입니다. 만약 가상 환경 폴더를 다른 곳으로 이동시켰다면 activate.batActivate.ps1activate 파일 안의 VIRTUAL_ENV 부분을 적절하게 수정해줍니다.

참고로 가상 환경을 사용하는 IDLE을 실행하려면 가상 환경을 활성화 시킨 뒤 idlelib 모듈을 실행하면 됩니다. 이렇게 하면 IDLE에서도 현재 가상 환경의 패키지를 사용할 수 있습니다.

Windows

(example) C:projectexample>pythonw.exe -m idlelib

macOS, 리눅스

(example) ~/example$ python3 -m idlelib
참고 | 아나콘다 가상 환경 만들기

아나콘다는 아나콘다 전용 가상 환경을 제공하며 conda를 사용하여 가상 환경을 만듭니다. conda는 아나콘다 설치 폴더의 Scripts 안에 들어있습니다.

  • conda create --name 가상환경이름

C:project>C:UsersdojangAnaconda3Scriptsconda.exe create --name example

conda는 가상 환경을 현재 폴더에 생성하지 않고 아나콘다 설치 폴더의 envs 안에 생성합니다.

  • 예) C:₩Users₩dojang₩Anaconda3₩envs₩example

가상 환경을 활성화할 때는 아나콘다 설치 폴더의 Scripts₩activate에 가상 환경 이름을 지정하여 실행해야 합니다.

  • activate 가상환경이름

C:project>C:UsersdojangAnaconda3Scriptsactivate example
(example) C:project>

아나콘다 가상 환경에 패키지를 설치할 때는 pip 대신 conda를 사용해야 합니다. 만약 pip를 사용하면 아나콘다 설치 폴더의 Lib/site-packages 안에 패키지가 저장되므로 주의해야 합니다.

  • conda install 패키지

(example) C:project>conda install numpy

다음은 conda의 주요 명령입니다.

  • 현재 환경 정보 출력: conda info

  • 패키지 검색: conda search 패키지

  • 특정 버전의 패키지를 설치:

    • conda install 패키지=버전 (예: conda install numpy=1.11.3)

    • conda install 패키지=버전=파이썬버전 (예: conda install numpy=1.11.3=py36_0)

  • 패키지 업데이트: conda update 패키지

  • 패키지 목록 출력: conda list

    패키지 삭제: conda remove 패키지

  • 패키지 목록 및 버전 정보 저장: conda list --export > package-list.txt

  • 패키지 목록으로 설치: conda install --file package-list.txt

https://dojang.io/mod/page/view.php?id=1168

반응형

'Program > Python' 카테고리의 다른 글

numpy meshgrid  (0) 2017.12.02
window에서 패키지 설치 에러 해결하기  (0) 2017.11.21
파이썬 내장함수  (0) 2017.11.20
비트 연산자 사용하기  (0) 2017.11.20
주피터 노트북 한글 폰트 설정  (0) 2017.11.20
반응형

함수

설명

abs(숫자)

숫자의 절댓값을 반환

all(반복가능한객체)

반복 가능한 객체의 요소가 모두 참이면 True, 하나라도 거짓이면 False

예) all([1, 2, 3])은 Trueall([1, 0, 3])은 False

any(반복가능한객체)

반복 가능한 객체의 요소가 하나라도 참이면 True, 모두 거짓이면 False

예) any([1, 0, 0])은 Trueany([0, 0, 0])은 False

bin(정수)

정수를 2진수로 된 문자열로 만들어서 반환

bool(값)

값을 TrueFalse로 변환하여 반환. 0이 아닌 숫자, 내용이 있는 객체는 True를 반환, 0None, 비어있는 객체는 False를 반환

bytes(값, 인코딩)

바이트 객체를 반환, bytes는 바이트 단위(8비트)로 문자열을 저장하는 자료형

예) bytes('안녕', encoding='utf-8')은 b'\xec\x95\x88\xeb\x85\x95'

chr(코드값)

ASCII 코드값에 해당하는 문자를 반환. 예) chr(97)은 'a'

dict(반복가능한객체)

반복 가능한 객체로 딕셔너리를 생성하여 반환

dir(객체)

객체의 네임스페이스를 반환, 객체를 지정하지 않으면 현재 네임스페이스를 반환

divmod(a, b)

두 숫자의 몫과 나머지를 튜플로 반환

enumerate

객체에 들어있는 요소의 순서(인덱스)와 요소를 튜플로 묶어서 enumerate 객체를 반환

예) list(enumerate(['a', 'b', 'c']))는 [(0, 'a'), (1, 'b'), (2, 'c')]

eval(문자열)

문자열 형태의 파이썬 코드를 실행하고 결과를 반환(문자열을 파이썬 인터프리터에서 실행). 예) eval('print(1, 2)')는 1 2 출력

filter(함수, 반복가능한객체)

반복 가능한 객체에서 특정 조건에 맞는 요소만 가져옴

예) list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4]))는 [2, 4]

float(값)

숫자나 문자열을 실수로 변환하여 반환

help(객체)

객체의 이름, 매개변수, 독스트링(__doc__ 속성)을 도움말 형태로 출력

hex(정수)

정수를 16진수로 된 문자열로 만들어서 반환

id(객체)

객체의 고유한 값을 반환(CPython에서는 메모리 주소)

input(문자열)

사용자의 입력을 받아서 문자열을 반환

int(값)

숫자나 문자열을 정수로 변환하여 반환, int(문자열, 진법)은 해당 진법으로 된 문자열을 10진수로 변환하여 반환

isinstance(객체, 클래스)

객체가 클래스의 인스턴스인지 확인, 클래스의 인스턴스가 맞으면 True, 아니면 False

issubclass(파생클래스, 기반클래스)

클래스가 특정 클래스의 파생 클래스인지 확인, 파생 클래스가 맞으면 True, 아니면 False

iter(객체)

객체에서 이터레이터를 반환. 객체의 __iter__ 메서드를 호출해줌

len(객체)

객체의 길이(요소 개수)를 반환

list(반복가능한객체)

반복 가능한 객체로 리스트를 생성하여 반환

map(함수, 반복가능한객체)

반복 가능한 객체의 요소를 지정된 함수로 처리한 뒤 map 객체를 반환

예) list(map(lambda x: x + 10, [1, 2]))는 [11, 12]

min(반복가능한객체)

max(반복가능한객체)

min은 반복 가능한 객체의 요소 중에서 가장 작은 요소를 반환, max는 반복 가능한 객체의 요소 중에서 가장 큰 요소를 반환

next(이터레이터)

이터레이터에서 요소를 차례대로 가져와서 반환, 이터레이터의 __iter__ 메서드를 호출해줌

oct(정수)

정수를 8진수로 된 문자열로 만들어서 반환

open(파일이름, 파일모드)

지정된 파일을 열어서 파일 객체를 반환, encoding을 지정하면 파일을 해당 인코딩으로 처리(텍스트 모드에만 적용됨)

예) open('hello.txt', 'w', encoding='utf-8')은 파일의 인코딩을 UTF-8로 저장

ord(문자)

문자의 ASCII 코드를 반환. 예) ord('a')는 97

pow(값, 지수)

값을 지수만큼 거듭제곱한 값을 반환

range(횟수)

지정된 횟수만큼 숫자를 생성하는 반복 가능한 객체를 반환, range(시작, 끝, 증가폭)처럼 숫자의 범위와 증가폭을 지정할 수 있음

repr(객체)

파이썬 인터프리터에서 실행할 수 있는 문자열을 반환. repr에서 반환된 문자열은 eval로 실행할 수 있음

reversed(반복가능한객체)

반복 가능한 객체에서 요소의 순서를 뒤집어서 반환

set(반복가능한객체)

반복 가능한 객체로 세트를 생성하여 반환

sorted(반복가능한객체)

반복 가능한 객체의 요소를 오름차순 정렬하여 반환, reversed=True를 지정하면 내림차순 정렬 예) sorted([8, 5, 2])는 [2, 5, 8]

str(값)

값을 문자열로 변환하여 반환

super()

현재 클래스의 기반 클래스 객체를 반환

sum(반복가능한객체)

반복 가능한 객체에 들어있는 모든 요소의 합을 반환

tuple(반복가능한객체)

반복 가능한 객체로 튜플을 생성하여 반환

type(객체)

객체의 클래스(자료형) 객체를 반환. type('클래스이름', 기반클래스튜플, 속성메서드딕셔너리)는 클래스 객체를 생성하여 반환

zip(반복가능한객체, ...)

같은 개수로 된 반복 가능한 객체를 여러 개 넣으면 순서대로 각 요소를 튜플로 묶어서 zip 객체를 반환

예) list(zip([1, 2, 3], [97, 98, 99]))는 [(1, 97), (2, 98), (3, 99)]

반응형
반응형

컴퓨터는 2진수(0과 1)를 사용하므로 대부분의 프로그래밍 언어는 2진수를 다루는 연산자를 제공합니다. 그런데 2진수로 변환하는 방법을 잘 모른다면 어떻게 해야 할까요? 걱정하지 않아도 됩니다. 파이썬에서는 간단하게 2진수와 10진수를 서로 변환할 수 있습니다.

다음과 같이 10진수를 2진수로 된 문자열로 변환할 때는 bin을 사용합니다. 반대로 2진수에서 10진수로는 입력 즉시 변환됩니다. 만약 2진수가 문자열 형태라면 int에 문자열과 2를 지정하여 10진수로 변환할 수 있습니다. 그리고 코드에서 2진수를 직접 입력할 때는 맨 앞에 0b를 붙입니다(binary의 b).

  • bin(정수)
  • int('2진수문자열', 2)
>>> bin(13)    # 10진수 13을 2진수로 변환
'0b1101'
>>> 0b1101     # 2진수 1101을 10진수로 변환
13
>>> int('1101', 2)    # 2진수로 된 문자열 1101을 10진수로 변환
13

10진수 13을 2진수로 표현하면 1101이 됩니다. 여기서 2진수의 자릿수는 일정한 값을 가지는데 낮은 자릿수부터 2의 거듭제곱 순서로 커집니다(20부터 시작). 따라서 1101은 8, 4, 1이 위치한 자릿수가 1이므로 8 + 4 + 1 = 13입니다.

이제 2진수의 각 자릿수를 AND, OR, XOR, NOT 연산을 하는 비트 논리 연산자를 사용해보겠습니다.

  • a & b
  • a | b
  • a ^ b
  • ~x
>>> bin(0b1101 & 0b1001)    # 비트 AND
'0b1001'
>>> 13 & 9                  # 비트 AND
9
>>> bin(0b1101 | 0b1001)    # 비트 OR
'0b1101'
>>> 13 | 9                  # 비트 OR
13
>>> bin(0b1101 ^ 0b1001)    # 비트 XOR
'0b100'
>>> 13 ^ 9                  # 비트 XOR
4
>>> bin(~0b1101)            # 비트 NOT
'-0b1110'
>>> ~13                     # 비트 NOT
-14

먼저 &|^~ 연산자로 각 비트를 연산했을 때의 결과(진리표)는 다음과 같습니다. 진리표의 내용이 많아 보여도 각 연산자의 특성만 이해하면 진리표 전체를 외우지 않아도 됩니다.

▼ 표 48-1 &, |, ^, ~의 연산 결과

연산자

비트1

비트2

결과

&

0

0

0

0

1

0

1

0

0

1

1

1

|

0

0

0

0

1

1

1

0

1

1

1

1

^

0

0

0

0

1

1

1

0

1

1

1

0

~

0

 

1

1

 

0

예를 들어 & 연산자는 비트 AND이므로 두 값이 모두 1일 때 1입니다. 따라서 하나라도 0이면 0이 나옵니다. 즉, 0b1101과 0b1001을 비트 AND 연산했을 때 0 & 1은 0 그리고 1 & 1은 1이 나오므로 0b1001이 됩니다. 10진수로 표현하면 13 & 9는 9가 되겠죠?

>>> bin(0b1101 & 0b1001)
0b1001
>>> 13 & 9
9

다음과 같이 비트 논리 연산자는 각 자릿수를 연산하여 결과를 만듭니다. 이때 각 자릿수의 연산 결과는 다른 자릿수에 영향을 미치지 않습니다.

▼ 그림 48-1 비트 단위 연산
그림 48 1 비트 단위 연산

이번에는 비트의 위치를 이동시키는 시프트 연산자입니다. 시프트 연산자는 << 또는 >> 다음에 비트를 이동시킬 횟수를 지정합니다. <<는 비트를 왼쪽으로 이동시키고, >>는 비트를 오른쪽으로 이동시킵니다.

  • a << b
  • a >> b
>>> 0b0011 << 2    # 비트를 왼쪽으로 2번 이동
12
>>> bin(12)
'0b1100'
>>> 0b1100 >> 2    # 비트를 오른쪽으로 2번 이동
3
>>> bin(3)
'0b11'

참고로 비트를 오른쪽으로 이동시켰을 때 1이 들어갈 공간이 없다면 1은 사라집니다. 즉, 계속 비트를 오른쪽으로 이동시키면 1은 모두 사라지고 최종 결과는 0b0이 됩니다.

다음은 파이썬의 비트 연산자입니다.

▼ 표 48-2 파이썬 비트 연산자

연산자

기능

문법

설명

&

비트 AND

a & b

a와 b의 비트를 AND 연산

|

비트 OR

a | b

a와 b의 비트를 OR 연산

^

비트 XOR

a ^ b

a와 b의 비트를 XOR 연산(배타적 OR, Exclusive OR)

~

비트 NOT

~x

x의 비트를 뒤집음

<< 

비트 왼쪽 시프트

a << b

a의 비트를 b번 왼쪽으로 이동시킴

>> 

비트 오른쪽 시프트

a >> b

a의 비트를 b번 오른쪽으로 이동시킴

&=

비트 AND 연산 후 할당

a &= b

a와 b의 비트를 AND 연산한 후 결과를 a에 할당

|=

비트 OR 연산 후 할당

a |= b

a와 b의 비트를 OR 연산한 후 결과를 a에 할당

^=

비트 XOR 연산 후 할당

a ^= b

a와 b의 비트를 XOR 연산한 후 결과를 a에 할당

<<=

비트 왼쪽 시프트 후 할당

a <<= b

a의 비트를 b번 왼쪽으로 이동시킨 후 결과를 a에 할당

>>=

비트 오른쪽 시프트 후 할당

a >>= b

a의 비트를 b번 오른쪽으로 이동시킨 후 결과를 a에 할당



https://dojang.io/mod/page/view.php?id=1160

반응형

+ Recent posts

반응형