अपेक्षा-अधिकतमकरण को समझने के लिए संख्यात्मक उदाहरण


117

मैं EM एल्गोरिथ्म पर एक अच्छी समझ पाने की कोशिश कर रहा हूं, इसे लागू करने और इसका उपयोग करने में सक्षम होने के लिए। मैंने एक पूरा दिन सिद्धांत और एक कागज को पढ़ने में बिताया, जहां ईएम का उपयोग रडार से आने वाली स्थिति की जानकारी का उपयोग करके एक विमान को ट्रैक करने के लिए किया जाता है। ईमानदारी से, मुझे नहीं लगता कि मैं अंतर्निहित विचार को पूरी तरह से समझता हूं। क्या कोई मुझे एक साधारण उदाहरण के लिए ईएम के कुछ पुनरावृत्तियों (3-4) को एक सरल समस्या के लिए इंगित कर सकता है (जैसे कि गॉसियन वितरण के मापदंडों का आकलन करना या एक साइनसोइडल श्रृंखला का एक अनुक्रम या एक पंक्ति फिटिंग)।

यहां तक ​​कि अगर कोई मुझे कोड के एक टुकड़े (सिंथेटिक डेटा के साथ) को इंगित कर सकता है, तो मैं कोड के माध्यम से कदम रखने की कोशिश कर सकता हूं।


1
k- साधन बहुत अधिक है, लेकिन निरंतर विचरण के साथ, और अपेक्षाकृत सरल है।
EngrStudent 11

2
@ arjsgh21 क्या आप विमान के बारे में उल्लेखित कागज पोस्ट कर सकते हैं? बहुत दिलचस्प लगता है। धन्यवाद
वाकान टंका

1
ऑनलाइन एक ट्यूटोरियल है जो एम एल्गोरिथ्म की एक बहुत ही स्पष्ट गणितीय समझ प्रदान करने का दावा करता है "ईएम डिमिस्टिफाईड: ए एक्सपेक्टेशन-मैक्सिमाइजेशन ट्यूटोरियल" हालांकि, उदाहरण इतना बुरा है कि यह समझ से बाहर है।
शमीसेन एक्सपर्ट

जवाबों:


98

यह ईएम को एक व्यावहारिक और (मेरी राय में) बहुत सहज 'सिक्का-टॉस' उदाहरण के साथ सीखने का एक नुस्खा है:

  1. Do और Batzoglou द्वारा इस लघु EM ट्यूटोरियल पेपर को पढ़ें । यह वह स्कीमा है जिसमें सिक्का टॉस का उदाहरण समझाया गया है:

    यहां छवि विवरण दर्ज करें

  2. आपके सिर में प्रश्न चिह्न हो सकते हैं, विशेष रूप से इस बात के संबंध में कि प्रत्याशा कदम में संभावनाएँ कहाँ से आती हैं। कृपया इस गणित स्टैक एक्सचेंज पृष्ठ पर स्पष्टीकरण पर एक नज़र डालें ।

  3. इस कोड को देखें / चलाएं जो मैंने पायथन में लिखा था जो आइटम 1 के ईएम ट्यूटोरियल पेपर में सिक्का-टॉस समस्या के समाधान का अनुकरण करता है:

    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

1) के बीच परिवर्तन: @ stackoverflowuser2010, सुधार दो डेल्टा पर लग रहा है pA_heads[j+1]और pA_heads[j]और 2) के बीच परिवर्तन pB_heads[j+1]और pB_heads[j]। और यह अधिकतम दो बदलाव लेता है। उदाहरण यदि के लिए Delta_A=0.001और Delta_B=0.02कदम से, सुधार jकरने के लिए j+1किया जाएगा 0.02
झूबर्ब

1
@Zhubarb: क्या यह EM में अभिसरण अभिकलन के लिए एक मानक दृष्टिकोण है, या यह कि आप कुछ लेकर आए हैं? यदि यह एक मानक दृष्टिकोण है, तो क्या आप संदर्भ का हवाला दे सकते हैं?
stackoverflowuser2010

