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)
from sklearn.linear_model import SGDClassifier
sgd = SGDClassifier(loss='log', random_state=42) #손실함수 : 로지스틱 손실 함수
sgd.fit(x_train_all, y_train_all)
sgd.score(x_test, y_test)
0.8333333333333334
: SGDClassifier 클래스의 다른 매개변수들을 바꿔 모델의 성능을 높이는 것
from sklearn.linear_model import SGDClassifier
sgd = SGDClassifier(loss='hinge', random_state=42)
sgd.fit(x_train_all, y_train_all)
sgd.score(x_test, y_test)
0.9385964912280702 → 로지스틱 회귀로 만든 모델보다 성능이 좋아짐
#데이터 세트 준비
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)
#검증 세트 분할 -> 앞서 전체 데이터 세트를 훈련 세트:테스트 세트=8:2로 나누고, 훈련 세트를 훈련세트:검증 세트=8:2로 다시 나눔
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 → 455개의 훈련 세트가 훈련 세트(x_train) 364개, 검증 세트 (x_val) 91개로 나뉨
from sklearn.linear_model import SGDClassifier
sgd = SGDClassifier(loss='log', random_state=42)
sgd.fit(x_train, y_train)
sgd.score(x_val, y_val)
0.6923076923076923
#박스 플롯으로 두 특성의 스케일 확인하기
print(cancer.feature_names[[2,3]])
plt.boxplot(x_train[:,2:4])
plt.xlabel('feature')
plt.ylabel('value')
plt.show()
⇒ mean perimeter는 주로 100~200사이에, .mean area는 주로 200~2000사이에 값들이 집중되어 있음
class SingleLayer:
def __init__(self,learning_rate=0.1):
#1.__init__() 메서드 작성
self.w = None
self.b = None
self.losses=[] #손실함수의 결과값을 저장할 리스트. 샘플마다 손실 함수를 계산하고 그 결괏값을 모두 더해 샘플 개수로 나눈 평균값을 저장
self.w_history =[] #가중치를 기록할 변수
self.lr = learning_rate #학습률 파라미터 -> 가중치의 업데이트 양을 조절 => 적절한 학습률을 이용해 손실 함수의 표면을 천천히 이동하며 전역 최솟값을 찾음
#2.정방향 계산
def forpass(self,x):
z = np.sum(x*self.w)+self.b
return z
#3.역방향 계산
def backprop(self,x,err):
w_grad = x*err
b_grad = 1*err
return w_grad, b_grad
def add_bias(self,x):
return np.c_[np.ones((x.shpae[0],1)),x] #행렬의 맨 앞에 1로 채워진 열 벡터를 추가
#5.activation()메서드 구현;시그모이드 계산
def activation(self,z):
a = 1/(1+np.exp(-z))
return a
#6.훈련을 위한 fit()메서드 구현
def fit(self, x, y, epochs=100):
self.w = np.ones(x.shape[1])
self.b = 0
self.w_history.append(self.w.copy()) #w_history에 가중치 기록
#넘파이 배열을 리스트에 추가하면 실제 값 복사가 아닌 배열 참조 -> 가중치 변수 self.w의 값이 바뀔 떄마다 그 값을 복사해 w_history에 추가해야 함
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) #활성화 함수 적용
err = -(y[i]-a) #오차 계산
w_grad, b_grad = self.backprop(x[i],err) #역방향 계산
self.w -= self.lr*w_grad #학습률을 적용해 가중치 업데이트
self.b -= b_grad #절편 업데이트
#가중치 기록
self.w_history.append(self.w.copy())
#안전한 로그 계산을 위해 클리핑한 후 손실을 누적
a = np.clip(a, 1e-10, 1-1e-10) #np.clip() : 주어진 범위 밖의 값을 범위 양 끝의 값으로 잘라냄
loss +=-(y[i]*np.log(a)+(1-y[i])*np.log(1-a))
self.losses.append(loss/len(y)) #에포크마다 평균 손실 저장
#7.에측하는 메서드 구현
def predict(self,x):
z = [self.forpass(x_i) for x_i in x]
return np.array(z) > 0
#8.정확도 계산을 위한 score()메서드 구현
def score(self,x,y):
return np.mean(self.predict(x)==y)
#모델 훈련하고 평가하기
layer1 = SingleLayer()
layer1.fit(x_train, y_train)
layer1.score(x_val,y_val)
0.9120879120879121 → 정확도 약 91%
#가중치 확인하기
w2=[]
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]')
plt.show()
mean perimeter에 비해 mean area의 스케일이 커 w3 값이 학습 과정에서 큰 폭으로 흔들리며 변화, w2는 0부터 시작하여 조금씩 최적값에 가까워짐
⇒ w3에 대한 그레디언트가 크기 때문에 w3 축을 따라 가중치가 크게 요동치고 있다
: 가중치의 최적값에 도달하는 동안 w3 값이 크게 요동치므로 모델이 불안정하게 수렴
→ 스케일 조정!
#1.넘파이로 표준화 구현하기
train_mean = np.mean(x_train,axis=0)
train_std = np.std(x_train,axis=0) #mean()과 srd()함수의 axis 매개변수를 0으로 지정하면 2차원 배열의 열을 기준으로 통계치를 계산해 하나의 행 벡터로 반환
x_train_scaled=(x_train-train_mean)/train_std #훈련 세트 x_train에서 평균을 빼고 표준편차로 나눔
#2.모델 훈련하기
layer2=SingleLayer()
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')
plt.xlabel('w[2]')
plt.ylabel('w[3]')
plt.show()
#성능 평가
layer2.score(x_val,y_val)
0.37362637362637363 → 정확도 약 37%
⇒ 모델은 훈련 세트와 검증 세트의 스케일이 비슷할 것으로 기대했으나, 검증 세트의 스케일은 바꾸지 않아 성능이 좋지 않음 → 검증 세트도 표준화 전처리
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)
0.967032967032967 → 정확도 약 97%
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('feature1')
plt.ylabel('feature2')
plt.legend(['train set','val.set'])
plt.show()
w_grad+=alpha*np.sign(w) #alpha:규제 하이퍼파라미터
w_grad+=alpha*w