Juliaで学ぶ Hamiltonian Monte Carlo (NUTS 入り)

Post on 29-Nov-2014

2.120 views 3 download

description

サンプルコード: https://github.com/bicycle1885/JuliaTokyo2HMC

Transcript of Juliaで学ぶ Hamiltonian Monte Carlo (NUTS 入り)

Julia Tokyo #2

Juliaで学ぶHamiltonian Monte Carlo

(NUTS入り)

佐藤 建太 (Kenta Sato)

@bicycle1885

1 / 55

コンテンツ

自己紹介

マルコフ連鎖モンテカルロ法 (MCMC)

Metropolis-HastingsHamiltonian Monte Carlo (HMC)No-U-Turn Sampler (NUTS)

Juliaで使えるMCMCパッケージ

2 / 55

こんなヒトのための発表です

MCMCによるサンプリングをブラックボックスにしたくない

Stanなどのサンプラーの原理を垣間見たい

Juliaで使えるサンプラーを知りたい

3 / 55

注意

時間の都合上、MCMC自体は簡単に触れる程度です

数学的に厳密な話は期待しないで下さい

内容には細心の注意を払っていますが、専門外ゆえ間違いがある

かもしれません

その時はその場で指摘していただけると助かります

4 / 55

自己紹介 / パッケージ紹介

5 / 55

自己紹介

佐藤 建太

Twitter/GitHub: @bicycle1885

所属: 東大大学院農学部

専門: Bioinformatics

Julia歴: 今年の初め頃~現在

好きな言語: Julia / Haskell

よく使う言語: Python / R

6 / 55

Haskellもよろしくお願いします

『Haskellによる並列・並行プログラミング』の翻訳レビューをしまし

た。

7 / 55

パッケージ紹介

DocOpt.jl - https://github.com/docopt/DocOpt.jl

ヘルプメッセージをパースし、コマンドライン引数のパースをする

RandomForests.jl -https://github.com/bicycle1885/RandomForests.jl

機械学習アルゴリズムRandom ForestのJulia実装

GeneOntology.jl - https://github.com/bicycle1885/GeneOntology.jl

生物学のGene Ontologyのツールキットを目指してる

8 / 55

マルコフ連鎖モンテカルロ法

(MCMC)

9 / 55

MCMC法とは

言わずと知れた、確率分布 からのサンプルをマルコフ連鎖を用いて得るサンプリング手法のひとつ。

得られたサンプル自体は分布の期待値や分散など色々な値を計算する

のに使われる。

マルコフ連鎖とは、現在の状態のみで次の状態の確率分布が決まる確

率過程のことをいう。

このとき、状態 から へ遷移する確率を と書き、遷移確

率と呼ぶ。

P(x)

P( = x < = , … , = ) = P( = x < = )Xt+1 X1 x1 Xt xt Xt+1 Xt xt

x x′ T( ; x)x′

10 / 55

マルコフ連鎖の図

遷移確率 T( ; x) = P( = < = x)x′ Xt+1 x′ Xt11 / 55

正しいMCMC

目的の確率分布 からサンプリングするには、遷移確率が満たさ

なければならない性質がある。

分布の不変性:

エルゴード性:

各サンプラーがこれらを満たすということの説明などは今日はしない

(できない)。

π(x)

π(x) = D T(x; )π( )dx′ x′ x′

(x) → π(x) as t → 7, for any (x)P(t) P(0)

12 / 55

高次元空間でのサンプリングは難しい

確率密度関数 やその非正規化密度関数 があるとする。

高次元空間でのサンプリングの難しさ

"濃い"領域は、空間上のごく一部に集中している

しかしそれがどこかはサンプリング前には分からない

2つの戦略

1. その場から濃い方へ濃い方へと進み

2. 濃いところを見つけたらそこから薄いところへはあまり行かない

➠ MCMCはまさにそのような戦略をとる

p(x) (x)p ̃

p(x)

13 / 55

Metropolis­HastingsNight View with Tokyo Tower Special Lightup (Shibakouen, Tokyo, Japan) by t-mizo is licensed under CC BY 2.0

14 / 55

Metropolis­Hastings

MCMCサンプリングのひとつで、提案分布というサンプリングしたい

