การตอบสนองนี้ได้รับการแก้ไขอย่างมีนัยสำคัญจากรูปแบบเดิม ข้อผิดพลาดของคำตอบดั้งเดิมของฉันจะกล่าวถึงด้านล่าง แต่ถ้าคุณต้องการดูคร่าว ๆ ว่าคำตอบนี้ดูเหมือนว่าก่อนที่ฉันจะทำการแก้ไขครั้งใหญ่ลองดูที่สมุดบันทึกต่อไปนี้: https://nbviewer.jupyter.org/github /dmarx/data_generation_demo/blob/54be78fb5b68218971d2568f1680b4f783c0a79a/demo.ipynb
TL; DR: ใช้ KDE (หรือขั้นตอนที่คุณเลือก) เพื่อประมาณจากนั้นใช้ MCMC เพื่อดึงตัวอย่างจากโดยที่มอบให้โดยโมเดลของคุณ จากตัวอย่างเหล่านี้คุณสามารถประมาณ "ดีที่สุด" โดยปรับ KDE ที่สองให้พอดีกับตัวอย่างที่คุณสร้างขึ้นและเลือกการสังเกตที่เพิ่ม KDE ให้มากที่สุดเพื่อประเมิน posteriori (MAP) สูงสุดP ( X | Y ) ∝ P ( Y | X ) P ( X ) P ( Y | X ) XP(X)P(X|Y)∝P(Y|X)P(X)P(Y|X)X
การประมาณความน่าจะเป็นสูงสุด
... และทำไมมันไม่ทำงานที่นี่
ในการตอบกลับดั้งเดิมของฉันเทคนิคที่ฉันแนะนำคือใช้ MCMC เพื่อประเมินความเป็นไปได้สูงสุด โดยทั่วไป MLE เป็นวิธีการที่ดีในการค้นหาคำตอบที่ "ดีที่สุด" ต่อความน่าจะเป็นแบบมีเงื่อนไข แต่เรามีปัญหาที่นี่: เนื่องจากเรากำลังใช้แบบจำลองแบบจำแนก (ป่าสุ่มในกรณีนี้) ความน่าจะเป็นของเราจะถูกคำนวณเทียบกับขอบเขตการตัดสินใจ . จริง ๆ แล้วมันไม่สมเหตุสมผลเลยที่จะพูดถึงวิธีแก้ปัญหา "ที่ดีที่สุด" กับแบบจำลองเช่นนี้เพราะเมื่อเราอยู่ห่างจากขอบเขตของคลาสมากพอโมเดลก็จะคาดการณ์สิ่งนั้นสำหรับทุกสิ่ง หากเรามีคลาสมากพอบางคนก็อาจ "ล้อมรอบ" อย่างสมบูรณ์ซึ่งในกรณีนี้จะไม่มีปัญหา แต่คลาสที่ขอบเขตของข้อมูลของเราจะถูก "ขยาย" โดยค่าที่ไม่จำเป็นต้องเป็นไปได้
เพื่อสาธิตฉันจะใช้ประโยชน์จากรหัสความสะดวกสบายบางอย่างที่คุณสามารถหาได้ที่นี่ซึ่งให้GenerativeSampler
คลาสที่ล้อมรอบโค้ดจากการตอบกลับดั้งเดิมของฉันรหัสเพิ่มเติมสำหรับโซลูชันที่ดีกว่านี้และคุณสมบัติเพิ่มเติมบางอย่างที่ฉันเล่นด้วย บางอันไม่ได้) ซึ่งฉันอาจจะไม่ได้เข้าไปที่นี่
np.random.seed(123)
sampler = GenerativeSampler(model=RFC, X=X, y=y,
target_class=2,
prior=None,
class_err_prob=0.05, # <-- the score we use for candidates that aren't predicted as the target class
rw_std=.05, # <-- controls the step size of the random walk proposal
verbose=True,
use_empirical=False)
samples, _ = sampler.run_chain(n=5000)
burn = 1000
thin = 20
X_s = pca.transform(samples[burn::thin,:])
# Plot the iris data
col=['r','b','g']
for i in range(3):
plt.scatter(*X_r[y==i,:].T, c=col[i], marker='x')
plt.plot(*X_s.T, 'k')
plt.scatter(*X_s.T, c=np.arange(X_s.shape[0]))
plt.colorbar()
plt.show()
ในการสร้างภาพข้อมูลนี้ x เป็นข้อมูลจริงและคลาสที่เราสนใจนั้นเป็นสีเขียว จุดเชื่อมต่อสายเป็นตัวอย่างที่เราวาดและสีของพวกมันสอดคล้องกับลำดับที่พวกมันถูกสุ่มตัวอย่างด้วยตำแหน่งลำดับ "ผอมบาง" ที่กำหนดโดยฉลากแถบสีทางด้านขวา
อย่างที่คุณเห็นเครื่องเก็บตัวอย่างถูกแยกออกจากข้อมูลอย่างรวดเร็วและจากนั้นจะแขวนห่างจากค่าของพื้นที่ฟีเจอร์ที่สอดคล้องกับการสังเกตของจริง เห็นได้ชัดว่านี่เป็นปัญหา
วิธีหนึ่งที่เราสามารถโกงได้คือเปลี่ยนฟังก์ชั่นข้อเสนอของเราเพื่อให้ฟีเจอร์ใช้ค่าที่เราสังเกตเห็นในข้อมูลเท่านั้น มาลองดูกันว่ามันจะเปลี่ยนพฤติกรรมของผลลัพธ์ของเราอย่างไร
np.random.seed(123)
sampler = GenerativeSampler(model=RFC, X=X, y=y,
target_class=2,
prior=None,
class_err_prob=0.05,
verbose=True,
use_empirical=True) # <-- magic happening under the hood
samples, _ = sampler.run_chain(n=5000)
X_s = pca.transform(samples[burn::thin,:])
# Constrain attention to just the target class this time
i=2
plt.scatter(*X_r[y==i,:].T, c='k', marker='x')
plt.scatter(*X_s.T, c='g', alpha=0.3)
#plt.colorbar()
plt.show()
sns.kdeplot(X_s, cmap=sns.dark_palette('green', as_cmap=True))
plt.scatter(*X_r[y==i,:].T, c='k', marker='x')
plt.show()
นี่คือการปรับปรุงที่สำคัญและโหมดการกระจายของเราสอดคล้องกับสิ่งที่เรากำลังมองหา แต่ก็ชัดเจนว่าเรายังคงสร้างการสังเกตจำนวนมากที่ไม่สอดคล้องกับค่าที่เป็นไปได้ของดังนั้นเราจึงไม่ควรจริงๆ เชื่อใจการกระจายตัวนี้เช่นกันX
ทางออกที่ชัดเจนที่นี่คือการรวมอย่างใดอย่างหนึ่งเพื่อยึดกระบวนการสุ่มตัวอย่างของเราไปยังพื้นที่ของพื้นที่คุณสมบัติที่ข้อมูลมีแนวโน้มที่จะใช้จริง ดังนั้นแทนที่จะลองตัวอย่างจากความน่าจะเป็นร่วมของความน่าจะเป็นที่กำหนดโดยแบบจำลองและการประมาณการเชิงตัวเลขสำหรับกำหนดโดย KDE ให้พอดีกับชุดข้อมูลทั้งหมด ตอนนี้เรากำลัง ... สุ่มตัวอย่างจาก ... ....P(X)P(Y|X)P(X)P(Y|X)P(X)
ป้อนกฎของเบย์
หลังจากที่คุณทำให้ฉันเป็นคลื่นน้อยด้วยคณิตศาสตร์ที่นี่ฉันเล่นด้วยจำนวนที่พอเหมาะ (ด้วยเหตุนี้ฉันสร้างGenerativeSampler
สิ่ง) และฉันพบปัญหาที่ฉันวางไว้ข้างต้น ฉันรู้สึกงี่เง่าจริง ๆ เมื่อฉันทำให้การรับรู้นี้ แต่เห็นได้ชัดว่าสิ่งที่คุณขอเรียกร้องให้ใช้กฎของ Bayes และฉันขอโทษสำหรับการถูกไล่ออกก่อนหน้านี้
หากคุณไม่คุ้นเคยกับกฎของเบย์ดูเหมือนว่า:
P(B|A)=P(A|B)P(B)P(A)
ในหลาย ๆ แอปพลิเคชันตัวหารเป็นค่าคงที่ซึ่งทำหน้าที่เป็นคำที่ปรับขนาดเพื่อให้แน่ใจว่าตัวเศษรวมกับ 1 ดังนั้นกฎมักจะได้รับการปรับปรุงใหม่ดังนั้น:
P(B|A)∝P(A|B)P(B)
หรือเป็นภาษาอังกฤษธรรมดา: "หลังเป็นสัดส่วนกับโอกาสครั้งก่อน"
ดูคุ้นเคยไหม ตอนนี้:
P(X|Y)∝P(Y|X)P(X)
ใช่นี่คือสิ่งที่เราทำไว้ก่อนหน้านี้โดยการสร้างประมาณการสำหรับ MLE ที่ยึดกับการกระจายข้อมูลที่สังเกตได้ ฉันไม่เคยคิดเกี่ยวกับการปกครองของเบย์ด้วยวิธีนี้ แต่มันก็สมเหตุสมผลแล้วขอบคุณที่ให้โอกาสฉันได้ค้นพบมุมมองใหม่นี้
เพื่อย้อนรอยเล็กน้อย MCMC เป็นหนึ่งในแอปพลิเคชั่นของกฎ Bayes ที่เราสามารถมองข้ามตัวส่วน เมื่อเราคำนวณอัตราส่วน acceptanc,จะใช้ค่าเดียวกันทั้งในตัวเศษและส่วนการยกเลิกและทำให้เราสามารถดึงตัวอย่างจากการแจกแจงความน่าจะเป็นแบบไม่ปกติP(Y)
ดังนั้นเมื่อมีข้อมูลเชิงลึกนี้แล้วเราจำเป็นต้องรวมข้อมูลก่อนหน้านี้มาทำเช่นนั้นโดยปรับ KDE มาตรฐานให้เหมาะสมและดูว่ามันจะเปลี่ยนแปลงผลลัพธ์ของเราอย่างไร
np.random.seed(123)
sampler = GenerativeSampler(model=RFC, X=X, y=y,
target_class=2,
prior='kde', # <-- the new hotness
class_err_prob=0.05,
rw_std=.05, # <-- back to the random walk proposal
verbose=True,
use_empirical=False)
samples, _ = sampler.run_chain(n=5000)
burn = 1000
thin = 20
X_s = pca.transform(samples[burn::thin,:])
# Plot the iris data
col=['r','b','g']
for i in range(3):
plt.scatter(*X_r[y==i,:].T, c=col[i], marker='x')
plt.plot(*X_s.T, 'k--')
plt.scatter(*X_s.T, c=np.arange(X_s.shape[0]), alpha=0.2)
plt.colorbar()
plt.show()
ดีกว่ามาก! ตอนนี้เราสามารถประมาณค่า "ดีที่สุด"โดยใช้สิ่งที่เรียกว่าการประเมิน "สูงสุด a หลัง" ซึ่งเป็นวิธีแฟนซีในการบอกว่าเราพอดี KDE ที่สอง - แต่สำหรับตัวอย่างของเราในครั้งนี้ - และค้นหาค่าที่เพิ่มสูงสุด KDE ได้คือค่าที่สอดคล้องกับโหมดของY)P ( X | Y )XP(X|Y)
# MAP estimation
from sklearn.neighbors import KernelDensity
from sklearn.model_selection import GridSearchCV
from scipy.optimize import minimize
grid = GridSearchCV(KernelDensity(), {'bandwidth': np.linspace(0.1, 1.0, 30)}, cv=10, refit=True)
kde = grid.fit(samples[burn::thin,:]).best_estimator_
def map_objective(x):
try:
score = kde.score_samples(x)
except ValueError:
score = kde.score_samples(x.reshape(1,-1))
return -score
x_map = minimize(map_objective, samples[-1,:].reshape(1,-1)).x
print(x_map)
x_map_r = pca.transform(x_map.reshape(1,-1))[0]
col=['r','b','g']
for i in range(3):
plt.scatter(*X_r[y==i,:].T, c=col[i], marker='x')
sns.kdeplot(*X_s.T, cmap=sns.dark_palette('green', as_cmap=True))
plt.scatter(x_map_r[0], x_map_r[1], c='k', marker='x', s=150)
plt.show()
และคุณก็มีแล้ว: 'X' สีดำขนาดใหญ่คือค่าประมาณ MAP ของเรา (รูปทรงเหล่านั้นคือ KDE ของด้านหลัง)