Catatan: kode di balik jawaban ini dapat ditemukan di sini .
Misalkan kita memiliki beberapa data yang diambil sampelnya dari dua kelompok yang berbeda, merah dan biru:
Di sini, kita bisa melihat titik data mana yang termasuk dalam grup merah atau biru. Ini memudahkan untuk menemukan parameter yang menjadi ciri setiap grup. Misalnya, rata-rata dari grup merah adalah sekitar 3, rata-rata grup biru adalah sekitar 7 (dan kita dapat menemukan cara yang tepat jika kita mau).
Ini, secara umum, dikenal sebagai estimasi kemungkinan maksimum . Dengan adanya beberapa data, kami menghitung nilai parameter (atau parameter) yang paling menjelaskan data tersebut.
Sekarang bayangkan bahwa kita tidak dapat melihat nilai mana yang diambil sampelnya dari kelompok mana. Semuanya tampak ungu bagi kami:
Di sini kita memiliki pengetahuan bahwa ada dua kelompok nilai, tetapi kita tidak tahu kelompok mana nilai tertentu itu termasuk.
Masih dapatkah kita memperkirakan sarana untuk kelompok merah dan biru yang paling sesuai dengan data ini?
Ya, seringkali kita bisa! Maksimalisasi Harapan memberi kita cara untuk melakukannya. Ide yang sangat umum di balik algoritme adalah ini:
- Mulailah dengan perkiraan awal tentang kemungkinan setiap parameter.
- Hitung kemungkinan setiap parameter menghasilkan titik data.
- Hitung bobot untuk setiap titik data yang menunjukkan apakah lebih merah atau lebih biru berdasarkan kemungkinan itu dihasilkan oleh parameter. Gabungkan bobot dengan data ( ekspektasi ).
- Hitung perkiraan yang lebih baik untuk parameter menggunakan data yang disesuaikan dengan berat badan ( maksimalisasi ).
- Ulangi langkah 2 hingga 4 hingga estimasi parameter bertemu (proses berhenti menghasilkan estimasi yang berbeda).
Langkah-langkah ini memerlukan penjelasan lebih lanjut, jadi saya akan membahas masalah yang dijelaskan di atas.
Contoh: memperkirakan mean dan deviasi standar
Saya akan menggunakan Python dalam contoh ini, tetapi kodenya harus cukup mudah dipahami jika Anda tidak terbiasa dengan bahasa ini.
Misalkan kita memiliki dua kelompok, merah dan biru, dengan nilai yang didistribusikan seperti pada gambar di atas. Secara spesifik, setiap grup berisi nilai yang diambil dari distribusi normal dengan parameter berikut:
import numpy as np
from scipy import stats
np.random.seed(110) # for reproducible 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))) # for later use...
Berikut adalah gambar grup merah dan biru ini lagi (untuk menyelamatkan Anda dari keharusan menggulir ke atas):
Ketika kita dapat melihat warna dari setiap titik (yaitu kelompok mana tempat itu berada), sangat mudah untuk memperkirakan mean dan deviasi standar untuk setiap kelompok. Kami hanya meneruskan nilai merah dan biru ke fungsi bawaan di NumPy. Sebagai contoh:
>>> np.mean(red)
2.802
>>> np.std(red)
0.871
>>> np.mean(blue)
6.932
>>> np.std(blue)
2.195
Tetapi bagaimana jika kita tidak dapat melihat warna dari titik-titik tersebut? Artinya, alih-alih merah atau biru, setiap titik telah diwarnai ungu.
Untuk mencoba dan memulihkan parameter mean dan deviasi standar untuk grup merah dan biru, kita dapat menggunakan Expectation Maximization.
Langkah pertama kita ( langkah 1 di atas) adalah menebak nilai parameter untuk mean dan deviasi standar setiap grup. Kami tidak harus menebak dengan cerdas; kami dapat memilih nomor yang kami suka:
# 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
Estimasi parameter ini menghasilkan kurva lonceng yang terlihat seperti ini:
Ini adalah perkiraan yang buruk. Kedua cara (garis putus-putus vertikal) terlihat jauh dari "tengah" apapun untuk kelompok titik yang masuk akal, misalnya. Kami ingin meningkatkan perkiraan ini.
Langkah selanjutnya ( langkah 2 ) adalah menghitung kemungkinan setiap titik data muncul di bawah tebakan parameter saat ini:
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)
Di sini, kami hanya menempatkan setiap titik data ke dalam fungsi kepadatan probabilitas untuk distribusi normal menggunakan tebakan kami saat ini pada mean dan deviasi standar untuk merah dan biru. Ini memberitahu kita, misalnya, bahwa dengan dugaan kami saat ini titik data pada 1,761 adalah jauh lebih mungkin merah (0,189) dari biru (0,00003).
Untuk setiap titik data, kita dapat mengubah dua nilai kemungkinan ini menjadi bobot ( langkah 3 ) sehingga dijumlahkan menjadi 1 sebagai berikut:
likelihood_total = likelihood_of_red + likelihood_of_blue
red_weight = likelihood_of_red / likelihood_total
blue_weight = likelihood_of_blue / likelihood_total
Dengan perkiraan kami saat ini dan bobot yang baru kami hitung, kami sekarang dapat menghitung perkiraan baru untuk mean dan deviasi standar dari grup merah dan biru ( langkah 4 ).
Kami dua kali menghitung mean dan deviasi standar menggunakan semua titik data, tetapi dengan bobot yang berbeda: sekali untuk bobot merah dan satu kali untuk bobot biru.
Bagian penting dari intuisi adalah semakin besar bobot warna pada titik data, semakin banyak titik data yang memengaruhi perkiraan selanjutnya untuk parameter warna tersebut. Ini memiliki efek "menarik" parameter ke arah yang benar.
def estimate_mean(data, weight):
"""
For each data point, multiply the point by the probability it
was drawn from the colour's distribution (its "weight").
Divide by the total weight: essentially, we're finding where
the weight is centred among our data points.
"""
return np.sum(data * weight) / np.sum(weight)
def estimate_std(data, weight, mean):
"""
For each data point, multiply the point's squared difference
from a mean value by the probability it was drawn from
that distribution (its "weight").
Divide by the total weight: essentially, we're finding where
the weight is centred among the values for the difference of
each data point from the mean.
This is the estimate of the variance, take the positive square
root to find the standard deviation.
"""
variance = np.sum(weight * (data - mean)**2) / np.sum(weight)
return np.sqrt(variance)
# 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)
Kami memiliki perkiraan baru untuk parameter. Untuk memperbaikinya lagi, kita dapat melompat kembali ke langkah 2 dan mengulangi prosesnya. Kami melakukan ini hingga perkiraan bertemu, atau setelah beberapa iterasi dilakukan ( langkah 5 ).
Untuk data kami, lima iterasi pertama dari proses ini terlihat seperti ini (iterasi terbaru memiliki tampilan yang lebih kuat):
Kami melihat bahwa rata-rata sudah menyatu pada beberapa nilai, dan bentuk kurva (diatur oleh deviasi standar) juga menjadi lebih stabil.
Jika kami melanjutkan untuk 20 iterasi, kami berakhir dengan yang berikut:
Proses EM telah menyatu ke nilai-nilai berikut, yang ternyata sangat dekat dengan nilai sebenarnya (di mana kita dapat melihat warna - tidak ada variabel tersembunyi):
| EM guess | Actual | Delta
----------+----------+--------+-------
Red mean | 2.910 | 2.802 | 0.108
Red std | 0.854 | 0.871 | -0.017
Blue mean | 6.838 | 6.932 | -0.094
Blue std | 2.227 | 2.195 | 0.032
Dalam kode di atas, Anda mungkin telah memperhatikan bahwa estimasi baru untuk deviasi standar dihitung menggunakan estimasi mean dari iterasi sebelumnya. Pada akhirnya, tidak masalah jika kita menghitung nilai baru untuk mean terlebih dahulu karena kita hanya mencari varian nilai (tertimbang) di sekitar titik pusat. Kami masih akan melihat estimasi untuk parameter yang bertemu.