分布とは別の分布から候補点を取り出し、"良い値"ならその点を受理し、そうでなければその場にとどまる。

候補点を生成する提案分布 は相関のない正規分布など、サン

プリングしやすい分布に設定する。

候補点 は以下の確率 で受理される:

ここで、 はサンプリングしたい分布 の非正規化密度関数

q( < θ)θ̃

θ̃ A( < )θ̃ θ(m)

A( < ) = min (1, ))θ̃ θ(m) ( )q( < )p ̃ θ̃ θ(m) θ̃

( )q( < )p ̃ θ(m) θ̃ θ(m)

(θ)p ̃ p(θ)

15 / 55

Metropolis­Hastingsのアルゴリズム

非正規化確率分布関数 からサンプリングする

1. 初期状態 を決め、 に設定する

2. 提案分布 から新たな点 をとる

3. 確率 で をサンプルとして受理し、そうでなければ

棄却する

4. 受理された場合は と設定し、棄却された場合は と設定する

5. として、2~4を 個のサンプルが得られるまで繰り返す

(θ)p ̃

θ(0) m ← 0

q( < )θ̃ θ(m) θ̃

A( < )θ̃ θ(m) θ̃

←θ(m+1) θ̃ ←θ(m+1) θ(m)

m ← m + 1 M

16 / 55

提案分布は正規分布(randn)

# p: (unnormalized) probability density function# θ₀: initial state# M: number of samples# ϵ: step sizefunction metropolis(p::Function, θ₀::Vector{Float64}, M::Int, ϵ::Float64) d = length(θ₀) # allocate samples' holder samples = Array(typeof(θ₀), M) # set the current state to the initial state θ = θ₀ for m in 1:M # generate a candidate sample from # the proposal distribution (normal distribution) θ ̃= randn(d) * ϵ + θ if rand() < min(1.0, p(θ)̃ / p(θ)) # accept the proposal θ = θ̃ end samples[m] = θ print_sample(θ) end samplesend

metropolis.jl

17 / 55

呼び出し側

2変数の変数間に相関のある正規分布

# meanμ = [0.0, 0.0]# covariance matrixΣ = [1.0 0.8; 0.8 1.0]# precision matrixΛ = inv(Σ)# unnormalized multivariate normal distributionnormal = x -> exp(-0.5 * ((x - μ)' * Λ * (x - μ))[1])

初期値x₀ 、サンプル数M、ステップ幅 ϵを指定してサンプリング

samples = metropolis(normal, x₀, M, ϵ)

18 / 55

結果 ­ created with Gadfly.jl

x-4 -2 0 2 4

500

200

400

300

1

100

Iteration

-4

-2

0

2

4

y

Metropolis (ϵ = 1.0)

19 / 55

x-3 -2 -1 0 1

500

200

400

300

1

100

Iteration

-2.0

-1.5

-1.0

-0.5

0.0

0.5

y

Metropolis (ϵ = 0.1)

x-3 -2 -1 0 1 2

500

200

400

300

1

100

Iteration

-3

-2

-1

0

1

2

3

y

Metropolis (ϵ = 0.5)

x-4 -2 0 2 4

500

200

400

300

1

100

Iteration

-4

-2

0

2

4

y

Metropolis (ϵ = 1.0)

x-3 -2 -1 0 1 2 3

500

200

400

300

1

100

Iteration

-3

-2

-1

0

1

2

3

y

Metropolis (ϵ = 2.0)

結果 ­ created with Gadfly.jl

20 / 55

Metropolis­Hastingsの問題点

1. 棄却率のトレードオフ

ステップサイズ の値で、棄却率と性能のトレードオフがある

2. ランダムウォーク

サンプルの列がランダムウォークをする

ϵ

21 / 55

問題1: 棄却率のトレードオフ

確率分布の値が集中してるのはごく一部だけ。

ステップサイズ 大 ➠ 大きく動けるが、棄却率が上がる

ステップサイズ 小 ➠ 棄却率は抑えられるが、あまり動けない

MCMCからなるべく独立なサンプルを得るにはステップサイズを大きくしたいが、棄却率が上がるためサンプリングの効率が悪くなるトレ

ードオフがある。

次元(サンプリングする変数)によってパラメータ の良い値が異なり、調節が難しい。

ϵ

ϵ

ϵ

