微分できない関数の勾配を求めて最小値を導く方法

モチベーション

  • 部分的に微分できない関数をなんとか微分したい
  • L1正則化(Lasso)を用いると,過学習を防げる場合がある

関数の種類

目的関数の最小値を求めたい機会は多々ある,目的関数を微分することで最小値を求めることがあるが,関数の形によっては,部分的に微分できない場合がある.例えば,は で微分不可能である.

  • 関数が連続でない:境界( の 付近)において,左極限と右極限が一致しないもの
  • 級関数:1階微分可能であり,それが連続であるもの
  • 級関数:n階微分可能であり,n階の導関数が連続であるもの
  • 滑らかな関数:任意階微分可能であり,任意階の導関数が連続であるもの. 級関数.

劣微分・劣勾配

凸関数を最小化するために勾配情報が欲しいが,凸関数はいつでも微分可能であるとは限らない.例えば, は凸関数だが, で微分可能でない.このような関数に対して勾配(劣勾配)を定義してみる.

真凸関数 かつ の元で, が

を満たすとき, を における劣勾配という.劣勾配の集合 を劣微分という.

の における劣微分は,以下より, と求まる.

  • の場合, なので,
  • の場合, なので,
  • の場合, なので,

もっと複雑な関数を微分したい

座標降下法(Coordinate Descent)または勾配降下法(Gradient Descent)を用いる.

  • 座標降下法:目的関数の変数を一つずつ最小化する
  • 勾配降下法:あとで書く

座標降下法(Coodinate Descent)によるL1正則化(Lasso)の最適化

LassoとはLeast absolute shrinkage and selection operatorの略であり,以下の式で表される.以下を最小にするような を探したいが,微分できない項を含んでいる.ここで, , である. は列ベクトル からj列目を除いたものを表す.

は正則化項に含まれないので, を で微分することで の最小値が求まる.

次に, を で偏微分した値を0とする.

は行列Xのk列ベクトルを表し, はそれの転置ベクトルを表す.また, は行列Xからk列ベクトルを除いたものを表す.

について整理すると, の場合と の場合ができる.

ここで劣微分を導入する.具体的には,(1)式または(2)式を満たさない場合は問答無用で とするだけ.λの大きさによって とする範囲を制御できる.これらをまとめてsoft thresholding operatorと呼ぶ.

で,以下の手順に沿って を求める.以下のように,目的変数(最小化したい変数)を一つずつ触って,目的関数(今回はLasso)を最小化する方法を,座標降下法(Coodinate Descent)という.

  1. を適当な値で初期化
  2. を計算
  3. while が収束条件を満たすまで
    • for k in 1,2,…,D
      • を計算
    • を計算
import numpy as np
import copy

class MyLasso:
  def __init__(self, alpha=1.0, max_iter=100):
    self.alpha = alpha
    self.max_iter = max_iter
    self.coef_ = None
    self.intercept_ = None

  def fit(self, X, t):
    X = np.column_stack((np.ones(len(X)),X)) # (1,x1,x2,...)
    w = np.zeros(X.shape[1])
    
    w[0] = np.sum(t - np.dot(X[:,1:], w[1:]))/(X.shape[0])
    
    for iter in range(self.max_iter):
        for i in range(1, len(w)):
            _w = copy.deepcopy(w)
            _w[i] = 0.0

            w[i] = 0.0
            a = (np.dot(X[:, i], t - np.dot(X, _w)) - self.alpha)/(X[:, i]**2).sum()
            if a > 0.0 : w[i] = a
            a = (np.dot(X[:, i], t - np.dot(X, _w)) + self.alpha)/(X[:, i]**2).sum()
            if a < 0.0 : w[i] = a

        w[0] = np.sum(t - np.dot(X[:,1:], w[1:]))/(X.shape[0])

        self.intercept_ = w[0]
        self.coef_ = w[1:]

    return self

  def predict(self, X):
    y = np.dot(X, self.coef_)
    y += self.intercept_*np.ones(len(y))
    return y

にσ=1.0のノイズを加えた50点を教師データとして,Lassoで予測した結果が以下である.

from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures

def f(x):
    return  x**2 + 2*x + 3

N = 50
X = np.zeros((N,1))
np.random.seed(seed=2)
X[:,0] = np.random.uniform(-3, 3, N)
noise = 1.0*np.random.randn(N)
t = f(X[:,0])+ noise

X_train, X_test, t_train, t_test = train_test_split(X, t, random_state=0)

fig, ax = plt.subplots(1, 3, figsize = (15, 5))

for degree, ax in zip([1,3,9],ax):
    pipeline = Pipeline([
        ('polynominal_features', PolynomialFeatures(degree=degree)),
        ('linear_regression', MyLasso(alpha=0.01))
    ])

    lr = pipeline.fit(X_train, t_train)
    print(pipeline.steps[1][1].intercept_)
    print(pipeline.steps[1][1].coef_)
    
    Xcont = np.linspace(np.min(X), np.max(X), 200).reshape((200, 1))
    ax.plot(Xcont, f(Xcont), label='original')
    ax.plot(X, t,'.', label='training data')
    ax.plot(Xcont, lr.predict(Xcont), label='predictive')
    ax.set_title('degree : {}'.format(degree))
    ax.legend()
    ax.set_xlabel(r'$x$')
    ax.set_ylabel(r'$t$')

