๐ŸฉปfMRI

2. Using the GLM to fMRI: estimation

date
Apr 17, 2023
slug
fmri-glm-2-1
author
status
Public
tags
NI-edu
Dartbrain
summary
How to GLM with fMRI
type
Post
thumbnail
category
๐ŸฉปfMRI
updatedAt
Apr 18, 2023 05:10 AM

2.1 Recap of linear regression

2.1.1 Notation

  • (์„ ํ˜•) ํšŒ๊ท€ ๋ชจ๋ธ์„ ์ƒ์„ฑํ•  ๋•Œ, ์šฐ๋ฆฌ๋Š” ์ข…์† ๋ณ€์ˆ˜์™€ ๋…๋ฆฝ ๋ณ€์ˆ˜๋ฅผ ์ง€์ •ํ•จ
  • ์œ„์˜ ์‹์—์„œ y๋Š” ์ข…์† ๋ณ€์ˆ˜, x๋Š” ๋…๋ฆฝ ๋ณ€์ˆ˜์ž„ (N์€ observations์˜ ์ˆ˜, P๋Š” predictors์˜ ์ˆ˜์ž„)
np.random.seed(1)

# ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ์…‹ ๋งŒ๋“ค๊ธฐ
prespecified_covariance = np.array([[1, .8], [.8, 1]]) # y์™€ x๋Š” 0.8์˜ ์ƒ๊ด€๊ด€๊ณ„ ์ง€๋‹˜

# x ํ‰๊ท ์€ 3, y ํ‰๊ท ์€ 7์ด๋ฉด์„œ, ์„œ๋กœ์˜ ์ƒ๊ด€๊ด€๊ณ„๋Š” 0.8์ธ ๋ฐ์ดํ„ฐ 30๊ฐœ ๋งŒ๋“ค๊ธฐ
data = np.random.multivariate_normal(mean=[3, 7], cov=prespecified_covariance, size=30)
X = data[:, 0, np.newaxis] # (30, 1)
y = data[:, 1, np.newaxis] # (30, 1)

# intercept modeling
n_obs = y.size
intercept = np.ones((n_obs, 1)) # (30, 1)

tuple_with_arrays = (intercept, X)
X_with_icept = np.hstack(tuple_with_arrays) # (30, 2)

2.1.2 Parameters in linear regression

  • linear regression์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œํ˜„ํ•จ
    • ์—ฌ๊ธฐ์„œ, ๊ฐ (๋…๋ฆฝ) ๋ณ€์ˆ˜๊ฐ€ ์ข…์†๋ณ€์ˆ˜์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ์ •๋„๋ฅผ beta-parameter๋ผ๊ณ  ํ•จ
  • beta-parameter๋ฅผ ์ถ”์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ Ordinary Least Squares(OLS, ์ตœ์†Œ์ œ๊ณฑ๋ฒ•) ์‚ฌ์šฉํ•จ
    • y_hat์€ ์‹ค์ œ y(์ข…์†๋ณ€์ˆ˜)๊ฐ€ ์•„๋‹ˆ๋ผ ์„ ํ˜•ํšŒ๊ท€์‹์œผ๋กœ ์–ป์€ ์ข…์†๋ณ€์ˆ˜์˜ ์ถ”์ •์น˜์ž„
    • ์‹ค์ œ y(์ข…์†๋ณ€์ˆ˜)์™€ y_hat์˜ ์ฐจ์ด์˜ ์ œ๊ณฑ์„ ์ค„์ด๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•จ
from numpy.linalg import inv

est_betas = inv(X_with_icept.T @ X_with_icept) @ X_with_icept.T @ y

print(est_betas)
>>>
[[4.25897963] # parameter of intercept
 [0.88186203]] # parameter of x
notion image

2.1.3 Residuals and model fit

  • residual์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜ํ•จ
    • ์‹ค์ œ ์ข…์†๋ณ€์ˆ˜์—์„œ ์˜ˆ์ธก์น˜๋ฅผ ๋บ€ ๊ฐ’์ž„
  • ์•„๋ž˜์˜ MSE, R^2๋Š” ํšŒ๊ท€ ์˜ˆ์ธก ์ •๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ฒ™๋„์ž„
notion image
mse = np.mean((y - y_hat) ** 2) # Mean Square Error

