dcekit 2.16.1__tar.gz → 2.17.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {dcekit-2.16.1/dcekit.egg-info → dcekit-2.17.1}/PKG-INFO +1 -1
  2. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/generative_model/__init__.py +2 -0
  3. dcekit-2.17.1/dcekit/generative_model/ssgmr_xgmm.py +696 -0
  4. dcekit-2.17.1/dcekit/generative_model/ssgmr_xgmm_xma.py +65 -0
  5. {dcekit-2.16.1 → dcekit-2.17.1/dcekit.egg-info}/PKG-INFO +1 -1
  6. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit.egg-info/SOURCES.txt +2 -0
  7. {dcekit-2.16.1 → dcekit-2.17.1}/setup.py +1 -1
  8. {dcekit-2.16.1 → dcekit-2.17.1}/LICENSE +0 -0
  9. {dcekit-2.16.1 → dcekit-2.17.1}/LICENSE.txt +0 -0
  10. {dcekit-2.16.1 → dcekit-2.17.1}/MANIFEST.in +0 -0
  11. {dcekit-2.16.1 → dcekit-2.17.1}/README.md +0 -0
  12. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/__init__.py +0 -0
  13. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/design_of_exmeriments/__init__.py +0 -0
  14. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/design_of_exmeriments/bayesian_optimization.py +0 -0
  15. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/generative_model/gmr.py +0 -0
  16. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/generative_model/gtm.py +0 -0
  17. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/generative_model/vbgmr.py +0 -0
  18. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/just_in_time/__init__.py +0 -0
  19. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/just_in_time/jitpls.py +0 -0
  20. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/just_in_time/lwpls.py +0 -0
  21. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/learning/__init__.py +0 -0
  22. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/learning/ensemble_learning.py +0 -0
  23. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/learning/semi_supervised_learning.py +0 -0
  24. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/learning/transfer_learning.py +0 -0
  25. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/optimization/__init__.py +0 -0
  26. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/optimization/fast_opt_svr_hyperparams.py +0 -0
  27. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/optimization/iot.py +0 -0
  28. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/optimization/opt_dnn_hyperparams.py +0 -0
  29. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/optimization/opt_dnn_hyperparams_class.py +0 -0
  30. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/sampling/__init__.py +0 -0
  31. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/sampling/kennard_stone.py +0 -0
  32. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/validation/__init__.py +0 -0
  33. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/validation/applicability_domain.py +0 -0
  34. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/validation/metrics.py +0 -0
  35. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/validation/validation.py +0 -0
  36. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/variable_selection/__init__.py +0 -0
  37. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/variable_selection/basic_variable_selection.py +0 -0
  38. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/variable_selection/local_interpretation.py +0 -0
  39. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit/variable_selection/variable_importance.py +0 -0
  40. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit.egg-info/dependency_links.txt +0 -0
  41. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit.egg-info/requires.txt +0 -0
  42. {dcekit-2.16.1 → dcekit-2.17.1}/dcekit.egg-info/top_level.txt +0 -0
  43. {dcekit-2.16.1 → dcekit-2.17.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcekit
3
- Version: 2.16.1
3
+ Version: 2.17.1
4
4
  Summary: Data Chemical Engineering toolkit
5
5
  Home-page: https://github.com/hkaneko1985/dcekit/
6
6
  Author: Hiromasa Kaneko
@@ -7,3 +7,5 @@
7
7
  from .gmr import GMR
8
8
  from .gtm import GTM
9
9
  from .vbgmr import VBGMR
10
+ from .ssgmr_xgmm import SSGMRXGMM
11
+ from .ssgmr_xgmm_xma import SSGMRXGMMXMA
@@ -0,0 +1,696 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ @author: Hiromasa Kaneko
4
+ """
5
+
6
+ # Semi-supervised Gaussian Mixture Regression with x-only GMM pretraining
7
+ # (ssGMR-xGMM)
8
+
9
+ import numpy as np
10
+ from sklearn.mixture import GaussianMixture
11
+
12
+ from .gmr import GMR
13
+
14
+
15
+ _VALID_COVARIANCE_TYPES = ('full', 'diag', 'tied', 'spherical')
16
+
17
+
18
+ def _as_2d_float_array(values, name, allow_one_dimensional=False):
19
+ """Convert input data to a finite two-dimensional float array."""
20
+ array = np.asarray(values, dtype=float)
21
+ if array.ndim == 1 and allow_one_dimensional:
22
+ array = array.reshape(-1, 1)
23
+ if array.ndim != 2:
24
+ raise ValueError('{0} must be a two-dimensional array.'.format(name))
25
+ if array.shape[0] == 0 or array.shape[1] == 0:
26
+ raise ValueError('{0} must not be empty.'.format(name))
27
+ if not np.all(np.isfinite(array)):
28
+ raise ValueError('{0} contains NaN or infinite values.'.format(name))
29
+ return array
30
+
31
+
32
+ def _validate_variable_numbers(numbers, number_of_variables, name):
33
+ """Validate variable-number lists used by DCEKit GMR."""
34
+ values = np.asarray(numbers, dtype=int).reshape(-1)
35
+ if values.size == 0:
36
+ raise ValueError('{0} must contain at least one variable number.'.format(name))
37
+ if np.unique(values).size != values.size:
38
+ raise ValueError('{0} contains duplicated variable numbers.'.format(name))
39
+ if np.any(values < 0) or np.any(values >= number_of_variables):
40
+ raise ValueError(
41
+ '{0} contains a variable number outside [0, {1}].'.format(
42
+ name, number_of_variables - 1
43
+ )
44
+ )
45
+ return values
46
+
47
+
48
+ def _make_symmetric_positive_definite(matrix, minimum_eigenvalue):
49
+ """Symmetrize a matrix and clip its eigenvalues to a positive floor."""
50
+ matrix = np.asarray(matrix, dtype=float)
51
+ matrix = 0.5 * (matrix + matrix.T)
52
+ eigenvalues, eigenvectors = np.linalg.eigh(matrix)
53
+ eigenvalues = np.maximum(eigenvalues, minimum_eigenvalue)
54
+ matrix = (eigenvectors * eigenvalues).dot(eigenvectors.T)
55
+ return 0.5 * (matrix + matrix.T)
56
+
57
+
58
+ def _stabilize_joint_covariance(
59
+ covariance_x, covariance_xy, covariance_y, minimum_eigenvalue):
60
+ """
61
+ Construct a positive-definite block covariance while preserving the x and
62
+ y marginal covariance matrices. The cross-covariance is shrunk only when
63
+ required by the positive-definite constraint.
64
+ """
65
+ covariance_x = _make_symmetric_positive_definite(
66
+ covariance_x, minimum_eigenvalue
67
+ )
68
+ covariance_y = _make_symmetric_positive_definite(
69
+ covariance_y, minimum_eigenvalue
70
+ )
71
+ covariance_xy = np.asarray(covariance_xy, dtype=float)
72
+
73
+ cholesky_x = np.linalg.cholesky(covariance_x)
74
+ cholesky_y = np.linalg.cholesky(covariance_y)
75
+
76
+ # The block matrix is positive definite if all singular values of
77
+ # Lx^-1 Sxy Ly^-T are smaller than one.
78
+ normalized_cross_covariance = np.linalg.solve(
79
+ cholesky_x, covariance_xy
80
+ )
81
+ normalized_cross_covariance = np.linalg.solve(
82
+ cholesky_y, normalized_cross_covariance.T
83
+ ).T
84
+ singular_values = np.linalg.svd(
85
+ normalized_cross_covariance, compute_uv=False
86
+ )
87
+ largest_singular_value = (
88
+ singular_values[0] if singular_values.size else 0.0
89
+ )
90
+
91
+ cross_covariance_scaling = 1.0
92
+ maximum_allowed_singular_value = 1.0 - 1e-8
93
+ if largest_singular_value >= maximum_allowed_singular_value:
94
+ cross_covariance_scaling = (
95
+ maximum_allowed_singular_value / largest_singular_value
96
+ )
97
+ covariance_xy = covariance_xy * cross_covariance_scaling
98
+
99
+ number_of_x_variables = covariance_x.shape[0]
100
+ number_of_y_variables = covariance_y.shape[0]
101
+ covariance = np.zeros(
102
+ (number_of_x_variables + number_of_y_variables,
103
+ number_of_x_variables + number_of_y_variables),
104
+ dtype=float,
105
+ )
106
+ covariance[:number_of_x_variables, :number_of_x_variables] = covariance_x
107
+ covariance[:number_of_x_variables, number_of_x_variables:] = covariance_xy
108
+ covariance[number_of_x_variables:, :number_of_x_variables] = covariance_xy.T
109
+ covariance[number_of_x_variables:, number_of_x_variables:] = covariance_y
110
+ covariance = 0.5 * (covariance + covariance.T)
111
+ return covariance, cross_covariance_scaling
112
+
113
+
114
+ def _covariances_to_precisions(covariances, covariance_type):
115
+ """Convert covariance parameters to the precisions_init representation."""
116
+ if covariance_type == 'full':
117
+ number_of_components, number_of_variables, _ = covariances.shape
118
+ identity = np.eye(number_of_variables)
119
+ precisions = np.empty_like(covariances)
120
+ for component_number in range(number_of_components):
121
+ precision = np.linalg.solve(
122
+ covariances[component_number], identity
123
+ )
124
+ precisions[component_number] = 0.5 * (
125
+ precision + precision.T
126
+ )
127
+ return precisions
128
+ if covariance_type == 'tied':
129
+ identity = np.eye(covariances.shape[0])
130
+ precision = np.linalg.solve(covariances, identity)
131
+ return 0.5 * (precision + precision.T)
132
+ if covariance_type in ('diag', 'spherical'):
133
+ return 1.0 / covariances
134
+ raise ValueError('Invalid covariance_type: {0}'.format(covariance_type))
135
+
136
+
137
+ def _estimate_covariances_given_means(
138
+ dataset, responsibilities, effective_sample_sizes, means,
139
+ reg_covar, covariance_type):
140
+ """
141
+ Estimate covariance parameters with means supplied externally.
142
+
143
+ This helper is also used by ssGMR-xGMM-xMA after replacing the x-side
144
+ means with their anchors.
145
+ """
146
+ dataset = np.asarray(dataset, dtype=float)
147
+ responsibilities = np.asarray(responsibilities, dtype=float)
148
+ means = np.asarray(means, dtype=float)
149
+ number_of_samples, number_of_variables = dataset.shape
150
+ number_of_components = responsibilities.shape[1]
151
+
152
+ if covariance_type == 'full':
153
+ covariances = np.empty(
154
+ (number_of_components, number_of_variables, number_of_variables),
155
+ dtype=float,
156
+ )
157
+ for component_number in range(number_of_components):
158
+ centered_dataset = dataset - means[component_number]
159
+ covariance = (
160
+ centered_dataset.T * responsibilities[:, component_number]
161
+ ).dot(centered_dataset) / effective_sample_sizes[component_number]
162
+ covariance = 0.5 * (covariance + covariance.T)
163
+ covariance.flat[::number_of_variables + 1] += reg_covar
164
+ covariances[component_number] = covariance
165
+ return covariances
166
+
167
+ if covariance_type == 'tied':
168
+ covariance = np.zeros(
169
+ (number_of_variables, number_of_variables), dtype=float
170
+ )
171
+ for component_number in range(number_of_components):
172
+ centered_dataset = dataset - means[component_number]
173
+ covariance += (
174
+ centered_dataset.T * responsibilities[:, component_number]
175
+ ).dot(centered_dataset)
176
+ covariance /= responsibilities.sum()
177
+ covariance = 0.5 * (covariance + covariance.T)
178
+ covariance.flat[::number_of_variables + 1] += reg_covar
179
+ return covariance
180
+
181
+ if covariance_type == 'diag':
182
+ covariances = np.empty(
183
+ (number_of_components, number_of_variables), dtype=float
184
+ )
185
+ for component_number in range(number_of_components):
186
+ centered_dataset = dataset - means[component_number]
187
+ covariances[component_number] = (
188
+ responsibilities[:, component_number, np.newaxis]
189
+ * centered_dataset ** 2
190
+ ).sum(axis=0) / effective_sample_sizes[component_number]
191
+ covariances += reg_covar
192
+ return covariances
193
+
194
+ if covariance_type == 'spherical':
195
+ covariances = np.empty(number_of_components, dtype=float)
196
+ for component_number in range(number_of_components):
197
+ centered_dataset = dataset - means[component_number]
198
+ covariances[component_number] = (
199
+ responsibilities[:, component_number]
200
+ * np.sum(centered_dataset ** 2, axis=1)
201
+ ).sum() / (
202
+ effective_sample_sizes[component_number] * number_of_variables
203
+ )
204
+ covariances += reg_covar
205
+ return covariances
206
+
207
+ raise ValueError('Invalid covariance_type: {0}'.format(covariance_type))
208
+
209
+
210
+ def _compute_precision_cholesky(covariances, covariance_type):
211
+ """
212
+ Compute precision Cholesky factors using scikit-learn's convention.
213
+
214
+ For full and tied covariances, the returned matrix is inv(L).T where
215
+ covariance = L L.T.
216
+ """
217
+ if covariance_type == 'full':
218
+ number_of_components, number_of_variables, _ = covariances.shape
219
+ identity = np.eye(number_of_variables)
220
+ precision_cholesky = np.empty_like(covariances)
221
+ for component_number in range(number_of_components):
222
+ cholesky = np.linalg.cholesky(covariances[component_number])
223
+ precision_cholesky[component_number] = np.linalg.solve(
224
+ cholesky, identity
225
+ ).T
226
+ return precision_cholesky
227
+
228
+ if covariance_type == 'tied':
229
+ number_of_variables = covariances.shape[0]
230
+ cholesky = np.linalg.cholesky(covariances)
231
+ return np.linalg.solve(cholesky, np.eye(number_of_variables)).T
232
+
233
+ if covariance_type in ('diag', 'spherical'):
234
+ if np.any(covariances <= 0):
235
+ raise ValueError('Covariances must be positive.')
236
+ return 1.0 / np.sqrt(covariances)
237
+
238
+ raise ValueError('Invalid covariance_type: {0}'.format(covariance_type))
239
+
240
+
241
+ class SSGMRXGMM(GMR):
242
+ """
243
+ Semi-supervised GMR with x-only GMM pretraining (ssGMR-xGMM).
244
+
245
+ A GMM is first fitted to x data that do not require corresponding y
246
+ measurements. Its weights, x-side means, and x-side covariance structure
247
+ are then used to initialize a joint GMM fitted to the labeled [x, y]
248
+ samples. After fitting, all prediction methods inherited from ``GMR`` can
249
+ be used for forward prediction and direct inverse analysis.
250
+
251
+ Parameters
252
+ ----------
253
+ n_components : int, default=1
254
+ Number of Gaussian components.
255
+ covariance_type : {'full', 'diag', 'tied', 'spherical'}, default='full'
256
+ Covariance representation used in both the x-only and joint GMMs.
257
+ rep : {'mean', 'mode'}, default='mean'
258
+ Representative value returned by ``predict_rep``. ``'mode'`` selects
259
+ the conditional mean of the component with the largest responsibility;
260
+ ``'mean'`` returns the responsibility-weighted conditional mean.
261
+ tol : float, default=1e-3
262
+ EM convergence tolerance.
263
+ reg_covar : float, default=1e-6
264
+ Non-negative regularization added to covariance diagonals.
265
+ max_iter : int, default=100
266
+ Maximum number of EM iterations for both GMM stages.
267
+ x_gmm_n_init : int, default=1
268
+ Number of initializations for the x-only GMM.
269
+ init_params : {'kmeans', 'k-means++', 'random', 'random_from_data'},
270
+ default='kmeans'
271
+ Initialization method for the x-only GMM.
272
+ random_state : int, RandomState or None, default=None
273
+ Random seed.
274
+ display_flag : bool, default=False
275
+ DCEKit-compatible display flag used by inherited utility methods.
276
+ responsibility_floor : float, default=1e-12
277
+ Small floor applied to x-only responsibilities before calculating
278
+ y-side initial values.
279
+ verbose : int, default=0
280
+ Verbosity passed to scikit-learn's GMM implementation.
281
+ verbose_interval : int, default=10
282
+ Interval between verbose messages.
283
+
284
+ Notes
285
+ -----
286
+ ``labeled_dataset`` and ``x_for_pretraining`` must be expressed in exactly
287
+ the same x-variable scale and column order. The class intentionally does
288
+ not autoscale data, matching DCEKit's ``GMR`` behavior.
289
+ """
290
+
291
+ def __init__(
292
+ self, n_components=1, covariance_type='full', rep='mean',
293
+ tol=1e-3, reg_covar=1e-6, max_iter=100, x_gmm_n_init=1,
294
+ init_params='kmeans', random_state=None, display_flag=False,
295
+ responsibility_floor=1e-12, verbose=0, verbose_interval=10):
296
+ # GMR.__init__ currently uses super(self.__class__, self), which is not
297
+ # subclass-safe. Initialize GaussianMixture directly and inherit only
298
+ # GMR's regression/prediction methods.
299
+ GaussianMixture.__init__(
300
+ self,
301
+ n_components=n_components,
302
+ covariance_type=covariance_type,
303
+ tol=tol,
304
+ reg_covar=reg_covar,
305
+ max_iter=max_iter,
306
+ n_init=1,
307
+ init_params=init_params,
308
+ weights_init=None,
309
+ means_init=None,
310
+ precisions_init=None,
311
+ random_state=random_state,
312
+ warm_start=False,
313
+ verbose=verbose,
314
+ verbose_interval=verbose_interval,
315
+ )
316
+ self.rep = rep
317
+ self.x_gmm_n_init = x_gmm_n_init
318
+ self.display_flag = display_flag
319
+ self.responsibility_floor = responsibility_floor
320
+
321
+ def _check_method_parameters(self):
322
+ if self.covariance_type not in _VALID_COVARIANCE_TYPES:
323
+ raise ValueError(
324
+ 'covariance_type must be one of {0}.'.format(
325
+ _VALID_COVARIANCE_TYPES
326
+ )
327
+ )
328
+ if self.rep not in ('mean', 'mode'):
329
+ raise ValueError("rep must be either 'mean' or 'mode'.")
330
+ if not isinstance(self.x_gmm_n_init, (int, np.integer)) \
331
+ or self.x_gmm_n_init < 1:
332
+ raise ValueError('x_gmm_n_init must be a positive integer.')
333
+ if self.responsibility_floor < 0:
334
+ raise ValueError('responsibility_floor must be non-negative.')
335
+ if self.reg_covar < 0:
336
+ raise ValueError('reg_covar must be non-negative.')
337
+
338
+ def _prepare_joint_fit(self):
339
+ """Hook used by ssGMR-xGMM-xMA before joint EM starts."""
340
+ return None
341
+
342
+ def _build_initial_parameters(
343
+ self, labeled_dataset, x_labeled, y_labeled,
344
+ numbers_of_x, numbers_of_y):
345
+ number_of_components = self.n_components
346
+ number_of_variables = labeled_dataset.shape[1]
347
+ number_of_x_variables = len(numbers_of_x)
348
+ number_of_y_variables = len(numbers_of_y)
349
+ responsibilities = self.x_gmm_.predict_proba(x_labeled)
350
+ if self.responsibility_floor > 0:
351
+ responsibilities = np.maximum(
352
+ responsibilities, self.responsibility_floor
353
+ )
354
+ responsibilities /= responsibilities.sum(axis=1, keepdims=True)
355
+ effective_sample_sizes = responsibilities.sum(axis=0)
356
+ minimum_effective_sample_size = np.finfo(float).eps * 100
357
+ effective_sample_sizes = np.maximum(
358
+ effective_sample_sizes, minimum_effective_sample_size
359
+ )
360
+
361
+ initial_weights = np.asarray(self.x_gmm_.weights_, dtype=float).copy()
362
+ initial_weights /= initial_weights.sum()
363
+
364
+ initial_means = np.zeros(
365
+ (number_of_components, number_of_variables), dtype=float
366
+ )
367
+ initial_means[:, numbers_of_x] = self.x_gmm_.means_
368
+ initial_y_means = responsibilities.T.dot(y_labeled) \
369
+ / effective_sample_sizes[:, np.newaxis]
370
+ initial_means[:, numbers_of_y] = initial_y_means
371
+
372
+ minimum_eigenvalue = max(
373
+ float(self.reg_covar), np.finfo(float).eps * 100
374
+ )
375
+ cross_covariance_scaling = np.ones(
376
+ number_of_components, dtype=float
377
+ )
378
+
379
+ if self.covariance_type == 'full':
380
+ initial_covariances = np.zeros(
381
+ (number_of_components, number_of_variables,
382
+ number_of_variables),
383
+ dtype=float,
384
+ )
385
+ for component_number in range(number_of_components):
386
+ sample_weights = responsibilities[:, component_number]
387
+ centered_x = (
388
+ x_labeled - self.x_gmm_.means_[component_number]
389
+ )
390
+ centered_y = (
391
+ y_labeled - initial_y_means[component_number]
392
+ )
393
+ covariance_y = (
394
+ centered_y.T * sample_weights
395
+ ).dot(centered_y) / effective_sample_sizes[component_number]
396
+ covariance_y = _make_symmetric_positive_definite(
397
+ covariance_y, minimum_eigenvalue
398
+ )
399
+ covariance_xy = (
400
+ centered_x.T * sample_weights
401
+ ).dot(centered_y) / effective_sample_sizes[component_number]
402
+ covariance, scaling = _stabilize_joint_covariance(
403
+ self.x_gmm_.covariances_[component_number],
404
+ covariance_xy,
405
+ covariance_y,
406
+ minimum_eigenvalue,
407
+ )
408
+ cross_covariance_scaling[component_number] = scaling
409
+ initial_covariances[component_number][
410
+ np.ix_(numbers_of_x, numbers_of_x)
411
+ ] = covariance[
412
+ :number_of_x_variables, :number_of_x_variables
413
+ ]
414
+ initial_covariances[component_number][
415
+ np.ix_(numbers_of_x, numbers_of_y)
416
+ ] = covariance[
417
+ :number_of_x_variables, number_of_x_variables:
418
+ ]
419
+ initial_covariances[component_number][
420
+ np.ix_(numbers_of_y, numbers_of_x)
421
+ ] = covariance[
422
+ number_of_x_variables:, :number_of_x_variables
423
+ ]
424
+ initial_covariances[component_number][
425
+ np.ix_(numbers_of_y, numbers_of_y)
426
+ ] = covariance[
427
+ number_of_x_variables:, number_of_x_variables:
428
+ ]
429
+
430
+ elif self.covariance_type == 'diag':
431
+ initial_covariances = np.zeros(
432
+ (number_of_components, number_of_variables), dtype=float
433
+ )
434
+ initial_covariances[:, numbers_of_x] = self.x_gmm_.covariances_
435
+ for component_number in range(number_of_components):
436
+ centered_y = (
437
+ y_labeled - initial_y_means[component_number]
438
+ )
439
+ y_variances = (
440
+ responsibilities[:, component_number, np.newaxis]
441
+ * centered_y ** 2
442
+ ).sum(axis=0) / effective_sample_sizes[component_number]
443
+ initial_covariances[
444
+ component_number, numbers_of_y
445
+ ] = np.maximum(y_variances, minimum_eigenvalue)
446
+ initial_covariances = np.maximum(
447
+ initial_covariances, minimum_eigenvalue
448
+ )
449
+
450
+ elif self.covariance_type == 'tied':
451
+ covariance_xy_all = np.zeros(
452
+ (number_of_components, number_of_x_variables,
453
+ number_of_y_variables),
454
+ dtype=float,
455
+ )
456
+ covariance_y_all = np.zeros(
457
+ (number_of_components, number_of_y_variables,
458
+ number_of_y_variables),
459
+ dtype=float,
460
+ )
461
+ for component_number in range(number_of_components):
462
+ sample_weights = responsibilities[:, component_number]
463
+ centered_x = (
464
+ x_labeled - self.x_gmm_.means_[component_number]
465
+ )
466
+ centered_y = (
467
+ y_labeled - initial_y_means[component_number]
468
+ )
469
+ covariance_xy_all[component_number] = (
470
+ centered_x.T * sample_weights
471
+ ).dot(centered_y) / effective_sample_sizes[component_number]
472
+ covariance_y_all[component_number] = (
473
+ centered_y.T * sample_weights
474
+ ).dot(centered_y) / effective_sample_sizes[component_number]
475
+
476
+ component_weights = effective_sample_sizes \
477
+ / effective_sample_sizes.sum()
478
+ covariance_xy = np.tensordot(
479
+ component_weights, covariance_xy_all, axes=(0, 0)
480
+ )
481
+ covariance_y = np.tensordot(
482
+ component_weights, covariance_y_all, axes=(0, 0)
483
+ )
484
+ covariance, scaling = _stabilize_joint_covariance(
485
+ self.x_gmm_.covariances_, covariance_xy, covariance_y,
486
+ minimum_eigenvalue,
487
+ )
488
+ cross_covariance_scaling[:] = scaling
489
+ initial_covariances = np.zeros(
490
+ (number_of_variables, number_of_variables), dtype=float
491
+ )
492
+ initial_covariances[np.ix_(numbers_of_x, numbers_of_x)] = \
493
+ covariance[:number_of_x_variables, :number_of_x_variables]
494
+ initial_covariances[np.ix_(numbers_of_x, numbers_of_y)] = \
495
+ covariance[:number_of_x_variables, number_of_x_variables:]
496
+ initial_covariances[np.ix_(numbers_of_y, numbers_of_x)] = \
497
+ covariance[number_of_x_variables:, :number_of_x_variables]
498
+ initial_covariances[np.ix_(numbers_of_y, numbers_of_y)] = \
499
+ covariance[number_of_x_variables:, number_of_x_variables:]
500
+
501
+ else: # spherical
502
+ initial_covariances = np.zeros(number_of_components, dtype=float)
503
+ for component_number in range(number_of_components):
504
+ centered_y = (
505
+ y_labeled - initial_y_means[component_number]
506
+ )
507
+ y_variances = (
508
+ responsibilities[:, component_number, np.newaxis]
509
+ * centered_y ** 2
510
+ ).sum(axis=0) / effective_sample_sizes[component_number]
511
+ initial_covariances[component_number] = (
512
+ number_of_x_variables
513
+ * self.x_gmm_.covariances_[component_number]
514
+ + np.sum(y_variances)
515
+ ) / number_of_variables
516
+ initial_covariances = np.maximum(
517
+ initial_covariances, minimum_eigenvalue
518
+ )
519
+
520
+ initial_precisions = _covariances_to_precisions(
521
+ initial_covariances, self.covariance_type
522
+ )
523
+ return (
524
+ initial_weights,
525
+ initial_means,
526
+ initial_covariances,
527
+ initial_precisions,
528
+ responsibilities,
529
+ effective_sample_sizes,
530
+ cross_covariance_scaling,
531
+ )
532
+
533
+ def fit(
534
+ self, labeled_dataset, x_for_pretraining=None,
535
+ numbers_of_x=None, numbers_of_y=None):
536
+ """
537
+ Fit ssGMR-xGMM.
538
+
539
+ Parameters
540
+ ----------
541
+ labeled_dataset : array-like of shape (n_labeled, n_x + n_y)
542
+ Paired, consistently scaled [x, y] samples used for the joint GMM.
543
+ x_for_pretraining : array-like of shape (n_pretraining, n_x), optional
544
+ x samples used by the x-only GMM. They may include the labeled x
545
+ samples, additional unlabeled x samples, and, in a transductive
546
+ protocol, target-domain test x samples. If omitted, the labeled x
547
+ samples are used.
548
+ numbers_of_x : array-like of int
549
+ Column numbers of x in ``labeled_dataset``.
550
+ numbers_of_y : array-like of int
551
+ Column numbers of y in ``labeled_dataset``.
552
+
553
+ Returns
554
+ -------
555
+ self : SSGMRXGMM
556
+ Fitted model.
557
+ """
558
+ self._check_method_parameters()
559
+ labeled_dataset = _as_2d_float_array(
560
+ labeled_dataset, 'labeled_dataset'
561
+ )
562
+ number_of_variables = labeled_dataset.shape[1]
563
+ if numbers_of_x is None or numbers_of_y is None:
564
+ raise ValueError(
565
+ 'numbers_of_x and numbers_of_y must both be specified.'
566
+ )
567
+ numbers_of_x = _validate_variable_numbers(
568
+ numbers_of_x, number_of_variables, 'numbers_of_x'
569
+ )
570
+ numbers_of_y = _validate_variable_numbers(
571
+ numbers_of_y, number_of_variables, 'numbers_of_y'
572
+ )
573
+ if np.intersect1d(numbers_of_x, numbers_of_y).size:
574
+ raise ValueError('numbers_of_x and numbers_of_y must not overlap.')
575
+ if not np.array_equal(
576
+ np.sort(np.r_[numbers_of_x, numbers_of_y]),
577
+ np.arange(number_of_variables)):
578
+ raise ValueError(
579
+ 'numbers_of_x and numbers_of_y must jointly cover every '
580
+ 'column of labeled_dataset.'
581
+ )
582
+
583
+ x_labeled = labeled_dataset[:, numbers_of_x]
584
+ y_labeled = labeled_dataset[:, numbers_of_y]
585
+ if x_for_pretraining is None:
586
+ x_for_pretraining = x_labeled
587
+ else:
588
+ x_for_pretraining = np.asarray(
589
+ x_for_pretraining, dtype=float
590
+ )
591
+ if x_for_pretraining.ndim == 1 and len(numbers_of_x) == 1:
592
+ x_for_pretraining = x_for_pretraining.reshape(-1, 1)
593
+ if x_for_pretraining.ndim != 2:
594
+ raise ValueError(
595
+ 'x_for_pretraining must be a two-dimensional array.'
596
+ )
597
+ if x_for_pretraining.shape[1] == number_of_variables:
598
+ # Select x before the finite-value check so that a joint-style
599
+ # array may contain missing y values.
600
+ x_for_pretraining = x_for_pretraining[:, numbers_of_x]
601
+ elif x_for_pretraining.shape[1] != len(numbers_of_x):
602
+ raise ValueError(
603
+ 'x_for_pretraining must have either {0} x columns or {1} '
604
+ 'joint-data columns.'.format(
605
+ len(numbers_of_x), number_of_variables
606
+ )
607
+ )
608
+ x_for_pretraining = _as_2d_float_array(
609
+ x_for_pretraining, 'x_for_pretraining'
610
+ )
611
+
612
+ if labeled_dataset.shape[0] < self.n_components:
613
+ raise ValueError(
614
+ 'The number of labeled samples must be at least '
615
+ 'n_components.'
616
+ )
617
+ if x_for_pretraining.shape[0] < self.n_components:
618
+ raise ValueError(
619
+ 'The number of x pretraining samples must be at least '
620
+ 'n_components.'
621
+ )
622
+
623
+ self.numbers_of_x_ = numbers_of_x.copy()
624
+ self.numbers_of_y_ = numbers_of_y.copy()
625
+ self.number_of_x_variables_ = len(numbers_of_x)
626
+ self.number_of_y_variables_ = len(numbers_of_y)
627
+
628
+ self.x_gmm_ = GaussianMixture(
629
+ n_components=self.n_components,
630
+ covariance_type=self.covariance_type,
631
+ tol=self.tol,
632
+ reg_covar=self.reg_covar,
633
+ max_iter=self.max_iter,
634
+ n_init=self.x_gmm_n_init,
635
+ init_params=self.init_params,
636
+ random_state=self.random_state,
637
+ warm_start=False,
638
+ verbose=self.verbose,
639
+ verbose_interval=self.verbose_interval,
640
+ )
641
+ self.x_gmm_.fit(x_for_pretraining)
642
+
643
+ (
644
+ self.initial_weights_,
645
+ self.initial_means_,
646
+ self.initial_covariances_,
647
+ self.initial_precisions_,
648
+ self.x_responsibilities_for_labeled_samples_,
649
+ self.x_effective_sample_sizes_,
650
+ self.cross_covariance_scaling_,
651
+ ) = self._build_initial_parameters(
652
+ labeled_dataset,
653
+ x_labeled,
654
+ y_labeled,
655
+ numbers_of_x,
656
+ numbers_of_y,
657
+ )
658
+
659
+ # GaussianMixture consumes these public initialization attributes.
660
+ self.weights_init = self.initial_weights_.copy()
661
+ self.means_init = self.initial_means_.copy()
662
+ self.precisions_init = np.array(
663
+ self.initial_precisions_, copy=True
664
+ )
665
+ self._prepare_joint_fit()
666
+
667
+ # Call GaussianMixture.fit directly to retain GMR's prediction methods
668
+ # while avoiding GMR.__init__'s subclassing issue.
669
+ GaussianMixture.fit(self, labeled_dataset)
670
+ return self
671
+
672
+ def fit_from_xy(self, x_labeled, y_labeled, x_for_pretraining=None):
673
+ """
674
+ Convenience interface when x and y are supplied as separate arrays.
675
+ """
676
+ x_labeled = _as_2d_float_array(
677
+ x_labeled, 'x_labeled', allow_one_dimensional=True
678
+ )
679
+ y_labeled = _as_2d_float_array(
680
+ y_labeled, 'y_labeled', allow_one_dimensional=True
681
+ )
682
+ if x_labeled.shape[0] != y_labeled.shape[0]:
683
+ raise ValueError(
684
+ 'x_labeled and y_labeled must have the same number of samples.'
685
+ )
686
+ labeled_dataset = np.c_[x_labeled, y_labeled]
687
+ numbers_of_x = np.arange(x_labeled.shape[1])
688
+ numbers_of_y = np.arange(
689
+ x_labeled.shape[1], labeled_dataset.shape[1]
690
+ )
691
+ return self.fit(
692
+ labeled_dataset,
693
+ x_for_pretraining=x_for_pretraining,
694
+ numbers_of_x=numbers_of_x,
695
+ numbers_of_y=numbers_of_y,
696
+ )
@@ -0,0 +1,65 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ @author: Hiromasa Kaneko
4
+ """
5
+
6
+ # Semi-supervised Gaussian Mixture Regression with x-only GMM pretraining
7
+ # and x-mean anchoring (ssGMR-xGMM-xMA)
8
+
9
+ import numpy as np
10
+
11
+ from .ssgmr_xgmm import (
12
+ SSGMRXGMM,
13
+ _compute_precision_cholesky,
14
+ _estimate_covariances_given_means,
15
+ )
16
+
17
+
18
+ class SSGMRXGMMXMA(SSGMRXGMM):
19
+ """
20
+ Semi-supervised GMR with x-only GMM pretraining and x-mean anchoring.
21
+
22
+ Initialization is identical to :class:`SSGMRXGMM`. During every M-step of
23
+ the joint-GMM EM algorithm, the x-side means are replaced by the x-only GMM
24
+ means. Mixture weights, y-side means, and all covariance parameters remain
25
+ trainable. The covariance update is calculated around the anchored means,
26
+ as specified by ssGMR-xGMM-xMA.
27
+
28
+ The constructor and ``fit`` interface are the same as ``SSGMRXGMM``.
29
+ After fitting, all prediction methods inherited from DCEKit's ``GMR`` can
30
+ be used for forward prediction and direct inverse analysis.
31
+ """
32
+
33
+ def _prepare_joint_fit(self):
34
+ self.x_mean_anchors_ = self.initial_means_[
35
+ :, self.numbers_of_x_
36
+ ].copy()
37
+
38
+ def _m_step(self, dataset, log_responsibilities, xp=None):
39
+ """M-step with hard anchoring of the x-side component means."""
40
+ dataset = np.asarray(dataset, dtype=float)
41
+ log_responsibilities = np.asarray(
42
+ log_responsibilities, dtype=float
43
+ )
44
+ responsibilities = np.exp(log_responsibilities)
45
+ effective_sample_sizes = responsibilities.sum(axis=0)
46
+ effective_sample_sizes += 10 * np.finfo(float).eps
47
+
48
+ self.weights_ = effective_sample_sizes \
49
+ / effective_sample_sizes.sum()
50
+ means = responsibilities.T.dot(dataset) \
51
+ / effective_sample_sizes[:, np.newaxis]
52
+ means[:, self.numbers_of_x_] = self.x_mean_anchors_
53
+ self.means_ = means
54
+
55
+ self.covariances_ = _estimate_covariances_given_means(
56
+ dataset,
57
+ responsibilities,
58
+ effective_sample_sizes,
59
+ self.means_,
60
+ self.reg_covar,
61
+ self.covariance_type,
62
+ )
63
+ self.precisions_cholesky_ = _compute_precision_cholesky(
64
+ self.covariances_, self.covariance_type
65
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcekit
3
- Version: 2.16.1
3
+ Version: 2.17.1
4
4
  Summary: Data Chemical Engineering toolkit
5
5
  Home-page: https://github.com/hkaneko1985/dcekit/
6
6
  Author: Hiromasa Kaneko
@@ -14,6 +14,8 @@ dcekit/design_of_exmeriments/bayesian_optimization.py
14
14
  dcekit/generative_model/__init__.py
15
15
  dcekit/generative_model/gmr.py
16
16
  dcekit/generative_model/gtm.py
17
+ dcekit/generative_model/ssgmr_xgmm.py
18
+ dcekit/generative_model/ssgmr_xgmm_xma.py
17
19
  dcekit/generative_model/vbgmr.py
18
20
  dcekit/just_in_time/__init__.py
19
21
  dcekit/just_in_time/jitpls.py
@@ -13,7 +13,7 @@ with open('LICENSE.txt') as f:
13
13
 
14
14
  setup(
15
15
  name='dcekit',
16
- version='2.16.1',
16
+ version='2.17.1',
17
17
  description='Data Chemical Engineering toolkit',
18
18
  long_description=readme,
19
19
  author='Hiromasa Kaneko',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes