Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

数学基礎

Open In Colab

本講義は、深層学習(主にニューラルネットワーク)について解説します。ここでは、ニューラルネットワークの理解に必要な数学の基本知識をおさらいします。

1微分

1.1微分の概念

微分とは、結論から言うと、変数の微小な変化に対応する、関数の変化量を求めることです。

微分を用いると接線の傾きを計算することができます。このことから、微分が関数の最小化問題に有用なツールであることがわかります。

  • xxからhだけ離れた点𝑥+h𝑥+ℎを考え, 2点を通る直線の傾きを求めることができます。

a=f(x+h)f(x)(x+h)xa= \frac{f(x + h) - f(x)}{(x+h)-x}
  • 次にhhh0h \rightarrow 0のように小さくしていけば、直線の開始点と終了点の2点が1点に収束し、1点での接線として考えることができます。このように、平均変化率の極限値が存在するならば、微分可能であると言います。この式をffの導関数 (derivative)と呼び、f(x)f'(x)と書きます。関数f(x)f(x)の微分係数f(a)f'(a)はその曲線の点(a,f(a))(a,f(a))(接点)における接線の傾きです。

f(x)=limh0f(x+h)f(x)hf'(x)= \lim_{h \rightarrow 0} \frac{f(x + h) - f(x)}{h}
  • 導関数を求めることを微分(differentiation)するといいます。 記号の使い方として、f(x)f'(x)dfdx(x)\frac{df}{dx} (x) または ddxf(x) \frac{d}{dx}f (x)と書きます。

1.2Pythonによる微分計算

  • Pythonで分の近似計算関数を実装することができます

# 微分の近似計算の精度と計算の安定性のバランスをとるために、中心差分法といった関数の前後の点を使用して計算する方法を用いる
def numerical_diff(f,x):
    h = 1e-4 # 微小な変化量: 0.0001
    nd = (f(x+h) - f(x-h))/(2 * h)
    return nd
def function_1(x):
    return x**3 - 3*x**2 + x
print(numerical_diff(function_1, 4)) # 25.0 に近い値が期待される
25.000000010031442

PyTorchでは、微分を計算するために自動微分機能を利用することができます。

import torch

# 入力値をテンソルとして作成し、勾配計算を有効にする
x = torch.tensor(4.0, requires_grad=True)

# 関数の出力を計算
y = function_1(x)

# 勾配を計算
y.backward()

# 勾配を表示
print(x.grad.item())
25.0

1.3微分の公式

覚えておくと便利な微分の公式がありますので,以下に幾つか紹介していきます。