numerator = np.sum((y - y_hat) ** 2) # ๊ด€์ธก๊ฐ’ - ์ถ”์ •๊ฐ’(SSR, Residual Sum of Squares)
denominator = np.sum((y - np.mean(y)) ** 2) # ๊ด€์ธก๊ฐ’ - ๊ด€์ธก๊ฐ’์˜ ํ‰๊ท (SST, Total Sum of Squares)
r_squared = 1 - numerator / denominator

print('The Rยฒ value is: %.3f' % r_squared)
>>> The Rยฒ value is: 0.549 # 1์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์ข‹์€ ์„ฑ๋Šฅ

2.2 GLM in fMRI analyses

2.2.1 Variables

  • ์šฐ๋ฆฌ๋Š” regressionํ•˜๊ธฐ ์œ„ํ•ด ๋…๋ฆฝ๋ณ€์ˆ˜์™€ ์ข…์†๋ณ€์ˆ˜๋ฅผ ์ง€์ •ํ•  ํ•„์š”๊ฐ€ ์žˆ์Œ
  • ๋…๋ฆฝ๋ณ€์ˆ˜๋Š” (์‹œ๊ฐ) ์ž๊ทน ์‹œ์ ์ด๋ฉฐ, ๋…๋ฆฝ๋ณ€์ˆ˜๋กœ๋ถ€ํ„ฐ ์˜ˆ์ธกํ•˜๊ณ ์ž ํ•˜๋Š” ๋ณ€์ˆ˜(์ข…์†๋ณ€์ˆ˜)๋Š” voxel signal์ž„
1) Defining dependent variables
  • fMRI ๋ฐ์ดํ„ฐ๋กœ๋Š” discreteํ•œ ๋ณ€์ˆ˜์ด๋ฏ€๋กœ continuousํ•˜๊ฒŒ plotํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Œ
voxel_signal = np.load('example_voxel_signal.npy')
plt.figure(figsize=(25, 5))
plt.plot(voxel_signal) # discrete value to continuous line
plt.show()
notion image
2) Defining independent variables
  • 50์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ 1์ดˆ๊ฐ„ ์‚ฌ๊ฐํ˜• ํ™”๋ฉด๊ณผ ์› ํ™”๋ฉด์„ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ์ž๊ทน์„ ์ค„ ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Œ
notion image
onsets_squares = np.array([10, 110, 210, 310, 410, 510, 610, 710], dtype=int) # ์‚ฌ๊ฐํ˜• ํ™”๋ฉด ์ž๊ทน
onsets_circles = np.array([60, 160, 260, 360, 460, 560, 660, 760], dtype=int) # ์› ํ™”๋ฉด ์ž๊ทน

onsets_all = np.concatenate((onsets_squares, onsets_circles))
print(onsets_all)
>>> [ 10 110 210 310 410 510 610 710  60 160 260 360 460 560 660 760]

predictor_all = np.zeros(800)
predictor_all[onsets_all] = 1
print(predictor_all)
>>>
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 1., ...]
3) Resampling
  • ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋…๋ฆฝ๋ณ€์ˆ˜(stimulus_predictor, 800)๋Š” ์‹ค์ œ ์‹ ํ˜ธ ์‹œ๊ฐ„(400)๊ณผ ๋‹ค๋ฅธ ์‹œ๊ฐ„ ๋‹จ์œ„๋ฅผ ๊ฐ€์ง
    • fMRI๋Š” ๋Œ€๋žต 2์ดˆ๋™์•ˆ ์•„๋ž˜๋ถ€ํ„ฐ ์œ„๊นŒ์ง€ ์ธต์œผ๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ์Šค์บ”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž„
  • ์—ฌ๊ธฐ์—์„œ๋Š” 2์ดˆ๋งˆ๋‹ค ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ๋กœ down-sampling ํ†ตํ•ด ํ•ด๊ฒฐ ๊ฐ€๋Šฅ
  • Python์—์„œ scipy.interpolate ํŒจํ‚ค์ง€ ์•ˆ์˜ interp1d ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋ฆฌ์ƒ˜ํ”Œ๋ง ๊ฐ€๋Šฅ
    • interp1d ํ•จ์ˆ˜ ์‚ฌ์šฉ ์‹œ์— resampler ๋ณ€์ˆ˜ ์•ˆ์— ์›ํ•˜๋Š” ์Šค์ผ€์ผ์„ ์ €์žฅํ•˜๋ฉด ๊ทธ ์Šค์ผ€์ผ์— ๋งž์ถฐ ๋ฆฌ์ƒ˜ํ”Œ๋ง ๊ฐ€๋Šฅ
    • (+ ์‹ค์ œ๋กœ๋Š” ์ •๋ฐ€ํ•˜๊ฒŒ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ€๋ฆฌ์ดˆ ๊ทœ๋ชจ๋กœ ์‚ฌ์šฉํ•จ, [initial upsampling of predictors] ๋ถ€๋ถ„ ํ™•์ธํ•  ๊ฒƒ)
