はじめに
『パターン認識と機械学習』の独学時のまとめです。一連の記事は「数式の行間埋め」または「R・Pythonでのスクラッチ実装」からアルゴリズムの理解を補助することを目的としています。本とあわせて読んでください。
この記事は、3.1.4項「正則化最小二乗法」を補足する内容です。LpノルムをR言語で作図します。
【前節の内容】
【他の節一覧】
【この節の内容】
・Lpノルム
3.1.4項の正則化で利用するLpノルム($L^p$ノルム)をグラフで確認します。
利用するパッケージを読み込みます。
# 利用するパッケージ library(tidyverse) library(gganimate)
・定義式の確認
$M$次元ベクトル$\mathbf{w} = (w_1, w_2, \cdots, w_M)$のLpノルム$\|\mathbf{w}\|_p$は、次の式で定義されます。
ベクトルを$\|$で挟んでそのベクトルのノルムを表します。$\sqrt[n]{x} = x^{\frac{1}{n}}$であり、$(\sqrt[n]{x})^n = (x^{\frac{1}{n}})^n = x$です。
(Lpノルムと呼ぶくらいなので)$p$を使うことが多いようですが、本に合わせて$q$を使うことにします。
・L1ノルム
$q = 1$のときL1ノルム($L^1$ノルム)と呼びます。L1ノルムのグラフを確認します。
$q = 1$のとき、$x^{\frac{1}{1}} = x$なので累乗根$\sqrt{}$が外れ、次の式になります。
つまり、L1ノルム$\|\mathbf{w}\|_1$は、$\mathbf{w}$の各要素の絶対値$|w_j|$の総和です。
作図用の$\mathbf{w}$の点を作成して、L1ノルムを計算します。2次元のグラフで描画するため$M = 2$とします。
# 作図用のwの範囲を指定 w_vec <- seq(-10, 10, by = 0.1) # 値を指定 q <- 1 # Lpノルムを計算 norm_df <- tidyr::tibble( w_1 = rep(w_vec, times = length(w_vec)), # w1の値 w_2 = rep(w_vec, each = length(w_vec)), # w2の値 Lq_norm = (abs(w_1)^q + abs(w_2)^q)^(1 / q) # ノルム ) # 確認 head(norm_df)
## # A tibble: 6 x 3 ## w_1 w_2 Lq_norm ## <dbl> <dbl> <dbl> ## 1 -10 -10 20 ## 2 -9.9 -10 19.9 ## 3 -9.8 -10 19.8 ## 4 -9.7 -10 19.7 ## 5 -9.6 -10 19.6 ## 6 -9.5 -10 19.5
グラフとして描画する$w_j$の値をseq()
で作成してw_vec
とします。処理が重い場合は、この値を調整してください。
w_vec
として作成した値に対して、全ての組み合わせを持つように$\mathbf{w} = (w_1, w_2)$の値を作成します。w_1, w_2
列の各行が、1つの点$\mathbf{w}$に対応します。
絶対値はabs()
で計算できます。
作図用に、$\mathbf{w}$の値とL1ノルムの値をデータフレームに格納します。
L1ノルムの等高線グラフを作成します。
# ノルムの等高線図を作成 ggplot(norm_df, aes(x = w_1, y = w_2, z = Lq_norm, color = ..level..)) + geom_contour() + # 等高線グラフ #geom_contour(breaks = 1.0) + # 等高線グラフ:(値を指定) coord_fixed(ratio = 1) + # アスペクト比 labs(title = expression(group("|", group("|", w, "|"), "|")[q] == sqrt(sum(group("|", w[j], "|")^q, j==1, M), q)), subtitle = paste0("q=", q), x = expression(w[1]), y = expression(w[2]), color = paste0("L", q, " norm"))
geom_contour()
で等高線を描画できます。breaks
引数に等高線を描く値を指定できます。
L1ノルムの値(z軸の値)が1の線だけ描画してみます。
L1ノルムは、$M = 2$のとき3Dグラフを水平に切断した断面を上から見ると、正方形になります。
・L2ノルム
$q = 2$のときL2ノルム($L^2$ノルム)と呼びます。L2ノルムのグラフを確認します。
$q = 2$のとき、$|x|^2 = x^2$なので絶対値が外れ、次の式になります。
つまり、L2ノルム$\|\mathbf{w}\|_2$は、$\mathbf{w}$の各要素の二乗和の平方根です。
$q = 2$のグラフを確認します。q
に2
を代入すると、先ほどのコードで処理できます。
L2ノルムは、$M = 2$のとき3Dグラフを水平に切断した断面を上から見ると、円形になります。
・正則化項
正則化では累乗根の計算を行わず、次の式で計算します。
正則化項のグラフも確認しましょう。
# 正則化項を計算 E_df <- tidyr::tibble( w_1 = rep(w_vec, times = length(w_vec)), # w1の値 w_2 = rep(w_vec, each = length(w_vec)), # w2の値 E_w = (abs(w_1)^q + abs(w_2)^q) / q # 正則化項 ) # 正則化項の等高線図を作成 ggplot(E_df, aes(x = w_1, y = w_2, z = E_w, color = ..level..)) + geom_contour() + # 等高線グラフ #geom_contour(breaks = 1.0) + # 等高線グラフ:(値を指定) coord_fixed(ratio = 1) + # アスペクト比 labs(title = expression(E[w](w) == sum(group("|", w[j], "|")^q, j==1, M)), subtitle = paste0("q=", q), x = expression(w[1]), y = expression(w[2]), color = expression(E[w](w)))
値は変わりますが、形状は変化していません。
・おまけ:qとグラフの形状の関係
最後に、$q$の値とグラフの形状の関係をアニメーションで確認します。
・作図コード(クリックで展開)
for
ループでq
の値を変更して繰り返しノルムを計算して、計算結果をデータフレームに追加していきます。
# qの最大値を指定 q_max <- 7.5 # 使用するqの値を作成 q_vec <- seq(0.5, q_max, by = 0.1) # qごとにノルムを計算 anime_df <- tidyr::tibble() for(q in q_vec) { # Lpノルムを計算 tmp_norm_df <- tidyr::tibble( q = q, w_1 = rep(w_vec, times = length(w_vec)), # w1の値 w_2 = rep(w_vec, each = length(w_vec)), # w2の値 Lq_norm = (abs(w_1)^q + abs(w_2)^q)^(1 / q) # ノルム ) # 結果を結合 anime_df <- rbind(anime_df, tmp_norm_df) } # 確認 head(anime_df)
## # A tibble: 6 x 4 ## q w_1 w_2 Lq_norm ## <dbl> <dbl> <dbl> <dbl> ## 1 0.5 -10 -10 40. ## 2 0.5 -9.9 -10 39.8 ## 3 0.5 -9.8 -10 39.6 ## 4 0.5 -9.7 -10 39.4 ## 5 0.5 -9.6 -10 39.2 ## 6 0.5 -9.5 -10 39.0
gganimate
パッケージを利用してアニメーション(gif画像)を作成します。
# ノルムの等高線図を作成 anime_graph <- ggplot(anime_df, aes(x = w_1, y = w_2, z = Lq_norm, color = ..level..)) + geom_contour() + # 等高線グラフ gganimate::transition_manual(q) + # フレーム coord_fixed(ratio = 1) + # アスペクト比 labs(title = expression(group("|", group("|", w, "|"), "|")[q] == sqrt(sum(group("|", w[j], "|")^q, j==1, M), q)), subtitle = paste0("q={current_frame}"), x = expression(w[1]), y = expression(w[2]), color = paste0("Lp norm")) # gif画像に変換 gganimate::animate(anime_graph, nframes = length(q_vec), fps = 10)
この項では、Lpノルムを確認しました。次項では、L1ノルムとL2ノルムを利用して正則化を行います。
参考文献
- C.M.ビショップ著,元田 浩・他訳『パターン認識と機械学習 上下』,丸善出版,2012年.
おわりに
Lpノルムを検索すると正方形や円形のグラフが出てくるけど、これはあくまで等高線の1本または断面図なんだな。
接線の図も微妙にアレで、グラフの端と端が接しているわけではなく、更に言うとz軸の値は異なるので接してもいない(と思う?)。こっちは次の記事でやります。
【次節の内容】