빅데이터 | 머신러닝 | 딥러닝/딥러닝

[딥러닝 기초] Neural Network (훈련 노하우)

냠냠:) 2020. 4. 24. 23:47

[이 글은 "Do it 딥러닝 입문" 책을 보고 공부한 내용을 복습하고자 정리한 글입니다.]

 

목표 - 주어진 데이터 셋으로 어떻게 훈련했을 때 가장 효율이 좋은지 알아본다.

 

[배운 점]

모듈

- sklean.linear_model 모듈을 통해 모델을 임포트 할 수 있다. (ex. SGDClassifier)

 

- model의 하이퍼파라미터로 loss함수를 지정해줄 수 있다. (ex. hinge, log)

 

훈련데이터 세트

- 테스트 세트로 모델을 튜닝하면 테스트 세트에 대해서만 좋은 성능을 낼 수 있으므로 실전에서 좋은 성능을 기대하기 어렵다.(실전 투입 마지막 한 번만 사용하는 게 좋다.)

 

- 훈련세트를 훈련세트와 검증세트로 나눠서 활용한다. (비율은 80 20 20)

 

스케일(Scale)

- 우리는 잘 가공되어있는 데이터만 활용할 수는 없다. 그러므로 어떤 데이터들을 적절히 가공하는 '데이터 전처리'과정이 필요하다.

 

- 그중 각 특성의 스케일을 조정하는 것이 있는데, 두 특성의 스케일 차이가 크게 나게 되면 모델 성능에 영향을 준다.

 

- 스케일을 조정하는 방법 중 하나에 '표준화(standardization)'이 있다. 특성 값에서 평균을 빼고 표준편차로 나눈 값을 사용한다.

 

- 넘파이의 mean(), std()를 사용하면 평균과 표준 편차를 계산해준다.

 

- 스케일을 맞춘 데이터의 가중치는 빠르게 최적 값으로 근접한다.

 

- 훈련 세트와 검증 세트의 전처리 즉, 스케일을 조정할 경우 훈련세트를 조정한 평균과 표준편차를 이용해서 조정해준다. 

 

 

[코드]

ScaleModification
In [1]:
import matplotlib.pyplot as plt
import numpy as np
In [2]:
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()
x = cancer.data
y = cancer.target
x_train_all, x_test, y_train_all, y_test = train_test_split(x,y, stratify=y, test_size=0.2, random_state=42)
In [3]:
from sklearn.linear_model import SGDClassifier       #SGDClassifier 모델을 가져옴
sgd = SGDClassifier(loss='log', random_state=42)      #loss함수를 log로 함
sgd.fit(x_train_all,y_train_all)
sgd.score(x_test,y_test)                              #정확도
Out[3]:
0.8333333333333334
In [4]:
from sklearn.linear_model import SGDClassifier     #위와 똑같은 방식
sgd = SGDClassifier(loss='hinge', random_state=42)  #SVM문제를 푸는 모델로 만들어짐(hinge)
sgd.fit(x_train_all,y_train_all)                    #SVM? : 훈련데이터의 클래스를 구분하는 경계선을 찾는 작업
sgd.score(x_test,y_test)                            #정확도
Out[4]:
0.9385964912280702
In [5]:
### 검증세트
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()
x = cancer.data
y = cancer.target
x_train_all, x_test, y_train_all, y_test = train_test_split(x,y, stratify=y, test_size=0.2, random_state=42)
In [6]:
###우리는 훈련데이터를 가지고 또 한번 훈련데이터와 검증데이터로 나눈다.
x_train, x_val, y_train, y_val = train_test_split(x_train_all, y_train_all, stratify = y_train_all, test_size=0.2, random_state= 42)
print(len(x_train),len(x_val))
364 91
In [7]:
sgd= SGDClassifier(loss='log',random_state=42)    #샘플 데이터가 너무 적기 때문에 성능이 더 안좋아짐.
sgd.fit(x_train,y_train)
sgd.score(x_val,y_val)
Out[7]:
0.6923076923076923
In [8]:
print(cancer.feature_names[[2,3]])               #feature_namse는 특성의 이름을 확인한다.
['mean perimeter' 'mean area']
In [9]:
plt.boxplot(x_train[:, 2:4])                     #서로 스케일이 너무 다르다.
plt.show()
In [10]:
class SingleLayer():
    def __init__(self, learning_rate=0.1):
        self.w = None       # 데이터 특성이 많기 때문에 가중치를 미리 초기화하지 않음.
        self.b = None
        self.losses = []
        self.w_history=[]   #가중치를 기록
        self.lr=learning_rate
        
    def forpass(self, x):
        z = np.sum(x * self.w) + self.b   # 직선의 방정식 즉 hypothesis #np.sum을 사용하면 1차원 넘파이배열에서 배열의 요소끼리 사칙연산을 할 수 있음.
        return z
    
    def backprop(self, x, err):
        w_grad = x * err                   #가중치에 대한 그레이디언트 계산
        b_grad = 1 * err                   #절편에 대한 그레이디언트 계산
        return w_grad, b_grad
    
    def fit(self, x, y, epochs=100):
        self.w = np.ones(x.shape[1])       #가중치를 초기화하는데 x의 shape과 똑같고 1채움 즉 각 행값 
        self.b = 0
        self.w_history.append(self.w.copy())  #가중치를 기록한다.
        np.random.seed(42)                    #무작위로 시드를 지정한다.
        for i in range(epochs):
            loss = 0
            indexes = np.random.permutation(np.arange(len(x))) #인덱스를 섞음. 데이터가 섞여서 훈련될수록 손실 함수의 값이 효율적으로 줄어듬
            for i in indexes:
                z = self.forpass(x[i])                     #정방향 계산
                a = self.activation(z)                     #activation함수 적용
                err = -(y[i] - a)                          #실제값과 예측값 오차계산
                w_grad, b_grad = self.backprop(x[i], err)  #역방향 계산
                self.w -= w_grad * self.lr                   #업데이트(학습률 적용)
                self.b -= b_grad
                self.w_history.append(self.w.copy())       #w값을 카피 한 뒤 변화율을 보기위해 append해준다.
                a = np.clip(a, 1e-10, 1-1e-10)             #인잔힌 로그 계산을 위해 클리핑 한 후 손실을 누적
            
                
                loss += -(y[i] * np.log(a) + (1-y[i]) * np.log(1-a))  #에포크마다 평균 손실을 저장
            self.losses.append(loss/len(y))               #에포크마다 평균 손실을 저장
                
    def activation(self, z):
        a = 1 / (1 + np.exp(-z))
        return a
    
    def predict(self, x):
        z = [self.forpass(x_i) for x_i in x]              #hypothesis를 구해 반환
        return np.array(z) > 0                            #스텝 함수 적용
    
    def score(self, x, y):
        return np.mean(self.predict(x)==y)
