0Pricing
Pandas & NumPy Academy · 강의

선형 시스템 풀기

np.linalg.solve를 사용해 Ax=b를 풀고, 수치적 안정성 측면에서 단순한 역행렬 곱셈 방식과 비교합니다.

선형 시스템 풀기은(는) CoddyKit의 무료 Pandas & NumPy Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Pandas & NumPy Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Pandas & NumPy Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

What Is a Linear System?

A system of linear equations can be written in matrix form as Ax = b, where A is the coefficient matrix, x is the vector of unknowns, and b is the right-hand side vector. For example, two equations with two unknowns can be expressed as a 2×2 matrix equation. Solving the system means finding x such that A multiplied by x equals b. NumPy's np.linalg.solve(A, b) handles this efficiently and accurately.

import numpy as np

# System: 2x + y = 5
#         x + 3y = 10
A = np.array([[2.0, 1.0],
              [1.0, 3.0]])
b = np.array([5.0, 10.0])

x = np.linalg.solve(A, b)
print('Solution x:', x)
print('Verify A @ x == b:', np.allclose(A @ x, b))

Why Not Use the Inverse?

Mathematically, the solution to Ax = b is x = A⁻¹b. But computing np.linalg.inv(A) @ b is slower and less accurate than calling np.linalg.solve(A, b) directly. solve() uses LU decomposition internally, which requires fewer operations and accumulates less floating-point error. The rule of thumb: if you are solving for x in Ax=b, always use solve, never inv.

import numpy as np

A = np.random.rand(100, 100)
b = np.random.rand(100)

# Avoid this pattern
x_inv = np.linalg.inv(A) @ b

# Prefer this
x_solve = np.linalg.solve(A, b)

print('Max difference:', np.max(np.abs(x_inv - x_solve)))
# solve is faster AND more accurate

Requirements for solve()

np.linalg.solve(A, b) requires that A is square and non-singular. If A is singular (determinant is zero), the function raises np.linalg.LinAlgError: Singular matrix. A is singular when rows are linearly dependent — for example, one equation is just a multiple of another. Always check that your system is well-posed before calling solve. If A is not square, use np.linalg.lstsq() instead.

import numpy as np

# Well-posed system
A = np.array([[1.0, 2.0], [3.0, 4.0]])
b = np.array([5.0, 6.0])
print('Solution:', np.linalg.solve(A, b))

# Singular system (row 2 = 3 * row 1)
A_singular = np.array([[1.0, 2.0], [3.0, 6.0]])
try:
    np.linalg.solve(A_singular, b)
except np.linalg.LinAlgError as e:
    print('Error:', e)

LU Decomposition Under the Hood

np.linalg.solve internally applies LU decomposition: it factors A into a lower-triangular matrix L and an upper-triangular matrix U such that A = LU. Solving then becomes two simpler triangular solves (forward and back substitution), which are far more efficient than Gaussian elimination repeated from scratch. This is the same algorithm used by MATLAB, Fortran LAPACK, and virtually every scientific computing library.

import numpy as np
from scipy import linalg

A = np.array([[2.0, 1.0, -1.0],
              [-3.0, -1.0, 2.0],
              [-2.0, 1.0, 2.0]])
b = np.array([8.0, -11.0, -3.0])

# NumPy solve (uses LAPACK dgesv under the hood)
x = np.linalg.solve(A, b)
print('Solution:', x)
print('Check:', np.allclose(A @ x, b))

Solving Multiple Right-Hand Sides

np.linalg.solve(A, B) can handle a matrix B with multiple columns, solving for each column simultaneously. If B has shape (n, k), the result X has shape (n, k), where each column of X is the solution for the corresponding column of B. This is useful in physics simulations and machine learning where you need to solve the same coefficient matrix with many different right-hand sides.

import numpy as np

A = np.array([[3.0, 1.0],
              [1.0, 2.0]])

# Three right-hand side vectors at once
B = np.array([[9.0, 3.0, 6.0],
              [8.0, 4.0, 2.0]])

X = np.linalg.solve(A, B)
print('Solutions X shape:', X.shape)  # (2, 3)
print('Verify A @ X == B:', np.allclose(A @ X, B))

Least Squares with np.linalg.lstsq()

When the system is over-determined (more equations than unknowns), no exact solution exists. np.linalg.lstsq(A, b) finds the x that minimises the sum of squared residuals ||Ax - b||² — this is exactly what linear regression computes. It returns the solution x, residuals, rank, and singular values. The rcond=None argument suppresses a deprecation warning and sets the cutoff for treating singular values as zero.

import numpy as np

# Over-determined: 4 equations, 2 unknowns
A = np.array([[1.0, 1.0],
              [1.0, 2.0],
              [1.0, 3.0],
              [1.0, 4.0]])
b = np.array([2.0, 2.5, 3.5, 4.5])

x, residuals, rank, sv = np.linalg.lstsq(A, b, rcond=None)
print('Least-squares solution:', x)
print('Residuals:', residuals)
print('Rank:', rank)

Applying solve() in Linear Regression