(c)=0(x)=1(cf(x))=cf(x)(xn)=nxn1(f(x)+g(x))=f(x)+g(x)(f(x)g(x))=f(x)g(x)+f(x)g(x)(f(g(x)))=df(u)dududx=f(g(x))g(x)\begin{split}\begin{align} \left( c\right) ^{'}&=0 \\ \left( x\right)^{'}&=1\\ \left( cf(x) \right)^{'} &= c f'(x) \\ \left( x^{n} \right)^{'} &=nx^{n-1} \\ \left( f(x) + g(x) \right) ^{'} &=f^{'}(x)+g^{'}(x) \\ \left( f(x) g(x) \right) ^{'} &= f^{'}(x)g(x) + f(x)g^{'}(x) \\ \left( f(g(x)) \right) ^{'} &= \frac{df(u)}{du}\frac{du}{dx} = f^{'}(g(x)) \cdot g^{'}(x) \\ \end{align}\end{split}

1.4合成関数の微分

𝑦=𝑓(𝑥)𝑦=𝑓(𝑥)𝑧=𝑔(𝑦)𝑧=𝑔(𝑦)の合成関数とは、𝑓𝑓を適用したあとに𝑔𝑔を適用する関数、すなわち 𝑧=𝑔(𝑓(𝑥))𝑧=𝑔(𝑓(𝑥))のことを指します。

合成関数の導関数がそれぞれの導関数の積で与えられる性質は連鎖律(chain rule)と言います。

ddxf(g(x))=df(u)dududx\frac{d}{dx} f(g(x)) = \frac{df(u)}{du}\frac{du}{dx}

1.5偏微分

機械学習において、多くの場合、複数の入力変数 𝑥1,𝑥2,,𝑥n𝑥_1,𝑥_2,…,𝑥_nを用いて𝑦𝑦を予測する多変数関数が扱われます。

偏微分とは、nn変数関数のある一つの変数以外のn1n-1個の変数の値を固定し、残りの1つの変数について関数を微分することです。

例えば、ある入力 𝑥n𝑥_nにのみ注目する偏微分は以下のように表します。

xnf(x1,x2,,xn)\frac{\partial}{\partial x_{n}} f(x_1, x_2, \dots, x_n)

微分を意味する記号が、𝑑𝑑から\partialに変わっています。こうすると、xn\frac{\partial}{\partial x_{n}}xnx_n以外を定数と考え、 xnx_nにのみ着目して微分を行うという意味となります。

Source
import numpy as np
import matplotlib.pyplot as plt
from sympy import symbols, diff
#plt.style.use('seaborn-poster') 

x, y = symbols('x y')

# 関数の定義
f = x**2 + y**2

# 偏微分の計算
partial_x = diff(f, x)
partial_y = diff(f, y)

print("Partial Derivative with respect to x:", partial_x)
print("Partial Derivative with respect to y:", partial_y)

# 可視化のためのデータ生成
X, Y = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
Z = X**2 + Y**2
Zx = 2*X
Zy = 2*Y

fig = plt.figure(figsize=(20, 6))

# 関数の可視化
ax1 = fig.add_subplot(1, 3, 1, projection='3d')
ax1.plot_surface(X, Y, Z, cmap='jet')
ax1.set_title("Function: $f(x, y) = x^2 + y^2$",size=20)
ax1.set_xlabel('$x$', labelpad=15)
ax1.set_ylabel('$y$', labelpad=15)
ax1.set_zlabel('$f(x, y)$', labelpad=15)

# xに関する偏微分
ax2 = fig.add_subplot(1, 3, 2, projection='3d')
ax2.plot_surface(X, Y, Zx, cmap='jet')
ax2.set_title(r"Partial Derivative: $\frac{\partial}{\partial x}$",size=20)
ax2.set_xlabel('$x$', labelpad=15)
ax2.set_ylabel('$y$', labelpad=15)
ax2.set_zlabel(r'$\frac{\partial f}{\partial x}$', labelpad=15)

# yに関する偏微分
ax3 = fig.add_subplot(1, 3, 3, projection='3d')
ax3.plot_surface(X, Y, Zy, cmap='jet')
ax3.set_title(r"Partial Derivative: $\frac{\partial}{\partial y}$",size=20)
ax3.set_xlabel('$x$', labelpad=15)
ax3.set_ylabel('$y$', labelpad=15)
ax3.set_zlabel(r'$\frac{\partial f}{\partial y}$', labelpad=15)

plt.tight_layout()
plt.show()
<Figure size 2000x600 with 3 Axes>

2線型代数

2.1ベクトル

2.1.1ベクトルとは

ベクトル(vector)とは、大きさと向きを持つ量です。ベクトルは、数が一列に並んだ集まりとして表現できます。例えば、

x=[x1x2x3], y=[y1y2yN]\begin{split}{\bf x}= \begin{bmatrix} x_{1} \\ x_{2} \\ x_{3} \end{bmatrix}, \ {\bf y}=\begin{bmatrix} y_{1} \\ y_{2} \\ \vdots \\ y_{N} \end{bmatrix}\end{split}

上の例のように、その要素を縦方向に並べたものは列ベクトルと呼びます。一方、

z=[z1z2z3]{\bf z}=\begin{bmatrix} z_{1} & z_{2} & z_{3} \end{bmatrix}

のように、要素を横方向に並べたものは行ベクトルと呼びます。

一般的には、ベクトルを数式で書く際には, W\mathbf{W}のように太字の記号で表現するか、W\vec{W}のようにベクトルの上に矢印を付けてベクトルを示すことが多いです。

2.1.2ベクトルの基本演算

加算(足し算)及び減算(引き算)は同じサイズのベクトル同士の間だけで成立します。

[123]+[456]=[1+42+53+6]=[579]\begin{split}\begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}+\begin{bmatrix} 4 \\ 5 \\ 6 \end{bmatrix}=\begin{bmatrix} 1 + 4 \\ 2 + 5 \\ 3 + 6 \end{bmatrix}=\begin{bmatrix} 5 \\ 7 \\ 9 \end{bmatrix}\end{split}