22 / 55

問題2: ランダムウォーク

Metropolis-Hastingsから得られたサンプル列は、ランダムウォークを

している

提案分布が提示する候補点 は、現在の値 からみて等方的

が移動した先からすぐに戻ってきてしまうことがある

ランダムウォークでは(おおまかに言って)反復回数の平方根に比例した距離しか進めない

空間を端から端まで渡るのにかなり反復回数が必要になる

θ̃ θ(m)

θ(m)

23 / 55

Hamiltonian Mote Carlo (HMC)Hamiltonian circuit on a small rhombicosidodecahedron by fdecomite is licensed under CC BY 2.0

24 / 55

Hamiltonian Monte Carlo

Hamiltonian Monte Carlo法(HMC)は、ハミルトン力学(Hamiltoniandynamics)を元に考案されたMCMC法のひとつ。

確率密度関数の勾配を利用する (離散的な確率分布はできない)

空間での粒子の運動を追ってサンプルを得る

他のMCMCのアルゴリズムと比較して、相関の少ない良いサンプ

ルが得られる

この手法を発展させたNo-U-Turn Sampler (NUTS)はStanというベイズ推定のためのプログラミング言語に実装されている

25 / 55

Boltzmann分布

状態 のエネルギー と確率分布 は次のように関連付けられる。

ここで、 は確率分布の正規化定数である。

これを逆に使えば、確率分布のエネルギーが計算できる。

x E(x) P(x)

P(x) = exp (+E(x))1Z

Z

E(x) = + log P(x) + log Z

26 / 55

ハミルトン力学

粒子の運動を考える。 を粒子の位置ベクトル、 を運動量ベクトルとした時の粒子の運動を決めるハミルトン方程式:

ここで、ハミルトニアン はポテンシャルエネルギー と運動エネルギー の和として定義される。

θ r

dθi

dtdri

dt

=�H�ri

= +�H�θi

H(θ, r) U(θ)K(r)

H(θ, r) = U(θ) + K(r)

27 / 55

サンプリングへの応用

変数

位置ベクトル : サンプリングしたい確率変数

運動量ベクトル : 運動の補助的な変数

エネルギー

ポテンシャルエネルギー : Boltzmann分布を基に設定

運動エネルギー : 適当な運動エネルギーに設定

位置ベクトルと運動量ベクトルの同時分布 は より以下のように分解できる。

θ

r

U(θ)

K(r)

p(θ, r)H(θ, r) = U(θ) + K(r)

p(θ, r) = exp (+H(θ, r)) = exp (+U(θ)) exp (+K(r))1Z

1Z

28 / 55

HMCの受容確率

提示された候補点に関する受容確率は以下のようになる。

理論的には、 の値は不変なので ゆえ必ず受理される ( ) はずだが、コンピュータで数値的にハミルトン方

程式を離散化して解くと必ず誤差が発生するため現実的には棄却率は

ゼロでない。

A( < θ) = min (1, exp {H(θ, r) + H( , )})θ̃ θ̃ r ̃

H H(θ, r) + H( , ) = 0θ̃ r ̃ A = 1

29 / 55

Leapfrog離散化

ハミルトン方程式は解析的に解くのは難しいので、数値積分を行う。

そこでは、Leapfrog離散化という以下の更新式をつかう。

(t + ϵ/2)ri

(t + ϵ)θi

(t + ϵ)ri

= (t) +riϵ2�U(θ(t))�θi

= (t) + ϵ (t + ϵ/2)θi ri

= (t + ϵ/2) +riϵ2�U(θ(t + ϵ))

�θi

30 / 55

なぜLeapfrog離散化なのか

同時分布 を不変にするためには、 の体積を不変にしなければならない

しかし、Euler法などでは(精度の悪さを無視しても)体積が変化してしまうので、 が不変にならない

Leapfrog離散化では、3つの更新式はそれぞれ剪断写像(shearmapping)なので、それぞれ適用しても体積が変化しない

"VerticalShear m=1.25" by RobHar - Own work using Inkscape. Licensed under Public domain via Wikimedia Commons -

http://commons.wikimedia.org/wiki/File:VerticalShear_m%3D1.25.svg#mediaviewer/File:VerticalShear_m%3D1.25.svg