यहाँ EM के अभिसरण पर एक संदर्भ दिया गया है। मैंने कुछ समय पहले कोड लिखा था इसलिए बहुत अच्छी तरह से याद नहीं कर सकता। मुझे विश्वास है कि आप इस विशेष मामले के लिए कोड में जो देखते हैं वह मेरी अभिसरण मानदंड है। विचार यह है कि पुनरावृत्तियों को रोकना जब A और B के लिए अधिकतम सुधार से कम हो delta
झूबर्ब

1
शानदार, वहाँ कुछ भी अच्छा कोड की तरह स्पष्ट करने के लिए पाठ के पैराग्राफ नहीं कर सकते हैं
jon_simon

63

ऐसा लगता है कि आपके प्रश्न के दो भाग हैं: अंतर्निहित विचार और एक ठोस उदाहरण। मैं अंतर्निहित विचार से शुरू करता हूं, फिर नीचे एक उदाहरण से लिंक करता हूं।


बीबी

लोगों के साथ जो सबसे आम मामला है, वह शायद मिश्रण वितरण है। हमारे उदाहरण के लिए, आइए एक सरल गाऊसी मिश्रण मॉडल देखें:

आपके पास विभिन्न साधनों और इकाई प्रसरण के साथ दो अलग-अलग अविभाज्य गाऊसी वितरण हैं।

आपके पास डेटा बिंदुओं का एक समूह है, लेकिन आप सुनिश्चित नहीं हैं कि कौन से वितरण किस बिंदु से आए हैं, और आप दो वितरणों के साधनों के बारे में निश्चित नहीं हैं।

और अब आप फंस गए हैं:

  • यदि आपको वास्तविक साधन पता था, तो आप यह पता लगा सकते हैं कि कौन से डेटा बिंदु किस गौसियन से आए हैं। उदाहरण के लिए, यदि किसी डेटा बिंदु का मूल्य बहुत अधिक था, तो यह संभवतः उच्च माध्य के साथ वितरण से आया था। लेकिन आप नहीं जानते कि साधन क्या हैं, इसलिए यह काम नहीं करेगा।

  • यदि आप जानते हैं कि प्रत्येक बिंदु किस वितरण से आया है, तो आप प्रासंगिक बिंदुओं के नमूना साधनों का उपयोग करके दो वितरणों का अनुमान लगा सकते हैं। लेकिन आप वास्तव में नहीं जानते कि कौन से वितरण को असाइन करने के लिए कौन से बिंदु हैं, इसलिए यह या तो काम नहीं करेगा।

इसलिए न तो दृष्टिकोण ऐसा लगता है जैसे यह काम करता है: आपको उत्तर खोजने से पहले उत्तर जानने की आवश्यकता होगी, और आप फंस गए हैं।

EM आपको एक ही बार में पूरी प्रक्रिया से निपटने के बजाय इन दो ट्रैक्टेबल चरणों के बीच वैकल्पिक करने देता है।

आपको दो साधनों के बारे में एक अनुमान के साथ शुरुआत करने की आवश्यकता होगी (हालांकि आपका अनुमान जरूरी नहीं कि बहुत सटीक हो, आपको कहीं और शुरू करने की आवश्यकता है)।

यदि साधनों के बारे में आपका अनुमान सही था, तो आपके पास मेरे पहले बुलेट बिंदु में कदम उठाने के लिए पर्याप्त जानकारी होगी, और आप (संभावित रूप से) प्रत्येक डेटा बिंदु को दो गॉसियनों में से एक को सौंप सकते हैं। भले ही हमें पता हो कि हमारा अनुमान गलत है, फिर भी हम इसे आजमाएँ। और फिर, प्रत्येक बिंदु के निर्दिष्ट वितरण को देखते हुए, आप दूसरी गोली बिंदु का उपयोग करके साधनों के लिए नए अनुमान प्राप्त कर सकते हैं। यह पता चलता है कि, हर बार जब आप इन दो चरणों के माध्यम से लूप करते हैं, तो आप मॉडल की संभावना पर कम बाध्य सुधार कर रहे हैं।