多項式モデルで予測し,具体的には1次,3次,5次のモデルで予測してみた.

linear_regression_with_lasso

各モデルが予測した式は以下(係数を小数点以下第一位に丸めている)であり,3次以上の項の係数は0か非常に小さな値になっている.これはL1正則化によりスパースなモデルを求めることができたことを意味する.

1次式モデル:

3次式モデル:

5次式モデル:

Lassoを考慮したモデルがなぜスパースになるのかを知るには,ラグランジュの未定乗数法を知ると良い

ラグランジュの未定乗数法とは

制約条件 が与えられた状態で,関数 の最小値または最大値を求めるために便利なのがラグランジュの未定乗数法である.具体的には,ラグランジュ関数 とラグランジュ乗数 を用いて,以下を解けば良い.

なぜこれで停留点を求めることができるか

を行列で書き直すと,

となり,ベクトル をλ倍したものがベクトル であることを表している.では,ベクトル とは何か.

制約面 上の点 と を考えると, の周りでのテイラー展開より以下が得られる.

いま, と は制約面上に存在しているので, が成立するためには,以下が成り立つ必要がある.

内積で書き直すと,

と が十分に小さければ, は点 における制約面 の接線を表すので,ベクトル は接線の法線ベクトルを表す.

先程の

より,ベクトル とベクトル が点 で並行であれば,その点で が停留点を持つ.

「ベクトル とベクトル が点 で並行である」という部分が肝である.例えば,下図において,制約条件 は赤の等高線, はc1,c2,c3の等高線で表し,c1 < c2 < c3 とすると,勾配 は図のようになる(わざと と が接するように描いている).ラグランジュ乗数法により勾配 と勾配 が並行な点,すなわち(x0,y0)において, を満たす の停留点(下図の場合は最小値)となることがわかる.

lagrange_multiplier

に対する1次独立性が必要

制約条件 が複数ある場合,ラグランジュの未定乗数法によって最適解を求めるためには, がそれぞれ1次独立である必要がある.1次独立な と の線形和によって, を表現する必要があることが理由.

最適性の2次の十分条件を満たせば局所最適解

関数 は二階微分可能とし,局所最適解を とする. は1次独立であり,ラグランジュ乗数を とする.

ここで,部分空間 を以下のように定義する.

更に,行列 を以下のように定義する.

これら について, を用いて,以下を満たせば は局所最適解である.

つまり,ヘッセ行列が正定値であれば, は局所最適解である.

ちなみに,2次の必要条件は,上記ヘッセ行列が半正定値であるという,十分条件を緩くしたものになる.

ラグランジュ乗数法のKKT条件

これまでは制約条件 が0である元で議論してきたが, に範囲がある場合を考える.

下図において, を赤の等高線で表し,内側に向かって が大きくなっているとすると, は楕円の内部に該当し,勾配 は図のような向きになる.また, を緑の等高線で表し,c1 < c2 < c3 であるとすると,勾配 は図のような向きになる.

lagrange_multiplier_kkt1

この場合,「制約条件 の元で を最大にする点」をラグランジュ乗数法により,点(x0,y0)と求めることができる.

また,「制約条件 の元で を最小にする点」は,ラグランジュ乗数法により,下図の点(x1,y1)と求めることができる.

lagrange_multiplier_kkt2

更に,下図の場合,「制約条件 の元で を最大にする点」は,c1 < c2 < c3 であるとするなら,ラグランジュ乗数法のλを0とし,単に の最大点を求めれば良いので,点(x2,y2)と求まる.

lagrange_multiplier_kkt3

これらの条件をまとめたものが以下のKKT条件(Karush-Kuhn-Tucker condition)である.

  • の元で を最大化するには以下を解く
  • の元で を最小化するには以下を解く

条件式がややこしく見えるが,言っていることは単純で,「最大化したいのか最小化したいのかに注意し,勾配 と の向きをλの正負で制御して,ラグランジュ乗数法を解け」ということである.

は相補性条件と呼ばれ, または のどちらかが0になることを表している.

正則化とラグランジュ乗数法

以下の二乗和誤差と正則化項を合わせた関数 を最小化したいとする.ξは正則化項の係数であり,正であるとする.

いま,以下の二乗和誤差の最小値を

以下の制約条件で求めるとする.

の最小化には,KKT条件 と の元で,以下のラグランジュ関数を解く必要がある.

ここで,

であるので, とすると,内部的には,二乗和誤差+正則化の最小値を求めていることになる. であるので, である必要が出てくる.KKT条件 より, なので以下が成り立つ.

q=1のときの と を以下のように図示してみた.

visualize_of_lasso

更に,断面を簡易的に図示してみた.ラグランジュ乗数法の仕組みにより,制約条件の元で,関数の停留点を求めるには,勾配ベクトル のスカラーλ倍したものが,勾配ベクトル と並行である必要があるので,停留点はもちろん,ηもλに依存する.

fragment_of_lasso

上図の停留点においてw1=0となっている.疎(スパース)な解が得られている例であり,lassoの特徴である.この特徴を用いることで,モデルの複雑さを抑えることができるが,必ずスパースな解が得られるとは限らない.例えば,下図のような場合はスパースでない解が得られ,関数f(w)が制約条件g(w)>0内に入っている場合は,関数f(w)そのものの最小値となる.

visualize_of_lasso_without_sparse

近接勾配法

たぶんつづく

関連記事 Related Posts

B!