p(θ, r) H(θ, r)

p(θ, r)

31 / 55

HMCによるサンプリングアルゴリズム

1. 初期状態 を決め、 に設定する

2. 運動量を正規分布などからサンプリングする

3. からステップサイズ でLeapfrog離散化による更新を 回繰り返し、 を得る

4. 確率 で受理し、そうでなけ

れば棄却する

5. 受理された場合は と設定し、棄却された場合は と設定する

6. として、2~5を 個のサンプルが得られるまで繰り返す

θ(0) m ← 0

θ(m) ϵ Lθ̃

min (1, exp {H(θ, r) + H( , )})θ̃ r ̃

←θ(m+1) θ̃ ←θ(m+1) θ(m)

m ← m + 1 M

32 / 55

# U : potential energy function# ∇U : gradient of the potential energy function# θ₀ : initial state# M : number of samples# ϵ : step size# L : number of stepsfunction hmc(U::Function, ∇U::Function, θ₀::Vector{Float64}, M::Int, ϵ::Float64, L::Int) d = length(θ₀) # allocate sampels' holder samples = Array(typeof(θ₀), M) # set the current sate to the initail state θ = θ₀ for m in 1:M # sample momentum variable p = randn(d) H = U(θ) + p ⋅ p / 2 θ ̃= θ for l in 1:L p -= ϵ / 2 * ∇U(θ)̃ # half step in momentum variable θ ̃+= ϵ * p # full step in location variable p -= ϵ / 2 * ∇U(θ)̃ # half step in momentum variable again end H̃ = U(θ)̃ + p ⋅ p / 2 if randn() < min(1.0, exp(H - H̃)) # accept the proposal θ = θ̃ end samples[m] = θ print_sample(θ) end samplesend

hmc.jl

33 / 55

結果 ­ created with Gadfly.jl

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-4

-2

0

2

4

y

HMC (ϵ = 0.1, L = 10)

L = 10 34 / 55

x-2 -1 0 1

100

200

300

1

500

400

Iteration

-2

-1

0

1

y

HMC (ϵ = 0.01, L = 10)

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-3

-2

-1

0

1

2

3

y

HMC (ϵ = 0.05, L = 10)

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-4

-2

0

2

4

y

HMC (ϵ = 0.1, L = 10)

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-4

-2

0

2

4

y

HMC (ϵ = 0.5, L = 10)

結果 ­ created with Gadfly.jl

L = 1035 / 55

x-0.3 -0.2 -0.1 0.0 0.1

100

200

300

1

500

400

Iteration

-0.3

-0.2

-0.1

0.0

0.1

y

HMC (ϵ = 0.01, L = 1)

x-1.0 -0.5 0.0 0.5

100

200

300

1

500

400

Iteration

-1.5

-1.0

-0.5

0.0

0.5

y

HMC (ϵ = 0.05, L = 1)

x-2 -1 0 1

100

200

300

1

500

400

Iteration

-2

-1

0

1

y

HMC (ϵ = 0.1, L = 1)

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-3

-2

-1

0

1

2

3

y

HMC (ϵ = 0.5, L = 1)

結果 ­ created with Gadfly.jl

L = 136 / 55

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-3

-2

-1

0

1

2

3

y

HMC (ϵ = 0.01, L = 50)

x-3 -2 -1 0 1 2 3

100

200

300

1

500

400

Iteration

-4

-2

0

2

4

y

HMC (ϵ = 0.05, L = 50)

x-4 -2 0 2 4

100

200

300

1

500

400

Iteration

-4

-2

0

2

4

y

HMC (ϵ = 0.1, L = 50)

x-2 -1 0 1 2 3

100

200

300

1

500

400

Iteration

-2

-1

0

1

2

3

y

HMC (ϵ = 0.5, L = 50)

結果 ­ created with Gadfly.jl

L = 5037 / 55

HMCが解決したこと

エネルギーの勾配情報を使うことで、可能になったこと:

ステップサイズ を十分小さくとればLeapfrogの積分誤差が小さくなり、棄却率を低く抑えられる

粒子が ステップ連続して滑らかに移動するため、ランダムウォ

ークと比べて遠くまで動ける

棄却率を抑えつつ前の位置より遠くまで動くことができるようにな

り、得られるサンプルがより独立なサンプルに近づいた。

