Open In Colab   Open in Kaggle

チュートリアル 1: 問題の枠組み設定

第2週、第1日目: モデリング演習

Neuromatch Academy 提供

コンテンツ作成者: Marius 't Hart, Megan Peters, Paul Schrater, Gunnar Blohm, Konrad Kording, Marius Pachitariu

コンテンツレビュアー: Eric DeWitt, Tara van Viegen, Marius Pachitariu

制作編集者: Ella Batty, Spiros Chavlis


チュートリアルの目的

先週は、神経科学においてモデルが何をもたらすかについての理解を深めました。しかし、モデルはどのように構築するのでしょうか?今日は、あなたのプロジェクトのアイデアに基づいてモデリングの論理を考えながら、計算モデリングのプロセスを明確にしようとします。以下のモデリングの最初の4ステップを一緒に進めていきます(Blohm et al., 2019):

問題の枠組み設定

  1. 現象を見つけ、その現象についての質問を設定する
  2. 最先端の知見を理解する
  3. 基本的な要素を決定する
  4. 具体的で数学的に定義された仮説を立てる

デモ

列車の錯覚を題材にモデリングプロセスをデモンストレーションします。計算モデルと並行して、同じ現象に基づくデータ解析プロジェクトの質問と仮説を考えます。これは「考えよう!」セクションで行います。

お楽しみください!

セットアップ

# @title Install and import feedback gadget


from vibecheck import DatatopsContentReviewContainer
def content_review(notebook_section: str):
    return DatatopsContentReviewContainer(
        "",  # No text prompt
        notebook_section,
        {
            "url": "https://pmyvdlilci.execute-api.us-east-1.amazonaws.com/klab",
            "name": "neuromatch_cn",
            "user_key": "y1x3mpx5",
        },
    ).render()


feedback_prefix = "W2D1_T1"
# Imports
import numpy as np
import matplotlib.pyplot as plt

# for random distributions:
from scipy.stats import norm, poisson

# for logistic regression:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
# @title Figure settings
import logging
logging.getLogger('matplotlib.font_manager').disabled = True

%matplotlib inline
%config InlineBackend.figure_format = 'retina'
plt.style.use("https://raw.githubusercontent.com/NeuromatchAcademy/course-content/main/nma.mplstyle")
# @title Plotting Functions

def rasterplot(spikes,movement,trial):

  [movements, trials, neurons, timepoints] = np.shape(spikes)

  trial_spikes = spikes[movement,trial, :, :]

  trial_events = [((trial_spikes[x,:] > 0).nonzero()[0]-150)/100 for x in range(neurons)]

  plt.figure()
  dt=1/100
  plt.eventplot(trial_events, linewidths=1);
  plt.title('movement: %d - trial: %d'%(movement, trial))
  plt.ylabel('neuron')
  plt.xlabel('time [s]')
  plt.show()

def plotCrossValAccuracies(accuracies):
  f, ax = plt.subplots(figsize=(8, 3))
  ax.boxplot(accuracies, vert=False, widths=.7)
  ax.scatter(accuracies, np.ones(8))
  ax.set(xlabel="Accuracy", yticks=[],
         title=f"Average test accuracy: {accuracies.mean():.2%}")
  ax.spines["left"].set_visible(False)
  plt.show()
# @title Generate Data