The normal equations of ordinary least squares are A.T @ A @ x = A.T @ b, which is a square system solvable with np.linalg.solve. This gives the same result as lstsq when A.T @ A is well-conditioned. In practice, lstsq is preferred because it uses SVD and is stable even when columns are nearly collinear, but the normal equations help you understand the math behind regression.

import numpy as np

np.random.seed(0)
X = np.column_stack([np.ones(50), np.random.rand(50) * 10])
y = 3.0 + 2.0 * X[:, 1] + np.random.randn(50)

# Solve normal equations
coeffs = np.linalg.solve(X.T @ X, X.T @ y)
print('Intercept:', round(coeffs[0], 3))
print('Slope:', round(coeffs[1], 3))

Checking the Condition Number

Before solving, it is good practice to check the condition number with np.linalg.cond(A). A condition number close to 1 means the system is well-conditioned and the solution is reliable. A very large condition number (e.g., 1e12) means the matrix is nearly singular: small changes in b produce huge changes in x, and the solution may be numerically unreliable. In that case, consider regularisation (ridge regression) or using a more robust solver.

import numpy as np

# Well-conditioned
A1 = np.array([[2.0, 1.0], [1.0, 3.0]])
print('Condition number (well):', np.linalg.cond(A1))

# Ill-conditioned (nearly linearly dependent rows)
A2 = np.array([[1.0, 1.0], [1.0, 1.0001]])
print('Condition number (ill):', np.linalg.cond(A2))

Sparse Systems: When Not to Use linalg

For very large systems where A has mostly zero entries (a sparse matrix), storing the full matrix wastes memory and using dense np.linalg.solve is slow. SciPy provides scipy.sparse and scipy.sparse.linalg.spsolve that exploit sparsity for huge systems like finite element models or network problems. Understanding when your matrix is sparse is an important performance consideration in scientific computing.

import numpy as np

# Dense solve is fine for small-to-medium systems
A = np.random.rand(200, 200)
b = np.random.rand(200)
x = np.linalg.solve(A, b)
print('Dense solve OK, solution shape:', x.shape)

# For large sparse systems, use scipy.sparse.linalg.spsolve instead
# from scipy.sparse.linalg import spsolve
# x = spsolve(A_sparse, b)

Interpreting the Solution Vector

Once you have the solution vector x from np.linalg.solve(A, b), always verify it by computing A @ x and checking it against b using np.allclose(A @ x, b). In floating-point arithmetic, you rarely get exact equality — allclose checks within a small tolerance. The residual ||Ax - b|| should be near machine epsilon times the scale of the problem for a well-conditioned system.

import numpy as np

A = np.array([[4.0, 3.0, 2.0],
              [1.0, 5.0, 3.0],
              [2.0, 1.0, 6.0]])
b = np.array([10.0, 20.0, 15.0])

x = np.linalg.solve(A, b)
print('Solution:', np.round(x, 4))

# Residual check
residual = np.linalg.norm(A @ x - b)
print('Residual ||Ax - b||:', residual)
print('Valid:', np.allclose(A @ x, b))

Solving in Machine Learning Pipelines

Linear solvers appear throughout machine learning: linear regression solves the normal equations, Gaussian processes solve kernel matrix systems, and Kalman filters solve state-update equations. By understanding np.linalg.solve and lstsq, you can implement these algorithms from scratch and debug them when results are unexpected. NumPy's linalg module gives you the same numerical tools used in production scientific libraries.

import numpy as np

# Ridge regression: (X.T @ X + lambda * I) @ w = X.T @ y
np.random.seed(42)
X = np.random.rand(100, 5)
y = np.random.rand(100)
lambda_reg = 0.1
n_features = X.shape[1]

A = X.T @ X + lambda_reg * np.eye(n_features)
bvec = X.T @ y
w = np.linalg.solve(A, bvec)
print('Ridge regression weights:', np.round(w, 4))

Quick Check

Test your understanding of Data Analysis concepts from this lesson.

Lesson Recap

In this lesson you learned: np.linalg.solve(A, b) efficiently solves square linear systems using LU decomposition, np.linalg.lstsq() finds the least-squares solution for over-determined systems, and the condition number measures numerical stability — large values warn of near-singular matrices. Next up we explore eigenvalues and the SVD, which underpin PCA and many ML algorithms.

자주 묻는 질문

“선형 시스템 풀기” 강의는 무료인가요?

네 — “선형 시스템 풀기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Pandas & NumPy Academy 강의 전체를 잠금 해제할 수 있습니다. Pandas & NumPy Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“선형 시스템 풀기”에서 뭘 배우나요?

np.linalg.solve를 사용해 Ax=b를 풀고, 수치적 안정성 측면에서 단순한 역행렬 곱셈 방식과 비교합니다. 브라우저에서 직접 실행하는 실습 코드로 Pandas & NumPy Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Pandas & NumPy Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Pandas & NumPy Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“선형 시스템 풀기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Pandas & NumPy Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Pandas & NumPy Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. np.matmul과 @를 사용한 행렬 곱셈
  2. 행렬식, 역행렬, 전치행렬
  3. 선형 시스템 풀기
  4. 고윳값과 SVD 개요
← Pandas & NumPy Academy(으)로 돌아가기