Skip to content

Latest commit

 

History

History
385 lines (252 loc) · 12.7 KB

4장.md

File metadata and controls

385 lines (252 loc) · 12.7 KB

4장

<04-01 로지스틱 회귀>

분류 = 임의의 숫자를 예측하는 것이 아니라 정해진 종류 중에 한가지를 골라주는 문제 (이진분류 = 2가지 중 하나, 다진분류 = 여러가지 중 하나)

로지스틱 회귀는 이름은 회귀이지만 분류 알고리즘임

이진 분류

  • True(1) 양성클래스
  • False(0 or -1) 음성 클래스

퍼셉트론 알고리즘

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-19__7.52.21.png

w1x1 + w2x2 + b = z

(x1, x2 두가지 특성을 이용한다)

여러개의 특성을 표현하는 법

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-19__7.57.38.png

w 와 x 를 따로 표현하지 않고 w1x1, w2x2로 같이 표현하고 뉴런에는 덧셈만 표현해준다

특성의 수가 많아지면 ∑ 이용해서 표현

아달린 알고리즘

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-19__8.11.20.png

퍼셉트론과 유사한데 역방향 계산이 일어나는 부분이 다르다

역방향 계산이 퍼셉트론은 y_hat, 아달린은 z에서 시작함

로지스틱 회귀

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-19__8.10.31.png

z 값을 활성화 함수로 시그모이드 함수를 사용하여 a 값으로 변환한다. (a는 0~1 값)

이 값이 >0.5면 양성, ≤ 0.5면 음성 클래스로 판단한다

이 판단을 위해 활성화 함수를 사용하는 것


<04-02 시그모이드 함수>

시그모이드 함수 = 로지스틱 함수

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-19__8.43.18.png

z값이 -∞에서 +∞로 변할때 0에서 1 사이 값만 가짐

로지스틱 회귀 정리

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-19__8.52.17.png

선형 방정식의 값을 뉴런에서 계산 후 임의의 숫자인 z값을 로지스틱 함수 (시그모이드 함수)를 이용해 0 부터 1 사이의 값으로 압축한 후 0.5보다 크면 양성, 0.5 이하면 음성 클래스라고 판단한다 ⇒ 이진분류를 수행한 것

최적의 w와 b를 찾기 위해 a 값을 기준으로 오차역전파를 수행하게 됨(역방향 계산)


<04-03 로지스틱 손실 함수와 경사 하강법>

분류의 정확도 : 정확하게 분류된 횟수, 비율

회귀의 경우에는 제곱오차를 낮추는 것이 목표, 미분가능하기때문에 손실함수 사용해 경사하강법을 구현

하지만 분류는 정확도가 미분불가 → 다른 손실함수 사용 = 로지스틱 손실 함수.

$$L = -(ylog(a) + (1-y)(log(1-a))$$

                              (y : 타깃값, a : 활성화 함수의 출력)

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-20__4.44.06.png

y가 1일 경우 a는 1에 가까워야 하고, y가 0일 경우 a는 0에 가까워야 함

로지스틱 손실 함수 미분하기

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-20__4.58.41.png

미분의 연쇄 법칙(Chain Rule)

합성함수의 도함수

$y=f(u), u=g(x)$ $y=f(g(x))$

$ay/ax=ay/au*au/ax$

y를 x에 대해 미분할 때 함수가 복잡하다면 f와 g를 각각 나눠서 미분할 수 있다

y를 먼저 u에 대해 미분하고, u를 x에 대해 미분해서 각각 곱하면 됨

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__5.18.18.png

연쇄법칙을 뉴런그림에 나타낸 것

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__5.47.58.png

로지스틱 손실함수를 w로 미분하는 3단계

1. 로지스틱 손실 함수L을 a에 대해 미분하기

$L = -(ylog(a) + (1-y)(log(1-a))$

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__5.49.48.png

2. a를 z에 대해 미분하기

a = 1 / (1+e^-z)

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__6.30.00.png

3. z를 w에 대해 미분하기

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__6.31.50.png

전체 미분 과정 정리

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__6.33.20.png

절편에 대한 도함수

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__6.35.29.png


<04-04 분류용 데이터셋 준비>

유방암 데이터 셋 사용

  • 양성 종양(정상종양) → 음성 샘플 0
  • 악성 종양 → 양성 샘플(해결 과제) 1
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
#bunch클래스로 예제 데이터셋을 반환해주고 있음
#데이터 속성에는 훈련데이터(특성값), 타겟데이터에는 타겟값(레이블값)이 들어감
print(cancer.data.shape, cancer.target.shape)

(569, 30) (569,) // numpy 배열 형식 // 데이터 속성은 569개 샘플과 30개 특성, 타깃데이터는 1차원 배열

cancer.data[:3]
#cancer데이터의 처음 데이터부터 3개 출력 0,1,2 행
#상자수염그래프 그려서 데이터 파악하기
plt.boxplot(cancer.data)
plt.xlabel('feature')
plt.ylabel('value')
plt.show()

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-21__7.26.49.png

np.unique(cancer.target, return_counts=True)
#unique함수 -> 배열 값 중 고유한 값만 출력해줌
#return_counts매개변수의 기본값은 원래 false임 true로 바꿔주면
0 대한 카운트, 1 대한 카운트 개수를 알려줌 

(array([0, 1]), array([212,357]))

x = cancer.data
y = cancer.target

<04-05 로지스틱 회귀 위한 뉴런 만들기>

훈련 데이터 세트를 테스트 세트와 훈련 세트로 나눈다.

테스트 세트는 사용안하고 보관, 훈련 세트로 훈련 후 평가 때 테스트 세트 사용. 이 결과 좋으면 →일반화 성능 good

+) 훈련 세트가 테스트 세트보다 더 많아야 하고 양성, 음성 클래스는 두 세트에 골고루 섞여야 함

훈련 세트와 테스트 세트 나누기

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, stratify=y, test_size=0.2, random_state=42)
#20%정도 테스트 세트로 지정한 것. 
#stratify 매개변수는 양성,음성 클래스 비율을 동일하게 유지하는 옵션임
 print(x_train.shape, x_text.shape)

