From 1936246dd6d5c0445526c26960355dd004512d35 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:00:30 -0700 Subject: [PATCH 01/16] Added Deterministic Shared Response Model --- brainiak/funcalign/srm.py | 244 +++++++++++++++++- .../srm_image_prediction_example.ipynb | 123 ++++++++- .../funcalign/srm_image_prediction_example.py | 61 ++++- tests/funcalign/test_srm.py | 114 +++++++- 4 files changed, 517 insertions(+), 25 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index ff97f6b8a..11cab9f87 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -11,9 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Probabilistic Shared Response Model (SRM) +"""Shared Response Model (SRM) -This implementation is based on the following publications: +The implementations are based on the following publications: .. [Chen2015] "A Reduced-Dimension fMRI Shared Response Model", P.-H. Chen, J. Chen, Y. Yeshurun-Dishon, U. Hasson, J. Haxby, P. Ramadge @@ -39,7 +39,7 @@ from sklearn.utils.validation import NotFittedError __all__ = [ - "SRM", + "SRM", "DetSRM" ] logger = logging.getLogger(__name__) @@ -425,3 +425,241 @@ def _srm(self, data): logger.info('Objective function %f' % loglike) return sigma_s, w, mu, rho2, shared_response + + +class DetSRM(BaseEstimator, TransformerMixin): + """Deterministic Shared Response Model (DetSRM) + + Given multi-subject data, factorize it as a shared response S among all + subjects and an orthogonal transform W per subject: + + .. math:: X_i \\approx W_i S ,~for~all~i=1\dots N + + Parameters + ---------- + + n_iter : int, default: 10 + Number of iterations to run the algorithm. + + features : int, default: 50 + Number of features to compute. + + rand_seed : int, default: 0 + Seed for initializing the random number generator. + + verbose : boolean, default: False + Verbose mode flag. + + + Attributes + ---------- + + w_ : list of array, element i has shape=[voxels_i, features] + The orthogonal transforms (mappings) for each subject. + + s_ : array, shape=[features, samples] + The shared response. + + .. note:: + The number of voxels may be different between subjects. However, the + number of samples must be the same across subjects. + + The Deterministic Shared Response Model is approximated using the + Block Coordinate Descent (BCD) algorithm proposed in [Chen2015]_. + + This is a single node version. + + The run-time complexity is :math:`O(I (V T K + K^3))` and the memory + complexity is :math:`O(V T)` with I - the number of iterations, V - the + sum of voxels from all subjects, T - the number of samples, and K - the + number of features (typically, :math:`V \\gg T \\gg K`). + """ + + def __init__(self, n_iter=10, features=50, rand_seed=0, verbose=False): + self.n_iter = n_iter + self.features = features + self.rand_seed = rand_seed + self.verbose = verbose + return + + def fit(self, X, y=None): + """Compute the Deterministic Shared Response Model + + Parameters + ---------- + X : list of 2D arrays, element i has shape=[voxels_i, samples] + Each element in the list contains the fMRI data of one subject. + + y : not used + """ + if self.verbose: + logger.info('Starting Deterministic SRM') + + # Check the number of subjects + if len(X) <= 1: + raise ValueError("There are not enough subjects " + "({0:d}) to train the model.".format(len(X))) + + # Check for input data sizes + if X[0].shape[1] < self.features: + raise ValueError( + "There are not enough samples to train the model with " + "{0:d} features.".format(self.features)) + + # Check if all subjects have same number of TRs + number_trs = X[0].shape[1] + number_subjects = len(X) + for subject in range(number_subjects): + assert_all_finite(X[subject]) + if X[subject].shape[1] != number_trs: + raise ValueError("Different number of samples between subjects" + ".") + + # Run SRM + self.w_, self.s_ = self._srm(X) + + return self + + def transform(self, X, y=None): + """Use the model to transform data to the Shared Response subspace + + Parameters + ---------- + X : list of 2D arrays, element i has shape=[voxels_i, samples_i] + Each element in the list contains the fMRI data of one subject. + + y : not used + + + Returns + ------- + s : list of 2D arrays, element i has shape=[features_i, samples_i] + Shared responses from input data (X) + """ + + # Check if the model exist + if hasattr(self, 'w_') is False: + raise NotFittedError("The model fit has not been run yet.") + + # Check the number of subjects + if len(X) != len(self.w_): + raise ValueError("The number of subjects does not match the one" + " in the model.") + + s = [None] * len(X) + for subject in range(len(X)): + s[subject] = self.w_[subject].T.dot(X[subject]) + + return s + + def _objective_function(self, data, w, s): + """Calculate the objective function + + Parameters + ---------- + + data : list of 2D arrays, element i has shape=[voxels_i, samples] + Each element in the list contains the fMRI data of one subject. + + w : list of 2D arrays, element i has shape=[voxels_i, features] + The orthogonal transforms (mappings) :math:`W_i` for each subject. + + s : array, shape=[features, samples] + The shared response + + Returns + ------- + + objective : float + The objective function value. + """ + subjects = len(data) + objective = 0.0 + for m in range(subjects): + objective += \ + (np.linalg.norm(data[m] - w[m].dot(s), 'fro')**2)\ + * (0.5 / data[m].shape[1]) + + return objective + + def _compute_shared_response(self, data, w): + """ Compute the shared response S + + Parameters + ---------- + + data : list of 2D arrays, element i has shape=[voxels_i, samples] + Each element in the list contains the fMRI data of one subject. + + w : list of 2D arrays, element i has shape=[voxels_i, features] + The orthogonal transforms (mappings) :math:`W_i` for each subject. + + Returns + ------- + + s : array, shape=[features, samples] + The shared response for the subjects data with the mappings in w. + """ + s = np.zeros((w[0].shape[1], data[0].shape[1])) + for m in range(len(w)): + s = s + w[m].T.dot(data[m]) + s /= float(len(w)) + + return s + + def _srm(self, data): + """Expectation-Maximization algorithm for fitting the probabilistic SRM. + + Parameters + ---------- + + data : list of 2D arrays, element i has shape=[voxels_i, samples] + Each element in the list contains the fMRI data of one subject. + + + Returns + ------- + + w : list of array, element i has shape=[voxels_i, features] + The orthogonal transforms (mappings) :math:`W_i` for each subject. + + s : array, shape=[features, samples] + The shared response. + """ + + subjects = len(data) + + np.random.seed(self.rand_seed) + + # Initialization step: initialize the outputs with initial values, + # voxels with the number of voxels in each subject. + w, _ = _init_w_transforms(data, self.features) + shared_response = self._compute_shared_response(data, w) + if self.verbose: + # Calculate the current objective function value + objective = self._objective_function(data, w, shared_response) + logger.info('Objective function %f' % objective) + + # Main loop of the algorithm + for iteration in range(self.n_iter): + if self.verbose: + logger.info('Iteration %d' % (iteration + 1)) + + # Update each subject's mapping transform W_i: + for subject in range(subjects): + a_subject = data[subject].dot(shared_response.T) + perturbation = np.zeros(a_subject.shape) + np.fill_diagonal(perturbation, 0.001) + u_subject, _, v_subject = np.linalg.svd( + a_subject + perturbation, full_matrices=False) + w[subject] = u_subject.dot(v_subject) + + # Update the shared response: + shared_response = self._compute_shared_response(data, w) + + if self.verbose: + # Calculate the current objective function value + objective = self._objective_function(data, w, shared_response) + logger.info('Objective function %f' % objective) + + return w, shared_response diff --git a/examples/funcalign/srm_image_prediction_example.ipynb b/examples/funcalign/srm_image_prediction_example.ipynb index c82c349bb..a9fa3454b 100644 --- a/examples/funcalign/srm_image_prediction_example.ipynb +++ b/examples/funcalign/srm_image_prediction_example.ipynb @@ -53,7 +53,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -72,7 +72,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -91,7 +91,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -129,7 +129,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -147,7 +147,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -165,7 +165,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -184,7 +184,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -204,7 +204,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -223,7 +223,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -268,7 +268,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -315,6 +315,107 @@ "plot_confusion_matrix(cm, title=\"Confusion matrices for different test subjects\")\n", "print(\"The average accuracy among all subjects is {0:f} +/- {1:f}\".format(np.mean(accuracy), np.std(accuracy)))" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we repeat the experiment with the Deterministic SRM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "srm = brainiak.funcalign.srm.DetSRM(n_iter=10, features=50, verbose=True)\n", + "srm.fit(movie_data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Transform the image stimuli data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "image_data_shared = srm.transform(image_data)\n", + "for subject in range(subjects):\n", + " image_data_shared[subject] = stats.zscore(image_data_shared[subject], axis=1, ddof=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Run a leave-one-out cross validation with the subjects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "accuracy = np.zeros((subjects,))\n", + "cm = [None] * subjects\n", + "for subject in range(subjects):\n", + " # Concatenate the subjects' data for training into one matrix\n", + " train_subjects = list(range(subjects))\n", + " train_subjects.remove(subject)\n", + " TRs = image_data_shared[0].shape[1]\n", + " train_data = np.zeros((image_data_shared[0].shape[0], len(train_labels)))\n", + " for train_subject in range(len(train_subjects)):\n", + " start_index = train_subject*TRs\n", + " end_index = start_index+TRs\n", + " train_data[:, start_index:end_index] = image_data_shared[train_subjects[train_subject]]\n", + "\n", + " # Train a Nu-SVM classifier using scikit learn\n", + " classifier = NuSVC(nu=0.5, kernel='linear')\n", + " classifier = classifier.fit(train_data.T, train_labels)\n", + "\n", + " # Predict on the test data\n", + " predicted_labels = classifier.predict(image_data_shared[subject].T)\n", + " accuracy[subject] = sum(predicted_labels == test_labels)/float(len(predicted_labels))\n", + "\n", + " # Create a confusion matrix to see the accuracy of each class\n", + " cm[subject] = confusion_matrix(test_labels, predicted_labels)\n", + "\n", + " # Normalize the confusion matrix\n", + " cm[subject] = cm[subject].astype('float') / cm[subject].sum(axis=1)[:, np.newaxis]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot and print the results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "plot_confusion_matrix(cm, title=\"Confusion matrices for different test subjects with Deterministic SRM\")\n", + "print(\"Det. SRM: The average accuracy among all subjects is {0:f} +/- {1:f}\".format(np.mean(accuracy), np.std(accuracy)))" + ] } ], "metadata": { @@ -326,7 +427,7 @@ "language_info": { "codemirror_mode": { "name": "ipython", - "version": 3.0 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", diff --git a/examples/funcalign/srm_image_prediction_example.py b/examples/funcalign/srm_image_prediction_example.py index c8e69bf0d..975efa732 100644 --- a/examples/funcalign/srm_image_prediction_example.py +++ b/examples/funcalign/srm_image_prediction_example.py @@ -16,6 +16,7 @@ from sklearn.metrics import confusion_matrix from sklearn.svm import NuSVC import numpy as np +import brainiak.funcalign.srm # Load the input data that contains the movie stimuli for unsupervised training with SRM movie_data = scipy.io.loadmat('data/movie_data.mat') @@ -27,17 +28,17 @@ # Z-score the data for subject in range(subjects): - movie_data[subject] = stats.zscore(movie_data[subject],axis=1,ddof=1) + movie_data[subject] = stats.zscore(movie_data[subject], axis=1, ddof=1) # Run SRM with the movie data -import brainiak.funcalign.srm help(brainiak.funcalign.srm.SRM) srm = brainiak.funcalign.srm.SRM(n_iter=10, features=50, verbose=True) srm.fit(movie_data) # We define a function to present the output of the experiment. def plot_confusion_matrix(cm, title="Confusion Matrix"): - """Plots a confusion matrix for each subject""" + """Plots a confusion matrix for each subject + """ import matplotlib.pyplot as plt import math plt.figure() @@ -49,9 +50,9 @@ def plot_confusion_matrix(cm, title="Confusion Matrix"): for subject in range(subjects): plt.subplot(rows, cols, subject+1) plt.imshow(cm[subject], interpolation='nearest', cmap=plt.cm.bone) - plt.xticks(np.arange(classes), range(1,classes+1)) - plt.yticks(np.arange(classes), range(1,classes+1)) - cbar = plt.colorbar(ticks=[0.0,1.0], shrink=0.6) + plt.xticks(np.arange(classes), range(1, classes+1)) + plt.yticks(np.arange(classes), range(1, classes+1)) + cbar = plt.colorbar(ticks=[0.0, 1.0], shrink=0.6) cbar.set_clim(0.0, 1.0) plt.xlabel("Predicted") plt.ylabel("True label") @@ -70,7 +71,7 @@ def plot_confusion_matrix(cm, title="Confusion Matrix"): # Z-score the image data for subject in range(subjects): - image_data[subject] = stats.zscore(image_data[subject],axis=1,ddof=1) + image_data[subject] = stats.zscore(image_data[subject], axis=1, ddof=1) # Z-score the shared response data image_data_shared = srm.transform(image_data) @@ -84,7 +85,7 @@ def plot_confusion_matrix(cm, title="Confusion Matrix"): # Run a leave-one-out cross validation with the subjects train_labels = np.tile(labels, subjects-1) test_labels = labels -accuracy = np.zeros((subjects)) +accuracy = np.zeros((subjects,)) cm = [None] * subjects for subject in range(subjects): # Concatenate the subjects' data for training into one matrix @@ -113,7 +114,47 @@ def plot_confusion_matrix(cm, title="Confusion Matrix"): # Plot and print the results -plot_confusion_matrix(cm, title="Confusion matrices for different test subjects") -print("The average accuracy among all subjects is {0:f} +/- {1:f}".format(np.mean(accuracy), np.std(accuracy))) +plot_confusion_matrix(cm, title="Confusion matrices for different test subjects with Probabilistic SRM") +print("SRM: The average accuracy among all subjects is {0:f} +/- {1:f}".format(np.mean(accuracy), np.std(accuracy))) +# Now we repeat the experiment with the Deterministic SRM +# Fit the model +srm = brainiak.funcalign.srm.DetSRM(n_iter=10, features=50, verbose=True) +srm.fit(movie_data) +# Transform the image stimuli data +image_data_shared = srm.transform(image_data) +for subject in range(subjects): + image_data_shared[subject] = stats.zscore(image_data_shared[subject], axis=1, ddof=1) + +# Run a leave-one-out cross validation with the subjects +accuracy = np.zeros((subjects,)) +cm = [None] * subjects +for subject in range(subjects): + # Concatenate the subjects' data for training into one matrix + train_subjects = list(range(subjects)) + train_subjects.remove(subject) + TRs = image_data_shared[0].shape[1] + train_data = np.zeros((image_data_shared[0].shape[0], len(train_labels))) + for train_subject in range(len(train_subjects)): + start_index = train_subject*TRs + end_index = start_index+TRs + train_data[:, start_index:end_index] = image_data_shared[train_subjects[train_subject]] + + # Train a Nu-SVM classifier using scikit learn + classifier = NuSVC(nu=0.5, kernel='linear') + classifier = classifier.fit(train_data.T, train_labels) + + # Predict on the test data + predicted_labels = classifier.predict(image_data_shared[subject].T) + accuracy[subject] = sum(predicted_labels == test_labels)/float(len(predicted_labels)) + + # Create a confusion matrix to see the accuracy of each class + cm[subject] = confusion_matrix(test_labels, predicted_labels) + + # Normalize the confusion matrix + cm[subject] = cm[subject].astype('float') / cm[subject].sum(axis=1)[:, np.newaxis] + +# Plot and print the results +plot_confusion_matrix(cm, title="Confusion matrices for different test subjects with Deterministic SRM") +print("Det. SRM: The average accuracy among all subjects is {0:f} +/- {1:f}".format(np.mean(accuracy), np.std(accuracy))) diff --git a/tests/funcalign/test_srm.py b/tests/funcalign/test_srm.py index 6915ef78e..3d6e675ed 100644 --- a/tests/funcalign/test_srm.py +++ b/tests/funcalign/test_srm.py @@ -78,7 +78,18 @@ def test_can_instantiate(): assert s.s_.shape[0] == features, "Invalid computation of SRM! (wrong # features in S)" assert s.s_.shape[1] == samples, "Invalid computation of SRM! (wrong # samples in S)" - # Check that it does NOT run with non-matcing number of subjects + # Check that it does run to compute the shared response after the model computation + try: + new_s = s.transform(X) + except ValueError: + assert True, "Problem transforming new data with SRM." + + assert len(new_s) == subjects, "Invalid computation of SRM! (wrong # subjects after transform)" + for subject in range(subjects): + assert new_s[subject].shape[0] == features, "Invalid computation of SRM! (wrong # features after transform)" + assert new_s[subject].shape[1] == samples, "Invalid computation of SRM! (wrong # samples after transform)" + + # Check that it does NOT run with non-matching number of subjects try: s.transform(X[1]) assert True, "Success transforming with non-matching number of subjects" @@ -103,3 +114,104 @@ def test_can_instantiate(): print("Catched Exception number 2: different number of samples per subject") + +def test_det_srm(): + import brainiak.funcalign.srm + model = brainiak.funcalign.srm.DetSRM() + assert model, "Invalid DetSRM instance!" + + import numpy as np + + voxels = 100 + samples = 500 + subjects = 2 + features = 3 + + model = brainiak.funcalign.srm.DetSRM(verbose=True, n_iter=5, + features=features) + assert model, "Invalid DetSRM instance!" + + + # Create a Shared response S with K = 3 + theta = np.linspace(-4 * np.pi, 4 * np.pi, samples) + z = np.linspace(-2, 2, samples) + r = z**2 + 1 + x = r * np.sin(theta) + y = r * np.cos(theta) + + S = np.vstack((x, y, z)) + + X = [] + W = [] + Q, R = np.linalg.qr(np.random.random((voxels, features))) + W.append(Q) + X.append(Q.dot(S) + 0.1*np.random.random((voxels, samples))) + + # Check that transform does NOT run before fitting the model + try: + model.transform(X) + assert True, "Success transforming before fitting the model!" + except NotFittedError: + print("Caught Exception number 1: transforming before fitting the model") + + # Check that it does NOT run with 1 subject + try: + model.fit(X) + assert True, "Success running DetSRM with one subject!" + except ValueError: + print("Caught Exception number 1: running DetSRM with 1 subject") + + for subject in range(1, subjects): + Q, R = np.linalg.qr(np.random.random((voxels, features))) + W.append(Q) + X.append(Q.dot(S) + 0.1*np.random.random((voxels, samples))) + + # Check that runs with 2 subject + try: + model.fit(X) + except ValueError: + assert True, "Problem running DetSRM." + + assert len(model.w_) == subjects, "Invalid computation of DetSRM! (wrong # subjects in W)" + for subject in range(subjects): + assert model.w_[subject].shape[0] == voxels, "Invalid computation of DetSRM! (wrong # voxels in W)" + assert model.w_[subject].shape[1] == features, "Invalid computation of DetSRM! (wrong # features in W)" + assert model.s_.shape[0] == features, "Invalid computation of DetSRM! (wrong # features in S)" + assert model.s_.shape[1] == samples, "Invalid computation of DetSRM! (wrong # samples in S)" + + # Check that it does run to compute the shared response after the model computation + try: + new_s = model.transform(X) + except ValueError: + assert True, "Problem transforming new data with DetSRM." + + assert len(new_s) == subjects, "Invalid computation of DetSRM! (wrong # subjects after transform)" + for subject in range(subjects): + assert new_s[subject].shape[0] == features, "Invalid computation of DetSRM! (wrong # features after transform)" + assert new_s[subject].shape[1] == samples, "Invalid computation of DetSRM! (wrong # samples after transform)" + + # Check that it does NOT run with non-matching number of subjects + try: + model.transform(X[1]) + assert True, "Success transforming with non-matching number of subjects" + except ValueError: + print("Caught Exception number 2: transforming with non-matching number of subjects") + + # Check that it does not run without enough samples (TRs). + try: + model.set_params(features=(samples+1)) + model.fit(X) + assert True, "Success running DetSRM with more features than samples!" + except ValueError as e: + print("Catched Exception number 3: not enough samples") + + # Check that it does not run with different number of samples (TRs) + S2 = S[:,:-2] + X.append(Q.dot(S2)) + try: + model.fit(X) + assert True, "Success running DetSRM with different number of samples!" + except ValueError: + print("Catched Exception number 2: different number of samples per subject") + + From 512fc6ee84e0e7ca4ff021e45881135cbc29498e Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:17:48 -0700 Subject: [PATCH 02/16] Corrected complexity --- brainiak/funcalign/srm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 11cab9f87..5cd721997 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -469,7 +469,7 @@ class DetSRM(BaseEstimator, TransformerMixin): This is a single node version. - The run-time complexity is :math:`O(I (V T K + K^3))` and the memory + The run-time complexity is :math:`O(I (V T K))` and the memory complexity is :math:`O(V T)` with I - the number of iterations, V - the sum of voxels from all subjects, T - the number of samples, and K - the number of features (typically, :math:`V \\gg T \\gg K`). @@ -603,7 +603,7 @@ def _compute_shared_response(self, data, w): s = np.zeros((w[0].shape[1], data[0].shape[1])) for m in range(len(w)): s = s + w[m].T.dot(data[m]) - s /= float(len(w)) + s /= len(w) return s From 628b092471dddad39a355dd827b3e6b9f6aa1f13 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:36:53 -0700 Subject: [PATCH 03/16] Update srm.py --- brainiak/funcalign/srm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 5cd721997..b516e1edf 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -13,18 +13,18 @@ # limitations under the License. """Shared Response Model (SRM) -The implementations are based on the following publications: +This implementations are based on the following publications: .. [Chen2015] "A Reduced-Dimension fMRI Shared Response Model", P.-H. Chen, J. Chen, Y. Yeshurun-Dishon, U. Hasson, J. Haxby, P. Ramadge Advances in Neural Information Processing Systems (NIPS), 2015. http://papers.nips.cc/paper/5855-a-reduced-dimension-fmri-shared-response-model -.. [Anderson2016] "Scaling Up Machine Learning Algorithms For Multi-Subject - Neuroimaging Analysis", +.. [Anderson2016] "Enabling Factor Analysis on Thousand-Subject Neuroimaging Datasets", Michael J. Anderson, Mihai Capotă, Javier S. Turek, Xia Zhu, Theodore L. Willke, Yida Wang, Po-Hsuan Chen, Jeremy R. Manning, Peter J. Ramadge, - Kenneth A. Norman, under review, 2016. + Kenneth A. Norman, arXiv preprint, 2016. + https://arxiv.org/abs/1608.04647 """ # Authors: Po-Hsuan Chen (Princeton Neuroscience Institute) and Javier Turek From 4cf993269d4d0f5809606753da0580b0483c4797 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:37:39 -0700 Subject: [PATCH 04/16] Typo --- brainiak/funcalign/srm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index b516e1edf..1392b7f56 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -13,7 +13,7 @@ # limitations under the License. """Shared Response Model (SRM) -This implementations are based on the following publications: +These implementations are based on the following publications: .. [Chen2015] "A Reduced-Dimension fMRI Shared Response Model", P.-H. Chen, J. Chen, Y. Yeshurun-Dishon, U. Hasson, J. Haxby, P. Ramadge From 5d4c7b9a44648745e6cd66ab775bdc5214379916 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:39:37 -0700 Subject: [PATCH 05/16] too long documentation line --- brainiak/funcalign/srm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 1392b7f56..777fb1822 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -20,7 +20,8 @@ Advances in Neural Information Processing Systems (NIPS), 2015. http://papers.nips.cc/paper/5855-a-reduced-dimension-fmri-shared-response-model -.. [Anderson2016] "Enabling Factor Analysis on Thousand-Subject Neuroimaging Datasets", +.. [Anderson2016] "Enabling Factor Analysis on Thousand-Subject Neuroimaging + Datasets", Michael J. Anderson, Mihai Capotă, Javier S. Turek, Xia Zhu, Theodore L. Willke, Yida Wang, Po-Hsuan Chen, Jeremy R. Manning, Peter J. Ramadge, Kenneth A. Norman, arXiv preprint, 2016. From fac0661f4551e2086b52450e85d5a1805ca7bbb3 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:42:10 -0700 Subject: [PATCH 06/16] trailing white space removed --- brainiak/funcalign/srm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 777fb1822..00b434d83 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -20,7 +20,7 @@ Advances in Neural Information Processing Systems (NIPS), 2015. http://papers.nips.cc/paper/5855-a-reduced-dimension-fmri-shared-response-model -.. [Anderson2016] "Enabling Factor Analysis on Thousand-Subject Neuroimaging +.. [Anderson2016] "Enabling Factor Analysis on Thousand-Subject Neuroimaging Datasets", Michael J. Anderson, Mihai Capotă, Javier S. Turek, Xia Zhu, Theodore L. Willke, Yida Wang, Po-Hsuan Chen, Jeremy R. Manning, Peter J. Ramadge, From 6adfc01769b8caf9695da6e0a48d98e1f0e41be8 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Wed, 14 Sep 2016 14:54:30 -0700 Subject: [PATCH 07/16] Modified documentation and added arxiv link --- brainiak/funcalign/srm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 00b434d83..c285d7c20 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -13,7 +13,7 @@ # limitations under the License. """Shared Response Model (SRM) -These implementations are based on the following publications: +The implementations are based on the following publications: .. [Chen2015] "A Reduced-Dimension fMRI Shared Response Model", P.-H. Chen, J. Chen, Y. Yeshurun-Dishon, U. Hasson, J. Haxby, P. Ramadge @@ -641,7 +641,7 @@ def _srm(self, data): objective = self._objective_function(data, w, shared_response) logger.info('Objective function %f' % objective) - # Main loop of the algorithm + # Main loop of the algorithm for iteration in range(self.n_iter): if self.verbose: logger.info('Iteration %d' % (iteration + 1)) From 366f4e53a194d38c9b7a4348479f649f7317ef01 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 14:23:37 -0700 Subject: [PATCH 08/16] Removed unnecessary if's for logging --- brainiak/funcalign/srm.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index c285d7c20..7373ea83f 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -165,8 +165,7 @@ def fit(self, X, y=None): y : not used """ - if self.verbose: - logger.info('Starting Probabilistic SRM') + logger.info('Starting Probabilistic SRM') # Check the number of subjects if len(X) <= 1: @@ -357,8 +356,7 @@ def _srm(self, data): # Main loop of the algorithm (run for iteration in range(self.n_iter): - if self.verbose: - logger.info('Iteration %d' % (iteration + 1)) + logger.info('Iteration %d' % (iteration + 1)) # E-step: @@ -416,7 +414,7 @@ def _srm(self, data): rho2[subject] += trace_sigma_s rho2[subject] /= samples * voxels[subject] - if self.verbose: + if logger.isEnabledFor(20): # Calculate and log the current log-likelihood for checking # convergence loglike = self._likelihood( @@ -493,8 +491,7 @@ def fit(self, X, y=None): y : not used """ - if self.verbose: - logger.info('Starting Deterministic SRM') + logger.info('Starting Deterministic SRM') # Check the number of subjects if len(X) <= 1: @@ -636,15 +633,14 @@ def _srm(self, data): # voxels with the number of voxels in each subject. w, _ = _init_w_transforms(data, self.features) shared_response = self._compute_shared_response(data, w) - if self.verbose: + if logger.isEnabledFor(20): # Calculate the current objective function value objective = self._objective_function(data, w, shared_response) logger.info('Objective function %f' % objective) # Main loop of the algorithm for iteration in range(self.n_iter): - if self.verbose: - logger.info('Iteration %d' % (iteration + 1)) + logger.info('Iteration %d' % (iteration + 1)) # Update each subject's mapping transform W_i: for subject in range(subjects): @@ -658,7 +654,7 @@ def _srm(self, data): # Update the shared response: shared_response = self._compute_shared_response(data, w) - if self.verbose: + if logger.isEnabledFor(20): # Calculate the current objective function value objective = self._objective_function(data, w, shared_response) logger.info('Objective function %f' % objective) From cb80621d04c4586048f080bef8d3a5d628d2a63d Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 14:30:35 -0700 Subject: [PATCH 09/16] Changed the complexity description --- brainiak/funcalign/srm.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 7373ea83f..836b65d8b 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -142,10 +142,10 @@ class SRM(BaseEstimator, TransformerMixin): This is a single node version. - The run-time complexity is :math:`O(I (V T K + K^3))` and the memory - complexity is :math:`O(V T)` with I - the number of iterations, V - the - sum of voxels from all subjects, T - the number of samples, and K - the - number of features (typically, :math:`V \\gg T \\gg K`). + The run-time complexity is :math:`O(I (V T K + V K^2 + K^3))` and the + memory complexity is :math:`O(V T)` with I - the number of iterations, + V - the sum of voxels from all subjects, T - the number of samples, and + K - the number of features (typically, :math:`V \\gg T \\gg K`). """ def __init__(self, n_iter=10, features=50, rand_seed=0, verbose=False): @@ -468,10 +468,11 @@ class DetSRM(BaseEstimator, TransformerMixin): This is a single node version. - The run-time complexity is :math:`O(I (V T K))` and the memory + The run-time complexity is :math:`O(I (V T K + V K^2))` and the memory complexity is :math:`O(V T)` with I - the number of iterations, V - the - sum of voxels from all subjects, T - the number of samples, and K - the - number of features (typically, :math:`V \\gg T \\gg K`). + sum of voxels from all subjects, T - the number of samples, K - the + number of features (typically, :math:`V \\gg T \\gg K`), and N - the + number of subjects. """ def __init__(self, n_iter=10, features=50, rand_seed=0, verbose=False): @@ -575,10 +576,9 @@ def _objective_function(self, data, w, s): objective = 0.0 for m in range(subjects): objective += \ - (np.linalg.norm(data[m] - w[m].dot(s), 'fro')**2)\ - * (0.5 / data[m].shape[1]) + np.linalg.norm(data[m] - w[m].dot(s), 'fro')**2 - return objective + return objective * 0.5 / data[0].shape[1] def _compute_shared_response(self, data, w): """ Compute the shared response S From f05f087ebbfc41bb6ca164da2e0cab16b5fbb0ef Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 14:33:54 -0700 Subject: [PATCH 10/16] Removed verbose flag from SRM and DetSRM) --- brainiak/funcalign/srm.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 836b65d8b..4afbea4d7 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -109,9 +109,6 @@ class SRM(BaseEstimator, TransformerMixin): rand_seed : int, default: 0 Seed for initializing the random number generator. - verbose : boolean, default: False - Verbose mode flag. - Attributes ---------- @@ -148,11 +145,10 @@ class SRM(BaseEstimator, TransformerMixin): K - the number of features (typically, :math:`V \\gg T \\gg K`). """ - def __init__(self, n_iter=10, features=50, rand_seed=0, verbose=False): + def __init__(self, n_iter=10, features=50, rand_seed=0): self.n_iter = n_iter self.features = features self.rand_seed = rand_seed - self.verbose = verbose return def fit(self, X, y=None): @@ -446,9 +442,6 @@ class DetSRM(BaseEstimator, TransformerMixin): rand_seed : int, default: 0 Seed for initializing the random number generator. - verbose : boolean, default: False - Verbose mode flag. - Attributes ---------- @@ -475,11 +468,10 @@ class DetSRM(BaseEstimator, TransformerMixin): number of subjects. """ - def __init__(self, n_iter=10, features=50, rand_seed=0, verbose=False): + def __init__(self, n_iter=10, features=50, rand_seed=0): self.n_iter = n_iter self.features = features self.rand_seed = rand_seed - self.verbose = verbose return def fit(self, X, y=None): From 2956e8fdb1104eace7d581db8eb43b16b1a81d38 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 14:40:40 -0700 Subject: [PATCH 11/16] Added orthogonality check --- tests/funcalign/test_srm.py | 92 +++++++++++++------------------------ 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/tests/funcalign/test_srm.py b/tests/funcalign/test_srm.py index 3d6e675ed..7521877bb 100644 --- a/tests/funcalign/test_srm.py +++ b/tests/funcalign/test_srm.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from sklearn.utils.validation import NotFittedError - +import pytest def test_can_instantiate(): import brainiak.funcalign.srm @@ -26,8 +26,7 @@ def test_can_instantiate(): subjects = 2 features = 3 - s = brainiak.funcalign.srm.SRM(verbose=True, n_iter=5, - features=features) + s = brainiak.funcalign.srm.SRM(n_iter=5, features=features) assert s, "Invalid SRM instance!" @@ -47,18 +46,14 @@ def test_can_instantiate(): X.append(Q.dot(S) + 0.1*np.random.random((voxels, samples))) # Check that transform does NOT run before fitting the model - try: + with pytest.raises(NotFittedError) as excinfo: s.transform(X) - assert True, "Success transforming before fitting the model!" - except NotFittedError: - print("Caught Exception number 1: transforming before fitting the model") + print("Test: transforming before fitting the model") # Check that it does NOT run with 1 subject - try: + with pytest.raises(ValueError) as excinfo: s.fit(X) - assert True, "Success running SRM with one subject!" - except ValueError: - print("Caught Exception number 1: running SRM with 1 subject") + print("Test: running SRM with 1 subject") for subject in range(1, subjects): Q, R = np.linalg.qr(np.random.random((voxels, features))) @@ -66,23 +61,19 @@ def test_can_instantiate(): X.append(Q.dot(S) + 0.1*np.random.random((voxels, samples))) # Check that runs with 2 subject - try: - s.fit(X) - except ValueError: - assert True, "Problem running SRM." + s.fit(X) assert len(s.w_) == subjects, "Invalid computation of SRM! (wrong # subjects in W)" for subject in range(subjects): assert s.w_[subject].shape[0] == voxels, "Invalid computation of SRM! (wrong # voxels in W)" assert s.w_[subject].shape[1] == features, "Invalid computation of SRM! (wrong # features in W)" + ortho = np.linalg.norm(model.w_[subject].T.dot(model.w_[subject]) - np.eye(model.w_[subject].shape[1]), 'fro') + assert ortho < 1e-7, "A Wi mapping is not orthonormal in SRM." assert s.s_.shape[0] == features, "Invalid computation of SRM! (wrong # features in S)" assert s.s_.shape[1] == samples, "Invalid computation of SRM! (wrong # samples in S)" # Check that it does run to compute the shared response after the model computation - try: - new_s = s.transform(X) - except ValueError: - assert True, "Problem transforming new data with SRM." + new_s = s.transform(X) assert len(new_s) == subjects, "Invalid computation of SRM! (wrong # subjects after transform)" for subject in range(subjects): @@ -90,28 +81,22 @@ def test_can_instantiate(): assert new_s[subject].shape[1] == samples, "Invalid computation of SRM! (wrong # samples after transform)" # Check that it does NOT run with non-matching number of subjects - try: + with pytest.raises(ValueError) as excinfo: s.transform(X[1]) - assert True, "Success transforming with non-matching number of subjects" - except ValueError: - print("Caught Exception number 2: transforming with non-matching number of subjects") + print("Test: transforming with non-matching number of subjects") # Check that it does not run without enough samples (TRs). - try: + with pytest.raises(ValueError) as excinfo: s.set_params(features=(samples+1)) s.fit(X) - assert True, "Success running SRM with more features than samples!" - except ValueError as e: - print("Catched Exception number 3: not enough samples") + print("Test: not enough samples") # Check that it does not run with different number of samples (TRs) S2 = S[:,:-2] X.append(Q.dot(S2)) - try: + with pytest.raises(ValueError) as excinfo: s.fit(X) - assert True, "Success running SRM with different number of samples!" - except ValueError: - print("Catched Exception number 2: different number of samples per subject") + print("Test: different number of samples per subject") @@ -127,8 +112,7 @@ def test_det_srm(): subjects = 2 features = 3 - model = brainiak.funcalign.srm.DetSRM(verbose=True, n_iter=5, - features=features) + model = brainiak.funcalign.srm.DetSRM(n_iter=5, features=features) assert model, "Invalid DetSRM instance!" @@ -148,18 +132,14 @@ def test_det_srm(): X.append(Q.dot(S) + 0.1*np.random.random((voxels, samples))) # Check that transform does NOT run before fitting the model - try: + with pytest.raises(NotFittedError) as excinfo: model.transform(X) - assert True, "Success transforming before fitting the model!" - except NotFittedError: - print("Caught Exception number 1: transforming before fitting the model") + print("Test: transforming before fitting the model") # Check that it does NOT run with 1 subject - try: + with pytest.raises(ValueError) as excinfo: model.fit(X) - assert True, "Success running DetSRM with one subject!" - except ValueError: - print("Caught Exception number 1: running DetSRM with 1 subject") + print("Test: running DetSRM with 1 subject") for subject in range(1, subjects): Q, R = np.linalg.qr(np.random.random((voxels, features))) @@ -167,23 +147,19 @@ def test_det_srm(): X.append(Q.dot(S) + 0.1*np.random.random((voxels, samples))) # Check that runs with 2 subject - try: - model.fit(X) - except ValueError: - assert True, "Problem running DetSRM." + model.fit(X) assert len(model.w_) == subjects, "Invalid computation of DetSRM! (wrong # subjects in W)" for subject in range(subjects): assert model.w_[subject].shape[0] == voxels, "Invalid computation of DetSRM! (wrong # voxels in W)" assert model.w_[subject].shape[1] == features, "Invalid computation of DetSRM! (wrong # features in W)" + ortho = np.linalg.norm(model.w_[subject].T.dot(model.w_[subject]) - np.eye(model.w_[subject].shape[1]), 'fro') + assert ortho < 1e-7, "A Wi mapping is not orthonormal in DetSRM." assert model.s_.shape[0] == features, "Invalid computation of DetSRM! (wrong # features in S)" assert model.s_.shape[1] == samples, "Invalid computation of DetSRM! (wrong # samples in S)" # Check that it does run to compute the shared response after the model computation - try: - new_s = model.transform(X) - except ValueError: - assert True, "Problem transforming new data with DetSRM." + new_s = model.transform(X) assert len(new_s) == subjects, "Invalid computation of DetSRM! (wrong # subjects after transform)" for subject in range(subjects): @@ -191,27 +167,21 @@ def test_det_srm(): assert new_s[subject].shape[1] == samples, "Invalid computation of DetSRM! (wrong # samples after transform)" # Check that it does NOT run with non-matching number of subjects - try: + with pytest.raises(ValueError) as excinfo: model.transform(X[1]) - assert True, "Success transforming with non-matching number of subjects" - except ValueError: - print("Caught Exception number 2: transforming with non-matching number of subjects") + print("Test: transforming with non-matching number of subjects") # Check that it does not run without enough samples (TRs). - try: + with pytest.raises(ValueError) as excinfo: model.set_params(features=(samples+1)) model.fit(X) - assert True, "Success running DetSRM with more features than samples!" - except ValueError as e: - print("Catched Exception number 3: not enough samples") + print("Test: not enough samples") # Check that it does not run with different number of samples (TRs) S2 = S[:,:-2] X.append(Q.dot(S2)) - try: + with pytest.raises(ValueError) as excinfo: model.fit(X) - assert True, "Success running DetSRM with different number of samples!" - except ValueError: - print("Catched Exception number 2: different number of samples per subject") + print("Test: different number of samples per subject") From c75510cb5651e72b272a0326989599368a8e404f Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 14:54:02 -0700 Subject: [PATCH 12/16] Corrected SRM test --- tests/funcalign/test_srm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/funcalign/test_srm.py b/tests/funcalign/test_srm.py index 7521877bb..22e028321 100644 --- a/tests/funcalign/test_srm.py +++ b/tests/funcalign/test_srm.py @@ -67,7 +67,7 @@ def test_can_instantiate(): for subject in range(subjects): assert s.w_[subject].shape[0] == voxels, "Invalid computation of SRM! (wrong # voxels in W)" assert s.w_[subject].shape[1] == features, "Invalid computation of SRM! (wrong # features in W)" - ortho = np.linalg.norm(model.w_[subject].T.dot(model.w_[subject]) - np.eye(model.w_[subject].shape[1]), 'fro') + ortho = np.linalg.norm(s.w_[subject].T.dot(s.w_[subject]) - np.eye(s.w_[subject].shape[1]), 'fro') assert ortho < 1e-7, "A Wi mapping is not orthonormal in SRM." assert s.s_.shape[0] == features, "Invalid computation of SRM! (wrong # features in S)" assert s.s_.shape[1] == samples, "Invalid computation of SRM! (wrong # samples in S)" From 148a322b1f512e1a179c1a553864a4fc0298bda5 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 15:11:41 -0700 Subject: [PATCH 13/16] Added test for data fit of the model --- tests/funcalign/test_srm.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/funcalign/test_srm.py b/tests/funcalign/test_srm.py index 22e028321..a301cd4b4 100644 --- a/tests/funcalign/test_srm.py +++ b/tests/funcalign/test_srm.py @@ -69,6 +69,8 @@ def test_can_instantiate(): assert s.w_[subject].shape[1] == features, "Invalid computation of SRM! (wrong # features in W)" ortho = np.linalg.norm(s.w_[subject].T.dot(s.w_[subject]) - np.eye(s.w_[subject].shape[1]), 'fro') assert ortho < 1e-7, "A Wi mapping is not orthonormal in SRM." + difference = np.linalg.norm(X[subject] - s.w_[subject].dot(s.s_), 'fro') + assert difference < 1e-2, "Model seems incorrectly computed." assert s.s_.shape[0] == features, "Invalid computation of SRM! (wrong # features in S)" assert s.s_.shape[1] == samples, "Invalid computation of SRM! (wrong # samples in S)" @@ -155,6 +157,8 @@ def test_det_srm(): assert model.w_[subject].shape[1] == features, "Invalid computation of DetSRM! (wrong # features in W)" ortho = np.linalg.norm(model.w_[subject].T.dot(model.w_[subject]) - np.eye(model.w_[subject].shape[1]), 'fro') assert ortho < 1e-7, "A Wi mapping is not orthonormal in DetSRM." + difference = np.linalg.norm(X[subject] - model.w_[subject].dot(model.s_), 'fro') + assert difference < 1e-2, "Model seems incorrectly computed." assert model.s_.shape[0] == features, "Invalid computation of DetSRM! (wrong # features in S)" assert model.s_.shape[1] == samples, "Invalid computation of DetSRM! (wrong # samples in S)" From 1a84d07a7a0add7eb1ca62fc38a13608fb2827ab Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 15:20:40 -0700 Subject: [PATCH 14/16] remove verbose flag from example --- examples/funcalign/srm_image_prediction_example.ipynb | 4 ++-- examples/funcalign/srm_image_prediction_example.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/funcalign/srm_image_prediction_example.ipynb b/examples/funcalign/srm_image_prediction_example.ipynb index a9fa3454b..1364f0a65 100644 --- a/examples/funcalign/srm_image_prediction_example.ipynb +++ b/examples/funcalign/srm_image_prediction_example.ipynb @@ -114,7 +114,7 @@ }, "outputs": [], "source": [ - "srm = brainiak.funcalign.srm.SRM(n_iter=10, features=50, verbose=False)\n", + "srm = brainiak.funcalign.srm.SRM(n_iter=10, features=50)\n", "srm.fit(movie_data)" ] }, @@ -331,7 +331,7 @@ }, "outputs": [], "source": [ - "srm = brainiak.funcalign.srm.DetSRM(n_iter=10, features=50, verbose=True)\n", + "srm = brainiak.funcalign.srm.DetSRM(n_iter=10, features=50)\n", "srm.fit(movie_data)" ] }, diff --git a/examples/funcalign/srm_image_prediction_example.py b/examples/funcalign/srm_image_prediction_example.py index 975efa732..a65ffc327 100644 --- a/examples/funcalign/srm_image_prediction_example.py +++ b/examples/funcalign/srm_image_prediction_example.py @@ -32,7 +32,7 @@ # Run SRM with the movie data help(brainiak.funcalign.srm.SRM) -srm = brainiak.funcalign.srm.SRM(n_iter=10, features=50, verbose=True) +srm = brainiak.funcalign.srm.SRM(n_iter=10, features=50) srm.fit(movie_data) # We define a function to present the output of the experiment. @@ -119,7 +119,7 @@ def plot_confusion_matrix(cm, title="Confusion Matrix"): # Now we repeat the experiment with the Deterministic SRM # Fit the model -srm = brainiak.funcalign.srm.DetSRM(n_iter=10, features=50, verbose=True) +srm = brainiak.funcalign.srm.DetSRM(n_iter=10, features=50) srm.fit(movie_data) # Transform the image stimuli data From f17c0f33797146411e56f448cb4b0915d8a24cb8 Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 15:29:14 -0700 Subject: [PATCH 15/16] added relative error --- tests/funcalign/test_srm.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/funcalign/test_srm.py b/tests/funcalign/test_srm.py index a301cd4b4..5f9694260 100644 --- a/tests/funcalign/test_srm.py +++ b/tests/funcalign/test_srm.py @@ -70,7 +70,8 @@ def test_can_instantiate(): ortho = np.linalg.norm(s.w_[subject].T.dot(s.w_[subject]) - np.eye(s.w_[subject].shape[1]), 'fro') assert ortho < 1e-7, "A Wi mapping is not orthonormal in SRM." difference = np.linalg.norm(X[subject] - s.w_[subject].dot(s.s_), 'fro') - assert difference < 1e-2, "Model seems incorrectly computed." + datanorm = np.linalg.norm(X[subject], 'fro') + assert difference/datanorm < 1.0, "Model seems incorrectly computed." assert s.s_.shape[0] == features, "Invalid computation of SRM! (wrong # features in S)" assert s.s_.shape[1] == samples, "Invalid computation of SRM! (wrong # samples in S)" @@ -158,7 +159,8 @@ def test_det_srm(): ortho = np.linalg.norm(model.w_[subject].T.dot(model.w_[subject]) - np.eye(model.w_[subject].shape[1]), 'fro') assert ortho < 1e-7, "A Wi mapping is not orthonormal in DetSRM." difference = np.linalg.norm(X[subject] - model.w_[subject].dot(model.s_), 'fro') - assert difference < 1e-2, "Model seems incorrectly computed." + datanorm = np.linalg.norm(X[subject], 'fro') + assert difference/datanorm < 1.0, "Model seems incorrectly computed." assert model.s_.shape[0] == features, "Invalid computation of DetSRM! (wrong # features in S)" assert model.s_.shape[1] == samples, "Invalid computation of DetSRM! (wrong # samples in S)" From 2c78a672c4b902519cd05ef9664263f799a1887e Mon Sep 17 00:00:00 2001 From: Javier Turek Date: Tue, 20 Sep 2016 15:38:09 -0700 Subject: [PATCH 16/16] modify 20 by INFO constant --- brainiak/funcalign/srm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/brainiak/funcalign/srm.py b/brainiak/funcalign/srm.py index 4afbea4d7..f7a77b511 100644 --- a/brainiak/funcalign/srm.py +++ b/brainiak/funcalign/srm.py @@ -410,7 +410,7 @@ def _srm(self, data): rho2[subject] += trace_sigma_s rho2[subject] /= samples * voxels[subject] - if logger.isEnabledFor(20): + if logger.isEnabledFor(logging.INFO): # Calculate and log the current log-likelihood for checking # convergence loglike = self._likelihood( @@ -625,7 +625,7 @@ def _srm(self, data): # voxels with the number of voxels in each subject. w, _ = _init_w_transforms(data, self.features) shared_response = self._compute_shared_response(data, w) - if logger.isEnabledFor(20): + if logger.isEnabledFor(logging.INFO): # Calculate the current objective function value objective = self._objective_function(data, w, shared_response) logger.info('Objective function %f' % objective) @@ -646,7 +646,7 @@ def _srm(self, data): # Update the shared response: shared_response = self._compute_shared_response(data, w) - if logger.isEnabledFor(20): + if logger.isEnabledFor(logging.INFO): # Calculate the current objective function value objective = self._objective_function(data, w, shared_response) logger.info('Objective function %f' % objective)