def generateSpikeTrains():

  gain = 2
  neurons = 50
  movements = [0, 1, 2]
  repetitions = 800

  np.random.seed(37)

  # set up the basic parameters:
  dt = 1/100
  start, stop = -1.5, 1.5
  t = np.arange(start, stop+dt, dt)  # a time interval
  Velocity_sigma = 0.5  # std dev of the velocity profile
  Velocity_Profile = norm.pdf(t, 0, Velocity_sigma)/norm.pdf(0, 0, Velocity_sigma)  # The Gaussian velocity profile, normalized to a peak of 1

  # set up the neuron properties:
  Gains = np.random.rand(neurons) * gain  # random sensitivity between 0 and `gain`
  FRs = (np.random.rand(neurons) * 60 ) - 10  # random base firing rate between -10 and 50

  # output matrix will have this shape:
  target_shape = [len(movements), repetitions, neurons, len(Velocity_Profile)]

  # build matrix for spikes, first, they depend on the velocity profile:
  Spikes = np.repeat(Velocity_Profile.reshape([1, 1, 1, len(Velocity_Profile)]),
                     len(movements)*repetitions*neurons,axis=2).reshape(target_shape)

  # multiplied by gains:
  S_gains = np.repeat(np.repeat(Gains.reshape([1, 1, neurons]), len(movements)*repetitions, axis=1).reshape(target_shape[:3]),
                      len(Velocity_Profile)).reshape(target_shape)
  Spikes = Spikes * S_gains

  # and multiplied by the movement:
  S_moves = np.repeat( np.array(movements).reshape([len(movements), 1, 1, 1]),
                      repetitions*neurons*len(Velocity_Profile), axis=3 ).reshape(target_shape)
  Spikes = Spikes * S_moves

  # on top of a baseline firing rate:
  S_FR = np.repeat(np.repeat(FRs.reshape([1, 1, neurons]),
                             len(movements)*repetitions, axis=1).reshape(target_shape[:3]),
                   len(Velocity_Profile)).reshape(target_shape)
  Spikes = Spikes + S_FR

  # can not run the poisson random number generator on input lower than 0:
  Spikes = np.where(Spikes < 0, 0, Spikes)

  # so far, these were expected firing rates per second, correct for dt:
  Spikes = poisson.rvs(Spikes * dt)

  return(Spikes)


def subsetPerception(spikes):

  movements = [0, 1, 2]
  split = 400
  subset = 40
  hwin = 3

  [num_movements, repetitions, neurons, timepoints] = np.shape(spikes)

  decision = np.zeros([num_movements, repetitions])

  # ground truth for logistic regression:
  y_train = np.repeat([0, 1, 1],split)
  y_test = np.repeat([0, 1, 1],repetitions-split)

  m_train = np.repeat(movements, split)
  m_test = np.repeat(movements, split)

  # reproduce the time points:
  dt = 1/100
  start, stop = -1.5, 1.5
  t = np.arange(start, stop+dt, dt)

  w_idx = list( (abs(t) < (hwin*dt)).nonzero()[0] )
  w_0 = min(w_idx)
  w_1 = max(w_idx)+1  # python...

  # get the total spike counts from stationary and movement trials:
  spikes_stat = np.sum( spikes[0, :, :, :], axis=2)
  spikes_move = np.sum( spikes[1:, :, :, :], axis=3)

  train_spikes_stat = spikes_stat[:split, :]
  train_spikes_move = spikes_move[:, :split, :].reshape([-1, neurons])

  test_spikes_stat = spikes_stat[split:, :]
  test_spikes_move = spikes_move[:, split:, :].reshape([-1, neurons])

  # data to use to predict y:
  x_train = np.concatenate((train_spikes_stat, train_spikes_move))
  x_test  = np.concatenate((test_spikes_stat, test_spikes_move))

  # this line creates a logistics regression model object, and immediately fits it:
  population_model = LogisticRegression(solver='liblinear', random_state=0).fit(x_train, y_train)

  # solver, one of: 'liblinear', 'newton-cg', 'lbfgs', 'sag', and 'saga'
  # some of those require certain other options
  #print(population_model.coef_)       # slope
  #print(population_model.intercept_)  # intercept

  ground_truth = np.array(population_model.predict(x_test))
  ground_truth = ground_truth.reshape([3, -1])

  output = {}
  output['perception'] = ground_truth
  output['spikes'] = spikes[:, split:, :subset, :]

  return(output)


def getData():

  spikes = generateSpikeTrains()

  dataset = subsetPerception(spikes=spikes)

  return(dataset)


dataset = getData()
perception = dataset['perception']
spikes = dataset['spikes']

ステップ 0: はじめに

# @title Video 1: Introduction to tutorial
from ipywidgets import widgets
from IPython.display import YouTubeVideo
from IPython.display import IFrame
from IPython.display import display


class PlayVideo(IFrame):
  def __init__(self, id, source, page=1, width=400, height=300, **kwargs):
    self.id = id
    if source == 'Bilibili':
      src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'
    elif source == 'Osf':
      src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'
    super(PlayVideo, self).__init__(src, width, height, **kwargs)


def display_videos(video_ids, W=400, H=300, fs=1):
  tab_contents = []
  for i, video_id in enumerate(video_ids):
    out = widgets.Output()
    with out:
      if video_ids[i][0] == 'Youtube':
        video = YouTubeVideo(id=video_ids[i][1], width=W,
                             height=H, fs=fs, rel=0)
        print(f'Video available at https://youtube.com/watch?v={video.id}')
      else:
        video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,
                          height=H, fs=fs, autoplay=False)
        if video_ids[i][0] == 'Bilibili':
          print(f'Video available at https://www.bilibili.com/video/{video.id}')
        elif video_ids[i][0] == 'Osf':
          print(f'Video available at https://osf.io/{video.id}')
      display(video)
    tab_contents.append(out)
  return tab_contents


video_ids = [('Youtube', 'GyGNs1fLIYQ'), ('Bilibili', 'BV1Mf4y1b7xS')]
tab_contents = display_videos(video_ids, W=854, H=480)
tabs = widgets.Tab()
tabs.children = tab_contents
for i in range(len(tab_contents)):
  tabs.set_title(i, video_ids[i][0])
display(tabs)
# @title Submit your feedback
content_review(f"{feedback_prefix}_Introduction_to_tutorial_Video")

ステップ 1: 現象を見つけ、その現象についての質問を設定する

# @title Video 2: Asking a question
from ipywidgets import widgets
from IPython.display import YouTubeVideo
from IPython.display import IFrame
from IPython.display import display


class PlayVideo(IFrame):
  def __init__(self, id, source, page=1, width=400, height=300, **kwargs):
    self.id = id
    if source == 'Bilibili':
      src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'
    elif source == 'Osf':
      src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'
    super(PlayVideo, self).__init__(src, width, height, **kwargs)


def display_videos(video_ids, W=400, H=300, fs=1):
  tab_contents = []
  for i, video_id in enumerate(video_ids):
    out = widgets.Output()
    with out:
      if video_ids[i][0] == 'Youtube':
        video = YouTubeVideo(id=video_ids[i][1], width=W,
                             height=H, fs=fs, rel=0)
        print(f'Video available at https://youtube.com/watch?v={video.id}')
      else:
        video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,
                          height=H, fs=fs, autoplay=False)
        if video_ids[i][0] == 'Bilibili':
          print(f'Video available at https://www.bilibili.com/video/{video.id}')
        elif video_ids[i][0] == 'Osf':
          print(f'Video available at https://osf.io/{video.id}')
      display(video)
    tab_contents.append(out)
  return tab_contents


video_ids = [('Youtube', '4Gl8X_y_uoA'), ('Bilibili', 'BV1VK4y1M7dc')]
tab_contents = display_videos(video_ids, W=854, H=480)
tabs = widgets.Tab()
tabs.children = tab_contents
for i in range(len(tab_contents)):
  tabs.set_title(i, video_ids[i][0])
display(tabs)
# @title Submit your feedback
content_review(f"{feedback_prefix}_Asking_a_question_Video")
# @title Example projects step 1
from ipywidgets import widgets
from IPython.display import Markdown

markdown1 = '''

## Think! 1: Asking a question
<br>

<font size='3pt'>

The train illusion occurs when sitting on a train and viewing another train outside the window. Suddenly, the other train *seems* to move, i.e. you experience visual motion of the other train relative to your train. But which train is actually moving?

Often people mix this up. In particular, they think their own train might be moving when it's the other train that moves; or vice versa. The illusion is usually resolved once you gain vision of the surroundings that lets you disambiguate the relative motion; or if you experience strong vibrations indicating that it is indeed your own train that is in motion.

We assume that we have buily the train illusion model (see the other example project colab). That model predicts that accumulated sensory evidence from vestibular signals determines the decision of whether self-motion is experienced or not. We now have vestibular neuron data (simulated in our case, but let's pretend) and would like to see if that prediction holds true.

The data contains *N* neurons and *M* trials for each of 3 motion conditions: no self-motion, slowly accelerating self-motion and faster accelerating self-motion. In our data,
*N* = 40 and *M* = 400.

**Discuss between yourselves and come up with a question for this data analysis project.**
</font>
'''

#out2 = widgets.Output()
#with out2:
#  display(Markdown(markdown2))

out1 = widgets.Output()
with out1:
  display(Markdown(markdown1))

out = widgets.Tab([out1]) #, out2])
out.set_title(0, 'Data analysis')
#out.set_title(1, 'Data Analysis')

display(out)

復習として、以下の点を議論し、書き留めてください:

落とし穴を避けることを忘れずに!

落とし穴のまとめはこちらをクリック

質問があいまいすぎる

  • 覚えておいてください:科学は一歩ずつ進みます。小さな一歩を正しく踏み出しましょう…

モデリングしたい現象の正確な側面が不明確

  • 意味のある質問を設定できません

すでにツールキットを選んでしまっている

  • 科学的な質問に最適な答え方を深く考えることを妨げます

明確な目標がない

  • モデリングから何を得たいのか?

実験のアイデアがない

  • 目標を具体化し、その背後にある論理を考える助けになります

注意

最も難しいのはステップ1です。これが正しく設定されれば、他のステップは比較的簡単になります。しかし:多くの場合、ステップ1は終わったと思っても、後のステップ(どこでも)で質問や目標が思ったほど明確でなかったことに気づきます。ステップ1に戻ることはよくあることです。気にしないでください。後でステップ1に戻っても構いません。今は次のステップに進みましょう。

# @title Submit your feedback
content_review(f"{feedback_prefix}_Asking_your_own_question_Discussion")

ステップ 2: 最先端の知見と背景の理解

ここでは文献レビューの方法を学びます(このチュートリアルの後にプロジェクトのために行ってください!)。

# @title Video 3: Literature Review & Background Knowledge
from ipywidgets import widgets
from IPython.display import YouTubeVideo
from IPython.display import IFrame
from IPython.display import display


class PlayVideo(IFrame):
  def __init__(self, id, source, page=1, width=400, height=300, **kwargs):
    self.id = id
    if source == 'Bilibili':
      src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'
    elif source == 'Osf':
      src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'
    super(PlayVideo, self).__init__(src, width, height, **kwargs)


def display_videos(video_ids, W=400, H=300, fs=1):
  tab_contents = []
  for i, video_id in enumerate(video_ids):
    out = widgets.Output()
    with out:
      if video_ids[i][0] == 'Youtube':
        video = YouTubeVideo(id=video_ids[i][1], width=W,
                             height=H, fs=fs, rel=0)
        print(f'Video available at https://youtube.com/watch?v={video.id}')
      else:
        video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,
                          height=H, fs=fs, autoplay=False)
        if video_ids[i][0] == 'Bilibili':
          print(f'Video available at https://www.bilibili.com/video/{video.id}')
        elif video_ids[i][0] == 'Osf':
          print(f'Video available at https://osf.io/{video.id}')
      display(video)
    tab_contents.append(out)
  return tab_contents


video_ids = [('Youtube', 'd8zriLaMc14'), ('Bilibili', 'BV1by4y1M7TZ')]
tab_contents = display_videos(video_ids, W=854, H=480)
tabs = widgets.Tab()
tabs.children = tab_contents
for i in range(len(tab_contents)):
  tabs.set_title(i, video_ids[i][0])
display(tabs)
# @title Submit your feedback
content_review(f"{feedback_prefix}_Literature_Review_and_Background_Knowledge_Video")
# @title Submit your feedback
content_review(f"{feedback_prefix}_Literature_review_Discussion")

