期待値最大化を理解するための数値例


117

EMアルゴリズムを十分に把握して、実装して使用できるようにしています。私は丸1日、理論と、レーダーからの位置情報を使用して航空機を追跡するためにEMが使用される論文を読みました。正直なところ、私は根本的なアイデアを完全に理解しているとは思わない。簡単な問題(ガウス分布や正弦波系列のシーケンスの推定、線のフィッティングなど)のためのEMの数回の反復(3-4)を示す数値例を誰かに教えていただけますか。

誰かが(合成データを使用して)コードの一部を指し示すことができたとしても、そのコードをステップスルーしてみることができます。


1
k-meansは非常にemですが、分散は一定であり、比較的単純です。
EngrStudent

2
@ arjsgh21航空機に関する言及された論文を投稿してください。とても興味深いですね。ありがとう
和漢短歌

1
Emアルゴリズム "EM Demystified:An Expectation-Maximization Tutorial"の非常に明確な数学的理解を提供すると主張するオンラインチュートリアルがありますが、この例は非常に悪いので、理解不能な境界線があります。
三味線エキスパート

回答:


98

これは、実用的で(私の意見では)非常に直感的な「コイントス」の例でEMを学習するためのレシピです。

  1. DoとBatzoglouによるこの短いEMチュートリアルペーパーを読んでください。これは、コイントスの例が説明されているスキーマです。

    ここに画像の説明を入力してください

  2. 特に「期待」ステップの確率がどこから来るのかについて、頭に疑問符が付いている場合があります。この数学スタック交換ページの説明をご覧ください

  3. アイテム1のEMチュートリアルペーパーのコイントス問題の解決策をシミュレートするPythonで記述したこのコードを見てください。

    import numpy as np
    import math
    import matplotlib.pyplot as plt
    
    ## E-M Coin Toss Example as given in the EM tutorial paper by Do and Batzoglou* ##
    
    def get_binomial_log_likelihood(obs,probs):
        """ Return the (log)likelihood of obs, given the probs"""
        # Binomial Distribution Log PDF
        # ln (pdf)      = Binomial Coeff * product of probabilities
        # ln[f(x|n, p)] =   comb(N,k)    * num_heads*ln(pH) + (N-num_heads) * ln(1-pH)
    
        N = sum(obs);#number of trials  
        k = obs[0] # number of heads
        binomial_coeff = math.factorial(N) / (math.factorial(N-k) * math.factorial(k))
        prod_probs = obs[0]*math.log(probs[0]) + obs[1]*math.log(1-probs[0])
        log_lik = binomial_coeff + prod_probs
    
        return log_lik
    
    # 1st:  Coin B, {HTTTHHTHTH}, 5H,5T
    # 2nd:  Coin A, {HHHHTHHHHH}, 9H,1T
    # 3rd:  Coin A, {HTHHHHHTHH}, 8H,2T
    # 4th:  Coin B, {HTHTTTHHTT}, 4H,6T
    # 5th:  Coin A, {THHHTHHHTH}, 7H,3T
    # so, from MLE: pA(heads) = 0.80 and pB(heads)=0.45
    
    # represent the experiments
    head_counts = np.array([5,9,8,4,7])
    tail_counts = 10-head_counts
    experiments = zip(head_counts,tail_counts)
    
    # initialise the pA(heads) and pB(heads)
    pA_heads = np.zeros(100); pA_heads[0] = 0.60
    pB_heads = np.zeros(100); pB_heads[0] = 0.50
    
    # E-M begins!
    delta = 0.001  
    j = 0 # iteration counter
    improvement = float('inf')
    while (improvement>delta):
        expectation_A = np.zeros((len(experiments),2), dtype=float) 
        expectation_B = np.zeros((len(experiments),2), dtype=float)
        for i in range(0,len(experiments)):
            e = experiments[i] # i'th experiment
              # loglikelihood of e given coin A:
            ll_A = get_binomial_log_likelihood(e,np.array([pA_heads[j],1-pA_heads[j]])) 
              # loglikelihood of e given coin B
            ll_B = get_binomial_log_likelihood(e,np.array([pB_heads[j],1-pB_heads[j]])) 
    
              # corresponding weight of A proportional to likelihood of A 
            weightA = math.exp(ll_A) / ( math.exp(ll_A) + math.exp(ll_B) ) 
    
              # corresponding weight of B proportional to likelihood of B
            weightB = math.exp(ll_B) / ( math.exp(ll_A) + math.exp(ll_B) ) 
    
            expectation_A[i] = np.dot(weightA, e) 
            expectation_B[i] = np.dot(weightB, e)
    
        pA_heads[j+1] = sum(expectation_A)[0] / sum(sum(expectation_A)); 
        pB_heads[j+1] = sum(expectation_B)[0] / sum(sum(expectation_B)); 
    
        improvement = ( max( abs(np.array([pA_heads[j+1],pB_heads[j+1]]) - 
                        np.array([pA_heads[j],pB_heads[j]]) )) )
        j = j+1
    
    plt.figure();
    plt.plot(range(0,j),pA_heads[0:j], 'r--')
    plt.plot(range(0,j),pB_heads[0:j])
    plt.show()
    