from scipy.interpolate import interp1d

original_scale = np.arange(0, 800, 1)  # from 0 to 800 seconds
print(original_scale)
>>> 800

resampler = interp1d(original_scale, predictor_all)

desired_scale = np.arange(0, 800, 2)
print(desired_scale)
>>> 400 # (0, 2, 4, ... 800, in volumes)

predictor_all_ds = resampler(desired_scale)
  • resampling์„ ๊ฑฐ์นœ ๋…๋ฆฝ๋ณ€์ˆ˜์™€ ์ข…์†๋ณ€์ˆ˜๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ์Œ
plt.figure(figsize=(25, 10))
plt.plot(voxel_signal)
plt.plot(predictor_all_ds + voxel_signal.mean(), lw=3)
plt.show()
notion image

2.2.2 Regression on fMRI data & interpretation parameters

  • ์šฐ๋ฆฌ์˜ ์˜ˆ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•ด ํšŒ๊ท€์‹์„ ๋งŒ๋“ค์—ˆ์„ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๋ก ์„ ์–ป์„ ์ˆ˜ ์žˆ์Œ
Code
def test_regression_signal_simple(mse_ans, r2_ans, X, y):
    X = np.hstack((np.ones((X.shape[0], 1)), X))
    b = np.linalg.lstsq(X, y, rcond=None)[0]
    y_hat = X.dot(b)
    c_ans_mse = np.mean((y - y_hat) ** 2)
    c_ans_r2 = pearsonr(y, y_hat)[0] ** 2
    try:
        np.testing.assert_almost_equal(mse_ans, c_ans_mse)
    except AssertionError as e:
        try:
            print('Your MSE is %.3f; I expected %.3f' % (mse_ans, c_ans_mse))
            raise e
        finally:
            e = None
            del e

    else:
        print('MSE is correct')
    try:
        np.testing.assert_almost_equal(r2_ans, c_ans_r2)
    except AssertionError as e:
        try:
            print('Your R2 is %.3f; I expected %.3f' % (r2_ans, c_ans_r2))
            raise e
        finally:
            e = None
            del e

    else:
        print('R2 is correct')
    print('Well done!')

test_regression_signal_simple(mse_simple, r2_simple, predictor_all_ds, voxel_signal)
def plot_signal_and_predicted_signal(y, X, x_lim=None, y_lim=None):
    """ Plots a signal and its GLM prediction. """
    des = np.hstack((np.ones((y.size, 1)), X))
    betas_simple = np.linalg.lstsq(des, y, rcond=None)[0]
    plt.figure(figsize=(15, 5))
    plt.plot(y)
    plt.plot(des @ betas_simple, lw=2)
    plt.xlabel('Time (in volumes)', fontsize=15)
    plt.ylabel('Activity (A.U.)', fontsize=15)
    
    if x_lim is not None:
        plt.xlim(x_lim)
    
    if y_lim is not None:
        plt.ylim(y_lim)
    
    plt.legend(['True signal', 'Predicted signal'], loc='upper right', fontsize=15)
    plt.title("Signal and predicted signal", fontsize=25)
    plt.grid()
    plt.show()

plot_signal_and_predicted_signal(voxel_signal, predictor_all_ds, x_lim, y_lim)
notion image
notion image