ステップ 3: 基本的な要素の決定

# @title Video 4: Determining basic ingredients
from ipywidgets import widgets
from IPython.display import YouTubeVideo
from IPython.display import IFrame
from IPython.display import display


class PlayVideo(IFrame):
  def __init__(self, id, source, page=1, width=400, height=300, **kwargs):
    self.id = id
    if source == 'Bilibili':
      src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'
    elif source == 'Osf':
      src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'
    super(PlayVideo, self).__init__(src, width, height, **kwargs)


def display_videos(video_ids, W=400, H=300, fs=1):
  tab_contents = []
  for i, video_id in enumerate(video_ids):
    out = widgets.Output()
    with out:
      if video_ids[i][0] == 'Youtube':
        video = YouTubeVideo(id=video_ids[i][1], width=W,
                             height=H, fs=fs, rel=0)
        print(f'Video available at https://youtube.com/watch?v={video.id}')
      else:
        video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,
                          height=H, fs=fs, autoplay=False)
        if video_ids[i][0] == 'Bilibili':
          print(f'Video available at https://www.bilibili.com/video/{video.id}')
        elif video_ids[i][0] == 'Osf':
          print(f'Video available at https://osf.io/{video.id}')
      display(video)
    tab_contents.append(out)
  return tab_contents


video_ids = [('Youtube', 'XpEj-p7JkFE'), ('Bilibili', 'BV1Mq4y1x77s')]
tab_contents = display_videos(video_ids, W=854, H=480)
tabs = widgets.Tab()
tabs.children = tab_contents
for i in range(len(tab_contents)):
  tabs.set_title(i, video_ids[i][0])
display(tabs)
# @title Submit your feedback
content_review(f"{feedback_prefix}_Submit_your_feedback_Video")
# @title Example projects step 3
from ipywidgets import widgets
from IPython.display import Markdown, Math

markdown1 = r'''

## Think! 3: Determine your basic ingredients
<br>
<font size='3pt'>

The idea of our project is that the vestibular signals are noisy so that they might be mis-interpreted by the brain. We did some brainstorming and think that we need to somehow extract the self-motion judgements from the spike counts of our neurons. Based on that, our algorithm needs to make a decision: was there self motion or not? This is a classical 2-choice classification problem.
We will have to transform the raw spike data into the right input for the algorithm (spike pre-processing).

In order to address our question we need to design an appropriate computational data analysis pipeline.

**What ingredients do we need? What variables from the data should we extract, and what methods should we apply to them?**

*Discuss for about 10 minutes*
</font>
'''

# No idea why this is necessary but math doesn't render properly without it
display(Markdown(r""))


#out2 = widgets.Output()
#with out2:
#  display(Markdown(markdown2))


out1 = widgets.Output()
with out1:
  display(Markdown(markdown1))

out = widgets.Tab([out1])
out.set_title(0, 'Data analysis project')
#out.set_title(1, 'Data Analysis')

display(out)

落とし穴を避けることを忘れずに!

落とし穴のまとめはこちらをクリック

経験豊富だからもう要素について考える必要はない

  • そう思っているだけかもしれません…

要素が思い浮かばない

  • 実験の可能性を考えてみてください。刺激は何ですか?パラメータは?何を制御しますか?何を測定しますか?

入力と出力はすべて揃っている

  • 良いですね!でもそれらをつなぐものは何でしょう?それを考えることでモデルや仮説の形が見えてきます

つなぐもの(=メカニズム)が思い浮かばない

  • モデリングと学習を続けることで、潜在的なメカニズムのライブラリが増えます
  • 文献はしばしば仮説を通じてヒントを与えてくれます
  • それでも思い浮かばなければ、要素が足りていないのかもしれません
# @title Submit your feedback
content_review(f"{feedback_prefix}_Determine_your_basic_ingredients_Discussion")

ステップ 4: 具体的で数学的に定義された仮説の立案

# @title Video 5: Formulating a hypothesis
from ipywidgets import widgets
from IPython.display import YouTubeVideo
from IPython.display import IFrame
from IPython.display import display