2
@Zhubarb:ループの終了条件を説明してください(つまり、アルゴリズムが収束するタイミングを決定するために)。「改善」変数は何を計算しますか?
stackoverflowuser2010 14年

間の1)の変化:@ stackoverflowuser2010、改善は2つのデルタを見てpA_heads[j+1]pA_heads[j]と2)の間の変化pB_heads[j+1]pB_heads[j]。そして、2つの変更の最大値を取ります。たとえば、Delta_A=0.001との場合、Delta_B=0.02ステップからステップjへの改善はとなりj+1ます0.02
ジュバル14年

1
@Zhubarb:それはEMのコンバージェンスを計算するための標準的なアプローチですか、それともあなたが思いついたものですか?標準的なアプローチの場合、参考文献を引用してください。
stackoverflowuser2010 14年

ここでは EMの収束の参照があります。少し前にコードを書いたので、あまりよく覚えられません。コードに表示されるのは、この特定のケースの収束基準であると思います。アイデアは、AとBの改善の最大値が未満の場合に反復を停止することですdelta
ジュバル14年

1
すばらしい、テキストの段落ができないことを明確にする良いコードのようなものはありません
-jon_simon

63

あなたの質問には2つの部分があるように思えます:根本的なアイデアと具体的な例です。根本的なアイデアから始め、次に下部の例にリンクします。


あなたが知る必要があるようにそれはそうどこEMはキャッチ22状況で有用であるあなたが計算できる前に、あなたが知っている必要が使用すると、計算することができます前に。B B AABBA

人々が扱う最も一般的なケースは、おそらく混合分布です。この例では、単純なガウス混合モデルを見てみましょう。

平均と単位分散が異なる2つの異なる単変量ガウス分布があります。

たくさんのデータポイントがありますが、どの分布からどのポイントが来たかはわかりません。また、2つの分布の平均についてもわかりません。

そして今、あなたは立ち往生しています:

  • 真の意味がわかっていれば、どのデータポイントがどのガウス分布に由来しているかを把握できます。たとえば、データポイントの値が非常に高い場合、平均値が高い分布に由来する可能性があります。しかし、その意味がわからないため、これは機能しません。

  • 各ポイントがどの分布に由来するかがわかっている場合、関連ポイントのサンプル平均を使用して2つの分布の平均を推定できます。しかし、どの分布にどのポイントを割り当てるかは実際にはわからないため、これも機能しません。

したがって、どちらのアプローチもうまくいくようには見えません。答えを見つける前に答えを知る必要があり、行き詰まっています。

EMでできることは、プロセス全体を一度に取り組むのではなく、これら2つの扱いやすいステップを交互に行うことです。

2つの手段についての推測から始める必要があります(推測は必ずしも非常に正確である必要はありませんが、どこかから始める必要があります)。

平均についての推測が正確であれば、上記の最初の箇条書きのステップを実行するのに十分な情報が得られ、各データポイントを(確率的に)2つのガウス分布のいずれかに割り当てることができます。推測が間違っていることはわかっていますが、とにかくこれを試してみましょう。そして、各ポイントに割り当てられた分布が与えられると、2番目の弾丸ポイントを使用して平均の新しい推定値を取得できます。これら2つのステップをループするたびに、モデルの尤度の下限を改善していることがわかります。

それはすでにかなりクールです。上の箇条書きの2つの提案は個別に機能するようには見えませんでしたが、それらを一緒に使用してモデルを改善することができます。EM の本当の魔法は、十分な反復の後、下限が非常に高くなるため、EMとローカル最大値の間にスペースがなくなることです。その結果、ローカルで尤度を最適化しました。