スカラ倍とはベクトルにスカラを掛ける演算です。

10[123]=[101102103]=[102030]\begin{split} 10 \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}=\begin{bmatrix} 10 * 1 \\ 10 * 2 \\ 10 * 3 \end{bmatrix}=\begin{bmatrix} 10 \\ 20 \\ 30 \end{bmatrix}\end{split}

複数のベクトル a1,a2,,an\vec{a}_1, \vec{a}_2, \dots, \vec{a}_n に対して、

c1a1+c2a2++cnanc_1\vec{a}_1 + c_2\vec{a}_2 + \dots + c_n\vec{a}_n

の形で作られるベクトルを線形結合といいます。

それらのベクトルをスカラーで伸ばしたり、足したりして作れるすべてのベクトルの集合をそれらのベクトルが張る空間(span)と呼びます

「あるベクトル集合のすべての線形結合を集めた集合」は、ベクトル空間になる。

ベクトル空間 VV において、 その空間を張る最小限のベクトルの組(基底)が存在します。

このとき、

dim(V)=基底を構成するベクトルの本数\dim(V) = \text{基底を構成するベクトルの本数}

を次元(dimension)と呼びます。

2.1.3ベクトルの内積

内積 (ドット積) とは、同じサイズの2つのベクトルは、それぞれのベクトルの同じ位置に対応する要素同士を掛け、それらを足し合わせる計算です。𝐱𝐱𝐲𝐲の内積は𝐱𝐲𝐱\cdot𝐲で表されます。

[123][456]=1×4+2×5+3×6=32\begin{split}\begin{aligned}& \begin{bmatrix} 1 & 2 & 3 \end{bmatrix} \cdot \begin{bmatrix} 4 \\ 5 \\ 6 \end{bmatrix} = 1 \times 4 + 2 \times 5 + 3 \times 6 = 32 \end{aligned}\end{split}

ドット積は、2つのベクトルの長さと角度に関係しています:

ab=abcosθ\mathbf{a} \cdot \mathbf{b} = |\mathbf{a}|\,|\mathbf{b}|\,\cos\theta

ここで:

  • a|\mathbf{a}|b|\mathbf{b}| はベクトルの長さ(ノルム)

  • θ\theta はベクトル間の角度

2.2行列

2.2.1行列とは

行列 (matrix) は同じサイズのベクトルを複数個並べたものです。例えば、

X=[x11x12x21x22x31x32]\begin{split} {\bf X} = \begin{bmatrix} x_{11} & x_{12} \\ x_{21} & x_{22} \\ x_{31} & x_{32} \end{bmatrix} \end{split}

X\mathbf{X}は「 3 行 2 列の行列」になります。

2.2.2行列積

行列の乗算には、行列積、外積、要素積(アダマール積)など複数の方法があります。 ここではそのうち、機械学習の多くの問題で登場します行列積について説明します。

行列A\mathbf{A}と行列B\mathbf{B}の行列積はAB\mathbf{AB}と書き 、A\mathbf{A}の各行とB\mathbf{B}の各列の内積を並べたものとして定義されます。

例えば、行列A\mathbf{A}1行目の行ベクトルと、行列B\mathbf{B}1列目の列ベクトルの内積の結果は、A\mathbf{A}B\mathbf{B}の行列積の結果を表す行列C\mathbf{C}11列目に対応します。

内積が定義される条件はベクトルのサイズが等しいということでしたが、ここでもそれが成り立つために、A\mathbf{A}の行のサイズと B\mathbf{B} の列のサイズが一致する必要があります。

2.2.3転置

転置(transpose)とは、mmnn 列の行列 A\mathbf{A} に対して、 A\mathbf{A}(i,j)(i, j) 要素と (j,i)(j, i) 要素を入れ替えて、nnmm 列の行列に変換する操作です。転置は行列の右肩にTTと書くことで表します。