2.2.3 Using the BOLD response in GLM models

  • ์šฐ๋ฆฌ๊ฐ€ ํ˜„์‹ค์—์„œ BOLD ์‹ ํ˜ธ์˜ ์ฆ‰๊ฐ์  ๋ณ€ํ™”๋ฅผ ๋ชฉ๊ฒฉํ•˜๋Š” ๊ฒƒ์€ ๋น„ํ˜„์‹ค์ ์ž„
    • ์ง„์ •ํ•œ ์‹ ๊ฒฝ ํ™œ๋™์€ ์•ฝ 5-7์ดˆ ํ›„์— ์ตœ๊ณ ์กฐ์— ๋‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž„
  • ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” canonical HRF์™€ convolution ๊ณ„์‚ฐ์„ ํ†ตํ•ด์„œ BOLD ์‹ ํ˜ธ์™€ ์œ ์‚ฌํ•œ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž„
ย 
1) The canonical HRF(ํ‘œ์ค€ HRF)
  • fMRI ๋ฐ์ดํ„ฐ์˜ BOLD ์‹ ํ˜ธ ๋‹จ๋ณ€๋Ÿ‰ ๋ถ„์„(univariate analyses): ๋ณต์…€์€ ํ•ญ์ƒ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ž๊ทน์— ๋ฐ˜์‘ํ•œ๋‹ค๊ณ  ๊ฐ€์ • โ†’ using โ€œcanonical haemodynamic response function (HRF)โ€
  • canonical HRF: formalization of how the a voxel is going to respond to a stimulus
  • ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” HRF function์€ double-gamma HRF์ž„(ํ•˜๋‚˜๋Š” overshoot์„ ๋ชจ๋ธ๋งํ•˜๊ณ  ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” undershoot์„ ๋ชจ๋ธ๋งํ•จ)
TR = 2 # ์Šค์บ” ์ง€์† ์‹œ๊ฐ„
osf = 2 # ์˜ค๋ฒ„์ƒ˜ํ”Œ๋ง
length_hrf = 32  # sec

canonical_hrf = glover_hrf(tr=TR, oversampling=osf, time_length=length_hrf, onset=0) # glover_hrf๋Š” double-gamma HRF ํ•จ์ˆ˜์ž„
canonical_hrf /= canonical_hrf.max()
print(canonical_hrf.size)
>>> 32

t = np.arange(0, canonical_hrf.size)
plt.plot(t, canonical_hrf)
notion image
ย 
2) Convolution
  • HRF์™€ ์ž๊ทน ์‚ฌ๊ฑด์˜ ์ฝ˜๋ณผ๋ฃจ์…˜ ์—ฐ์‚ฐ์€ ์ž๊ทน๊ณผ BOLD response์˜ ์ง€์—ฐ๋˜๋Š” ๋ชจ์–‘์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์Œ
notion image
predictor_conv = np.convolve(predictor_all.squeeze(), canonical_hrf)
print(predictor_conv)
>>> (831,)

predictor_conv = predictor_conv[:predictor_all.size]
print(predictor_conv.shape)
>>> (800,)

predictor_conv = predictor_conv[:, np.newaxis]
print(predictor_conv.shape)
>>> (800, 1)
plt.plot(predictor_all)
plt.plot(predictor_conv)
notion image
ย 
3) Linear scaling
  • BOLD response๋ฅผ (์ฃผ์–ด์ง„ ํ˜•ํƒœ์˜) ์„ ํ˜•์œผ๋กœ ํ™•์žฅํ•˜๋Š” ๊ฒƒ
plt.figure(figsize=(15, 5))

N = 40
one_stim = np.zeros(N)
one_stim[5] = 1
one_stim_conv = np.convolve(one_stim, canonical_hrf)[:N]
two_stim = np.zeros(N)
two_stim[[5, 6]] = 1
two_stim_conv = np.convolve(two_stim, canonical_hrf)[:N]
three_stim = np.zeros(N)
three_stim[[5, 6, 7]] = 1
three_stim_conv = np.convolve(three_stim, canonical_hrf)[:N]

plt.plot(one_stim_conv)
plt.plot(two_stim_conv)
plt.plot(three_stim_conv)
plt.show()
notion image
ย 
4) Resampling revisited
  • convolved predictor์™€ BOLD signal์˜ ์Šค์ผ€์ผ ๋งž์ถ”๊ธฐ ์œ„ํ•ด resampling์„ ๋‹ค์‹œ ํ•จ
original_scale = np.arange(0, 800)
resampler = interp1d(original_scale, np.squeeze(predictor_conv))

