zahra_plus_comments.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. """
  2. Working on Bayesian formula model + penalty == MAP approach with prior = penalty
  3. """
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. from scipy import optimize, stats
  7. #here is data
  8. assert np.all(X > 0), "All X must be > 0"
  9. n1, n0 = int(y.sum()), int((1-y).sum())
  10. m_emp = n1 / (n1 + n0) # approx Prob(AE)
  11. print(f"AE={n1}, NC={n0}, empirical p_AE={m_emp:.6f}")
  12. # Model: AE ~ BetaPrime(a,b,scale), NC ~ LogNormal(mu, sigma)
  13. # p_ae = Prob(AE)
  14. # θ = [p_ae, a, b, sc, mu_nc, sig_nc]
  15. def logpdf_betaprime(x, a, b, scale):
  16. return stats.betaprime.logpdf(x, a=a, b=b, scale=scale)
  17. def logpdf_lognorm(x, mu, sigma):
  18. return stats.lognorm.logpdf(x, s=sigma, scale=np.exp(mu))
  19. def neg_conditional_ll(theta, X, y, eps=1e-12):
  20. p_ae, a, b, sc, mu_nc, sig_nc = theta
  21. logf1 = logpdf_betaprime(X, a, b, sc) # AE
  22. logf0 = logpdf_lognorm(X, mu_nc, sig_nc) # NC
  23. # computing p(AE|x) = 1/(1 + exp(-logit))
  24. logit = np.log(p_ae) - np.log(1.0 - p_ae) + (logf1 - logf0)
  25. p = 1.0 / (1.0 + np.exp(-np.clip(logit, -50, 50)))
  26. # computing general nllf = -llf
  27. # llf = sum_i log(p(y_i|x_i))
  28. # = sum_i y_i log( p(AE|x) + (1- y_i) log(1 - p(AE|x)); p(NC|x) = 1 -p(AE|x)
  29. nllf = -np.sum(y*np.log(p+eps) + (1-y)*np.log(1-p+eps))
  30. return nllf
  31. """
  32. MAP regularization
  33. mean = m_emp, concentration = TAU
  34. using beta distribution B(alpha, beta). The parameters are set as
  35. alpha = m_emp * TAU, beta = (1-m_emp) * TAU
  36. this yields E[X] = alpha/(alpha+beta) = m_emp
  37. Var[X] = m_emp(1- m_emp)/(tau +1)
  38. this could be variance between institutions collected by Katja,
  39. my estimate is
  40. var = 5%^2
  41. This yields tau about 30.
  42. """
  43. TAU = 100.0 # ↑ increase to pull p_AE closer to empirical prior
  44. alpha = max(m_emp * TAU, 1e-6)
  45. beta = max((1 - m_emp) * TAU, 1e-6)
  46. def neg_log_prior_p(p, eps=1e-12):
  47. # -log Beta(p | alpha, beta) up to a constant
  48. return -( (alpha - 1)*np.log(p + eps) + (beta - 1)*np.log(1 - p + eps) )
  49. def neg_posterior(theta, X, y):
  50. nll = neg_conditional_ll(theta, X, y)
  51. return nll + neg_log_prior_p(theta[0]) # add prior penalty on p_AE only
  52. # Bounds (optionally enforce heavy AE tail with b ≤ 1)
  53. # =========================
  54. HEAVY_TAIL = True # set False if you don't want to force the right asymptote
  55. b_upper = 1.0 if HEAVY_TAIL else 50.0
  56. bounds = [
  57. (1e-3, 1-1e-3), # p_ae (free, but regularized by Beta prior)
  58. (0.20, 50.0), # a (AE BetaPrime)
  59. (0.20, b_upper), # b (AE BetaPrime) <-- heavy tail if ≤ 1
  60. (0.01, 10.0), # scale (AE BetaPrime)
  61. (np.log(X).min()-2.0, np.log(X).max()+2.0), # mu_nc (LogNormal)
  62. (0.05, 2.0), # sigma_nc (LogNormal)
  63. ]
  64. # Initialization
  65. p0 = np.clip(m_emp, bounds[0][0], bounds[0][1])
  66. X0 = X[y==0]
  67. mu0 = float(np.mean(np.log(X0)))
  68. sig0 = float(np.std(np.log(X0), ddof=0))
  69. mu0 = np.clip(mu0, bounds[4][0], bounds[4][1])
  70. sig0 = np.clip(sig0, bounds[5][0], bounds[5][1])
  71. X1 = X[y==1]
  72. m1 = float(np.mean(X1))
  73. a0, b0 = 2.5, min(0.8, b_upper) # start with heavy-tail-ish b if allowed
  74. sc0 = np.clip(m1 * (b0 - 1 + 1e-6) / max(a0, 1e-6), bounds[3][0], bounds[3][1])
  75. theta0 = np.array([p0, a0, b0, sc0, mu0, sig0], dtype=float)
  76. # FREE prior (for comparison)
  77. # =========================
  78. res_free = optimize.minimize(
  79. fun=neg_conditional_ll,
  80. x0=theta0,
  81. args=(X, y),
  82. method="L-BFGS-B",
  83. bounds=bounds,
  84. options=dict(maxiter=4000, ftol=1e-12)
  85. )
  86. theta_free = res_free.x
  87. print("\n[FREE prior] p_AE =", float(theta_free[0]), " CLL =", -res_free.fun)
  88. # MAP prior (regularized toward empirical)
  89. # =========================
  90. res_map = optimize.minimize(
  91. fun=neg_posterior,
  92. x0=theta0,
  93. args=(X, y),
  94. method="L-BFGS-B",
  95. bounds=bounds,
  96. options=dict(maxiter=4000, ftol=1e-12)
  97. )
  98. theta_map = res_map.x
  99. print("[MAP prior] p_AE =", float(theta_map[0]), " CLL(post) =", -res_map.fun)
  100. # MAP parameters
  101. p_hat, a_hat, b_hat, sc_hat, mu_hat, sig_hat = theta_map
  102. print("\nFitted (MAP) parameters:")
  103. print(f" p_AE = {p_hat:.6f} (empirical {m_emp:.6f}, TAU={TAU})")
  104. print(f" AE BetaPrime: a={a_hat:.4f}, b={b_hat:.4f}, scale={sc_hat:.4f}")
  105. print(f" NC LogNormal: mu={mu_hat:.4f}, sigma={sig_hat:.4f}")
  106. # Posterior & plot
  107. def predict_proba(x):
  108. x = np.asarray(x, dtype=float)
  109. logf1 = logpdf_betaprime(x, a_hat, b_hat, sc_hat)
  110. logf0 = logpdf_lognorm(x, mu_hat, sig_hat)
  111. logit = np.log(p_hat) - np.log(1.0 - p_hat) + (logf1 - logf0)
  112. return 1.0 / (1.0 + np.exp(-np.clip(logit, -50, 50)))
  113. x_grid = np.linspace(max(1e-6, X.min()*0.6), max(X.max()*2.0, 8.0), 600)
  114. p_grid = predict_proba(x_grid)
  115. plt.figure(figsize=(8,5))
  116. plt.plot(x_grid, p_grid, 'k-', linewidth=2, label="P(AE | X) [MAP]")
  117. plt.scatter(X[y==1], np.ones(n1), marker='x', label="AE samples")
  118. plt.scatter(X[y==0], np.zeros(n0), marker='o', label="NC samples")
  119. plt.xlabel("SUV feature X"); plt.ylabel("Predicted P(AE | X)")
  120. plt.title("BetaPrime–LogNormal with MAP prior on p_AE")
  121. plt.ylim(-0.05, 1.05); plt.legend(); plt.grid(True)
  122. plt.show() (edited)