したがって、モデルを改善しただけでなく、インクリメンタル更新で見つけることができる最良のモデルを見つけました。


ウィキペディアのこのページは、もう少し複雑な例(2次元のガウス分布と未知の共分散)を示していますが、基本的な考え方は同じです。またR、例を実装するための十分にコメントされたコードも含まれています。

コードでは、「期待値」ステップ(Eステップ)は私の最初の箇条書きに対応します。各ガウスの現在のパラメーターが与えられると、どのデータポイントに対してどのガウスが責任を負うかがわかります。「最大化」ステップ(Mステップ)は、2番目の箇条書きのように、これらの割り当てが与えられると、平均と共分散を更新します。

アニメーションで見ることができるように、これらの更新により、アルゴリズムはひどい推定値のセットから非常に優れた推定値のセットにすぐに移動できます。EMが検出する2つのガウス分布を中心とする2つの点の雲があるようです。


13

以下は、平均と標準偏差の推定に使用される期待値最大化(EM)の例です。コードはPythonで記述されていますが、言語に精通していなくても簡単に理解できるはずです。

EMの動機

以下に示す赤と青の点は、それぞれ特定の平均と標準偏差を持つ2つの異なる正規分布から描画されます。

ここに画像の説明を入力してください

赤分布の「真の」平均および標準偏差パラメーターの合理的な近似値を計算するには、赤点を非常に簡単に調べて各点の位置を記録し、おなじみの式を使用します(青のグループについても同様)。 。

次に、ポイントのグループが2つあることがわかっているが、どのポイントがどのグループに属しているかがわからない場合を考えます。つまり、色は隠されています:

ここに画像の説明を入力してください

ポイントを2つのグループに分割する方法はまったく明らかではありません。現在、位置を見て、赤分布または青分布のパラメーターの推定値を計算することはできません。

これは、EMを使用して問題を解決できる場所です。

EMを使用してパラメーターを推定する

上記のポイントを生成するために使用されるコードは次のとおりです。ポイントが引き出された正規分布の実際の平均と標準偏差を確認できます。変数redblueは、それぞれ赤と青のグループの各ポイントの位置を保持します。

import numpy as np
from scipy import stats

np.random.seed(110) # for reproducible random results

# set parameters
red_mean = 3
red_std = 0.8

blue_mean = 7
blue_std = 2

# draw 20 samples from normal distributions with red/blue parameters
red = np.random.normal(red_mean, red_std, size=20)
blue = np.random.normal(blue_mean, blue_std, size=20)

both_colours = np.sort(np.concatenate((red, blue)))

各ポイントの色を確認できる場合、ライブラリ関数を使用して平均と標準偏差を回復しようとします。

>>> np.mean(red)
2.802
>>> np.std(red)
0.871
>>> np.mean(blue)
6.932
>>> np.std(blue)
2.195

しかし、色は私たちから隠されているので、EMプロセスを開始します...

まず、各グループのパラメーターの値を推測するだけです(ステップ1)。これらの推測は必ずしも良いものである必要はありません。

# estimates for the mean
red_mean_guess = 1.1
blue_mean_guess = 9

# estimates for the standard deviation
red_std_guess = 2
blue_std_guess = 1.7

ここに画像の説明を入力してください

かなり悪い推測-平均点は、ポイントグループの「中間」から遠く離れているように見えます。

EMを続行し、これらの推測を​​改善するために、平均と標準偏差の推測の下に表示される各データポイント(その秘密の色に関係なく)の尤度を計算します(ステップ2)。

変数both_coloursは各データポイントを保持します。この関数stats.normは、指定されたパラメーターを使用して、正規分布の下でポイントの確率を計算します。

likelihood_of_red = stats.norm(red_mean_guess, red_std_guess).pdf(both_colours)
likelihood_of_blue = stats.norm(blue_mean_guess, blue_std_guess).pdf(both_colours)

これは、たとえば、現在の推測では、1.761のデータポイントは青(0.00003)よりも赤(0.189)である可能性がはるかに高いことを示しています。

これらの2つの尤度値を重みに変換して(ステップ3)、次のように合計が1になるようにすることができます。

likelihood_total = likelihood_of_red + likelihood_of_blue

red_weight = likelihood_of_red / likelihood_total
blue_weight = likelihood_of_blue / likelihood_total

現在の推定値と新しく計算された重みを使用して、パラメータの新しい、おそらくより良い推定値を計算できます(ステップ4)。平均値の関数と標準偏差の関数が必要です。