desired_scale = np.arange(0, 800, 2)
predictor_conv_ds = resampler(desired_scale)
ย 
5) Initial upsampling of predictors
  • ์ž๊ทน์€ ๋”ฑ ๋–จ์–ด์ง€๋Š” ์ดˆ ๋‹จ์œ„๋กœ(10s ๋“ฑ) ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์Œ
  • ๋ณด๋‹ค ์ •ํ™•ํ•œ ์ดˆ ๋‹จ์œ„๋กœ ์ž๊ทน ์˜ˆ์ธก๊ธฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด 100์œผ๋กœ ์˜ค๋ฒ„์ƒ˜ํ”Œ๋ง์„ ํ•จ
onsets2 = np.array([3.62, 16.26, 34.12, 42.98])  # ์ •๋ฐ€ํ•œ ์ž๊ทน ์‹œ๊ฐ„
duration = 50  # ์ž๊ทน ๊ฐ„ ์‚ฌ์ด ์‹œ๊ฐ„
osf = 100  # osf = OverSampling Factor

pred2 = np.zeros(duration * osf)
onsets2_in_msec = (onsets2 * osf).astype(int)
pred2[onsets2_in_msec] = 1
osf = 100 * TR # TR = 2
hrf_ms = glover_hrf(tr=TR, oversampling=osf, time_length=32)
hrf_ms /= hrf_ms.max()

t = np.arange(0, hrf_ms.size)

plt.figure(figsize=(12, 5))
plt.plot(t, hrf_ms)
notion image
pred2_conv = np.convolve(pred2, hrf_ms)[:pred2.size]
plt.plot(pred2_conv)
notion image
6) Downsampling
  • Initial upsampling์„ ํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค์‹œ downsampling์„ ํ†ตํ•ด ์ฐจ์›์˜ ํฌ๊ธฐ๋ฅผ ๋งž์ถค
resampler = interp1d(original_scale, pred2_conv)
desired_scale = np.arange(0, 50, TR)
pred2_conv_ds = resampler(desired_scale)
ย 
7) Fitting an HRF-informed model
intercept = np.ones((predictor_conv_ds.size, 1))
X_conv = np.hstack((intercept, predictor_conv_ds))
betas_conv = inv(X_conv.T @ X_conv) @ X_conv.T @ voxel_signal

plt.subplot(2, 1, 1)
plt.plot(voxel_signal)
plt.plot(X_conv @ betas_conv, lw=2)

X_simple = np.hstack((icept, predictor_all_ds))
betas_simple = np.array([1000.64701684, 1.02307437])

plt.subplot(2, 1, 2)
plt.plot(voxel_signal)
plt.plot(X_simple @ betas_simple, lw=2)
notion image
print(betas_conv[1], betas_simple[1])
>>> (8.181, 1.023)

plot_signal_and_predicted_signal_zoom(voxel_signal, predictor_conv_ds, x_lim=(25, 65), y_lim=(995, 1010))
notion image
from numpy.linalg import lstsq  # numpy implementation of OLS, because we're lazy

y_hat_conv = X_conv @ betas_conv
y_hat_orig = X_simple @ lstsq(X_simple, voxel_signal, rcond=None)[0]

MSE_conv = ((y_hat_conv - voxel_signal) ** 2).mean()
MSE_orig = ((y_hat_orig - voxel_signal) ** 2).mean()

print(MSE_conv, MSE_orig)
>>> (6.022, 10.327)

R2_conv = 1 - (np.sum((voxel_signal - y_hat_conv) ** 2) / np.sum((voxel_signal - voxel_signal.mean()) ** 2))
R2_orig = 1 - (np.sum((voxel_signal - y_hat_orig) ** 2) / np.sum((voxel_signal - voxel_signal.mean()) ** 2))

print(R2_conv, R2_orig)
>>> (0.41919, 0.00388)
ย 
8) Temporal basis functions
  • HRF ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด ์› ์‹ ํ˜ธ๋ณด๋‹ค ์ž๊ทน์ด ๋А๋ฆฌ๊ฒŒ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Œ
  • ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Temporal basis function์„ ์ด์šฉํ•จ(ex. double-gamma basis functions)
  • double-gamma basis functions์„ 1๊ณ„ ๋ฏธ๋ถ„ํ•˜๋ฉด ์‹œ๊ฐ„์˜ ์ง€์—ฐ์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๊ณ (temporal), 2๊ณ„ ๋ฏธ๋ถ„ํ•˜๋ฉด ๋„ˆ๋น„์˜ ์ฐจ์ด๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Œ(dispersion) (๊ฑฐ๋ฆฌ-์†๋„-๊ฐ€์†๋„์˜ ๊ด€๊ณ„๋ž‘ ์œ ์‚ฌํ•จ)