यह पहले से ही बहुत अच्छा है: भले ही बुलेट बिंदुओं में दो सुझाव ऐसा न लगे हों कि वे व्यक्तिगत रूप से काम करेंगे, फिर भी आप मॉडल को बेहतर बनाने के लिए उनका उपयोग कर सकते हैं। असली ईएम का जादू है कि, पर्याप्त पुनरावृत्तियों के बाद, निम्न बाउंड इतनी अधिक है कि इसमें और स्थानीय अधिकतम के बीच किसी भी स्थान नहीं होगा किया जाएगा। परिणामस्वरूप, और आप स्थानीय रूप से संभावना को अनुकूलित कर चुके हैं।

इसलिए आपने केवल मॉडल में सुधार नहीं किया है, आपको सबसे अच्छा संभव मॉडल मिल गया है जो वृद्धिशील अपडेट के साथ मिल सकता है।


विकिपीडिया का यह पृष्ठ थोड़ा अधिक जटिल उदाहरण (द्वि-आयामी गाऊसी और अज्ञात कोवरियन) दिखाता है, लेकिन मूल विचार एक ही है। इसमें Rउदाहरण को लागू करने के लिए अच्छी तरह से टिप्पणी की गई कोड भी शामिल है ।

कोड में, "एक्सपेक्टेशन" स्टेप (ई-स्टेप) मेरी पहली बुलेट पॉइंट से मेल खाती है: यह पता लगाना कि प्रत्येक गॉसियन के लिए वर्तमान मापदंडों को देखते हुए, कौन से गॉसियन को प्रत्येक डेटा पॉइंट की जिम्मेदारी मिलती है। "मैक्सिमाइज़ेशन" स्टेप (एम-स्टेप) इन असाइनमेंट को दिए गए साधनों और सहूलियतों को अपडेट करता है, जैसा कि मेरे दूसरे बुलेट पॉइंट में है।

जैसा कि आप एनीमेशन में देख सकते हैं, ये अपडेट एल्गोरिदम जल्दी से भयानक अनुमानों के एक सेट से बहुत अच्छे लोगों के एक सेट पर जाने की अनुमति देते हैं: वास्तव में दो गॉसियन वितरणों पर केंद्रित बिंदुओं के दो बादल प्रतीत होते हैं जो ईएम पाता है।


13

यहाँ मतलब और मानक विचलन का अनुमान लगाने के लिए उपयोग किए जाने वाले एक्सपेक्टेशन मैक्सिमाइजेशन (ईएम) का एक उदाहरण है। कोड पाइथन में है, लेकिन यह आसान होना चाहिए, भले ही आप भाषा से परिचित न हों।

ईएम के लिए प्रेरणा

नीचे दिखाए गए लाल और नीले बिंदु दो अलग-अलग सामान्य वितरणों से तैयार किए गए हैं, जिनमें से प्रत्येक एक विशेष माध्य और मानक विचलन के साथ हैं:

यहां छवि विवरण दर्ज करें

लाल वितरण के लिए "सही" माध्य और मानक विचलन मापदंडों के उचित अनुमानों की गणना करने के लिए, हम बहुत आसानी से लाल बिंदुओं को देख सकते हैं और प्रत्येक की स्थिति को रिकॉर्ड कर सकते हैं, और फिर परिचित सूत्रों का उपयोग कर सकते हैं (और इसी तरह नीले समूह के लिए) ।

अब उस मामले पर विचार करें जहां हम जानते हैं कि अंकों के दो समूह हैं, लेकिन हम यह नहीं देख सकते हैं कि कौन सा बिंदु किस समूह का है। दूसरे शब्दों में, रंग छिपे हुए हैं:

यहां छवि विवरण दर्ज करें

यह बिल्कुल स्पष्ट नहीं है कि अंकों को दो समूहों में कैसे विभाजित किया जाए। अब हम केवल वितरण और लाल वितरण या नीले वितरण के मापदंडों के अनुमानों की गणना करने में असमर्थ हैं।

यह वह जगह है जहां ईएम का उपयोग समस्या को हल करने के लिए किया जा सकता है।

मापदंडों का अनुमान लगाने के लिए ईएम का उपयोग करना

