본문 바로가기

인공지능/이론 적용

Single-layer Perceptron 적용

Single-layer Perceptron - 적용

섭씨 화씨 변환

섭씨를 화씨로 변환시키는 공식을 Single-layer Perceptron이 찾아나가게끔 하겠다.


먼저 -50~100 사이의 랜덤한 섭씨값 100개를 만들고 각각을 화씨로 변환한다.

In [1]:
import numpy as np

celsius = np.random.randint(low=-50, high=101, size=100)
fahrenheit = celsius * 1.8 + 32

print("celsius\n", celsius)
print("\n")
print("fahrenheit\n", fahrenheit)
celsius
 [ 29  88  57  32  53  21  84  78  45 -22   2  40  62  35 -39  18  43 -23
  57   0  73  85  66  17  67 -27  -5  -6  65   2 -32  64  62  30 -15  88
 -38  15 -50 -36  48  11  84 -28 -29  85 -36  62  82   0 -46  46  13  74
  46  86 -39  32  -7  95  -6 -22 -34  56 -23  47  -4 -25   1  84 -40  28
 -47  54  35  30  68 -48  64  70  -2 -13  73 -39  23  -5  19  13  81 -25
  65  55  31  10 -45  25  96 -41  80  89]


fahrenheit
 [ 84.2 190.4 134.6  89.6 127.4  69.8 183.2 172.4 113.   -7.6  35.6 104.
 143.6  95.  -38.2  64.4 109.4  -9.4 134.6  32.  163.4 185.  150.8  62.6
 152.6 -16.6  23.   21.2 149.   35.6 -25.6 147.2 143.6  86.    5.  190.4
 -36.4  59.  -58.  -32.8 118.4  51.8 183.2 -18.4 -20.2 185.  -32.8 143.6
 179.6  32.  -50.8 114.8  55.4 165.2 114.8 186.8 -38.2  89.6  19.4 203.
  21.2  -7.6 -29.2 132.8  -9.4 116.6  24.8 -13.   33.8 183.2 -40.   82.4
 -52.6 129.2  95.   86.  154.4 -54.4 147.2 158.   28.4   8.6 163.4 -38.2
  73.4  23.   66.2  55.4 177.8 -13.  149.  131.   87.8  50.  -49.   77.
 204.8 -41.8 176.  192.2]

  • error공식은 단순히 label값과 예측값 차이들의 절댓값 평균을 사용하였다.
  • 루프 중에 출력을 너무 자주하면 속도저하가 심하므로 10000번에 1번씩 출력한다.
  • learning_rate를 잘 조절해주어야 변화의 폭이 너무 적어지는 현상이나 반대로 +와 -를 오가며 숫자가 한없이 커져가는 발산현상을 방지할 수 있다.
In [2]:
num_epoch = 1000000

w = 0
b = 0

learning_rate = 0.0001

for epoch in range(num_epoch):
    fahrenheit_predict = celsius * w + b
    
    error = np.abs(fahrenheit - fahrenheit_predict).mean()
    
    if epoch % 10000 == 0:
        print(f"{epoch}, {error:.5f}, weight = {w:.3f}, b = {b:.3f}")
    
    if error < 1.0e-4:
        break
        
    w += learning_rate * ((fahrenheit - fahrenheit_predict) * celsius).mean()
    b += learning_rate * (fahrenheit - fahrenheit_predict).mean()
    
print("-----" * 10)
print(f"{epoch}, {error:.5f}, weight = {w:.3f}, b = {b:.3f}")
0, 90.62000, weight = 0.000, b = 0.000
10000, 11.36885, weight = 1.946, b = 17.057
20000, 5.31239, weight = 1.868, b = 25.017
30000, 2.48235, weight = 1.832, b = 28.737
40000, 1.15994, weight = 1.815, b = 30.475
50000, 0.54201, weight = 1.807, b = 31.288
60000, 0.25327, weight = 1.803, b = 31.667
70000, 0.11835, weight = 1.802, b = 31.844
80000, 0.05530, weight = 1.801, b = 31.927
90000, 0.02584, weight = 1.800, b = 31.966
100000, 0.01207, weight = 1.800, b = 31.984
110000, 0.00564, weight = 1.800, b = 31.993
120000, 0.00264, weight = 1.800, b = 31.997
130000, 0.00123, weight = 1.800, b = 31.998
140000, 0.00058, weight = 1.800, b = 31.999
150000, 0.00027, weight = 1.800, b = 32.000
160000, 0.00013, weight = 1.800, b = 32.000
--------------------------------------------------
163006, 0.00010, weight = 1.800, b = 32.000