class PlayVideo(IFrame):
  def __init__(self, id, source, page=1, width=400, height=300, **kwargs):
    self.id = id
    if source == 'Bilibili':
      src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'
    elif source == 'Osf':
      src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'
    super(PlayVideo, self).__init__(src, width, height, **kwargs)


def display_videos(video_ids, W=400, H=300, fs=1):
  tab_contents = []
  for i, video_id in enumerate(video_ids):
    out = widgets.Output()
    with out:
      if video_ids[i][0] == 'Youtube':
        video = YouTubeVideo(id=video_ids[i][1], width=W,
                             height=H, fs=fs, rel=0)
        print(f'Video available at https://youtube.com/watch?v={video.id}')
      else:
        video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,
                          height=H, fs=fs, autoplay=False)
        if video_ids[i][0] == 'Bilibili':
          print(f'Video available at https://www.bilibili.com/video/{video.id}')
        elif video_ids[i][0] == 'Osf':
          print(f'Video available at https://osf.io/{video.id}')
      display(video)
    tab_contents.append(out)
  return tab_contents


video_ids = [('Youtube', 'nHXMSXLcd9A'), ('Bilibili', 'BV1fh411h7aX')]
tab_contents = display_videos(video_ids, W=854, H=480)
tabs = widgets.Tab()
tabs.children = tab_contents
for i in range(len(tab_contents)):
  tabs.set_title(i, video_ids[i][0])
display(tabs)
# @title Submit your feedback
content_review(f"{feedback_prefix}_Formulating_your_hypothesis_Video")
# @title Example projects step 4
from ipywidgets import widgets
from IPython.display import Markdown

# Not writing in latex because that didn't render in jupyterbook

markdown1 = r'''

##  Think! 4: Formulating your hypothesis
<br>

<font size='3pt'>

We think that noise in the signal drives whether or not people perceive self motion. Maybe the brain uses the strongest signal at peak acceleration to decide on self motion, but we actually think it is better to accumulate evidence over some period of time. We want to test this. The noise idea also means that when the signal-to-noise ratio is higher, the brain does better, and this would be in the faster acceleration condition. We want to test this too.

**Come up with hypotheses focussing on specific details of our overall research question.**

Can you write down your hypotheses mathematically, using for example the ingredients and variables from the previous step?

*Work on this for 15 minutes.*

</font>
'''

# No idea why this is necessary but math doesn't render properly without it
display(Markdown(r""))

out1 = widgets.Output()
with out1:
  display(Markdown(markdown1))

out = widgets.Tab([out1])
out.set_title(0, 'Data Analysis project')

display(out)

落とし穴を避けることを忘れずに!

落とし穴のまとめはこちらをクリック

仮説は不要、モデルをいじって遊ぶだけ

  • 仮説は目標を決めて明確にする助けになります。もちろん遊んでも構いません…

仮説が質問と合っていない(またはその逆)

  • これはプロセスの正常な一部です!
  • ステップ1に戻り、質問・現象・目標を見直しましょう

数学的な仮説を書けない

  • 多くの場合、要素や仮説の明確さが不足しています
  • または「構造的」仮説、つまり特定のモデル構成要素が現象の説明や質問の答えに重要だと期待している場合もあります

もし(Blohm et al., 2019)のモデリングのステップ5~10についてもっと学びたい場合は、後で論文を読んだり、こちら$のオプションノートブックを参照してください。ただし、Neuromatch Academyでは、プロジェクト支援のために特別に作られた新しいアプリ、NMAプロジェクトプランナーの使用を推奨しています。

# @title Submit your feedback
content_review(f"{feedback_prefix}_Formulating_your_hypothesis_Discussion")

NMAプロジェクトプランナー