A=[142536], AT=[123456]\begin{split} {\bf A} =\begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix}, \ {\bf A}^{\rm T}=\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} \end{split}

転置について、以下の定理を覚えておきましょう。

(1) (AT)T=A(2) (AB)T=BTAT(3) (ABC)T=CTBTAT\begin{split}\begin{aligned} &\left( 1\right) \ \left( {\bf A}^{\rm T} \right)^{\rm T} = {\bf A} \\ &\left( 2\right) \ \left( {\bf A}{\bf B} \right)^{\rm T} = {\bf B}^{\rm T}{\bf A}^{\rm T}\\ &\left( 3\right) \ \left( {\bf A}{\bf B}{\bf C} \right)^{\rm T} = {\bf C}^{\rm T}{\bf B}^{\rm T}{\bf A}^{\rm T} \end{aligned}\end{split}

2.2.4ベクトルによる微分と勾配

線形結合とは、スカラー倍したベクトル同士を足し合わせることです。

例えば、

b=[34], x=[x1x2]bTx=[34][x1x2]=3x1+4x2\begin{split}\begin{aligned} {\bf b} &=\begin{bmatrix} 3 \\ 4 \end{bmatrix}, \ {\bf x} = \begin{bmatrix} x_{1} \\ x_{2} \end{bmatrix}\\ {\bf b}^{\rm T}{\bf x} &= \begin{bmatrix} 3 & 4 \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} = 3x_1 + 4x_2 \end{aligned}\end{split}

のようにx\mathbf{x}の要素であるx1x_1およびx2x_2に関して一次式となっています。

𝐛T𝐱\mathbf{𝐛^T𝐱}をベクトルx\mathbf{x}で微分したものを、

x(bTx)\frac{\partial}{\partial {\bf x}} \left( {\bf b}^{\rm T}{\bf x} \right)

と表します。

「ベクトルで微分」とは、ベクトルのそれぞれの要素で対象を微分し、その結果を要素に対応する位置に並べてベクトルを作ることです。つまり、

x(bTx)=x(3x1+4x2)=[x1(3x1+4x2)x2(3x1+4x2)]=[34]\begin{split} \begin{aligned} \frac{\partial}{\partial {\bf x}} \left( {\bf b}^{\rm T} {\bf x} \right) &= \frac{\partial}{\partial {\bf x}} \left( 3x_1 + 4x_2 \right) \\ &= \begin{bmatrix} \frac{\partial}{\partial x_1} \left( 3x_1 + 4x_2 \right) & \frac{\partial}{\partial x_2} \left( 3x_1 + 4x_2 \right) \end{bmatrix} \end{aligned} \end{split} = \begin{bmatrix} 3 & 4 \end{bmatrix}

入力ベクトルの要素毎に出力に対する偏微分を計算し、それらを並べてベクトルにしたものが 勾配 (gradient) と言います。

つまり、多変数関数における傾きのようなもので、1変数関数のようにスカラーではなく、勾配で最も急な変化の方向とその大きさを示しています。勾配ベクトルは以下のように定義されます:

f=(fx,fy,)\nabla f = \left( \frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}, \ldots \right)

ここで、関数f(x,y)=x2+y2f(x,y)=x^2+y^2の値等高線とその勾配を同時にプロットします。赤い矢印が勾配ベクトルを示しており、各点で関数の最も急な上昇方向を指し示しています。

Source

# 関数とその勾配
def function(x, y):
    return x**2 + y**2

def gradient(x, y):
    dfdx = 2*x
    dfdy = 2*y
    return dfdx, dfdy

# 座標の生成
x = np.linspace(-5, 5, 20)
y = np.linspace(-5, 5, 20)
X, Y = np.meshgrid(x, y)
Z = function(X, Y)
U, V = gradient(X, Y)

# 可視化
plt.figure(figsize=(10, 8))
plt.contour(X, Y, Z, levels=50, cmap='jet')  # 値を等高線で表示
plt.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=10, color='red', width=0.005)  # 勾配ベクトルを矢印で表示
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.title('Gradient of $f(x, y) = x^2 + y^2$')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.grid(True)
plt.show()
<Figure size 1000x800 with 1 Axes>