brainscore-language 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. brainscore_language/__init__.py +80 -0
  2. brainscore_language/__main__.py +14 -0
  3. brainscore_language/artificial_subject.py +140 -0
  4. brainscore_language/benchmark_helpers/__init__.py +47 -0
  5. brainscore_language/benchmarks/blank2014/__init__.py +4 -0
  6. brainscore_language/benchmarks/blank2014/benchmark.py +50 -0
  7. brainscore_language/benchmarks/blank2014/ceiling.py +204 -0
  8. brainscore_language/benchmarks/blank2014/test.py +75 -0
  9. brainscore_language/benchmarks/fedorenko2016/__init__.py +5 -0
  10. brainscore_language/benchmarks/fedorenko2016/benchmark.py +56 -0
  11. brainscore_language/benchmarks/futrell2018/__init__.py +4 -0
  12. brainscore_language/benchmarks/futrell2018/__pycache__/__init__.cpython-311.pyc +0 -0
  13. brainscore_language/benchmarks/futrell2018/__pycache__/benchmark.cpython-311.pyc +0 -0
  14. brainscore_language/benchmarks/futrell2018/benchmark.py +90 -0
  15. brainscore_language/benchmarks/futrell2018/test.py +43 -0
  16. brainscore_language/benchmarks/pereira2018/__init__.py +5 -0
  17. brainscore_language/benchmarks/pereira2018/benchmark.py +101 -0
  18. brainscore_language/benchmarks/pereira2018/ceiling_packaging.py +268 -0
  19. brainscore_language/benchmarks/pereira2018/test.py +88 -0
  20. brainscore_language/benchmarks/syntaxgym/README.md +79 -0
  21. brainscore_language/benchmarks/syntaxgym/__init__.py +71 -0
  22. brainscore_language/benchmarks/syntaxgym/benchmark.py +172 -0
  23. brainscore_language/benchmarks/syntaxgym/gpt2_precomputed.py +1131 -0
  24. brainscore_language/benchmarks/syntaxgym/requirements.txt +2 -0
  25. brainscore_language/benchmarks/syntaxgym/sg_prediction.py +234 -0
  26. brainscore_language/benchmarks/syntaxgym/sg_suite.py +192 -0
  27. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/center_embed.json +2014 -0
  28. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/center_embed_mod.json +2239 -0
  29. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/cleft.json +4865 -0
  30. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/cleft_modifier.json +5506 -0
  31. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/fgd_hierarchy.json +6056 -0
  32. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/fgd_object.json +3699 -0
  33. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/fgd_pp.json +3699 -0
  34. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/fgd_subject.json +3699 -0
  35. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/mvrr.json +3413 -0
  36. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/mvrr_mod.json +3862 -0
  37. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npi_orc_any.json +6450 -0
  38. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npi_orc_ever.json +6450 -0
  39. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npi_src_any.json +6450 -0
  40. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npi_src_ever.json +6450 -0
  41. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npz_ambig.json +2929 -0
  42. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npz_ambig_mod.json +3314 -0
  43. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npz_obj.json +2929 -0
  44. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/npz_obj_mod.json +3314 -0
  45. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/number_orc.json +2934 -0
  46. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/number_prep.json +2629 -0
  47. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/number_src.json +2934 -0
  48. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/reflexive_orc_fem.json +2934 -0
  49. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/reflexive_orc_masc.json +2934 -0
  50. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/reflexive_prep_fem.json +2629 -0
  51. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/reflexive_prep_masc.json +2629 -0
  52. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/reflexive_src_fem.json +2934 -0
  53. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/reflexive_src_masc.json +2934 -0
  54. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/subordination.json +1701 -0
  55. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/subordination_orc-orc.json +2439 -0
  56. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/subordination_pp-pp.json +2439 -0
  57. brainscore_language/benchmarks/syntaxgym/suites/syntaxgym-2020/subordination_src-src.json +2439 -0
  58. brainscore_language/benchmarks/syntaxgym/test.py +94 -0
  59. brainscore_language/benchmarks/syntaxgym/test_suites.json +33 -0
  60. brainscore_language/benchmarks/tuckute2024/__init__.py +7 -0
  61. brainscore_language/benchmarks/tuckute2024/benchmark.py +57 -0
  62. brainscore_language/benchmarks/wikitext_next_word/__init__.py +4 -0
  63. brainscore_language/benchmarks/wikitext_next_word/benchmark.py +65 -0
  64. brainscore_language/benchmarks/wikitext_next_word/test.py +25 -0
  65. brainscore_language/data/blank2014/__init__.py +22 -0
  66. brainscore_language/data/blank2014/data_packaging.py +508 -0
  67. brainscore_language/data/blank2014/test.py +28 -0
  68. brainscore_language/data/fedorenko2010_localization/__init__.py +34 -0
  69. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run1_stim_set1.csv +49 -0
  70. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run1_stim_set2.csv +49 -0
  71. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run1_stim_set3.csv +49 -0
  72. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run1_stim_set4.csv +49 -0
  73. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run1_stim_set5.csv +49 -0
  74. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run2_stim_set1.csv +49 -0
  75. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run2_stim_set2.csv +49 -0
  76. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run2_stim_set3.csv +49 -0
  77. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run2_stim_set4.csv +49 -0
  78. brainscore_language/data/fedorenko2010_localization/langloc_fmri_run2_stim_set5.csv +49 -0
  79. brainscore_language/data/fedorenko2016/__init__.py +23 -0
  80. brainscore_language/data/fedorenko2016/data_packaging.py +114 -0
  81. brainscore_language/data/fedorenko2016/test.py +14 -0
  82. brainscore_language/data/futrell2018/__init__.py +27 -0
  83. brainscore_language/data/futrell2018/__pycache__/__init__.cpython-311.pyc +0 -0
  84. brainscore_language/data/futrell2018/data_packaging.py +133 -0
  85. brainscore_language/data/futrell2018/test.py +19 -0
  86. brainscore_language/data/pereira2018/__init__.py +27 -0
  87. brainscore_language/data/pereira2018/data_packaging.py +171 -0
  88. brainscore_language/data/pereira2018/test.py +26 -0
  89. brainscore_language/data/tuckute2024/__init__.py +18 -0
  90. brainscore_language/data/tuckute2024/data_packaging.py +138 -0
  91. brainscore_language/data/tuckute2024/test.py +24 -0
  92. brainscore_language/data/wikitext/__init__.py +12 -0
  93. brainscore_language/data/wikitext/requirements.txt +1 -0
  94. brainscore_language/data/wikitext/test.py +11 -0
  95. brainscore_language/metrics/accuracy/__init__.py +4 -0
  96. brainscore_language/metrics/accuracy/metric.py +15 -0
  97. brainscore_language/metrics/accuracy/test.py +23 -0
  98. brainscore_language/metrics/cka/__init__.py +4 -0
  99. brainscore_language/metrics/cka/metric.py +111 -0
  100. brainscore_language/metrics/linear_predictivity/__init__.py +5 -0
  101. brainscore_language/metrics/linear_predictivity/metric.py +184 -0
  102. brainscore_language/metrics/linear_predictivity/requirements.txt +1 -0
  103. brainscore_language/metrics/linear_predictivity/test.py +62 -0
  104. brainscore_language/metrics/pearson_correlation/__init__.py +4 -0
  105. brainscore_language/metrics/pearson_correlation/__pycache__/__init__.cpython-311.pyc +0 -0
  106. brainscore_language/metrics/pearson_correlation/__pycache__/metric.cpython-311.pyc +0 -0
  107. brainscore_language/metrics/pearson_correlation/metric.py +18 -0
  108. brainscore_language/metrics/pearson_correlation/requirements.txt +1 -0
  109. brainscore_language/metrics/pearson_correlation/test.py +26 -0
  110. brainscore_language/metrics/rdm/__init__.py +13 -0
  111. brainscore_language/metrics/rdm/metric.py +106 -0
  112. brainscore_language/model_helpers/__init__.py +1 -0
  113. brainscore_language/model_helpers/container.py +329 -0
  114. brainscore_language/model_helpers/embedding.py +132 -0
  115. brainscore_language/model_helpers/huggingface.py +326 -0
  116. brainscore_language/model_helpers/localize.py +171 -0
  117. brainscore_language/model_helpers/modeling_suma.py +1176 -0
  118. brainscore_language/model_helpers/preprocessing.py +24 -0
  119. brainscore_language/models/earley_parser/__init__.py +4 -0
  120. brainscore_language/models/earley_parser/parser.py +322 -0
  121. brainscore_language/models/earley_parser/requirements.txt +1 -0
  122. brainscore_language/models/earley_parser/test.py +267 -0
  123. brainscore_language/models/earley_parser/treebank/sample_treebank +11 -0
  124. brainscore_language/models/earley_parser/utils.py +162 -0
  125. brainscore_language/models/glove/__init__.py +4 -0
  126. brainscore_language/models/glove/model.py +38 -0
  127. brainscore_language/models/glove/test.py +19 -0
  128. brainscore_language/models/gpt/__init__.py +33 -0
  129. brainscore_language/models/gpt/__pycache__/__init__.cpython-311.pyc +0 -0
  130. brainscore_language/models/gpt/test.py +77 -0
  131. brainscore_language/models/lm1b/__init__.py +406 -0
  132. brainscore_language/models/lm1b/data_utils.py +152 -0
  133. brainscore_language/models/lm1b/requirements.txt +1 -0
  134. brainscore_language/models/lm1b/test.py +67 -0
  135. brainscore_language/models/random_embedding/__init__.py +35 -0
  136. brainscore_language/models/random_embedding/test.py +52 -0
  137. brainscore_language/models/rnng/__init__.py +63 -0
  138. brainscore_language/models/rnng/test.py +30 -0
  139. brainscore_language/models/rnnlm/__init__.py +12 -0
  140. brainscore_language/models/rnnlm/test.py +58 -0
  141. brainscore_language/models/suma/__init__.py +30 -0
  142. brainscore_language/submission/__init__.py +0 -0
  143. brainscore_language/submission/config.py +7 -0
  144. brainscore_language/submission/endpoints.py +59 -0
  145. brainscore_language/utils/__init__.py +17 -0
  146. brainscore_language/utils/ceiling.py +17 -0
  147. brainscore_language/utils/s3.py +42 -0
  148. brainscore_language/utils/transformations.py +248 -0
  149. brainscore_language/utils/xarray.py +54 -0
  150. brainscore_language-2.1.0.dist-info/METADATA +87 -0
  151. brainscore_language-2.1.0.dist-info/RECORD +154 -0
  152. brainscore_language-2.1.0.dist-info/WHEEL +5 -0
  153. brainscore_language-2.1.0.dist-info/licenses/LICENSE +21 -0
  154. brainscore_language-2.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,80 @@