今日のプロジェクト時間の残りとサマースクール終了まで、オンラインツール(こちら)を使って、質問、仮説、データセット、手法などプロジェクトの要素を整理します。このツールは大規模言語モデルを基盤としており、あなたの回答をセクションの要件と比較し、回答がどれだけ要件に合っているか、改善のために何ができるかフィードバックを提供します。入力した内容は使用しているコンピュータに保存され、簡単に保存・読み込み・グループ間で共有できます。まずは一人が画面共有しながら入力を始め、保存したファイルをグループ全員で共有しましょう。

NMAでは、プロジェクトプランナーの入力順序は重要視していません。プロジェクトによっては、データセットをよく理解してから質問を設定する方が良い場合もありますし、列車の錯覚のように現象のモデルを作る場合は質問から始める方が良い場合もあります。重要なのはサマースクール終了時にすべてのセクションが埋まっていて、各セクションのスコアが良好(7以上、チェックはしませんのでご安心を)であることです。

以下の動画でKonradがこのアプリについて詳しく説明しています!

# @title Video 6: The NMA project planner
from ipywidgets import widgets
from IPython.display import YouTubeVideo
from IPython.display import IFrame
from IPython.display import display


class PlayVideo(IFrame):
  def __init__(self, id, source, page=1, width=400, height=300, **kwargs):
    self.id = id
    if source == 'Bilibili':
      src = f'https://player.bilibili.com/player.html?bvid={id}&page={page}'
    elif source == 'Osf':
      src = f'https://mfr.ca-1.osf.io/render?url=https://osf.io/download/{id}/?direct%26mode=render'
    super(PlayVideo, self).__init__(src, width, height, **kwargs)


def display_videos(video_ids, W=400, H=300, fs=1):
  tab_contents = []
  for i, video_id in enumerate(video_ids):
    out = widgets.Output()
    with out:
      if video_ids[i][0] == 'Youtube':
        video = YouTubeVideo(id=video_ids[i][1], width=W,
                             height=H, fs=fs, rel=0)

        print(f'Video available at https://youtube.com/watch?v={video.id}')
      else:
        video = PlayVideo(id=video_ids[i][1], source=video_ids[i][0], width=W,
                          height=H, fs=fs, autoplay=False)
        if video_ids[i][0] == 'Bilibili':
          print(f'Video available at https://www.bilibili.com/video/{video.id}')
        elif video_ids[i][0] == 'Osf':
          print(f'Video available at https://osf.io/{video.id}')
      display(video)
    tab_contents.append(out)
  return tab_contents


video_ids = [('Youtube', '_umEOcDjqMA'), ('Bilibili', 'BV1QHjWzVEy9')]
tab_contents = display_videos(video_ids, W=854, H=480)
tabs = widgets.Tab()
tabs.children = tab_contents
for i in range(len(tab_contents)):
  tabs.set_title(i, video_ids[i][0])
display(tabs)

まとめ

このチュートリアルでは、モデリングのプロセスのいくつかのステップを進めました。

さらに、今後2週間のプロジェクト開発全体を案内するNMAプロジェクトプランナーを紹介しました。今日の残り時間はプロジェクトプランナーとプロジェクトガイドの追加指導を使って進めてください。どのセクションでも進展は良い進展です。プロジェクトによって進む順序は異なりますが、最終的にすべてのセクションが埋まっていることが重要です。あなたとチームにとって最適な順序を見つけ、必要に応じて各セクションを何度も見直して、しっかりした全体のプロジェクト計画を作りましょう。


参考文献

Blohm G, Kording KP, Schrater PR (2020). 神経科学のためのモデリングハウツーガイド. eNeuro, 7(1) ENEURO.0352-19.2019. doi: 10.1523/ENEURO.0352-19.2019

Kording KP, Blohm G, Schrater P, Kay K (2020). 計算神経科学における多様な目標の理解. Neurons, Behavior, Data Analysis, and Theory 3(6). arXiv: arxiv:2002.03211v1, url: https://nbdt.scholasticahq.com/article/16723-appreciating-the-variety-of-goals-in-computational-neuroscience

Schrater PR, Peters MK, Kording KP, Blohm G (2019). 神経科学におけるモデリングを意思決定プロセスとして捉える. OSF pre-print. url: https://osf.io/w56vt/