def estimate_mean(data, weight):
    return np.sum(data * weight) / np.sum(weight)

def estimate_std(data, weight, mean):
    variance = np.sum(weight * (data - mean)**2) / np.sum(weight)
    return np.sqrt(variance)

これらは、データの平均と標準偏差に通常の関数に非常に似ています。違いは、weight各データポイントに重みを割り当てるパラメーターの使用です。

この重み付けは、EMの鍵です。データポイントの色の重みが大きいほど、データポイントはその色のパラメータの次の推定値に大きく影響します。最終的に、これは各パラメーターを正しい方向に引く効果があります。

新しい推測は、次の関数を使用して計算されます。

# new estimates for standard deviation
blue_std_guess = estimate_std(both_colours, blue_weight, blue_mean_guess)
red_std_guess = estimate_std(both_colours, red_weight, red_mean_guess)

# new estimates for mean
red_mean_guess = estimate_mean(both_colours, red_weight)
blue_mean_guess = estimate_mean(both_colours, blue_weight)

EMプロセスは、ステップ2以降のこれらの新しい推測で繰り返されます。特定の反復回数(20回など)の間、またはパラメーターが収束するまでステップを繰り返すことができます。

5回の反復の後、最初の悪い推測が改善し始めるのがわかります。

ここに画像の説明を入力してください

20回の反復後、EMプロセスはほぼ収束しました。

ここに画像の説明を入力してください

比較のために、色情報が隠されていない場合に計算された値と比較したEMプロセスの結果を以下に示します。

          | EM guess | Actual 
----------+----------+--------
Red mean  |    2.910 |   2.802
Red std   |    0.854 |   0.871
Blue mean |    6.838 |   6.932
Blue std  |    2.227 |   2.195

注:この回答は、スタックオーバーフローに関する私の回答をここに適用したものです。


10

Zhubarbの回答に続いて、DoとBatzoglouの「コイン投げ」EMの例をGNU Rでmle実装しました。stats4パッケージの機能を使用していることに注意してください。

require("stats4");

## sample data from Do and Batzoglou
ds<-data.frame(heads=c(5,9,8,4,7),n=c(10,10,10,10,10),
    coin=c("B","A","A","B","A"),weight_A=1:5*0)

## "baby likelihood" for a single observation
llf <- function(heads, n, theta) {
  comb <- function(n, x) { #nCr function
    return(factorial(n) / (factorial(x) * factorial(n-x)))
  }
  if (theta<0 || theta >1) { # probabilities should be in [0,1]
    return(-Inf);
  }
  z<-comb(n,heads)* theta^heads * (1-theta)^(n-heads);
  return (log(z))
}

## the "E-M" likelihood function
em <- function(theta_A,theta_B) {
  # expectation step: given current parameters, what is the likelihood
  # an observation is the result of tossing coin A (vs coin B)?
  ds$weight_A <<- by(ds, 1:nrow(ds), function(row) {
    llf_A <- llf(row$heads,row$n, theta_A);
    llf_B <- llf(row$heads,row$n, theta_B);

    return(exp(llf_A)/(exp(llf_A)+exp(llf_B)));
  })

  # maximisation step: given params and weights, calculate likelihood of the sample
  return(- sum(by(ds, 1:nrow(ds), function(row) {
    llf_A <- llf(row$heads,row$n, theta_A);
    llf_B <- llf(row$heads,row$n, theta_B);

    return(row$weight_A*llf_A + (1-row$weight_A)*llf_B);
  })))
}

est<-mle(em,start = list(theta_A=0.6,theta_B=0.5), nobs=NROW(ds))

1
@ user3096626最大化ステップで、Aコインの可能性(row $ weight_A)に対数確率(llf_A)を掛ける理由を説明してください。特別なルールや理由がありますか?つまり、可能性や対数尤度を単に増やすだけで、裾を混ぜることはしないということです。また、新しいトピック
Alina


5

Zhubarbの答えは素晴らしいですが、残念ながらPythonにあります。以下は、同じ問題で実行されたEMアルゴリズムのJava実装です(Do and Batzoglouの記事2008年)。パラメーターがどのように収束するかを確認するために、標準出力にいくつかのprintfを追加しました。

