다변수 Newton–Raphson 방법
재료공학도를 위한 수치해석
- 1. 학습 목표
- 2. 비선형 연립방정식
- 3. Jacobian 행렬
- 4. 다변수 Newton–Raphson 방법
- 5. 손으로 계산하는 첫 번째 반복
- 6. Python 구현
- 7. 세 변수로 확장
- 8. 수렴과 주의 사항
- 9. 경사하강법과의 차이
- 10. 정리
- 11. 연습 문제
1. 학습 목표
이번 강의가 끝나면 다음을 할 수 있어야 한다.
- 비선형 연립방정식을 벡터 함수 $\boldsymbol F(\boldsymbol x)=\boldsymbol0$으로 나타낼 수 있다.
- Jacobian 행렬을 편미분으로 구성할 수 있다.
- 다변수 Newton–Raphson 방법의 선형 시스템을 유도할 수 있다.
- 역행렬을 직접 계산하지 않고 갱신량을 구할 수 있다.
- Python으로 다변수 비선형 연립방정식을 풀 수 있다.
- 경사하강법과 다변수 Newton–Raphson 방법의 목적을 구분할 수 있다.
2. 비선형 연립방정식
두 변수 $x,y$에 관한 두 방정식을 생각해보자.
\[\begin{aligned} x^2+y^2&=5,\\ x-y&=1. \end{aligned}\]모든 항을 왼쪽으로 옮기면
\[f_1(x,y)=x^2+y^2-5=0\] \[f_2(x,y)=x-y-1=0\]이다. 이를 하나의 벡터 함수로 나타내면
\[\boldsymbol F(\boldsymbol x) = \begin{bmatrix} f_1(x,y)\\ f_2(x,y) \end{bmatrix} = \begin{bmatrix} x^2+y^2-5\\ x-y-1 \end{bmatrix}, \qquad [\boldsymbol x]= \begin{bmatrix} x\\y \end{bmatrix}.\]우리가 찾는 해는
\[\boldsymbol F(\boldsymbol x)=\boldsymbol0\]을 만족한다. 각 방정식은 평면에서 하나의 곡선을 나타내며, 두 곡선의 교점이 연립방정식의 해이다. 이 예제에는 $(2,1)$과 $(-1,-2)$라는 두 해가 있다.
3. Jacobian 행렬
Jacobian 행렬은 벡터 함수의 모든 일계 편미분을 모은 행렬이다.
\[J_{ij}=\frac{\partial f_i}{\partial x_j}\]두 변수의 경우
\[\boldsymbol J(x,y) = \begin{bmatrix} \dfrac{\partial f_1}{\partial x}& \dfrac{\partial f_1}{\partial y}\\[6pt] \dfrac{\partial f_2}{\partial x}& \dfrac{\partial f_2}{\partial y} \end{bmatrix}.\]앞의 예제에서는
\[\boldsymbol J(x,y) = \begin{bmatrix} 2x&2y\\ 1&-1 \end{bmatrix}.\]Jacobian의 $i$번째 행은 함수 $f_i$의 기울기 벡터를 전치한 것이다.
4. 다변수 Newton–Raphson 방법
4.1. 선형화
현재 근삿값을 $\boldsymbol x^{(k)}$, 작은 변화량을 $\Delta\boldsymbol x^{(k)}$라고 하자. 벡터 함수의 1차 Taylor 근사는
\[\boldsymbol F \left( \boldsymbol x^{(k)}+\Delta\boldsymbol x^{(k)} \right) \approx \boldsymbol F\left(\boldsymbol x^{(k)}\right) + \boldsymbol J\left(\boldsymbol x^{(k)}\right) \Delta\boldsymbol x^{(k)}\]이다. 새로운 점에서 함수값이 영벡터가 되도록 왼쪽을 0으로 놓으면
\[\boxed{ \boldsymbol J\left(\boldsymbol x^{(k)}\right) \Delta\boldsymbol x^{(k)} = -\boldsymbol F\left(\boldsymbol x^{(k)}\right) }\]을 얻는다. 이 선형 연립방정식을 풀어 변화량을 구한 다음
\[\boxed{ \boldsymbol x^{(k+1)} = \boldsymbol x^{(k)}+\Delta\boldsymbol x^{(k)} }\]로 갱신한다.
$J^{-1}$을 명시적으로 계산하여 곱하기보다 가우스 소거법, LU 분해 또는 np.linalg.solve로 선형 시스템을 직접 푸는 것이 좋다.
4.2. 알고리듬
- 초기값 $\boldsymbol x^{(0)}$와 허용오차를 정한다.
- $\boldsymbol F(\boldsymbol x^{(k)})$와 $\boldsymbol J(\boldsymbol x^{(k)})$를 계산한다.
- $|\boldsymbol F(\boldsymbol x^{(k)})|_2$가 충분히 작으면 종료한다.
- $J\Delta\boldsymbol x=-F$를 풀어 변화량을 구한다.
- $\boldsymbol x^{(k+1)}=\boldsymbol x^{(k)}+\Delta\boldsymbol x$로 갱신한다.
- 최대 반복 횟수 안에서 2–5단계를 반복한다.
5. 손으로 계산하는 첫 번째 반복
초기값을
\[\boldsymbol x^{(0)} = \begin{bmatrix} 1.5\\0.5 \end{bmatrix}\]로 정하자. 함수 벡터와 Jacobian은
\[\boldsymbol F(1.5,0.5) = \begin{bmatrix} 1.5^2+0.5^2-5\\ 1.5-0.5-1 \end{bmatrix} = \begin{bmatrix} -2.5\\0 \end{bmatrix}\] \[\boldsymbol J(1.5,0.5) = \begin{bmatrix} 3&1\\ 1&-1 \end{bmatrix}\]이다. 따라서 변화량은
\[\begin{bmatrix} 3&1\\ 1&-1 \end{bmatrix} \begin{bmatrix} \Delta x\\ \Delta y \end{bmatrix} = \begin{bmatrix} 2.5\\0 \end{bmatrix}\]을 만족한다. 두 번째 식에서 $\Delta x=\Delta y$이고, 첫 번째 식에 대입하면
\[4\Delta x=2.5\]이므로
\[\Delta x=\Delta y=0.625\]이다. 다음 근삿값은
\[\boldsymbol x^{(1)} = \begin{bmatrix} 1.5\\0.5 \end{bmatrix} + \begin{bmatrix} 0.625\\0.625 \end{bmatrix} = \begin{bmatrix} 2.125\\1.125 \end{bmatrix}.\]6. Python 구현
import numpy as np
def multivariate_newton(func, jacobian, x0, tol=1e-10, max_iter=50):
x = np.asarray(x0, dtype=float).copy()
history = [x.copy()]
residual_history = [np.linalg.norm(func(x))]
for iteration in range(max_iter+1):
F = np.asarray(func(x), dtype=float)
if np.linalg.norm(F) <= tol:
return x, iteration, np.array(history), np.array(residual_history)
if iteration == max_iter:
break
J = np.asarray(jacobian(x), dtype=float)
try:
dx = np.linalg.solve(J, -F)
except np.linalg.LinAlgError as error:
raise np.linalg.LinAlgError(
"Jacobian이 특이하여 변화량을 계산할 수 없습니다."
) from error
x = x+dx
history.append(x.copy())
residual_history.append(np.linalg.norm(func(x)))
raise RuntimeError("최대 반복 횟수 안에 수렴하지 않았습니다.")
예제의 함수와 Jacobian을 정의한다.
def func(x):
x1, x2 = x
return np.array([
x1**2+x2**2-5,
x1-x2-1,
])
def jacobian(x):
x1, x2 = x
return np.array([
[2*x1, 2*x2],
[1.0, -1.0],
])
6.1. 두 근 구하기
초기값을 바꾸어 두 근을 구해보자.
for x0 in ([1.5, 0.5], [-0.5, -1.5]):
root, iterations, history, residuals = multivariate_newton(
func, jacobian, x0
)
print("initial:", x0)
print("root:", root)
print("iterations:", iterations)
print("residual norm:", np.linalg.norm(func(root)))
첫 번째 초기값에서는 $(2,1)$, 두 번째 초기값에서는 $(-1,-2)$에 수렴한다.
6.2. 수렴 기록 확인
import matplotlib.pyplot as plt
root, iterations, history, residuals = multivariate_newton(
func, jacobian, [1.5, 0.5]
)
plt.semilogy(range(len(residuals)), residuals, "o-")
plt.xlabel("iteration")
plt.ylabel(r"$\|\boldsymbol F(\boldsymbol x^{(k)})\|_2$")
plt.grid()
plt.show()
7. 세 변수로 확장
변수가 세 개이면 함수 벡터와 Jacobian도 세 개의 행과 열을 갖는다. 예를 들어
\[\boldsymbol F(x,y,z) = \begin{bmatrix} x^2+y+z-3\\ x+y^2+z-3\\ x+y+z^2-3 \end{bmatrix}\]이면
\[\boldsymbol J(x,y,z) = \begin{bmatrix} 2x&1&1\\ 1&2y&1\\ 1&1&2z \end{bmatrix}.\]$(1,1,1)$은 $F(1,1,1)=0$을 만족하는 해이다. 앞서 작성한 함수는 벡터의 차원에 관계없이 같은 방법으로 사용할 수 있다.
def func3(x):
x1, x2, x3 = x
return np.array([
x1**2+x2+x3-3,
x1+x2**2+x3-3,
x1+x2+x3**2-3,
])
def jacobian3(x):
x1, x2, x3 = x
return np.array([
[2*x1, 1.0, 1.0],
[1.0, 2*x2, 1.0],
[1.0, 1.0, 2*x3],
])
root, iterations, history, residuals = multivariate_newton(
func3, jacobian3, [0.8, 1.2, 0.9]
)
print(root)
8. 수렴과 주의 사항
단일변수 Newton–Raphson 방법과 마찬가지로 단순근에 충분히 가까운 초기값에서는 빠른 수렴을 기대할 수 있다. 다변수 문제의 단순근에서는 해에서의 Jacobian이 가역행렬이어야 한다.
- 초기값에 따라 서로 다른 근으로 수렴할 수 있다.
- Jacobian이 특이하거나 거의 특이하면 변화량을 구하기 어렵다.
- 초기값이 해에서 멀면 발산하거나 예상하지 않은 근으로 이동할 수 있다.
- 함수와 Jacobian의 행·열 순서를 서로 일치시켜야 한다.
- 잔차와 변화량을 확인하고 최대 반복 횟수를 설정해야 한다.
- 실제 문제에서는 변화량에 감쇠계수 $0<\lambda\leq1$을 곱하는 감쇠 Newton 방법을 사용하여 안정성을 높이기도 한다.
9. 경사하강법과의 차이
| 항목 | 경사하강법 | 다변수 Newton–Raphson 방법 |
|---|---|---|
| 목적 | 스칼라 함수 $f(\boldsymbol x)$의 최솟값 | 벡터 함수 $F(\boldsymbol x)=0$의 근 |
| 사용하는 미분 | 기울기 $\nabla f$ | Jacobian $J$ |
| 갱신량 | $-\alpha\nabla f$ | $J\Delta x=-F$의 해 |
| 선형 시스템 | 기본형에서는 필요하지 않음 | 매 반복마다 풀이 |
| 초기값 영향 | 수렴점과 속도에 영향 | 수렴하는 근과 성공 여부에 영향 |
10. 정리
- 비선형 연립방정식은 $F(x)=0$인 벡터 근 찾기 문제이다.
- Jacobian의 각 원소는 $J_{ij}=\partial f_i/\partial x_j$이다.
- 매 반복에서 $J\Delta x=-F$를 풀고 $x$에 $\Delta x$를 더한다.
- 역행렬을 직접 구하지 않고 선형 연립방정식을 푼다.
- 초기값과 Jacobian의 가역성이 수렴에 중요한 영향을 준다.
11. 연습 문제
-
다음 벡터 함수의 Jacobian을 구하라.
\[F(x,y)= \begin{bmatrix} x^2+y\\ x-y^2 \end{bmatrix}.\] -
예제의 $F(x,y)=[x^2+y^2-5,\ x-y-1]^T$에서 $F(2,1)$을 계산하라.
-
같은 예제에서 $J(2,1)$을 구하라.
-
초기값 $(1.5,0.5)$에서 풀어야 하는 선형 시스템의 우변 $-F$를 구하라.
-
변화량이 $\Delta x=(0.2,-0.1)^T$이고 현재점이 $x=(1,2)^T$이면 다음 근삿값을 구하라.
-
다음 코드의 빈칸을 채워 변화량을 구하라.
dx = np.linalg.solve(____, ____) -
Jacobian이 특이행렬이면 Newton 갱신이 어려운 이유를 한 문장으로 설명하라.
-
경사하강법과 다변수 Newton–Raphson 방법의 목적을 각각 적어라.