यहाँ ऊपर दिखाए गए बिंदुओं को उत्पन्न करने के लिए उपयोग किया गया कोड है। आप सामान्य वितरण के वास्तविक साधनों और मानक विचलन को देख सकते हैं, जिनसे अंक खींचे गए थे। चर redऔर blueबिंदु क्रमशः लाल और नीले समूहों में प्रत्येक बिंदु को पकड़ते हैं:

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

यहां छवि विवरण दर्ज करें

बहुत बुरा अनुमान है - मतलब यह है कि वे अंकों के समूह के किसी भी "मध्य" से एक लंबा रास्ता तय करते हैं।

ईएम के साथ जारी रखने और इन अनुमानों में सुधार करने के लिए, हम औसत और मानक विचलन ( चरण 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) होने की अधिक संभावना है।

हम इन दो संभावना मूल्यों को वज़न ( चरण 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)

बाद में चरण 2 से इन नए अनुमानों के साथ EM प्रक्रिया को दोहराया जाता है। हम दिए गए पुनरावृत्तियों की संख्या (20 कहते हैं) के लिए चरणों को दोहरा सकते हैं, या जब तक हम मापदंडों को नहीं देखते हैं।

पांच पुनरावृत्तियों के बाद, हम देखते हैं कि हमारे शुरुआती बुरे अनुमान बेहतर होने लगे हैं:

यहां छवि विवरण दर्ज करें

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

ज़ुबर्ब के जवाब के बाद, मैंने जीएनयू आर। ईएम उदाहरण में "ईएम उदाहरण" का प्रयोग करते हुए "डू एंड बत्ज़ोग्लू" सिक्का लागू किया - मैं पैकेज के 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 क्या आप यह बता सकते हैं कि अधिकतम संभावना में आप एक लॉग प्रायिकता (llf_A) द्वारा एक सिक्के (पंक्ति $ वेट_ए) की संभावना को कितने गुणा करते हैं? क्या कोई विशेष नियम या कारण है जो हम करते हैं? मेरा मतलब है कि कोई भी संभावना या तार्किकता को गुणा करेगा, लेकिन हेम को एक साथ नहीं मिलाएगा। मैंने एक नया विषय
Alina

9

उपरोक्त सभी महान संसाधनों की तरह दिखते हैं, लेकिन मुझे इस महान उदाहरण से जोड़ना होगा। यह बिंदुओं के समूह की दो पंक्तियों के मापदंडों को खोजने के लिए एक बहुत ही सरल व्याख्या प्रस्तुत करता है। ट्यूटोरियल Yair Weiss द्वारा है जबकि MIT में है।

http://www.cs.huji.ac.il/~yweiss/emTutorial.pdf
http://www.cs.huji.ac.il/~yweiss/tutorials.html


5

ज़ुब्बार द्वारा दिया गया जवाब बहुत अच्छा है, लेकिन दुर्भाग्य से यह पायथन में है। नीचे एक ही समस्या पर निष्पादित EM एल्गोरिथ्म का जावा कार्यान्वयन है (Do and Batzoglou, 2008 द्वारा लेख में प्रस्तुत किया गया है)। मैंने मानक आउटपुट में कुछ प्रिंटफ जोड़ा है यह देखने के लिए कि पैरामीटर कैसे परिवर्तित होते हैं।

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

जावा कोड इस प्रकार है:

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
@ गलेन_ बी - यह माटलैब है। मुझे आश्चर्य है कि उनके जवाब में किसी और कोड को अधिक बड़े पैमाने पर एनोटेट करने के लिए कितना विनम्र माना जाता है।
EngrStudent

4

खैर, मैं आपको मारिया एल रिज़ो द्वारा आर पर एक पुस्तक के माध्यम से जाने का सुझाव दूंगा। एक अध्याय में संख्यात्मक उदाहरण के साथ EM एल्गोरिथ्म का उपयोग होता है। मुझे याद है कि बेहतर समझ के लिए कोड के माध्यम से जाना।

इसके अलावा, इसे शुरुआत में देखने के बिंदु से देखने की कोशिश करें। हाथ से काम करना, एक भयावह समस्या जहां दो अलग-अलग सामान्य घनत्वों से 10 अवलोकन किए जाते हैं। यह R से मदद लेनी चाहिए। :)


2

θ=0.6θबी=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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.