tderiv_hrf = glover_time_derivative(tr=2, oversampling=2)
tderiv_hrf /= tderiv_hrf.max()

plt.plot(canonical_hrf, lw=3)
plt.plot(tderiv_hrf, c='tab:orange', lw=3)
notion image
y_tbs, X_tbs = simulate_signal(~)

# regression with HRF only
beta_canon = inv(X_canon_only.T @ X_canon_only) @ X_canon_only.T @ y_tbs
yhat_canon = X_canon_only @ beta_canon

# regression with HRF + temporal deriv.
beta_td = inv(X_tbs.T @ X_tbs) @ X_tbs.T @ y_tbs
yhat_td = X_tbs @ beta_td
notion image
notion image

Summary


Linear Model

Formation of Linear Model
Variables
  • Independent variables: stimulus of experiments
    • predict_all[onset_all] = 1
  • Dependent variables: fMRI signal data(make continuous from discrete values), and match for time level
    • scipy.interpolate
    • resampler = interp1d(original_scale, predict_all)

BOLD response in GLM

The canonical HRF + Convolution
  • ์šฐ๋ฆฌ๊ฐ€ ํ˜„์‹ค์—์„œ BOLD ์‹ ํ˜ธ์˜ ์ฆ‰๊ฐ์  ๋ณ€ํ™”๋ฅผ ๋ชฉ๊ฒฉํ•˜๋Š” ๊ฒƒ์€ ๋น„ํ˜„์‹ค์ ์ž„
    • ์ง„์ •ํ•œ ์‹ ๊ฒฝ ํ™œ๋™์€ ์•ฝ 5-7์ดˆ ํ›„์— ์ตœ๊ณ ์กฐ์— ๋‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž„
  • ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” canonical HRF์™€ convolution ๊ณ„์‚ฐ์„ ํ†ตํ•ด์„œ BOLD ์‹ ํ˜ธ์™€ ์œ ์‚ฌํ•œ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž„
ย 
canonical_hrf = glover_hrf(tr, oversampling, time_length, onset)
predictor_conv = np.convolve(predict_all, canonical_hrf)
Resampling
  • convolved predictor์™€ BOLD signal์˜ ์Šค์ผ€์ผ ๋งž์ถค
ย 
original_scale = np.arange(0, 800)
resampler = interp1d(original_scale, np.squeeze(predictor_conv))

desired_scale = np.arange(0, 800, 2)
predictor_conv_ds = resampler(desired_scale)
ย 
Initial upsampling of predictors
  • ์ •ํ™•ํ•œ ์ž๊ทน ์˜ˆ์ธก๊ธฐ(๋…๋ฆฝ๋ณ€์ˆ˜)๋ฅผ ์œ„ํ•ด ์˜ค๋ฒ„์ƒ˜ํ”Œ๋งํ•จ
  • ์ดํ›„ HRF + Convolution ๊ณ„์‚ฐ ๊ณผ์ • ๊ฑฐ์นจ
  • ๋งˆ์ง€๋ง‰์œผ๋กœ ์˜ค๋ฒ„์ƒ˜ํ”Œ๋ง ํ›„์—๋Š” ๋‹ค์šด์ƒ˜ํ”Œ๋ง ํ˜น์€ ๋ฆฌ์ƒ˜ํ”Œ๋ง ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ•จ
ย 
onsets2 = np.array([3.62, ...])
duration = 50
oversampling = 100
pred2[onsets2_in_msec] = 1
Temporal basis function
  • HRF ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด ์› ์‹ ํ˜ธ๋ณด๋‹ค ์ž๊ทน์ด ๋А๋ฆฌ๊ฒŒ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Œ
  • ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Temporal basis function์„ ์ด์šฉํ•จ(ex. double-gamma basis functions)
tderiv_hrf = glover_time_derivative(tr=2, oversampling=2)
tderiv_hrf /= tderiv_hrf.max()