-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpcm_fitModelGroupCrossval.m
executable file
·283 lines (264 loc) · 11.9 KB
/
pcm_fitModelGroupCrossval.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
function [T,theta_hat,G_pred]=pcm_fitModelGroupCrossval(Y,M,partitionVec,conditionVec,varargin);
% function [T,theta_hat,G_pred]=pcm_fitModelGroupCrossval(Y,M,partitionVec,conditionVec,varargin);
% Fits pattern component model(s) specified by M to data from a number of
% subjects.
% The model parameters are shared - the noise parameters are not.
% If provided with a G_hat and Sig_hat, it also automatically estimates close
% startingvalues.
%==========================================================================
% INPUT:
% Y: {#Subjects}.[#Conditions x #Voxels]
% Observed/estimated beta regressors from each subject.
% Preferably multivariate noise-normalized beta regressors.
%
% M: {#Model}
% Cell array of models to be fitted for each subject.
% Multiple competing models are stored in a cell array
% (e.g., M{3} means the third model).
%
% Requiered fields of each model structure are;
% .type: Type of the model to be fitted
% 'fixed': Fixed structure without parameter
% (except scale and noise for each subject)
% 'component': G is a sum of linear components,
% specified by Gc
% 'feature': G=A*A', with A a linear sum of
% weighted components Ac
% 'nonlinear': Nonlinear model with own function
% to return G-matrix and derivatives
% 'freedirect': Uses the mean estimated
% G-matrix from crossvalidation to get an
% estimate of the best achievable fit
% .numGparams: Scalar that defines the number of parameters
% included in model.
% .theta0: Vector of starting values for theta. If not given,
% the function attempts to estimate these from a
% crossvalidated version Values usually estimated from
% observed second-moment matrix.
% Model-specific field
% .modelpred': Modelling func. Must take theta values as vector
% and return predicated second moment matrix and
% derivatives in respect to parameters (for nonlinear models).
% .Gc: Linear component matrices (for type 'component')
% .Ac Linear component matrices (for type 'feature')
%
% partitionVec: {#Subjects} Cell array with partition assignment vector
% for each subject. Rows of partitionVec{subj} define
% partition assignment of rows of Y{subj}.
% Commonly these are the scanning run #s for beta
% regressors.If 1 vector is given, then partitions are
% assumed to be the same across subjects.
%
% conditionVec: {#Subjects} Cell array with condition assignment vector
% for each subject. Rows of conditionVec{subj} define
% condition assignment of rows of Y{subj}.
% If the (elements of) conditionVec are matrices, it is
% assumed to be the design matrix Z, allowing the
% specification individualized models.
%--------------------------------------------------------------------------
% OPTION:
% 'runEffect': How to deal with effects that may be specific to different
% imaging runs:
% 'random': Models variance of the run effect for each subject
% as a seperate random effects parameter.
% 'fixed': Consider run effect a fixed effect, will be removed
% implicitly using ReML (default).
% 'fitScale' Introduce additional scaling parameter for each
% participant? - default is yes
% 'isCheckDeriv: Check the derivative accuracy of theta params. Done using
% 'pcm_checkderiv'. This function compares input to finite
% differences approximations. See function documentation.
%
% 'MaxIteration': Number of max minimization iterations. Default is 1000.
%
% 'verbose': Optional flag to show display message in the command
% line (e.g., elapsed time). Default is 1.
%
% 'groupFit',T: Parameters theta from the group fit: This provides better starting
% values and can speed up the computation
%
% 'S': Specific assumed noise structure - usually inv(XX'*XX),
% where XX is the first-level design matrix used to
% estimate the activation estimates
%
% 'fitAlgorithm': Either 'NR' or 'minimize' - provides over-write for
% model specific algorithms
%
%
%--------------------------------------------------------------------------
% OUTPUT:
% T: Structure with following subfields:
% SN: Subject number
% likelihood: Crossvalidated likelihood
% scale: Fitted scaling parameter - exp(theta_scale)
% noise: Fitted noise parameter - exp(theta_noise)
% run: Fitted run parameter - exp(theta_run)
%
% theta_hat{model}: Cell array of models parameters from crossvalidation
% theta: numGparams{m}xnumSubj Matrix: Estimated parameters
% across the crossvalidation rounds from the training set.
% Gpred{model}(:,:,subject): Predicted second moment matrix for each model
% from cross-validation fit. 3rd dimension is for
% subjects
runEffect = 'fixed';
isCheckDeriv = 0;
MaxIteration = 1000;
verbose = 1;
groupFit = [];
fitScale = 1;
S = [];
fitAlgorithm = [];
pcm_vararginoptions(varargin,{'runEffect','isCheckDeriv','MaxIteration',...
'verbose','groupFit','fitScale','S','fitAlgorithm'});
numSubj = numel(Y);
% Determine number of models
if (~iscell(M))
M={M};
end;
numModels = numel(M);
% Preallocate output structure
T.SN = [1:numSubj]';
T.iterations = zeros(numSubj,1);
T.time = zeros(numSubj,1);
% Determine optimal algorithm for each of the models
if (~isempty(fitAlgorithm))
for m=1:numModels
M{m}.fitAlgorithm = fitAlgorithm;
end;
end;
M = pcm_optimalAlgorithm(M);
% Set up all parameters for the upcoming fit
[Z,B,X,YY,S,N,P,G_hat,noise0,run0]=...
pcm_setUpFit(Y,partitionVec,conditionVec,'runEffect',runEffect,'S',S);
% --------------------------------------------------------------
% Determine starting values for fit:
for m = 1:numModels
if (isempty(groupFit)) % No group fit: determine starting values
if (~isfield(M{m},'theta0'))
M{m}.theta0 = pcm_getStartingval(M{m},mean(G_hat,3));
end;
switch (M{m}.type)
case 'freedirect'
G0 = mean(G_hat,3);
M{m}.numGparams=0;
M{m}.Gc = G0;
otherwise
G0 = pcm_calculateG(M{m},M{m}.theta0);
end;
g0 = G0(:);
if (fitScale)
for s = 1:numSubj
g_hat = G_hat(:,:,s);
g_hat = g_hat(:);
scaling = (g0'*g_hat)/(g0'*g0);
if ((scaling<10e-5)||~isfinite(scaling)); scaling = 10e-5; end; % Enforce positive scaling
scale0(s,m) = log(scaling);
end;
end;
noise0(:,m)=noise0(:,1);
if (~isempty(run0))
run0(:,m)=run0(:,1);
end;
else % If group fit is given, start with those values
M{m}.theta0 = groupFit{m}(1:M{m}.numGparams);
indx = M{m}.numGparams;
noise0(:,m) = groupFit{m}(indx+1:indx+numSubj);
indx = indx+numSubj;
if (fitScale)
scale0(:,m) = groupFit{m}(indx+1:indx+numSubj);
indx = indx+numSubj;
end;
if (strcmp(runEffect,'random'))
run0(:,m) = groupFit{m}(indx+1:indx+numSubj);
end;
end;
end;
% -----------------------------------------------------
% Loop over subjects and obtain crossvalidated likelihoods
% -----------------------------------------------------
for s = 1:numSubj
% determine training set
notS = [1:numSubj];
notS(s) = [];
% Now loop over models
for m = 1:numModels
if (verbose)
if isfield(M{m},'name');
fprintf('Crossval Subj: %d model:%s',s,M{m}.name);
else
fprintf('Crossval Subj: %d model:%d',s,m);
end;
end;
tic;
% Now fit to all the subject but the left-out one
switch (M{m}.type)
case 'fixed'
if size(M{m}.Gc,3)>1
G = mean(M{m}.Gc(:,:,notS),3);
else
G = M{m}.Gc;
end
T.iterations(s,m)=0;
case 'freedirect'
G = mean(G_hat(:,:,notS),3); % uses the mean of all other subjects
G = pcm_makePD(G);
T.iterations(s,m)=0;
otherwise
% Generate the starting vector
x0 = [M{m}.theta0;noise0(notS,m)];
if (fitScale)
x0 = [x0;scale0(notS,m)];
end;
if (strcmp(runEffect,'random'))
x0 = [x0;run0(notS,m)]; % Start with G-params from group fit
end;
% Now do the Group fit
if (isempty(S))
fcn = @(x) pcm_likelihoodGroup(x,{YY{notS}},M{m},{Z{notS}},{X{notS}},P(notS(:)),...
'runEffect',{B{notS}},'fitScale',fitScale);
else
fcn = @(x) pcm_likelihoodGroup(x,{YY{notS}},M{m},{Z{notS}},{X{notS}},P(notS(:)),...
'runEffect',{B{notS}},'S',S(notS),'fitScale',fitScale);
end;
switch (M{m}.fitAlgorithm)
case 'minimize'
[theta,nlv,T.iterations(s,m)] = minimize(x0, fcn, MaxIteration);
T.fitLike(s,m)=-nlv(end);
case 'NR'
[theta,T.fitLike(s,m),T.iterations(s,m),T.reg(s,m)] = pcm_NR(x0, fcn,'verbose',verbose==2);
end;
theta_hat{m}(:,s)=theta(1:M{m}.numGparams);
G = pcm_calculateG(M{m},theta_hat{m}(1:M{m}.numGparams,s));
end;
% Collect G_pred used for each left-out subject
G_pred{m}(:,:,s) = G;
% Now get the fit the left-out subject
% (maximizing scale and noise coefficients)
x0 = noise0(s);
if fitScale
x0 = [x0;scale0(s,m)];
end;
if (strcmp(runEffect,'random'))
x0 = [x0;run0(s,m)];
end;
if (isempty(S))
fcn = @(x) pcm_likelihoodGroup(x,{YY{s}},G,{Z{s}},{X{s}},P(s),...
'runEffect',{B{s}},'fitScale',fitScale); % Minize scaling params only
else
fcn = @(x) pcm_likelihoodGroup(x,{YY{s}},G,{Z{s}},{X{s}},P(s),...
'runEffect',{B{s}},'S',S(s),'fitScale',fitScale); % Minize scaling params only
end;
[theta,T.likelihood(s,m)] = pcm_NR(x0, fcn);
T.time(s,m) = toc;
if verbose
fprintf('\t Iterations %d, Elapsed time: %3.3f\n',T.iterations(s,m),T.time(s,m));
end;
T.noise(s,m) = exp(theta(1));
if (fitScale)
T.scale(s,m) = exp(theta(2));
end;
if (strcmp(runEffect,'random'))
T.run(s,m) = exp(theta(fitScale+1));
end;
end; % Loop over Models
end; % Loop over Subjects