thetaA = 0.71301, thetaB = 0.58134
thetaA = 0.74529, thetaB = 0.56926
thetaA = 0.76810, thetaB = 0.54954
thetaA = 0.78316, thetaB = 0.53462
thetaA = 0.79106, thetaB = 0.52628
thetaA = 0.79453, thetaB = 0.52239
thetaA = 0.79593, thetaB = 0.52073
thetaA = 0.79647, thetaB = 0.52005
thetaA = 0.79667, thetaB = 0.51977
thetaA = 0.79674, thetaB = 0.51966
thetaA = 0.79677, thetaB = 0.51961
thetaA = 0.79678, thetaB = 0.51960
thetaA = 0.79679, thetaB = 0.51959
Final result:
thetaA = 0.79678, thetaB = 0.51960

Javaコードは以下のとおりです。

import java.util.*;

/*****************************************************************************
This class encapsulates the parameters of the problem. For this problem posed
in the article by (Do and Batzoglou, 2008), the parameters are thetaA and
thetaB, the probability of a coin coming up heads for the two coins A and B.
*****************************************************************************/
class Parameters
{
    double _thetaA = 0.0; // Probability of heads for coin A.
    double _thetaB = 0.0; // Probability of heads for coin B.

    double _delta = 0.00001;

    public Parameters(double thetaA, double thetaB)
    {
        _thetaA = thetaA;
        _thetaB = thetaB;
    }

    /*************************************************************************
    Returns true if this parameter is close enough to another parameter
    (typically the estimated parameter coming from the maximization step).
    *************************************************************************/
    public boolean converged(Parameters other)
    {
        if (Math.abs(_thetaA - other._thetaA) < _delta &&
            Math.abs(_thetaB - other._thetaB) < _delta)
        {
            return true;
        }

        return false;
    }

    public double getThetaA()
    {
        return _thetaA;
    }

    public double getThetaB()
    {
        return _thetaB;
    }

    public String toString()
    {
        return String.format("thetaA = %.5f, thetaB = %.5f", _thetaA, _thetaB);
    }

}


/*****************************************************************************
This class encapsulates an observation, that is the number of heads
and tails in a trial. The observation can be either (1) one of the
observed observations, or (2) an estimated observation resulting from
the expectation step.
*****************************************************************************/
class Observation
{
    double _numHeads = 0;
    double _numTails = 0;

    public Observation(String s)
    {
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == 'H')
            {
                _numHeads++;
            }
            else if (c == 'T')
            {
                _numTails++;
            }
            else
            {
                throw new RuntimeException("Unknown character: " + c);
            }
        }
    }

    public Observation(double numHeads, double numTails)
    {
        _numHeads = numHeads;
        _numTails = numTails;
    }

    public double getNumHeads()
    {
        return _numHeads;
    }

    public double getNumTails()
    {
        return _numTails;
    }

    public String toString()
    {
        return String.format("heads: %.1f, tails: %.1f", _numHeads, _numTails);
    }

}

/*****************************************************************************
This class runs expectation-maximization for the problem posed by the article
from (Do and Batzoglou, 2008).
*****************************************************************************/
public class EM
{
    // Current estimated parameters.
    private Parameters _parameters;

    // Observations from the trials. These observations are set once.
    private final List<Observation> _observations;

    // Estimated observations per coin. These observations are the output
    // of the expectation step.
    private List<Observation> _expectedObservationsForCoinA;
    private List<Observation> _expectedObservationsForCoinB;

    private static java.io.PrintStream o = System.out;

    /*************************************************************************
    Principal constructor.
    @param observations The observations from the trial.
    @param parameters The initial guessed parameters.
    *************************************************************************/
    public EM(List<Observation> observations, Parameters parameters)
    {
        _observations = observations;
        _parameters = parameters;
    }

    /*************************************************************************
    Run EM until parameters converge.
    *************************************************************************/
    public Parameters run()
    {

        while (true)
        {
            expectation();

            Parameters estimatedParameters = maximization();

            o.printf("%s\n", estimatedParameters);

            if (_parameters.converged(estimatedParameters)) {
                break;
            }

            _parameters = estimatedParameters;
        }

        return _parameters;

    }