결과를 보면 1.8과 32라는 값을 아주 잘 찾아내었음을 알 수 있다. 이렇게 찾아낸 weight값과 bias값을 이용해 섭씨가 주어지면 화씨를 예측할 수 있을 것이다. 비록 이 예시에서 사용된 섭씨와 화씨의 경우에는 1.8과 32가 포함된 관계식이 이미 존재하지만, 특징(ex 섭씨)과 그에 대한 결과(ex 화씨)만이 존재하는 다양한 사회현상, 자연현상, 데이터현상 등에 대해서도 적절한 weight값과 bias값을 찾아내어 새로주어지는 특징들로 결과가 어떻게 나올지 예측할 수도 있을 것이다.


보스턴 집값 예측


이번에는 사이킷-런에서 연습용으로 제공하는 현실데이터를 이용하여 보스턴 집값을 예측하여보겠다.


우선 사이킷런의 boston dataset을 로드하고 그것에 관한 정보를 볼 수 있는 key값들을 살펴본다.

In [3]:
from sklearn.datasets import load_boston

boston = load_boston()
boston.keys()
Out[3]:
dict_keys(['data', 'target', 'feature_names', 'DESCR', 'filename'])

feature가 담겨있는 data와 label이 담겨있는 target의 자료형이 무엇인지 조사해본다.

In [4]:
# load한 boston 데이터셋에 대한 여러가지 정보들을 보여준다.
for key in boston.keys():
    print(f"---{key}---")
    print(boston[key])
    print("\n")
---data---
[[6.3200e-03 1.8000e+01 2.3100e+00 ... 1.5300e+01 3.9690e+02 4.9800e+00]
 [2.7310e-02 0.0000e+00 7.0700e+00 ... 1.7800e+01 3.9690e+02 9.1400e+00]
 [2.7290e-02 0.0000e+00 7.0700e+00 ... 1.7800e+01 3.9283e+02 4.0300e+00]
 ...
 [6.0760e-02 0.0000e+00 1.1930e+01 ... 2.1000e+01 3.9690e+02 5.6400e+00]
 [1.0959e-01 0.0000e+00 1.1930e+01 ... 2.1000e+01 3.9345e+02 6.4800e+00]
 [4.7410e-02 0.0000e+00 1.1930e+01 ... 2.1000e+01 3.9690e+02 7.8800e+00]]