In [11]:
layer1 = SingleLayer()
layer1.fit(x_train,y_train)
layer1.score(x_val,y_val)
C:\Users\USER\Anaconda3\lib\site-packages\ipykernel_launcher.py:41: RuntimeWarning: overflow encountered in exp
Out[11]:
0.9120879120879121
In [12]:
w2 = []                     #mean perimeter비해 mean area스케일이 크므로 w3값(area)이 학습과정이 큰폭으로 흔들림
w3 = []
for w in layer1.w_history:
    w2.append(w[2])
    w3.append(w[3])
plt.plot(w2,w3)
plt.plot(w2[-1], w3[-1],'ro')
plt.xlabel("w[2]")
plt.ylabel("w[3]")
Out[12]:
Text(0, 0.5, 'w[3]')
In [13]:
train_mean = np.mean(x_train, axis=0)     #표준화는 특성값에서 평균을 빼고 표준편차로 나눈다.
train_std = np.std(x_train, axis=0)       #스케일 조정 방법 중 하나 
x_train_scaled= (x_train-train_mean) / train_std
In [14]:
layer2 = SingleLayer()                    #스케일을 조정해준 뒤 w값의 변화량을 본다.
layer2.fit(x_train_scaled,y_train)
w2 = []
w3 = []
for w in layer2.w_history:
    w2.append(w[2])
    w3.append(w[3])
plt.plot(w2,w3)
plt.plot(w2[-1],w3[-1],'ro')
Out[14]:
[<matplotlib.lines.Line2D at 0x2077dedb4c8>]
In [15]:
layer2.score(x_val,y_val)       #검증세트도 전처리과정을 거쳐야한다.
Out[15]:
0.37362637362637363
In [16]:
val_mean = np.mean(x_val, axis=0)
val_std = np.std(x_val, axis=0)
x_val_scaled= (x_val-val_mean)/val_std
layer2.score(x_val_scaled,y_val)
Out[16]:
0.967032967032967
In [17]:
plt.plot(x_train[:50,0], x_train[:50,1],'bo')
plt.plot(x_val[:50,0],x_val[:50,1],'ro')
plt.xlabel("feture1")
plt.ylabel("feture2")
plt.legend(['train set', 'val. set'])
plt.show()
In [18]:
plt.plot(x_train_scaled[:50,0], x_train_scaled[:50,1],'bo')
plt.plot(x_val_scaled[:50,0],x_val_scaled[:50,1],'ro')
plt.xlabel("feture1")
plt.ylabel("feture2")
plt.legend(['train set', 'val. set'])
plt.show()
#위 아래의 그래프가 다른이유는 각자의 표준편차와 평균을 이용했기 떄문이다.
In [19]:
x_val_scaled  = (x_val-train_mean)/train_std      #훈련 데이터로 전처리하기
plt.plot(x_train_scaled[:50,0], x_train_scaled[:50,1],'bo')
plt.plot(x_val_scaled[:50,0],x_val_scaled[:50,1],'ro')
plt.xlabel("feture1")
plt.ylabel("feture2")
plt.legend(['train set', 'val. set'])
plt.show()
In [20]:
layer2.score(x_val_scaled,y_val)           #검증 세트로 정확도 확인하기.
Out[20]:
0.967032967032967

 

반응형