    /*************************************************************************
    Given the observations and current estimated parameters, compute new
    estimated completions (distribution over the classes) and observations.
    *************************************************************************/
    private void expectation()
    {

        _expectedObservationsForCoinA = new ArrayList<Observation>();
        _expectedObservationsForCoinB = new ArrayList<Observation>();

        for (Observation observation : _observations)
        {
            int numHeads = (int)observation.getNumHeads();
            int numTails = (int)observation.getNumTails();

            double probabilityOfObservationForCoinA=
                binomialProbability(10, numHeads, _parameters.getThetaA());

            double probabilityOfObservationForCoinB=
                binomialProbability(10, numHeads, _parameters.getThetaB());

            double normalizer = probabilityOfObservationForCoinA +
                                probabilityOfObservationForCoinB;

            // Compute the completions for coin A and B (i.e. the probability
            // distribution of the two classes, summed to 1.0).

            double completionCoinA = probabilityOfObservationForCoinA /
                                     normalizer;
            double completionCoinB = probabilityOfObservationForCoinB /
                                     normalizer;

            // Compute new expected observations for the two coins.

            Observation expectedObservationForCoinA =
                new Observation(numHeads * completionCoinA,
                                numTails * completionCoinA);

            Observation expectedObservationForCoinB =
                new Observation(numHeads * completionCoinB,
                                numTails * completionCoinB);

            _expectedObservationsForCoinA.add(expectedObservationForCoinA);
            _expectedObservationsForCoinB.add(expectedObservationForCoinB);
        }
    }

    /*************************************************************************
    Given new estimated observations, compute new estimated parameters.
    *************************************************************************/
    private Parameters maximization()
    {

        double sumCoinAHeads = 0.0;
        double sumCoinATails = 0.0;
        double sumCoinBHeads = 0.0;
        double sumCoinBTails = 0.0;

        for (Observation observation : _expectedObservationsForCoinA)
        {
            sumCoinAHeads += observation.getNumHeads();
            sumCoinATails += observation.getNumTails();
        }

        for (Observation observation : _expectedObservationsForCoinB)
        {
            sumCoinBHeads += observation.getNumHeads();
            sumCoinBTails += observation.getNumTails();
        }

        return new Parameters(sumCoinAHeads / (sumCoinAHeads + sumCoinATails),
                              sumCoinBHeads / (sumCoinBHeads + sumCoinBTails));

        //o.printf("parameters: %s\n", _parameters);

    }

    /*************************************************************************
    Since the coin-toss experiment posed in this article is a Bernoulli trial,
    use a binomial probability Pr(X=k; n,p) = (n choose k) * p^k * (1-p)^(n-k).
    *************************************************************************/
    private static double binomialProbability(int n, int k, double p)
    {
        double q = 1.0 - p;
        return nChooseK(n, k) * Math.pow(p, k) * Math.pow(q, n-k);
    }

    private static long nChooseK(int n, int k)
    {
        long numerator = 1;

        for (int i = 0; i < k; i++)
        {
            numerator = numerator * n;
            n--;
        }

        long denominator = factorial(k);

        return (long)(numerator / denominator);
    }

    private static long factorial(int n)
    {
        long result = 1;
        for (; n >0; n--)
        {
            result = result * n;
        }

        return result;
    }

    /*************************************************************************
    Entry point into the program.
    *************************************************************************/
    public static void main(String argv[])
    {
        // Create the observations and initial parameter guess
        // from the (Do and Batzoglou, 2008) article.

        List<Observation> observations = new ArrayList<Observation>();
        observations.add(new Observation("HTTTHHTHTH"));
        observations.add(new Observation("HHHHTHHHHH"));
        observations.add(new Observation("HTHHHHHTHH"));
        observations.add(new Observation("HTHTTTHHTT"));
        observations.add(new Observation("THHHTHHHTH"));

        Parameters initialParameters = new Parameters(0.6, 0.5);

        EM em = new EM(observations, initialParameters);

        Parameters finalParameters = em.run();

        o.printf("Final result:\n%s\n", finalParameters);
    }
}

5
% Implementation of the EM (Expectation-Maximization)algorithm example exposed on:
% Motion Segmentation using EM - a short tutorial, Yair Weiss, %http://www.cs.huji.ac.il/~yweiss/emTutorial.pdf
% Juan Andrade, jandrader@yahoo.com

clear all
clc

%% Setup parameters
m1 = 2;                 % slope line 1
m2 = 6;                 % slope line 2
b1 = 3;                 % vertical crossing line 1
b2 = -2;                % vertical crossing line 2
x = [-1:0.1:5];         % x axis values
sigma1 = 1;             % Standard Deviation of Noise added to line 1
sigma2 = 2;             % Standard Deviation of Noise added to line 2

%% Clean lines
l1 = m1*x+b1;           % line 1
l2 = m2*x+b2;           % line 2

