Gradient Descent(경사 하강법):
데이터 과학을 하다보면, 최적화 문제들을 만나게 된다.
최적화문제 - 특정 상황에서 가장 적합한 모델을 찾는 문제
(예) 모델의 오류(error)를 '최소화', likelihood(우도) 를 '최대화'
타겟 함수를 최소(혹은 최대)로 만들어 주는 파라미터를 찾는 문제
곡선의 접선을 찾고, 접선의 기울기 방향으로 점을 이동시켜 나가면서
최소(최대)값을 찾는다.
import random
import matplotlib.pyplot as plt
def f(x):
return x ** 2
if __name__ == '__main__':
# 그래프를 그릴 x값의 범위 (-3.0, -2.9, ..., 2.9, 3.0)
xs = [x / 10 for x in range(-30, 31)]
# 그래프를 그릴 y 좌표들
ys = [f(x) for x in xs]
plt.plot(xs, ys)
plt.show()
# x**2 그래프에서 x=-1에서의 접선을 그리기 위해서
# 직선의 방정식 y = ax + b에서 기울기 a와 y절편 b를 찾아야 한다.
# 기울기 a는 x**2의 미분 값으로 찾는다.
# y절면은 (x1, f(x1))을 지나는 직선임을 이용해서 찾는다.
a = f_derivative(-1) # x=-1에서 접선의 기울기
x1, y1 = -1, f(-1) # x=-1에서 곡선과 접선이 만나는 점의 좌표
tangents = [tangent(x, a, x1, y1) for x in xs]
plt.plot(xs, ys)
plt.plot(xs, tangents)
plt.show()
def difference_quotient(f, x, h):
"""함수 f의 도함수의 근사값 """
return (f(x+h) - f(x)) / h
# 도함수의 근사값을 사용해서 x=-1에서 기울기를 찾음
# h값이 0에 가까울수록 더 정확한 접선의 기울기가 된다.
h= 1
a2 = difference_quotient(f, x=-1, h=h)
tangents2 = [tangent(x, a2, x1, y1) for x in xs]
plt.plot(xs, ys)
plt.plot(xs, tangents, label = 'actual')
plt.plot(xs, tangents2, label=f'estimate: h={h}')
plt.axhline(y=0, color='black') # y=0인 수직보조선
plt.axvline(x=0, color='black') # x=0인 수직보조선
plt.ylim(-2)
plt.legend()
plt.show()
# 실제 기울기(f_derivative)와 기울기 근사값 비교
xs = [x/10 for x in range(-10, 11)]
actuals = [f_derivative(x) for x in xs]
estimates_1 = [difference_quotient(f, x, h=1) for x in xs]
estimates_2 = [difference_quotient(f, x, h=0.1) for x in xs]
plt.scatter(xs, actuals, label='actual', marker='x')
plt.scatter(xs, estimates_1, label='h=1', marker='+')
plt.scatter(xs, estimates_2, label='h=0.1', marker='o')
plt.show()
def move(x, direction, step=-0.1):
"""좌표를 새로운 X좌표로 이동.
direction: 접선의 기울기.
step>0인 경우는 접선과 같은 방향으로 이동 -> 최댓값 찾기
step<0인 경우는 접선과 반대 방향으로 이동 -> 최솟값 찾기"""
return x + step * direction
# 경사 하강법
xs = [x / 10 for x in range(-30, 31)] # [-3, -2.9, ..., 2.9, 3.0]
ys = [f(x) for x in xs] # y = x**2 그래프의 y값들
init_x = -2 # 최솟값을 찾기 위해 시작할 x 좌표
for _ in range(5):
# x = init_x에서의 접선의 기울기
gradient = difference_quotient(f, init_x, h=0.01)
# 접선을 그래프로 그리기 위해서
tangent_estimates = [tangent(x, gradient, init_x, f(init_x)) for x in xs]
plt.plot(xs, tangent_estimates, label=f'x={init_x}')
# x 좌표를 새로운 좌표로 이동
init_x = move(init_x, gradient, step=-0.1)
# 기울기가 (-)이면 step이 양의 방향으로, (+)이면 음의 방향으로 이동
plt.plot(xs, ys)
plt.legend()
plt.ylim(bottom=-1)
plt.show()
# 임의의 점에서 시작하여 y=x**2의 최솟값을 찾음
random.seed(1128)
init_x = random.randint(-10, 10) # 시작 값
print(f'init_x = {init_x}')
tolerance = 0.0000001
# 두 개의 x좌표 사이의 거리가 tolerance 이하이면 반복문 종료
count = 0
while True: # 반복문
count += 1
# x좌표에서의 접선의 기울기를 계산
gradient = difference_quotient(f, init_x, h=0.001)
# 찾은 기울기를 사용해서 x좌표를 이동
next_x = move(init_x, gradient, step=-0.9) # 최소값은 (-), 최대값은 (+)
print(f'{count}: x = {next_x}')
if abs(next_x-init_x) < tolerance:
# 이동 전과 이동 후의 x값의 차이가 tolerance 미만이면 반복문 종료
break
else:
# 이동한 점이 다음 반복에서는 시작점이 되어야 한다
init_x = next_x
init_x = -8
1: x = 6.399100000007081
2: x = -5.120180000008816
3: x = 4.095244000010297
4: x = -3.277095200010109
5: x = 2.6207761600074946
6: x = -2.097520928005629
7: x = 1.6771167424043902
8: x = -1.3425933939231456
9: x = 1.0731747151384434
10: x = -0.8594397721107079
11: x = 0.6866518176885732
12: x = -0.5502214541508716
13: x = 0.4392771633207242
14: x = -0.35232173065657246
15: x = 0.2809573845252574
16: x = -0.22566590762020644
17: x = 0.17963272609616715
18: x = -0.1446061808769305
19: x = 0.11478494470154571
20: x = -0.09272795576123777
21: x = 0.07328236460899026
22: x = -0.05952589168719294
23: x = 0.046720713349754334
24: x = -0.038276570679803626
25: x = 0.029721256543842958
26: x = -0.024677005235074358
27: x = 0.01884160418805951
28: x = -0.015973283350447648
29: x = 0.011878626680358138
30: x = -0.010402901344286519
31: x = 0.007422321075429221
32: x = -0.006837856860343387
33: x = 0.004570285488274713
34: x = -0.00455622839061977
35: x = 0.002744982712495815
36: x = -0.0030959861699966515
37: x = 0.0015767889359973222
38: x = -0.002161431148797858
39: x = 0.0008291449190382869
40: x = -0.0015633159352306295
41: x = 0.00035065274818450406
42: x = -0.0011805221985476033
43: x = 4.441775883808237e-05
44: x = -0.0009355342070704658
45: x = -0.00015157263434362731
46: x = -0.0007787418925250982
47: x = -0.00027700648597992156
48: x = -0.0006783948112160628
49: x = -0.00035728415102714975
50: x = -0.0006141726791782802
51: x = -0.00040866185665737584
52: x = -0.0005730705146740993
53: x = -0.0004415435882607206
54: x = -0.0005467651293914235
55: x = -0.0004625878964868612
56: x = -0.0005299296828105111
57: x = -0.0004760562537515911
58: x = -0.0005191549969987271
59: x = -0.0004846760024010183
60: x = -0.0005122591980791854
61: x = -0.0004901926415366517
62: x = -0.0005078458867706787
63: x = -0.000493723290583457
64: x = -0.0005050213675332345
65: x = -0.0004959829059734125
66: x = -0.00050321367522127
67: x = -0.000497429059822984
68: x = -0.0005020567521416128
69: x = -0.0004983545982867097
70: x = -0.0005013163213706323
71: x = -0.0004989469429034942
72: x = -0.0005008424456772046
73: x = -0.0004993260434582364
74: x = -0.000500539165233411
75: x = -0.0004995686678132713
76: x = -0.000500345065749383
77: x = -0.0004997239474004937
78: x = -0.000500220842079605
79: x = -0.000499823326336316
80: x = -0.0005001413389309473
81: x = -0.0004998869288552422
82: x = -0.0005000904569158062
83: x = -0.000499927634467355
84: x = -0.000500057892426116
85: x = -0.0004999536860591072
86: x = -0.0005000370511527142
'Python > Python기초' 카테고리의 다른 글
Python 53_ 선형 회귀 (0) | 2020.02.03 |
---|---|
Python 52_ 편미분을 이용한 경사하강법 (0) | 2020.01.31 |
Python 50_ 가설과 통계적 추론 (0) | 2020.01.29 |
Python 49_spy.stats 모듈에서 PDF와 CDF (0) | 2020.01.28 |
Python 48_중심 극한 정리, 베르누이 확률 변수, 베르누이 확률 질량 함수, 이항 확률 변수 (0) | 2020.01.24 |