ϵ

L

38 / 55

HMCの難しさ

HMCの利点は、運動を調節する2つのパラメータ

ステップサイズ

ステップ数

の値がちょうど良い値に設定されているということに依存している。

分布の形状によってちょうど良い値が変わるため、これらの値をどん

な分布にもうまくいくよう予め設定するのは不可能。

ϵ

L

39 / 55

 と   の調整を間違えるとどうなるか

ステップサイズ :

が小さすぎる ➠ 粒子があまり動かない

が大きすぎる ➠ leapfrog離散化が荒すぎて棄却率が上がる

を小さくすると を大きくしないといけないため、計算コストもか

かる。

ステップ数 :

が小さすぎる ➠ ランダムウォークをしてしまう

が大きすぎる ➠ 粒子が引き返す (Uターン)

➠ 自動的にパラメータを調節したい

ϵ L

ϵ

ϵ

ϵ

ϵ L

L

L

L

40 / 55

No­U­Turn Sampler (NUTS)No Turning Back by Pak Gwei is licensed under CC BY-NC-SA 2.0

41 / 55

No­U­Turn Sampler

HMCはステップサイズ とステップ数 の2つのパラメータに敏感だ

ったが、 No-U-Turn Sampler (NUTS)ではこれらのパラメータ(特に )をうまいこと調節してくれる。

の調節 ➠ サンプリング前のdual averagingにより最適化

の調節 ➠ サンプリング中の粒子の怪しい運動を検出して止まる

ヒトが手でパラメータのチューニングすることなく最適なHMCサンプラーと同じくら質の良いサンプルが得られるようになっている。

ϵ L

L

ϵ

L

42 / 55

突然ですがNUTSのアルゴリズムです

(Hoffman & Gelman, 2014) 43 / 55

NUTSの要点

さすがに全部を紹介するのは厳しいので要点を紹介すると、

サンプル間の軌跡は、妥当な範囲で長い方がいい

なので予め を設定せず、軌跡をどんどん伸ばしていく

伸ばしすぎて運動がUターンを始めたら、軌跡を伸ばすのを止める

その軌跡にあるデータ点から、新しいサンプルを得る

軌跡からのサンプリングは、詳細釣り合いを崩さないようにする

L

詳細釣り合い(detailed balance)とは、分布が不変になるための十分条件❏

44 / 55

No! U­Turn!!

軌跡の長さの時間変化は、始点 から現在の点 までのベクトルと運動量ベクトル の積に比例する:

この値が 以下になったら、軌跡がUターンをし始めたことになる。

θ θ̃ r ̃

= ( + θ ( + θ) = ( + θddt

( + θ ( + θ)θ̃ )T θ̃ 2

θ̃ )T ddt

θ̃ θ̃ )Tr ̃

0

45 / 55

長いので気になる方はサンプルコードのnuts.jlを参照して下さい。

# L: logarithm of the joint density θ# ∇L: gradient of L# θ₀: initial state# M: number of samples# ϵ: step sizefunction nuts(L::Function, ∇L::Function, θ₀::Vector{Float64}, M::Int, ϵ::Float64) d = length(θ₀) samples = Array(typeof(θ₀), M) θ = θ₀ for m in 1:M r₀ = randn(d) u = rand() * exp(L(θ) - r₀ ⋅ r₀ / 2) θ⁻ = θ⁺ = θ r⁻ = r⁺ = r₀ C = Set([(θ, r₀)]) j = 0 s = 1 while s == 1 v = randbool() ? -1 : 1 if v == -1 θ⁻, r⁻, _, _, C′, s′ = build_tree(L, ∇L, θ⁻, r⁻, u, v, j, ϵ) else _, _, θ⁺, r⁺, C′, s′ = build_tree(L, ∇L, θ⁺, r⁺, u, v, j, ϵ) end if s′ == 1 C = C ∪ C′ end s = s′ * ((θ⁺ - θ⁻) ⋅ r⁻ ≥ 0) * ((θ⁺ - θ⁻) ⋅ r⁺ ≥ 0) j += 1 end θ, _ = rand(C) samples[m] = θ print_sample(θ) end samplesend

nuts.jl

46 / 55

結果 ­ created with Gadfly.jl