%% Adding noise to lines
p1 = l1 + sigma1*randn(size(l1));
p2 = l2 + sigma2*randn(size(l2));

%% showing ideal and noise values
figure,plot(x,l1,'r'),hold,plot(x,l2,'b'), plot(x,p1,'r.'),plot(x,p2,'b.'),grid

%% initial guess
m11(1) = -1;            % slope line 1
m22(1) = 1;             % slope line 2
b11(1) = 2;             % vertical crossing line 1
b22(1) = 2;             % vertical crossing line 2

%% EM algorithm loop
iterations = 10;        % number of iterations (a stop based on a threshold may used too)

for i=1:iterations

    %% expectation step (equations 2 and 3)
    res1 = m11(i)*x + b11(i) - p1;
    res2 = m22(i)*x + b22(i) - p2;
    % line 1
    w1 = (exp((-res1.^2)./sigma1))./((exp((-res1.^2)./sigma1)) + (exp((-res2.^2)./sigma2)));

    % line 2
    w2 = (exp((-res2.^2)./sigma2))./((exp((-res1.^2)./sigma1)) + (exp((-res2.^2)./sigma2)));

    %% maximization step  (equation 4)
    % line 1
    A(1,1) = sum(w1.*(x.^2));
    A(1,2) = sum(w1.*x);
    A(2,1) = sum(w1.*x);
    A(2,2) = sum(w1);
    bb = [sum(w1.*x.*p1) ; sum(w1.*p1)];
    temp = A\bb;
    m11(i+1) = temp(1);
    b11(i+1) = temp(2);

    % line 2
    A(1,1) = sum(w2.*(x.^2));
    A(1,2) = sum(w2.*x);
    A(2,1) = sum(w2.*x);
    A(2,2) = sum(w2);
    bb = [sum(w2.*x.*p2) ; sum(w2.*p2)];
    temp = A\bb;
    m22(i+1) = temp(1);
    b22(i+1) = temp(2);

    %% plotting evolution of results
    l1temp = m11(i+1)*x+b11(i+1);
    l2temp = m22(i+1)*x+b22(i+1);
    figure,plot(x,l1temp,'r'),hold,plot(x,l2temp,'b'), plot(x,p1,'r.'),plot(x,p2,'b.'),grid
end

4
生のコードに議論や説明を追加できますか?少なくともあなたが書いている言語を言及する多くの読者に有用であろう。
Glen_b

1
@Glen_b-これはMatLabです。回答のコードをより広範囲に注釈することは、どれほど礼儀正しいと考えられますか。
EngrStudent

4

まあ、マリアLリッツォのRに関する本を読むことをお勧めします。章の1つには、数値例を使用したEMアルゴリズムの使用が含まれています。理解を深めるためにコードを調べたことを覚えています。

また、最初はクラスタリングの観点から表示してみてください。2つの異なる正規密度から10個の観測値を取得するクラスタリング問題を手作業で解決します。これは役立つはずです。Rから助けを借りてください:)


2

θA=0.6θB=0.5

# gem install distribution
require 'distribution'

# error bound
EPS = 10**-6

# number of coin tosses
N = 10

# observations
X = [5, 9, 8, 4, 7]

# randomly initialized thetas
theta_a, theta_b = 0.6, 0.5

p [theta_a, theta_b]

loop do
  expectation = X.map do |h|
    like_a = Distribution::Binomial.pdf(h, N, theta_a)
    like_b = Distribution::Binomial.pdf(h, N, theta_b)

    norm_a = like_a / (like_a + like_b)
    norm_b = like_b / (like_a + like_b)

    [norm_a, norm_b, h]
  end

  maximization = expectation.each_with_object([0.0, 0.0, 0.0, 0.0]) do |(norm_a, norm_b, h), r|
    r[0] += norm_a * h; r[1] += norm_a * (N - h)
    r[2] += norm_b * h; r[3] += norm_b * (N - h)
  end

  theta_a_hat = maximization[0] / (maximization[0] + maximization[1])
  theta_b_hat = maximization[2] / (maximization[2] + maximization[3])

  error_a = (theta_a_hat - theta_a).abs / theta_a
  error_b = (theta_b_hat - theta_b).abs / theta_b

  theta_a, theta_b = theta_a_hat, theta_b_hat

  p [theta_a, theta_b]

  break if error_a < EPS && error_b < EPS
end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.