---target---
[24.  21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 15.  18.9 21.7 20.4
 18.2 19.9 23.1 17.5 20.2 18.2 13.6 19.6 15.2 14.5 15.6 13.9 16.6 14.8
 18.4 21.  12.7 14.5 13.2 13.1 13.5 18.9 20.  21.  24.7 30.8 34.9 26.6
 25.3 24.7 21.2 19.3 20.  16.6 14.4 19.4 19.7 20.5 25.  23.4 18.9 35.4
 24.7 31.6 23.3 19.6 18.7 16.  22.2 25.  33.  23.5 19.4 22.  17.4 20.9
 24.2 21.7 22.8 23.4 24.1 21.4 20.  20.8 21.2 20.3 28.  23.9 24.8 22.9
 23.9 26.6 22.5 22.2 23.6 28.7 22.6 22.  22.9 25.  20.6 28.4 21.4 38.7
 43.8 33.2 27.5 26.5 18.6 19.3 20.1 19.5 19.5 20.4 19.8 19.4 21.7 22.8
 18.8 18.7 18.5 18.3 21.2 19.2 20.4 19.3 22.  20.3 20.5 17.3 18.8 21.4
 15.7 16.2 18.  14.3 19.2 19.6 23.  18.4 15.6 18.1 17.4 17.1 13.3 17.8
 14.  14.4 13.4 15.6 11.8 13.8 15.6 14.6 17.8 15.4 21.5 19.6 15.3 19.4
 17.  15.6 13.1 41.3 24.3 23.3 27.  50.  50.  50.  22.7 25.  50.  23.8
 23.8 22.3 17.4 19.1 23.1 23.6 22.6 29.4 23.2 24.6 29.9 37.2 39.8 36.2
 37.9 32.5 26.4 29.6 50.  32.  29.8 34.9 37.  30.5 36.4 31.1 29.1 50.
 33.3 30.3 34.6 34.9 32.9 24.1 42.3 48.5 50.  22.6 24.4 22.5 24.4 20.
 21.7 19.3 22.4 28.1 23.7 25.  23.3 28.7 21.5 23.  26.7 21.7 27.5 30.1
 44.8 50.  37.6 31.6 46.7 31.5 24.3 31.7 41.7 48.3 29.  24.  25.1 31.5
 23.7 23.3 22.  20.1 22.2 23.7 17.6 18.5 24.3 20.5 24.5 26.2 24.4 24.8
 29.6 42.8 21.9 20.9 44.  50.  36.  30.1 33.8 43.1 48.8 31.  36.5 22.8
 30.7 50.  43.5 20.7 21.1 25.2 24.4 35.2 32.4 32.  33.2 33.1 29.1 35.1
 45.4 35.4 46.  50.  32.2 22.  20.1 23.2 22.3 24.8 28.5 37.3 27.9 23.9
 21.7 28.6 27.1 20.3 22.5 29.  24.8 22.  26.4 33.1 36.1 28.4 33.4 28.2
 22.8 20.3 16.1 22.1 19.4 21.6 23.8 16.2 17.8 19.8 23.1 21.  23.8 23.1
 20.4 18.5 25.  24.6 23.  22.2 19.3 22.6 19.8 17.1 19.4 22.2 20.7 21.1
 19.5 18.5 20.6 19.  18.7 32.7 16.5 23.9 31.2 17.5 17.2 23.1 24.5 26.6
 22.9 24.1 18.6 30.1 18.2 20.6 17.8 21.7 22.7 22.6 25.  19.9 20.8 16.8
 21.9 27.5 21.9 23.1 50.  50.  50.  50.  50.  13.8 13.8 15.  13.9 13.3
 13.1 10.2 10.4 10.9 11.3 12.3  8.8  7.2 10.5  7.4 10.2 11.5 15.1 23.2
  9.7 13.8 12.7 13.1 12.5  8.5  5.   6.3  5.6  7.2 12.1  8.3  8.5  5.
 11.9 27.9 17.2 27.5 15.  17.2 17.9 16.3  7.   7.2  7.5 10.4  8.8  8.4
 16.7 14.2 20.8 13.4 11.7  8.3 10.2 10.9 11.   9.5 14.5 14.1 16.1 14.3
 11.7 13.4  9.6  8.7  8.4 12.8 10.5 17.1 18.4 15.4 10.8 11.8 14.9 12.6
 14.1 13.  13.4 15.2 16.1 17.8 14.9 14.1 12.7 13.5 14.9 20.  16.4 17.7
 19.5 20.2 21.4 19.9 19.  19.1 19.1 20.1 19.9 19.6 23.2 29.8 13.8 13.3
 16.7 12.  14.6 21.4 23.  23.7 25.  21.8 20.6 21.2 19.1 20.6 15.2  7.
  8.1 13.6 20.1 21.8 24.5 23.1 19.7 18.3 21.2 17.5 16.8 22.4 20.6 23.9
 22.  11.9]


---feature_names---
['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO'
 'B' 'LSTAT']


---DESCR---
.. _boston_dataset:

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

**Data Set Characteristics:**  

    :Number of Instances: 506 

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

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

    :Missing Attribute Values: None

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

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


This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980.   N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.   
     
.. topic:: References

   - Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
   - Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.



---filename---
C:\Users\Owner\Anaconda3\lib\site-packages\sklearn\datasets\data\boston_house_prices.csv


DESCR에서 Attribute들에 대한 설명을 번역하면 다음과 같다.

  • CRIM: 자치 시(town) 별 1인당 범죄율
  • ZN: 25,000 평방피트를 초과하는 거주지역의 비율
  • INDUS: 비소매상업지역이 점유하고 있는 토지의 비율
  • CHAS: 찰스강의 경계에 위치해 있으면 1, 그렇지 않으면 0
  • NOX: 10ppm당 농축 일산화질소
  • RM: 주택 1가구당 평균 방의 개수
  • AGE: 1940년 이전에 건축된 소유주택의 비율
  • DIS: 5개의 보스턴 직업센터까지의 접근성 지수
  • RAD: 방사형 도로까지의 접근성 지수
  • TAX: 10,000 달러 당 재산세율
  • PTRATIO: 자치 시(town)별 학생/교사 비율
  • B: 1000(Bk-0.63)^2 (여기서 Bk는 자치시별 흑인의 비율)
  • LSTAT: 모집단의 하위계층 비율(%)
  • MEDV: 본인 소유의 주택 가격(중앙값) (단위: $1,000)

출처: ai-times

또한 data와 target에 있는 값들의 형태와 양을 확인해보고 일부를 출력도 해본다.

In [5]:
X = boston['data']

