1硬幣問題
先看一個拋硬幣問題,如果我們有A和B兩個不均勻硬幣,選擇任意一個硬幣拋10次(這裏我們知道選擇是的哪一個硬幣),共計選擇5次。正面記爲H,背面記爲T。記錄實驗結果,求A和B再拋正面向上的概率?
使用極大似然估計(Maximum likelihood)來算:
- 統計出每次實驗,正反面的次數
- 多次實驗結果相加
- 相除得到結果,P(A)=0.8,P(B)=0.45
但是在實際過程中,很有可能我們只知道有兩個硬幣,不知道每次選擇的哪一個硬幣,問是否能求出每個硬幣拋出正面的概率?
是不是第一感覺這個問題很無解,我都不知道選擇是哪個硬幣,我怎麼求解啊?
事實上是可以求出的。這裏使用的時EM算法。
使用期望最大值(Expectation maximization,EM)算法來算:
- 假設
$\hat{\theta}_A^{(0)}=0.6,\hat{\theta}_B^{(0)}=0.5$
- 統計每次的實驗結果,記錄正反面
- 通過貝葉斯公式,估計每次實驗選擇的A硬幣或是B硬幣的概率
- 依據計算出的選擇硬幣概率得到該概率下的正反面結果
- 5次平均得到
$\hat{\theta}_A^{(1)}\approx 0.71,\hat{\theta}_B^{(1)}\approx 0.58$
- 重複上面的過程,例如迭代10次後,得到
$\hat{\theta}_A^{(2)}\approx 0.8,\hat{\theta}_B^{(2)}\approx 0.52$
- $`就是使用EM算法計算出的概率值
這裏比較難理解的是:如何利用貝葉斯公式計算每次實驗選擇的A硬幣或是B硬幣的概率?
那麼先看下面這個例子:
假如現在有射擊運動員甲和乙,甲乙射擊靶心的概率爲$\hat{\theta}_{\text{甲}}=0.9$
,$`,如果現在有一組實驗結果爲
**直觀上的來看,非常大的概率是甲射擊的,但是也有可能是乙走狗屎運了。那麼該如何從概率的角度計算是誰射擊的? **
首先我們知道選擇甲和乙的概率爲p(甲)=p(乙)=0.5(先驗概率),本次實驗記爲E。通過貝葉斯公式
$`
故本次實驗有98%可能是A射擊的,2%的可能是B射擊的。
有了上面的案例,我們再回到拋硬幣的問題上,由貝葉斯公式:
p(A\vert E)=\frac{p(E|A)P(A)}{p(E)}
A 爲選用硬幣A,E爲本次實驗。而選擇兩個硬幣的概率是相同的:$p(A)=p(B)=0.5$
,且$p(E|A)=0.6^5\times 0.4^5\approx 0.0008,P(E|B)=0.5^5\times 0.5^5\approx 0.001$
\begin{array}{lll}
p(A|E)&=&\frac{p(E|A)p(A)}{p(E)}\\
&=&\frac{p(E|A)p(A)}{p(E|A)p(A)+p(E|B)p(B)}\\
&\approx & 0.45
\end{array}
有$0.45\times 10\times 0.5\approx 2.2$
,故第一次實驗結果平均下來,有2.22.2個A硬幣正面的可能。
最後將5次結果平均得到新的A,B拋硬幣正面估計值$\hat{\theta}_A^{(1)}\approx 0.71,\hat{\theta}_B^{(1)}\approx 0.58$
,,這是我們第一次迭代的值(這就是一次學習過程),照着這個流程迭代多次,得到最後的估測值。
上面我們計算出每次實驗中是拋A或拋B的概率值就是隱變量.這個過程就是EM算法的簡單案例。
2形式化EM算法
2.1 算法理論
`$
`$
$`
這是一個非凸問題,只能求局部極值,一般採用EM算法。
步驟如下:
(1).隨機化`$
(2).E步:計算
上式表示:第i個樣本落在第j個高斯的概率.
(3). M步:
(4).回到(2)直至收斂.
2.2 python實現
以西瓜數據集4.0爲例:
編號 | 密度 | 含糖率 |
---|---|---|
1 | 0.697 | 0.46 |
2 | 0.774 | 0.376 |
3 | 0.634 | 0.264 |
4 | 0.608 | 0.318 |
5 | 0.556 | 0.215 |
6 | 0.403 | 0.237 |
7 | 0.481 | 0.149 |
8 | 0.437 | 0.211 |
9 | 0.666 | 0.091 |
10 | 0.243 | 0.267 |
11 | 0.245 | 0.057 |
12 | 0.343 | 0.099 |
13 | 0.639 | 0.161 |
14 | 0.657 | 0.198 |
15 | 0.36 | 0.37 |
16 | 0.593 | 0.042 |
17 | 0.719 | 0.103 |
18 | 0.359 | 0.188 |
19 | 0.339 | 0.241 |
20 | 0.282 | 0.257 |
21 | 0.748 | 0.232 |
22 | 0.714 | 0.346 |
23 | 0.483 | 0.312 |
24 | 0.478 | 0.437 |
25 | 0.525 | 0.369 |
26 | 0.751 | 0.489 |
27 | 0.532 | 0.472 |
28 | 0.473 | 0.376 |
29 | 0.725 | 0.445 |
30 | 0.446 | 0.459 |
import numpy as np
# 預處理數據
def loadData(filename):
dataSet = []
fr = open(filename)
for line in fr.readlines():
curLine = line.strip().split(' ')
fltLine = list(map(float, curLine))
dataSet.append(fltLine)
return dataSet
# 計算高斯函數
def Gaussian(data,mean,cov):
dim = np.shape(cov)[0] # 計算維度
covdet = np.linalg.det(cov) # 計算|cov|
covinv = np.linalg.inv(cov) # 計算cov的逆
if covdet==0: # 以防行列式爲0
covdet = np.linalg.det(cov+np.eye(dim)*0.01)
covinv = np.linalg.inv(cov+np.eye(dim)*0.01)
m = data - mean
z = -0.5 * np.dot(np.dot(m, covinv),m) # 計算exp()裏的值
return 1.0/(np.power(np.power(2*np.pi,dim)*abs(covdet),0.5))*np.exp(z) # 返回概率密度值
# 獲取最初的聚類中心
def GetInitialMeans(data,K,criterion):
dim = data.shape[1] # 數據的維度
means = [[] for k in range(K)] # 存儲均值
minmax=[]
for i in range(dim):
minmax.append(np.array([min(data[:,i]),max(data[:,i])])) # 存儲每一維的最大最小值
minmax=np.array(minmax)
while True:
for i in range(K):
means[i]=[]
for j in range(dim):
means[i].append(np.random.random()*(minmax[j][1]-minmax[j][0])+minmax[j][0] ) #隨機產生means
means[i]=np.array(means[i])
if isdistance(means,criterion):
break
return means
# 用於判斷初始聚類簇中的means是否距離離得比較近
def isdistance(means,criterion=0.03):
K=len(means)
for i in range(K):
for j in range(i+1,K):
if criterion>np.linalg.norm(means[i]-means[j]):
return False
return True
dataSet=loadData('d:/watermelon4.txt')
means=GetInitialMeans(np.array(dataSet),3,0.03)
# K均值算法,估計大約幾個樣本屬於一個GMM
def Kmeans(data,K):
N = data.shape[0] # 樣本數量
dim = data.shape[1] # 樣本維度
means = GetInitialMeans(data,K,15)
means_old = [np.zeros(dim) for k in range(K)]
# 收斂條件
while np.sum([np.linalg.norm(means_old[k] - means[k]) for k in range(K)]) > 0.01:
means_old = cp.deepcopy(means)
numlog = [0] * K # 存儲屬於某類的個數
sumlog = [np.zeros(dim) for k in range(K)]
# E步
for i in range(N):
dislog = [np.linalg.norm(data[i]-means[k]) for k in range(K)]
tok = dislog.index(np.min(dislog))
numlog[tok]+=1 # 屬於該類的樣本數量加1
sumlog[tok]+=data[i] # 存儲屬於該類的樣本取值
# M步
for k in range(K):
means[k]=1.0 / numlog[k] * sumlog[k]
return means
def GMM(data,K):
N = data.shape[0]
dim = data.shape[1]
means= Kmeans(data,K)
convs=[0]*K
# 初始方差等於整體data的方差
for i in range(K):
convs[i]=np.cov(data.T)
pis = [1.0/K] * K
gammas = [np.zeros(K) for i in range(N)]
loglikelyhood = 0
oldloglikelyhood = 1
while np.abs(loglikelyhood - oldloglikelyhood) > 0.0001:
oldloglikelyhood = loglikelyhood
# E步
for i in range(N):
res = [pis[k] * Gaussian(data[i],means[k],convs[k]) for k in range(K)]
sumres = np.sum(res)
for k in range(K): # gamma表示第n個樣本屬於第k個混合高斯的概率
gammas[i][k] = res[k] / sumres
# M步
for k in range(K):
Nk = np.sum([gammas[n][k] for n in range(N)]) # N[k] 表示N個樣本中有多少屬於第k個高斯
pis[k] = 1.0 * Nk/N
means[k] = (1.0/Nk)*np.sum([gammas[n][k] * data[n] for n in range(N)],axis=0)
xdiffs = data - means[k]
convs[k] = (1.0/ Nk)*np.sum([gammas[n][k]* xdiffs[n].reshape(dim,1) * xdiffs[n] for n in range(N)],axis=0)
# 計算最大似然函數
loglikelyhood = np.sum(
[np.log(np.sum([pis[k] * Gaussian(data[n], means[k], convs[k]) for k in range(K)])) for n in range(N)])
print(means)
print(loglikelyhood)
GMM(np.array(dataSet),3)