test_bayes.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. # Author: Alexandre Gramfort <alexandre.gramfort@inria.fr>
  2. # Fabian Pedregosa <fabian.pedregosa@inria.fr>
  3. #
  4. # License: BSD 3 clause
  5. from math import log
  6. import numpy as np
  7. import pytest
  8. from sklearn import datasets
  9. from sklearn.linear_model import ARDRegression, BayesianRidge, Ridge
  10. from sklearn.utils import check_random_state
  11. from sklearn.utils._testing import (
  12. assert_almost_equal,
  13. assert_array_almost_equal,
  14. assert_array_less,
  15. )
  16. from sklearn.utils.extmath import fast_logdet
  17. diabetes = datasets.load_diabetes()
  18. def test_bayesian_ridge_scores():
  19. """Check scores attribute shape"""
  20. X, y = diabetes.data, diabetes.target
  21. clf = BayesianRidge(compute_score=True)
  22. clf.fit(X, y)
  23. assert clf.scores_.shape == (clf.n_iter_ + 1,)
  24. def test_bayesian_ridge_score_values():
  25. """Check value of score on toy example.
  26. Compute log marginal likelihood with equation (36) in Sparse Bayesian
  27. Learning and the Relevance Vector Machine (Tipping, 2001):
  28. - 0.5 * (log |Id/alpha + X.X^T/lambda| +
  29. y^T.(Id/alpha + X.X^T/lambda).y + n * log(2 * pi))
  30. + lambda_1 * log(lambda) - lambda_2 * lambda
  31. + alpha_1 * log(alpha) - alpha_2 * alpha
  32. and check equality with the score computed during training.
  33. """
  34. X, y = diabetes.data, diabetes.target
  35. n_samples = X.shape[0]
  36. # check with initial values of alpha and lambda (see code for the values)
  37. eps = np.finfo(np.float64).eps
  38. alpha_ = 1.0 / (np.var(y) + eps)
  39. lambda_ = 1.0
  40. # value of the parameters of the Gamma hyperpriors
  41. alpha_1 = 0.1
  42. alpha_2 = 0.1
  43. lambda_1 = 0.1
  44. lambda_2 = 0.1
  45. # compute score using formula of docstring
  46. score = lambda_1 * log(lambda_) - lambda_2 * lambda_
  47. score += alpha_1 * log(alpha_) - alpha_2 * alpha_
  48. M = 1.0 / alpha_ * np.eye(n_samples) + 1.0 / lambda_ * np.dot(X, X.T)
  49. M_inv_dot_y = np.linalg.solve(M, y)
  50. score += -0.5 * (
  51. fast_logdet(M) + np.dot(y.T, M_inv_dot_y) + n_samples * log(2 * np.pi)
  52. )
  53. # compute score with BayesianRidge
  54. clf = BayesianRidge(
  55. alpha_1=alpha_1,
  56. alpha_2=alpha_2,
  57. lambda_1=lambda_1,
  58. lambda_2=lambda_2,
  59. max_iter=1,
  60. fit_intercept=False,
  61. compute_score=True,
  62. )
  63. clf.fit(X, y)
  64. assert_almost_equal(clf.scores_[0], score, decimal=9)
  65. def test_bayesian_ridge_parameter():
  66. # Test correctness of lambda_ and alpha_ parameters (GitHub issue #8224)
  67. X = np.array([[1, 1], [3, 4], [5, 7], [4, 1], [2, 6], [3, 10], [3, 2]])
  68. y = np.array([1, 2, 3, 2, 0, 4, 5]).T
  69. # A Ridge regression model using an alpha value equal to the ratio of
  70. # lambda_ and alpha_ from the Bayesian Ridge model must be identical
  71. br_model = BayesianRidge(compute_score=True).fit(X, y)
  72. rr_model = Ridge(alpha=br_model.lambda_ / br_model.alpha_).fit(X, y)
  73. assert_array_almost_equal(rr_model.coef_, br_model.coef_)
  74. assert_almost_equal(rr_model.intercept_, br_model.intercept_)
  75. def test_bayesian_sample_weights():
  76. # Test correctness of the sample_weights method
  77. X = np.array([[1, 1], [3, 4], [5, 7], [4, 1], [2, 6], [3, 10], [3, 2]])
  78. y = np.array([1, 2, 3, 2, 0, 4, 5]).T
  79. w = np.array([4, 3, 3, 1, 1, 2, 3]).T
  80. # A Ridge regression model using an alpha value equal to the ratio of
  81. # lambda_ and alpha_ from the Bayesian Ridge model must be identical
  82. br_model = BayesianRidge(compute_score=True).fit(X, y, sample_weight=w)
  83. rr_model = Ridge(alpha=br_model.lambda_ / br_model.alpha_).fit(
  84. X, y, sample_weight=w
  85. )
  86. assert_array_almost_equal(rr_model.coef_, br_model.coef_)
  87. assert_almost_equal(rr_model.intercept_, br_model.intercept_)
  88. def test_toy_bayesian_ridge_object():
  89. # Test BayesianRidge on toy
  90. X = np.array([[1], [2], [6], [8], [10]])
  91. Y = np.array([1, 2, 6, 8, 10])
  92. clf = BayesianRidge(compute_score=True)
  93. clf.fit(X, Y)
  94. # Check that the model could approximately learn the identity function
  95. test = [[1], [3], [4]]
  96. assert_array_almost_equal(clf.predict(test), [1, 3, 4], 2)
  97. def test_bayesian_initial_params():
  98. # Test BayesianRidge with initial values (alpha_init, lambda_init)
  99. X = np.vander(np.linspace(0, 4, 5), 4)
  100. y = np.array([0.0, 1.0, 0.0, -1.0, 0.0]) # y = (x^3 - 6x^2 + 8x) / 3
  101. # In this case, starting from the default initial values will increase
  102. # the bias of the fitted curve. So, lambda_init should be small.
  103. reg = BayesianRidge(alpha_init=1.0, lambda_init=1e-3)
  104. # Check the R2 score nearly equals to one.
  105. r2 = reg.fit(X, y).score(X, y)
  106. assert_almost_equal(r2, 1.0)
  107. def test_prediction_bayesian_ridge_ard_with_constant_input():
  108. # Test BayesianRidge and ARDRegression predictions for edge case of
  109. # constant target vectors
  110. n_samples = 4
  111. n_features = 5
  112. random_state = check_random_state(42)
  113. constant_value = random_state.rand()
  114. X = random_state.random_sample((n_samples, n_features))
  115. y = np.full(n_samples, constant_value, dtype=np.array(constant_value).dtype)
  116. expected = np.full(n_samples, constant_value, dtype=np.array(constant_value).dtype)
  117. for clf in [BayesianRidge(), ARDRegression()]:
  118. y_pred = clf.fit(X, y).predict(X)
  119. assert_array_almost_equal(y_pred, expected)
  120. def test_std_bayesian_ridge_ard_with_constant_input():
  121. # Test BayesianRidge and ARDRegression standard dev. for edge case of
  122. # constant target vector
  123. # The standard dev. should be relatively small (< 0.01 is tested here)
  124. n_samples = 10
  125. n_features = 5
  126. random_state = check_random_state(42)
  127. constant_value = random_state.rand()
  128. X = random_state.random_sample((n_samples, n_features))
  129. y = np.full(n_samples, constant_value, dtype=np.array(constant_value).dtype)
  130. expected_upper_boundary = 0.01
  131. for clf in [BayesianRidge(), ARDRegression()]:
  132. _, y_std = clf.fit(X, y).predict(X, return_std=True)
  133. assert_array_less(y_std, expected_upper_boundary)
  134. def test_update_of_sigma_in_ard():
  135. # Checks that `sigma_` is updated correctly after the last iteration
  136. # of the ARDRegression algorithm. See issue #10128.
  137. X = np.array([[1, 0], [0, 0]])
  138. y = np.array([0, 0])
  139. clf = ARDRegression(max_iter=1)
  140. clf.fit(X, y)
  141. # With the inputs above, ARDRegression prunes both of the two coefficients
  142. # in the first iteration. Hence, the expected shape of `sigma_` is (0, 0).
  143. assert clf.sigma_.shape == (0, 0)
  144. # Ensure that no error is thrown at prediction stage
  145. clf.predict(X, return_std=True)
  146. def test_toy_ard_object():
  147. # Test BayesianRegression ARD classifier
  148. X = np.array([[1], [2], [3]])
  149. Y = np.array([1, 2, 3])
  150. clf = ARDRegression(compute_score=True)
  151. clf.fit(X, Y)
  152. # Check that the model could approximately learn the identity function
  153. test = [[1], [3], [4]]
  154. assert_array_almost_equal(clf.predict(test), [1, 3, 4], 2)
  155. @pytest.mark.parametrize("n_samples, n_features", ((10, 100), (100, 10)))
  156. def test_ard_accuracy_on_easy_problem(global_random_seed, n_samples, n_features):
  157. # Check that ARD converges with reasonable accuracy on an easy problem
  158. # (Github issue #14055)
  159. X = np.random.RandomState(global_random_seed).normal(size=(250, 3))
  160. y = X[:, 1]
  161. regressor = ARDRegression()
  162. regressor.fit(X, y)
  163. abs_coef_error = np.abs(1 - regressor.coef_[1])
  164. assert abs_coef_error < 1e-10
  165. def test_return_std():
  166. # Test return_std option for both Bayesian regressors
  167. def f(X):
  168. return np.dot(X, w) + b
  169. def f_noise(X, noise_mult):
  170. return f(X) + np.random.randn(X.shape[0]) * noise_mult
  171. d = 5
  172. n_train = 50
  173. n_test = 10
  174. w = np.array([1.0, 0.0, 1.0, -1.0, 0.0])
  175. b = 1.0
  176. X = np.random.random((n_train, d))
  177. X_test = np.random.random((n_test, d))
  178. for decimal, noise_mult in enumerate([1, 0.1, 0.01]):
  179. y = f_noise(X, noise_mult)
  180. m1 = BayesianRidge()
  181. m1.fit(X, y)
  182. y_mean1, y_std1 = m1.predict(X_test, return_std=True)
  183. assert_array_almost_equal(y_std1, noise_mult, decimal=decimal)
  184. m2 = ARDRegression()
  185. m2.fit(X, y)
  186. y_mean2, y_std2 = m2.predict(X_test, return_std=True)
  187. assert_array_almost_equal(y_std2, noise_mult, decimal=decimal)
  188. def test_update_sigma(global_random_seed):
  189. # make sure the two update_sigma() helpers are equivalent. The woodbury
  190. # formula is used when n_samples < n_features, and the other one is used
  191. # otherwise.
  192. rng = np.random.RandomState(global_random_seed)
  193. # set n_samples == n_features to avoid instability issues when inverting
  194. # the matrices. Using the woodbury formula would be unstable when
  195. # n_samples > n_features
  196. n_samples = n_features = 10
  197. X = rng.randn(n_samples, n_features)
  198. alpha = 1
  199. lmbda = np.arange(1, n_features + 1)
  200. keep_lambda = np.array([True] * n_features)
  201. reg = ARDRegression()
  202. sigma = reg._update_sigma(X, alpha, lmbda, keep_lambda)
  203. sigma_woodbury = reg._update_sigma_woodbury(X, alpha, lmbda, keep_lambda)
  204. np.testing.assert_allclose(sigma, sigma_woodbury)
  205. @pytest.mark.parametrize("dtype", [np.float32, np.float64])
  206. @pytest.mark.parametrize("Estimator", [BayesianRidge, ARDRegression])
  207. def test_dtype_match(dtype, Estimator):
  208. # Test that np.float32 input data is not cast to np.float64 when possible
  209. X = np.array([[1, 1], [3, 4], [5, 7], [4, 1], [2, 6], [3, 10], [3, 2]], dtype=dtype)
  210. y = np.array([1, 2, 3, 2, 0, 4, 5]).T
  211. model = Estimator()
  212. # check type consistency
  213. model.fit(X, y)
  214. attributes = ["coef_", "sigma_"]
  215. for attribute in attributes:
  216. assert getattr(model, attribute).dtype == X.dtype
  217. y_mean, y_std = model.predict(X, return_std=True)
  218. assert y_mean.dtype == X.dtype
  219. assert y_std.dtype == X.dtype
  220. @pytest.mark.parametrize("Estimator", [BayesianRidge, ARDRegression])
  221. def test_dtype_correctness(Estimator):
  222. X = np.array([[1, 1], [3, 4], [5, 7], [4, 1], [2, 6], [3, 10], [3, 2]])
  223. y = np.array([1, 2, 3, 2, 0, 4, 5]).T
  224. model = Estimator()
  225. coef_32 = model.fit(X.astype(np.float32), y).coef_
  226. coef_64 = model.fit(X.astype(np.float64), y).coef_
  227. np.testing.assert_allclose(coef_32, coef_64, rtol=1e-4)
  228. # TODO(1.5) remove
  229. @pytest.mark.parametrize("Estimator", [BayesianRidge, ARDRegression])
  230. def test_bayesian_ridge_ard_n_iter_deprecated(Estimator):
  231. """Check the deprecation warning of `n_iter`."""
  232. depr_msg = (
  233. "'n_iter' was renamed to 'max_iter' in version 1.3 and will be removed in 1.5"
  234. )
  235. X, y = diabetes.data, diabetes.target
  236. model = Estimator(n_iter=5)
  237. with pytest.warns(FutureWarning, match=depr_msg):
  238. model.fit(X, y)
  239. # TODO(1.5) remove
  240. @pytest.mark.parametrize("Estimator", [BayesianRidge, ARDRegression])
  241. def test_bayesian_ridge_ard_max_iter_and_n_iter_both_set(Estimator):
  242. """Check that a ValueError is raised when both `max_iter` and `n_iter` are set."""
  243. err_msg = (
  244. "Both `n_iter` and `max_iter` attributes were set. Attribute"
  245. " `n_iter` was deprecated in version 1.3 and will be removed in"
  246. " 1.5. To avoid this error, only set the `max_iter` attribute."
  247. )
  248. X, y = diabetes.data, diabetes.target
  249. model = Estimator(n_iter=5, max_iter=5)
  250. with pytest.raises(ValueError, match=err_msg):
  251. model.fit(X, y)