(455, 30) (114, 30)

np.unique(y_train, return_counts=True)

(array([0,1]), array([170, 285])

로지스틱 뉴런 구현하기

class LogisticNeuron:

	def __init__(self):
		self.w = None
		self.b = None

#정방향 계산
	def forpass(self, x):
		z = np.sum(x * self.w) + self.b   #직선방정식       
		return z

#역방향 계산
	def backprop(self, x, err):
		w_grad = x * err       #가중치에 대한 그래디언트 계산
		b_grad = 1 * err       #절편에 대한 그래디언트 계산
		return w_grad, b_grad
a = np.array([1,2,3])
b = np.array([3,4,5])
print(a+b)
print(a*b)
np.sum(a*b)   #a*b 결과 배열을 모두 더함

array([4, 6, 8])

array([3, 8, 15])

26

나머지 메서드 구현하기

def fit(self, x, y, epochs=100):
	self.w = np.ones(x.shape[1])       #가중치 초기화
	self.b = 0                         #절편 초기화
	for i in range(epochs):            #epochs만큼 반복
		for x_i, y_i in zip(x, y):       #모든 샘플에 대해 반복
			z = self.forpass(x_i)          #정방향 계산
			a = self.activation(z)         #활성화 함수 적용
			err = -(y_i - a)               #오차 계산
			w_grad, b_grad = self.backprop(x_i, err)    #역방향 계산
			self.w -= w_grad               #가중치 업데이트
			self.b -= b_grad               #절편 업데이트
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]     #선형 함수 적용
	a = self.activation(np.array(z))         #활성화 함수 적용
	return a > 0.5                           #계단 함수 적용
neuron = LogisticNeuron()
neuron.fit(x_train, y_train)

np.mean(neuron.predict(x_test) == y_test)

0.8245614035087719 →82%정도 맞춤


<04-06 로지스틱 회귀 뉴런으로 단일층 신경망 만들기>

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-22__4.27.50.png

은닉층이 여러개면 딥러닝, 심층 신경망

은닉층 없으면 단일층 신경망 = 로지스틱 회귀 모델

여러가지 경사 하강법

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-22__5.38.32.png

코드

def __init__(self):
	self.w = None
	self.b = None
	self.losses = []  #손실값 저장하는 리스트 만들기
	'''

def fit(self, x, y, epochs=100):
	self.w = np.ones(x.shape[1])          #가중치 초기화
	self.b = 0                          #절편 초기화
	for i in range(epochs):             #epochs만큼
			loss = 0
			indexs = np.random.permutation(np.arrange(len(x)))  #인덱스 섞기
			for i in index:                 #모든 샘플에 대해 반복
				z = self.forpass(x[i])        #정방향 계산
				a = self.activation(z)        #활성화 함수 적용
				err -(y[i] - a)               #오차 계산
				w_grad, b_grad = self.backprop(x[i], err)  #역방향 계산
				self.w -= w_grad              #가중치 업데이트
				self.b -= b_grad              #절편 업데이트
				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))

core()메서드 추가하고 단일층 신경망 훈련

def predict(self, x):
	z = [self.forpass(x_i) for x_i in x]  #정방향 계산   (활성화 함수 뺌)
	return np.array(z)>0                  #계단 함수 적용

def score(self, x, y):
	return np.mean(self.predict(x) == y
layer = SingleLayer()
layer.fit(x_train, y_train)
layer.score(x_test, y_test)

0.92983456

plt.plot(layer.losses)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

4%E1%84%8C%E1%85%A1%E1%86%BC%204c4f00b44979443aa999b3e6209c6795/_2021-01-22__5.51.10.png

손실값이 감소하는 모습을 볼 수 있음 → 훈련이 잘 됨


<04-07 사이킷런으로 로지스틱 회귀 수행>

sgd = SGDClassifier(loss='log', max_iter=100, tol=1e-3, random_state=42)
#경사하강법 분류기 여러 문제를 경사하강법으로 풀어줌
# loss='log'->로지스틱손실함수 지정한 것
sgd.fit(x_train, y_train)
sgd.score(x_test, y_test)

0.833333333334

sgd.predict(x_test[0:10])

array([0, 1, 0, 0, 0, 0, 1, 0, 0, 0])