x-3 -2 -1 0 1 2 3

100

400

200

1

300

500

Iteration

-3

-2

-1

0

1

2

3

y

NUTS (ϵ = 0.1)

47 / 55

x-3 -2 -1 0 1 2 3

100

400

200

1

300

500

Iteration

-4

-2

0

2

4

y

NUTS (ϵ = 0.01)

x-3 -2 -1 0 1 2 3

100

400

200

1

300

500

Iteration

-3

-2

-1

0

1

2

3

y

NUTS (ϵ = 0.05)

x-3 -2 -1 0 1 2 3

100

400

200

1

300

500

Iteration

-3

-2

-1

0

1

2

3

y

NUTS (ϵ = 0.1)

x-3 -2 -1 0 1 2 3

100

400

200

1

300

500

Iteration

-3

-2

-1

0

1

2

3

y

NUTS (ϵ = 0.5)

結果 ­ created with Gadfly.jl

48 / 55

Juliaで使えるMCMCパッケージ

49 / 55

Juliaで使えるMCMCパッケージ

MCMC.jl - https://github.com/JuliaStats/MCMC.jl

サンプラーのデパート (12種類!)

ドキュメントがない

Stan.jl - https://github.com/goedman/Stan.jl

Stanバインディング (via CmdStan)

そのうちMCMC.jlに取り込まれるっぽい?

Mamba.jl - https://github.com/brian-j-smith/Mamba.jl

かなり本気っぽいJulia製の実用的なMCMCフレームワーク

充実のドキュメント

50 / 55

まとめ

HMCは粒子の運動を追跡してサンプリングすることにより、棄却

率を下げられる

NUTSはHMCの難しいパラメータ調節を、自動化してくれる

Mamba.jlがJuliaの実用的なサンプラーの注目株か

51 / 55

参考

Bishop, C. M. (2006). Pattern recognition and machine learning.New York: springer. (元田浩 (2012) サンプリング法 パターン認識

と機械学習 下, pp.237-273. 丸善出版)

Hoffman, M. D., & Gelman, A. (2014). The No-U-Turn Sampler :Adaptively Setting Path Lengths in Hamiltonian Monte Carlo.Journal of Machine Learning Research, 15, 1351–1381.MacKay, D. J. C. (2003). Information Theory, Inference, andLearning Algorithms. Cambridge University Press.Neal, R. M. (2011). MCMC Using Hamiltonian Dynamics. InHandbook of Markov Chain Monte Carlo, pp.113-162. Chapman &Hall/CRC.

豊田秀樹 (2008). マルコフ連鎖モンテカルロ法 朝倉書店

52 / 55

おまけ

53 / 55

Juliaのソースコードと擬似コードの異常な類似

function build_tree(L::Function, ∇L::Function, θ::Vector{Float64}, r::Vector{Float64}, u::Float64, v::Int if j == 0 θ′, r′ = leapfrog(∇L, θ, r, v * ϵ) C′ = u ≤ exp(L(θ′) - r′ ⋅ r′ / 2) ? Set([(θ′, r′)]) : Set([]) s′ = int(L(θ′) - r′ ⋅ r′ / 2 > log(u) - Δmax) return θ′, r′, θ′, r′, C′, s′ else θ⁻, r⁻, θ⁺, r⁺, C′, s′ = build_tree(L, ∇L, θ, r, u, v, j - 1, ϵ) if v == -1 θ⁻, r⁻, _, _, C″, s″ = build_tree(L, ∇L, θ⁻, r⁻, u, v, j - 1, ϵ) else _, _, θ⁺, r⁺, C″, s″ = build_tree(L, ∇L, θ⁺, r⁺, u, v, j - 1, ϵ) end s′ = s′ * s″ * ((θ⁺ - θ⁻) ⋅ r⁻ ≥ 0) * ((θ⁺ - θ⁻) ⋅ r⁺ ≥ 0) C′ = C′ ∪ C″ return θ⁻, r⁻, θ⁺, r⁺, C′, s′ endend nuts.jl

54 / 55

ハミルトニアンの不変性の証明

dHdt

= { + }∑i

�H�xi

dxi

dt�H�pi

dpi

dt

= { + } = 0∑i

�H�pi

�H�xi

�H�pi

�H�xi

55 / 55