print(X.shape)
X[0]
(506, 13)
Out[5]:
array([6.320e-03, 1.800e+01, 2.310e+00, 0.000e+00, 5.380e-01, 6.575e+00,
       6.520e+01, 4.090e+00, 1.000e+00, 2.960e+02, 1.530e+01, 3.969e+02,
       4.980e+00])
In [6]:
y = boston['target']

print(y.shape)
y[0:10]
(506,)
Out[6]:
array([24. , 21.6, 34.7, 33.4, 36.2, 28.7, 22.9, 27.1, 16.5, 18.9])

DataFrame으로 데이터를 정리하여 좀 더 정돈된 상태에서 확인해보겠다.

In [7]:
import pandas as pd

df = pd.DataFrame(X, columns=boston['feature_names'])
df['MEDV'] = y

print(df.shape)
df.head()
(506, 14)
Out[7]:
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT MEDV
0 0.00632 18.0 2.31 0.0 0.538 6.575 65.2 4.0900 1.0 296.0 15.3 396.90 4.98 24.0
1 0.02731 0.0 7.07 0.0 0.469 6.421 78.9 4.9671 2.0 242.0 17.8 396.90 9.14 21.6
2 0.02729 0.0 7.07 0.0 0.469 7.185 61.1 4.9671 2.0 242.0 17.8 392.83 4.03 34.7
3 0.03237 0.0 2.18 0.0 0.458 6.998 45.8 6.0622 3.0 222.0 18.7 394.63 2.94 33.4
4 0.06905 0.0 2.18 0.0 0.458 7.147 54.2 6.0622 3.0 222.0 18.7 396.90 5.33 36.2

Single-layer Perceptron이 찾은 weight값들과 bias값이 훈련한 데이터셋이 아닌 새로운 데이터셋에 대하여도 잘 적용되는지 확인할 수 있기위하여 전체 데이터의 70%는 훈련용, 30%는 테스트용으로 분리하겠다.

In [8]:
from sklearn.model_selection import train_test_split

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

print("X_train :", X_train.shape)
print("X_test  :", X_test.shape)
print("y_train :", y_train.shape)
print("y_test  :", y_test.shape)
X_train : (354, 13)
X_test  : (152, 13)
y_train : (354,)
y_test  : (152,)

이제 본격적으로 Single-layer Perceptron코드를 작성해본다.

In [9]:
class SingleLayerPerceptron:    
    def __init__(self, n_estimators=100, learning_rate=1):
        self.n_estimators = n_estimators
        self.learning_rate = learning_rate
        
    def fit(self, x, y, print_cycle=0, pass_error=0):
        xT = x.T
        w = np.full((1, x.shape[1]), 0, dtype=float)
        b = 0
        
        for epoch in range(self.n_estimators):
            y_predict = w.dot(xT) + b
            
            error = np.abs(y_predict - y).mean()            
            if (0 < print_cycle) and (epoch % print_cycle == 0):
                print(f"epoch = {epoch}, error = {error:.5f}")
            if (0 < pass_error) and (error < pass_error):
                break
            
            w -= self.learning_rate * (y_predict - y).dot(x) / x.shape[0]
            b -= self.learning_rate * (y_predict - y).mean()
        
        self.w = w
        self.b = b
        
        print(f"epoch = {epoch}, error = {error:.5f}")
        for i in range(w.shape[1]):
            print(f"w{i+1} = {w[0][i]}")
        print(f"b = {b:.5f}")
        
    def predict(self, x):
        return self.w.dot(x.T) + self.b

클래스로부터 인스턴스를 생성한 후, 훈련시키고 그 결과를 확인한다.

In [10]:
model = SingleLayerPerceptron(n_estimators=int(1e+6), learning_rate=1e-6)
model.fit(X_train, y_train, print_cycle=int(1e+5), pass_error=5)
epoch = 0, error = 23.01582
epoch = 100000, error = 5.17270
epoch = 172547, error = 5.00000
w1 = -0.14064423980142207
w2 = 0.09397826213693049
w3 = -0.031483136955197076
w4 = 0.058533210647719894
w5 = 0.03136426116932972
w6 = 0.8050340867826369
w7 = 0.10659101516106542
w8 = 0.03719191200809899
w9 = 0.02089759675471669
w10 = 0.00360288970045801
w11 = 0.3819146406470547
w12 = 0.03229919867965103
w13 = -0.8433586993681086
b = 0.07250

훈련한 데이터셋이 아닌 처음 접하는 테스트셋에 대하여도 준수한 에러율을 보인다.

In [11]:
y_predict_test = model.predict(X_test)
print("error =", np.abs(y_predict_test - y_test).mean())
error = 5.212167936752081