1
+ from typing import Dict, Any, Union, Callable
2
+
3
+ from brainio.assemblies import DataAssembly
4
+ from brainscore_core.benchmarks import Benchmark
5
+ from brainscore_core.metrics import Score, Metric
6
+ from brainscore_core.plugin_management.conda_score import wrap_score
7
+ from brainscore_core.plugin_management.import_plugin import import_plugin
8
+ from brainscore_language.artificial_subject import ArtificialSubject
9
+
10
+ data_registry: Dict[str, Callable[[], Union[DataAssembly, Any]]] = {}
11
+ """ Pool of available data """
12
+
13
+ metric_registry: Dict[str, Callable[[], Metric]] = {}
14
+ """ Pool of available metrics """
15
+
16
+ benchmark_registry: Dict[str, Callable[[], Benchmark]] = {}
17
+ """ Pool of available benchmarks """
18
+
19
+ model_registry: Dict[str, Callable[[], ArtificialSubject]] = {}
20
+ """ Pool of available models """
21
+
22
+
23
+ def load_dataset(identifier: str) -> Union[DataAssembly, Any]:
24
+ import_plugin('brainscore_language', 'data', identifier)
25
+
26
+ return data_registry[identifier]()
27
+
28
+
29
+ def load_metric(identifier: str, *args, **kwargs) -> Metric:
30
+ import_plugin('brainscore_language', 'metrics', identifier)
31
+
32
+ return metric_registry[identifier](*args, **kwargs)
33
+
34
+
35
+ def load_benchmark(identifier: str) -> Benchmark:
36
+ import_plugin('brainscore_language', 'benchmarks', identifier)
37
+
38
+ return benchmark_registry[identifier]()
39
+
40
+
41
+ def load_model(identifier: str) -> ArtificialSubject:
42
+ import_plugin('brainscore_language', 'models', identifier)
43
+
44
+ model = model_registry[identifier]()
45
+ model.identifier = identifier
46
+
47
+ return model
48
+
49
+ def _run_score(model_identifier: str, benchmark_identifier: str) -> Score:
50
+ """
51
+ Score the model referenced by the `model_identifier` on the benchmark referenced by the `benchmark_identifier`.
52
+ """
53
+ model: ArtificialSubject = load_model(model_identifier)
54
+ benchmark: Benchmark = load_benchmark(benchmark_identifier)
55
+ score: Score = benchmark(model)
56
+ score.attrs['model_identifier'] = model_identifier
57
+ score.attrs['benchmark_identifier'] = benchmark_identifier
58
+ return score
59
+
60
+
61
+ def score(model_identifier: str, benchmark_identifier: str, conda_active: bool=False) -> Score:
62
+ """
63
+ Score the model referenced by the `model_identifier` on the benchmark referenced by the `benchmark_identifier`.
64
+ The model needs to implement the :class:`~brainscore_language.artificial_subject.ArtificialSubject` interface
65
+ so that the benchmark can interact with it.
66
+ The benchmark will be looked up from the :data:`~brainscore_language.benchmarks` and evaluates the model
67
+ (looked up from :data:`~brainscore_language.models`) on how brain-like it is under that benchmark's
68
+ experimental paradigm, primate measurements, comparison metric, and ceiling.
69
+ This results in a quantitative
70
+ `Score <https://brain-score-core.readthedocs.io/en/latest/modules/metrics.html#brainscore_core.metrics.Score>`_
71
+ ranging from 0 (least brain-like) to 1 (most brain-like under this benchmark).
72
+
73
+ :param model_identifier: the identifier for the model
74
+ :param benchmark_identifier: the identifier for the benchmark to test the model against
75
+ :return: a Score of how brain-like the candidate model is under this benchmark. The score is normalized by
76
+ this benchmark's ceiling such that 1 means the model matches the data to ceiling level.
77
+ """
78
+ return wrap_score(__file__,
79
+ model_identifier=model_identifier, benchmark_identifier=benchmark_identifier,
80
+ score_function=_run_score, conda_active=conda_active)
@@ -0,0 +1,14 @@
1
+ from pathlib import Path
2
+
3
+ import fire
4
+
5
+ from brainscore_language import score as _score_function
6
+
7
+
8
+ def score(model_identifier: str, benchmark_identifier: str, conda_active: bool=False):
9
+ result = _score_function(model_identifier, benchmark_identifier, conda_active)
10
+ print(result) # print instead of return because fire has issues with xarray objects
11
+
12
+
13
+ if __name__ == '__main__':
14
+ fire.Fire()
@@ -0,0 +1,140 @@
1
+ from typing import Dict, List, Union
2
+
3
+ from brainio.assemblies import DataAssembly
4
+
5
+
6
+ class ArtificialSubject:
7
+ def identifier(self) -> str:
8
+ """
9
+ The unique identifier for this model.
10
+
11
+ :return: e.g. `'glove'`, or `'distilgpt2'`
12
+ """
13
+ raise NotImplementedError()
14
+
15
+ class Task:
16
+ """ task to perform """
17
+ # ideally we would define these as enums (also for RecordingTarget and RecordingType)
18
+ # but accessing enums gives weird Enum objects whereas this setup just gives a string
19
+
20
+ next_word = 'next_word'
21
+ """
22
+ Predict the next word from the preceding context. Output a :class:`~brainio.assemblies.BehavioralAssembly` with
23
+ next-word predictions as the values and preceding stimulus in the `stimulus` coordinate.
24
+
25
+ Example:
26
+
27
+ Calling `digest_text(text = 'the quick brown')` could output
28
+
29
+ .. code-block:: python
30
+
31
+ {'behavior': <xarray.BehavioralAssembly (presentation: 1)>
32
+ array(['fox']), # the actual next word
33
+ Coordinates:
34
+ * presentation (presentation) MultiIndex
35
+ - stimulus (presentation) object 'the quick brown'
36
+ - stimulus_id (presentation) int64 0}
37
+
38
+ Example:
39
+
40
+ Calling `digest_text(text = ['the quick brown', 'fox jumps', 'over the lazy'])` could output
41
+
42
+ .. code-block:: python
43
+
44
+ {'behavior': <xarray.BehavioralAssembly (presentation: 3)>
45
+ array(['fox', 'over', 'dog']), # the actual next words
46
+ Coordinates:
47
+ * presentation (presentation) MultiIndex
48
+ - stimulus (presentation) object 'the quick brown' 'fox jumps', 'over the lazy'
49
+ - stimulus_id (presentation) int64 0 1 2}
50
+ """
51
+
52
+ reading_times = 'reading_times'
53
+ """
54
+ Output how long it took to read a given text, in milliseconds.
55
+ Output a :class:`~brainio.assemblies.BehavioralAssembly` with reading times in milliseconds as the values and
56
+ text in the `stimulus` coordinate.
57
+
58
+ Example:
59
+
60
+ Calling `digest_text(text = 'the quick brown')` could output
61
+
62
+ .. code-block:: python
63
+
64
+ {'behavior': <xarray.BehavioralAssembly (presentation: 1)>
65
+ array([329.15]), # reading time in milliseconds
66
+ Coordinates:
67
+ * presentation (presentation) MultiIndex
68
+ - stimulus (presentation) object 'the quick brown'
69
+ - stimulus_id (presentation) int64 0}
70
+
71
+ Example:
72
+
73
+ Calling `digest_text(text = ['the quick brown', 'fox jumps', 'over the lazy'])` could output
74
+
75
+ .. code-block:: python
76
+
77
+ {'behavior': <xarray.BehavioralAssembly (presentation: 3)>
78
+ array([329.15, 337.53, 341.13]), # reading times in milliseconds
79
+ Coordinates:
80
+ * presentation (presentation) MultiIndex
81
+ - stimulus (presentation) object 'the quick brown' 'fox jumps', 'over the lazy'
82
+ - stimulus_id (presentation) int64 0 1 2}
83
+ """
84
+
85
+ def start_behavioral_task(self, task: Task):
86
+ """
87
+ Instruct the subject to output behavior when queried with
88
+ :meth:`~brainscore_language.artificial_subject.ArtificialSubject.digest_text`. Calling
89
+ :meth:`~brainscore_language.artificial_subject.ArtificialSubject.digest_text` will then output a
90
+ :class:`~brainio.assemblies.BehavioralAssembly` in the `'behavior'` part of the output dict.
91
+
92
+ :param task: Which :class:`~brainscore_language.artificial_subject.ArtificialSubject.Task` to use.
93
+ """
94
+ raise NotImplementedError()
95
+
96
+ class RecordingTarget:
97
+ """ location to record from """
98
+ language_system = "language_system"
99
+ language_system_left_hemisphere = "language_system_left_hemisphere"
100
+ language_system_right_hemisphere = "language_system_right_hemisphere"
101
+
102
+ class RecordingType:
103
+ """ method of recording """
104
+
105
+ fMRI = "fMRI"
106
+ """ functional magnetic resonance imaging """
107
+ ECoG = "ECoG"
108
+ """ Electrocorticography """
109
+
110
+ def start_neural_recording(self, recording_target: RecordingTarget, recording_type: RecordingType):
111
+ """
112
+ Begin neural recordings from this subject. Calling
113
+ :meth:`~brainscore_language.artificial_subject.ArtificialSubject.digest_text` will then output a
114
+ :class:`~brainio.assemblies.NeuroidAssembly` in the `'neural'` part of the output dict.
115
+
116
+ :param recording_target: where in the language-involved parts of the brain to record from.
117
+ :param recording_type: What recording method should be used.
118
+ """
119
+ raise NotImplementedError()
120
+
121
+ def digest_text(self, text: Union[str, List[str]]) -> Dict[str, DataAssembly]:
122
+ """
123
+ :param text: text to pass to the subject, either a single string (e.g. `"the quick brown fox jumped"`),
124
+ or a list of strings (e.g. `["the quick", "brown fox jumped"]`)
125
+ :return: a dictionary mapping from `'behavior'` and `'neural'` to a :class:`~brainio.assemblies.DataAssembly`,
126
+ if the subject was instructed to
127
+ :meth:`~brainscore_language.artificial_subject.ArtificialSubject.start_behavioral_task` and/or
128
+ :meth:`~brainscore_language.artificial_subject.ArtificialSubject.start_neural_recording` respectively.
129
+ The :class:`~brainio.assemblies.DataAssembly` always contains a `presentation` dimension, corresponding to
130
+ the presentation of text stimuli (including a `stimulus` coordinate for the text,
131
+ and a `stimulus_id` coordinate that uniquely identifies each stimulus).
132
+ Values along the `presentation` dimension are ordered in the same way as the input.
133
+ The `behavior` output has no other dimension, while the `neural` output has a `neuroid` dimension
134
+ (including a `neuroid_id` coordinate to uniquely identify each recording unit), and sometimes a
135
+ `time_bin` dimension (including `time_bin_start` and `time_bin_end`).
136
+ Note that model state is not saved between every time that `digest_text` is called. For keeping states,
137
+ between different inputs, `text` should be a list.
138
+
139
+ """
140
+ raise NotImplementedError()
@@ -0,0 +1,47 @@
1
+ import numpy as np
2
+ from typing import List
3
+
4
+ from brainio.assemblies import walk_coords, array_is_element, DataAssembly
5
+
6
+
7
+ def ci_error(samples, center, confidence=.95):
8
+ low, high = 100 * ((1 - confidence) / 2), 100 * (1 - ((1 - confidence) / 2))
9
+ confidence_below, confidence_above = np.nanpercentile(samples, low), np.nanpercentile(samples, high)
10
+ confidence_below, confidence_above = center - confidence_below, confidence_above - center
11
+ return confidence_below, confidence_above
12
+
13
+
14
+ def manual_merge(*elements: List[DataAssembly], on='neuroid') -> DataAssembly:
15
+ """
16
+ Manually merge a set of assemblies where xarray's automated merge might fail.
17
+ This function likely covers only covers a small number of use-cases, and should thus be used with caution.
18
+ """
19
+ dims = elements[0].dims
20
+ assert all(element.dims == dims for element in elements[1:])
21
+ merge_index = dims.index(on)
22
+ # the coordinates in the merge index should have the same keys
23
+ assert _coords_match(elements, dim=on,
24
+ match_values=False), f"coords in {[element[on] for element in elements]} do not match"
25
+ # all other dimensions, their coordinates and values should already align
26
+ for dim in set(dims) - {on}:
27
+ assert _coords_match(elements, dim=dim,
28
+ match_values=True), f"coords in {[element[dim] for element in elements]} do not match"
29
+ # merge values without meta
30
+ merged_values = np.concatenate([element.values for element in elements], axis=merge_index)
31
+ # piece together with meta
32
+ result = type(elements[0])(merged_values, coords={
33
+ **{coord: (dims, values)
34
+ for coord, dims, values in walk_coords(elements[0])
35
+ if not array_is_element(dims, on)},
36
+ **{coord: (dims, np.concatenate([element[coord].values for element in elements]))
37
+ for coord, dims, _ in walk_coords(elements[0])
38
+ if array_is_element(dims, on)}}, dims=elements[0].dims)
39
+ return result
40
+
41
+
42
+ def _coords_match(elements, dim, match_values=False):
43
+ """ Helper method for `manual_merge` """
44
+ first_coords = [(key, tuple(value)) if match_values else key for _, key, value in walk_coords(elements[0][dim])]
45
+ other_coords = [[(key, tuple(value)) if match_values else key for _, key, value in walk_coords(element[dim])]
46
+ for element in elements[1:]]
47
+ return all(tuple(first_coords) == tuple(coords) for coords in other_coords)
@@ -0,0 +1,4 @@
1
+ from brainscore_language import benchmark_registry
2
+ from .benchmark import Blank2014Linear
3
+
4
+ benchmark_registry['Blank2014-linear'] = Blank2014Linear
@@ -0,0 +1,50 @@
1
+ import xarray as xr
2
+
3
+ from brainscore_core.benchmarks import BenchmarkBase
4
+ from brainscore_core.metrics import Score
5
+ from brainscore_language import load_dataset, load_metric
6
+ from brainscore_language.artificial_subject import ArtificialSubject
7
+ from brainscore_language.benchmarks.blank2014.ceiling import ExtrapolationCeiling
8
+ from brainscore_language.data.blank2014 import BIBTEX
9
+ from brainscore_language.utils.ceiling import ceiling_normalize
10
+
11
+
12
+ class Blank2014Linear(BenchmarkBase):
13
+ """
14
+ Evaluate model ability to predict neural activity in human language system functional regions of interest (fROIs)
15
+ in response to natural stories, recorded by Blank et al. 2014.
16
+ Alignment of neural activity between model and human subjects is evaluated via cross-validated linear predictivity.
17
+
18
+ This benchmark builds off the Blank2014 benchmark introduced in Schrimpf et al. 2021
19
+ (https://www.pnas.org/doi/10.1073/pnas.2105646118), but requires the model to have committed to neural readouts
20
+ (e.g. "layer 41 corresponds to the language system"), rather than testing every layer separately.
21
+ """
22
+
23
+ def __init__(self):
24
+ self.data = load_dataset('Blank2014.fROI')
25
+ self.metric = load_metric('linear_pearsonr')
26
+ ceiler = ExtrapolationCeiling()
27
+ ceiling = ceiler(assembly=self.data, metric=self.metric)
28
+ super(Blank2014Linear, self).__init__(
29
+ identifier='Blank2014-linear',
30
+ version=1,
31
+ parent='neural_language',
32
+ ceiling=ceiling,
33
+ bibtex=BIBTEX)
34
+
35
+ def __call__(self, candidate: ArtificialSubject) -> Score:
36
+ candidate.start_neural_recording(recording_target=ArtificialSubject.RecordingTarget.language_system,
37
+ recording_type=ArtificialSubject.RecordingType.fMRI)
38
+ stimuli = self.data['stimulus']
39
+ stories = self.data['story'].values
40
+ predictions = []
41
+ for story in sorted(set(stories)): # go over individual stories, sorting to keep consistency across runs
42
+ story_indexer = [stimulus_story == story for stimulus_story in stories]
43
+ story_stimuli = stimuli[story_indexer]
44
+ story_predictions = candidate.digest_text(story_stimuli.values)['neural']
45
+ story_predictions['stimulus_id'] = 'presentation', story_stimuli['stimulus_id'].values
46
+ predictions.append(story_predictions)
47
+ predictions = xr.concat(predictions, dim='presentation')
48
+ raw_score = self.metric(predictions, self.data)
49
+ score = ceiling_normalize(raw_score, self.ceiling)
50
+ return score
@@ -0,0 +1,204 @@
1
+ import itertools
2
+ import logging
3
+ import numpy as np
4
+ from numpy.random import RandomState
5
+ from scipy.optimize import curve_fit
6
+ from tqdm import tqdm, trange
7
+
8
+ from brainio.assemblies import array_is_element, walk_coords, DataAssembly, merge_data_arrays
9
+ from brainscore_core.metrics import Score
10
+ from brainscore_language.benchmark_helpers import ci_error, manual_merge
11
+ from brainscore_language.utils import fullname
12
+ from brainscore_language.utils.transformations import apply_aggregate
13
+
14
+
15
+ def v(x, v0, tau0):
16
+ return v0 * (1 - np.exp(-x / tau0))
17
+
18
+
19
+ class ExtrapolationCeiling:
20
+ def __init__(self, subject_column='subject_id', extrapolation_dimension='neuroid', num_bootstraps=100):
21
+ self._logger = logging.getLogger(fullname(self))
22
+ self.subject_column = subject_column
23
+ self.holdout_ceiling = HoldoutSubjectCeiling(subject_column=subject_column)
24
+ self.extrapolation_dimension = extrapolation_dimension
25
+ self.num_bootstraps = num_bootstraps
26
+
27
+ def __call__(self, assembly, metric):
28
+ scores = self.collect(assembly=assembly, metric=metric)
29
+ return self.extrapolate(scores)
30
+
31
+ def collect(self, assembly, metric):
32
+ num_subjects = len(set(assembly[self.subject_column].values))
33
+ subject_subsamples = self.build_subject_subsamples(num_subjects)
34
+ scores = []
35
+ for num_subjects in tqdm(subject_subsamples, desc='num subjects'):
36
+ selection_combinations = self.iterate_subsets(assembly, num_subjects=num_subjects)
37
+ for selections, sub_assembly in tqdm(selection_combinations, desc='selections'):
38
+ score = self.holdout_ceiling(assembly=sub_assembly, metric=metric)
39
+ score = score.expand_dims('num_subjects')
40
+ score['num_subjects'] = [num_subjects]
41
+ for key, selection in selections.items():
42
+ expand_dim = f'sub_{key}'
43
+ score = score.expand_dims(expand_dim)
44
+ score[expand_dim] = [str(selection)]
45
+ scores.append(score.raw)
46
+ scores = Score.merge(*scores)
47
+ assert hasattr(scores, 'neuroid_id')
48
+ return scores
49
+
50
+ def build_subject_subsamples(self, num_subjects):
51
+ return tuple(range(2, num_subjects + 1))
52
+
53
+ def iterate_subsets(self, assembly, num_subjects):
54
+ subjects = set(assembly[self.subject_column].values)
55
+ subject_combinations = list(itertools.combinations(sorted(subjects), num_subjects))
56
+ for sub_subjects in subject_combinations:
57
+ sub_assembly = assembly[{'neuroid': [subject in sub_subjects
58
+ for subject in assembly[self.subject_column].values]}]
59
+ yield {self.subject_column: sub_subjects}, sub_assembly
60
+
61
+ def average_collected(self, scores):
62
+ return scores.median('neuroid')
63
+
64
+ def extrapolate(self, ceilings):
65
+ neuroid_ceilings, bootstrap_params, endpoint_xs = [], [], []
66
+ for i in trange(len(ceilings[self.extrapolation_dimension]),
67
+ desc=f'{self.extrapolation_dimension} extrapolations'):
68
+ # extrapolate per-neuroid ceiling
69
+ neuroid_ceiling = ceilings.isel(**{self.extrapolation_dimension: [i]})
70
+ extrapolated_ceiling = self.extrapolate_neuroid(neuroid_ceiling.squeeze())
71
+ extrapolated_ceiling = self.add_neuroid_meta(extrapolated_ceiling, neuroid_ceiling)
72
+ neuroid_ceilings.append(extrapolated_ceiling)
73
+ # also keep track of bootstrapped parameters
74
+ neuroid_bootstrap_params = extrapolated_ceiling.bootstrapped_params
75
+ neuroid_bootstrap_params = self.add_neuroid_meta(neuroid_bootstrap_params, neuroid_ceiling)
76
+ bootstrap_params.append(neuroid_bootstrap_params)
77
+ # and endpoints
78
+ endpoint_x = self.add_neuroid_meta(extrapolated_ceiling.endpoint_x, neuroid_ceiling)
79
+ endpoint_xs.append(endpoint_x)
80
+ # merge and add meta
81
+ self._logger.debug("Merging neuroid ceilings")
82
+ neuroid_ceilings = manual_merge(*neuroid_ceilings, on=self.extrapolation_dimension)
83
+ neuroid_ceilings.attrs['raw'] = ceilings
84
+ self._logger.debug("Merging bootstrap params")
85
+ bootstrap_params = manual_merge(*bootstrap_params, on=self.extrapolation_dimension)
86
+ neuroid_ceilings.attrs['bootstrapped_params'] = bootstrap_params
87
+ self._logger.debug("Merging endpoints")
88
+ endpoint_xs = manual_merge(*endpoint_xs, on=self.extrapolation_dimension)
89
+ neuroid_ceilings.attrs['endpoint_x'] = endpoint_xs
90
+ # aggregate
91
+ ceiling = self.aggregate_neuroid_ceilings(neuroid_ceilings)
92
+ return ceiling
93
+
94
+ def add_neuroid_meta(self, target, source):
95
+ target = target.expand_dims(self.extrapolation_dimension)
96
+ for coord, dims, values in walk_coords(source):
97
+ if array_is_element(dims, self.extrapolation_dimension):
98
+ target[coord] = dims, values
99
+ return target
100
+
101
+ def aggregate_neuroid_ceilings(self, neuroid_ceilings):
102
+ ceiling = neuroid_ceilings.median(self.extrapolation_dimension)
103
+ ceiling.attrs['bootstrapped_params'] = neuroid_ceilings.bootstrapped_params.median(self.extrapolation_dimension)
104
+ ceiling.attrs['endpoint_x'] = neuroid_ceilings.endpoint_x.median(self.extrapolation_dimension)
105
+ ceiling.attrs['raw'] = neuroid_ceilings
106
+ return ceiling
107
+
108
+ def extrapolate_neuroid(self, ceilings):
109
+ # figure out how many extrapolation x points we have. E.g. for Pereira, not all combinations are possible
110
+ subject_subsamples = list(sorted(set(ceilings['num_subjects'].values)))
111
+ rng = RandomState(0)
112
+ bootstrap_params = []
113
+ for bootstrap in range(self.num_bootstraps):
114
+ bootstrapped_scores = []
115
+ for num_subjects in subject_subsamples:
116
+ num_scores = ceilings.sel(num_subjects=num_subjects)
117
+ # the sub_subjects dimension creates nans, get rid of those
118
+ num_scores = num_scores.dropna(f'sub_{self.subject_column}')
119
+ assert set(num_scores.dims) == {f'sub_{self.subject_column}', 'split'} or \
120
+ set(num_scores.dims) == {f'sub_{self.subject_column}'}
121
+ # choose from subject subsets and the splits therein, with replacement for variance
122
+ choices = num_scores.values.flatten()
123
+ bootstrapped_score = rng.choice(choices, size=len(choices), replace=True)
124
+ bootstrapped_scores.append(np.mean(bootstrapped_score))
125
+
126
+ params = self.fit(subject_subsamples, bootstrapped_scores)
127
+ params = DataAssembly([params], coords={'bootstrap': [bootstrap], 'param': ['v0', 'tau0']},
128
+ dims=['bootstrap', 'param'])
129
+ bootstrap_params.append(params)
130
+ bootstrap_params = merge_data_arrays(bootstrap_params)
131
+ # find endpoint and error
132
+ asymptote_threshold = .0005
133
+ interpolation_xs = np.arange(1000)
134
+ ys = np.array([v(interpolation_xs, *params) for params in bootstrap_params.values
135
+ if not np.isnan(params).any()])
136
+ median_ys = np.median(ys, axis=0)
137
+ diffs = np.diff(median_ys)
138
+ end_x = np.where(diffs < asymptote_threshold)[0].min() # first x where increase smaller than threshold
139
+ # put together
140
+ center = np.median(np.array(bootstrap_params)[:, 0])
141
+ error_low, error_high = ci_error(ys[:, end_x], center=center)
142
+ score = Score(center)
143
+ score.attrs['error_low'] = error_low
144
+ score.attrs['error_high'] = error_high
145
+ score.attrs['raw'] = ceilings
146
+ score.attrs['bootstrapped_params'] = bootstrap_params
147
+ score.attrs['endpoint_x'] = DataAssembly(end_x)
148
+ return score
149
+
150
+ def fit(self, subject_subsamples, bootstrapped_scores):
151
+ params, pcov = curve_fit(v, subject_subsamples, bootstrapped_scores,
152
+ # v (i.e. max ceiling) is between 0 and 1, tau0 unconstrained
153
+ bounds=([0, -np.inf], [1, np.inf]))
154
+ return params
155
+
156
+
157
+ class HoldoutSubjectCeiling:
158
+ def __init__(self, subject_column):
159
+ self.subject_column = subject_column
160
+ self._logger = logging.getLogger(fullname(self))
161
+
162
+ def __call__(self, assembly, metric):
163
+ subjects = set(assembly[self.subject_column].values)
164
+ scores = []
165
+ iterate_subjects = self.get_subject_iterations(subjects)
166
+ for subject in tqdm(iterate_subjects, desc='heldout subject'):
167
+ try:
168
+ subject_assembly = assembly[{'neuroid': [subject_value == subject
169
+ for subject_value in assembly[self.subject_column].values]}]
170
+ # run subject pool as neural candidate
171
+ subject_pool = subjects - {subject}
172
+ pool_assembly = assembly[
173
+ {'neuroid': [subject in subject_pool for subject in assembly[self.subject_column].values]}]
174
+ score = self.score(pool_assembly, subject_assembly, metric=metric)
175
+ # store scores
176
+ apply_raw = 'raw' in score.attrs and \
177
+ not hasattr(score.raw, self.subject_column) # only propagate if column not part of score
178
+ score = score.expand_dims(self.subject_column, _apply_raw=apply_raw)
179
+ score.__setitem__(self.subject_column, [subject], _apply_raw=apply_raw)
180
+ scores.append(score)
181
+ except NoOverlapException as e:
182
+ self._logger.debug(f"Ignoring no overlap {e}")
183
+ continue # ignore
184
+ except ValueError as e:
185
+ if "Found array with" in str(e):
186
+ self._logger.debug(f"Ignoring empty array {e}")
187
+ continue
188
+ else:
189
+ raise e
190
+
191
+ scores = Score.merge(*scores)
192
+ score = apply_aggregate(lambda scores: scores.mean(self.subject_column), scores)
193
+ score.attrs['error'] = scores.std(self.subject_column)
194
+ return scores
195
+
196
+ def get_subject_iterations(self, subjects):
197
+ return subjects # iterate over all subjects
198
+
199
+ def score(self, pool_assembly, subject_assembly, metric):
200
+ return metric(pool_assembly, subject_assembly)
201
+
202
+
203
+ class NoOverlapException(Exception):
204
+ pass
@@ -0,0 +1,75 @@
1
+ import copy
2
+ import numpy as np
3
+ from numpy.random import RandomState
4
+ from pytest import approx
5
+ from typing import Callable, Union, List
6
+
7
+ from brainio.assemblies import NeuroidAssembly
8
+ from brainscore_language import ArtificialSubject, load_benchmark
9
+
10
+
11
+ class TestBenchmark:
12
+ class DummyModel(ArtificialSubject):
13
+ def __init__(self, activity_for_text: Callable[[Union[str, List[str]]], NeuroidAssembly]):
14
+ self.activity_for_text = activity_for_text
15
+
16
+ def digest_text(self, stimuli):
17
+ neural_activity = self.activity_for_text(stimuli)
18
+ return {'neural': neural_activity}
19
+
20
+ def start_neural_recording(self, recording_target: ArtificialSubject.RecordingTarget,
21
+ recording_type: ArtificialSubject.RecordingType):
22
+ assert recording_target == ArtificialSubject.RecordingTarget.language_system
23
+ assert recording_type == ArtificialSubject.RecordingType.fMRI
24
+
25
+ def test_dummy_bad(self):
26
+ random_state = RandomState(0)
27
+
28
+ def activity_for_text(stimuli: Union[str, List[str]]) -> NeuroidAssembly:
29
+ num_stimuli = len(stimuli)
30
+ num_neuroids = 25
31
+ neural_activity = random_state.random(size=(num_stimuli, num_neuroids)) # presentation x neuroid
32
+ neural_activity = NeuroidAssembly(neural_activity,
33
+ coords={'stimulus_seq': ('presentation', np.arange(num_stimuli)),
34
+ 'stimulus_num': ('presentation', np.arange(num_stimuli)),
35
+ 'neuroid_id': ('neuroid', np.arange(num_neuroids)),
36
+ 'region': ('neuroid', ['some_region'] * num_neuroids)},
37
+ dims=['presentation', 'neuroid'])
38
+ neural_activity['stimulus'] = 'presentation', stimuli # copy over
39
+ return neural_activity
40
+
41
+ benchmark = load_benchmark('Blank2014-linear')
42
+ dummy_model = TestBenchmark.DummyModel(activity_for_text=activity_for_text)
43
+ score = benchmark(dummy_model)
44
+ assert score == 0
45
+
46
+ def test_exact(self):
47
+ benchmark = load_benchmark('Blank2014-linear')
48
+ exact_data = copy.deepcopy(benchmark.data)
49
+
50
+ def activity_for_text(stimuli: Union[str, List[str]]) -> NeuroidAssembly:
51
+ passage_activity = exact_data[{'presentation': [
52
+ list(exact_data['stimulus'].values).index(stimulus) for stimulus in stimuli]}]
53
+ # remove stimulus_id and stimulus coordinates to not trip up benchmark
54
+ passage_activity = passage_activity.reset_index('presentation')
55
+ del passage_activity['stimulus_id']
56
+ passage_activity = NeuroidAssembly(passage_activity) # index
57
+ return passage_activity
58
+
59
+ dummy_model = TestBenchmark.DummyModel(activity_for_text=activity_for_text)
60
+ score = benchmark(dummy_model)
61
+ assert score == approx(1)
62
+
63
+ def test_ceiling(self):
64
+ benchmark = load_benchmark(f'Blank2014-linear')
65
+ ceiling = benchmark.ceiling
66
+ assert ceiling == approx(.21026591, abs=.0005)
67
+
68
+ def test_ceiling_raw(self):
69
+ benchmark = load_benchmark(f'Blank2014-linear')
70
+ ceiling = benchmark.ceiling
71
+ assert hasattr(ceiling, 'raw')
72
+ assert set(ceiling.raw.dims) == {'neuroid'}
73
+ assert ceiling.raw.median() == ceiling
74
+ assert hasattr(ceiling.raw, 'raw')
75
+ assert set(ceiling.raw.raw.dims) == {'sub_subject_id', 'num_subjects', 'split', 'neuroid'}
@@ -0,0 +1,5 @@
1
+ from brainscore_language import benchmark_registry
2
+ from .benchmark import Fedorenko2016_ridge, Fedorenko2016_linear
3
+
4
+ benchmark_registry['Fedorenko2016-linear'] = Fedorenko2016_linear
5
+ benchmark_registry['Fedorenko2016-ridge'] = Fedorenko2016_ridge