OpenSesame で心理学実験をしてみよう #1 まずは実験を作ってみよう

遠隔実験の回

でもちょっと触れた OpenSesame

いいソフトだと思うのですが,日本語のリソースが極端に少ないので,このソフトを使った実験の組み方を説明してみます。

・なんてエラそうなこと言ってますが,ほんとーに簡単なソフトです。
・実験例としては,シンプルなフランカー課題を使います。刺激は色々なパターンがありますが,とりあえず自分たちの研究でも使った真ん中が f か,h かの判断を使います。
・フランカー課題とは何ぞ?というかたは,フランカー刺激とか,適合性効果とかそんなキーワードで検索してみてください。

ダウンロード

公式サイトからダウンロードします。記事執筆時点での最新の安定版は 3.2.8 です。Windows なら Installer 版,Mac は Package 版で良いです。他にも Linux 版があります。

指示に従ってインストールしてください。

ダウンロードリンクがバージョン名を含んでいますので,新しいバージョンがリリースされるとリンク先が変わる可能性があります。ご注意ください。公式サイトのトップページからリンクをたどった方が安全かもしれません。

ソフトの外観

OpenSesame を起動するとこんな感じ。画面は Mac 版ですが,Windows 版でも基本は同じです。

画像1

左側に小さなボタンが並んで居るのはアイテムツールバー,その隣がオーバービュー・エリア,右側にあるのがタブ・エリアです。アイテムツールバーには,色々な実験で使うパーツがリストされているので,ここからアイテムをオーバービュー・エリアにドラッグ・アンド・ドロップし,タブ・エリアで設定するという流れです。初期状態だと,"experiment" という名前の付いたシーケンス(sequence, 上から順に処理を実行)の中に,"getting_started", "welcome" という名前のついたアイテムが並んでいます。"getting_started" は notepad というアイテムタイプで,いってみれば単なるメモなので,実験の動作には影響しません。"welcome"は sketchpad というアイテムで,画面に何かを表示する機能を持ちます。sketchpad は,教示や刺激等,視覚的な要素を画面に提示するのに使います。

初期設定

まずは,"welcome" を選択して,OpenSesame のバージョンが書かれているテキストボックスを,最初の説明に変えてみましょうか。

画像2

変更はタブ・エリアに表示されたテキストボックスをダブルクリックして,テキストの中身を変えるだけです。この画面は実験の開始直後に表示される画面なので,例えば,インフォームド・コンセントとか書いとくとよいと思います。

あ,実験全体の設定を忘れてました。"新規実験" という名前のアイテムを選択しましょう。これは,experiment というアイテムタイプで,実験の基本設定を行うものです。

画像3

バックエンドは,実験に使う python のライブラリ(正しくはソフトウェア・レイヤー)ですが,ここは特に気にせずに,xpyriment でいいと思います。公式サイトのドキュメントでは,

・時間制御をミリ秒単位で行いたければ xpyriment か psycho を使う
・フォームが遅かったり,刺激の準備のために試行間間隔を長く取るなら legacy を使う
・PsychoPy ライブラリの機能を使いたければ psycho,Experiment ライブラリなら xpyriment,PyGame ライブラリなら legacy を使う
・バックエンドの種類によっては,全ての端末で使えない場合がある

と書いてます。
その他では,画面の解像度,デフォルトの描画色,背景色,デフォルトのフォントセットを指定できます(もちろん,それぞれの刺激毎にカスタマイズ可能です)。上の例では,背景色のみ黒 (black, RGB値 000000, 注) からグレー (RGB 値 = 333333) に変更しています。

注)# を入れると note ではハッシュタグと解釈されるので省略してます

練習試行の流れを設定する

まず,実験の流れを整理しましょう。OpenSesame では,基本的にオーバービュー・エリアにあるアイテムを上から順に実行します。すでに,最初の教示 ("welcome") が用意されていますので,この後に練習試行→本試行→終了メッセージを追加していけば良いですね。なお,練習試行,本試行は今回の場合それぞれ1試行ではなく,繰り返しが発生します。この辺は好みの問題もあるかもしれませんが,私は一つの処理ブロックは一つにまとめて整理したい派なので,練習試行のブロック,本試行のブロック,終了メッセージという形で分けたいと思います。

ブロックに相当する機能はシーケンス (sequence) というアイテムを使います。下向きの矢印のアイコンが付いているアイコンを "welcome" の後にドラッグ・アンド・ドロップで配置してください。

画像4

すると "new_sequence" という名前でシーケンスが作られます。
次に,シーケンスの中身を作っていきます。まず,練習試行のための教示を作りましょう。画面表示は sketchpad なのでレインボウカラーのアイコンを "new_sequence" にドラッグ・アンド・ドロップします。すると,シーケンスに挿入するか,シーケンスの後ろに挿入するか聞いてきますので,「new_sequence に挿入」を選びます。

次に,練習の各試行を定義します。練習試行の1試行は,注視点の表示→ブランク→ターゲットの表示→反応の取得→フィードバック→試行間のブランク,で構成されています。これを練習試行の回数分繰り返します。繰り返しの処理が必要なときは,ループ (loop) アイテムを使います。↑で教示用に配置したスケッチパッドの後に,ループ(シーケンスの隣の緑色のスプレッドシートみたいなアイコン)を配置してください。

画像5

タブ・エリアのスプレッドシートには,ループの中で使う変数を記述します。ここでは,画面に表示する刺激 (target),正解キー (correct_response),条件 (一致試行または不一致, contingency, 一致なら con,不一致なら incon),刺激の ID (stim_id) を定義しています。変数の名前は任意ですが,正解キーだけは correct_response にした方が良いです(後でキーレスポンスの所で説明します)。stim_id は無くても問題ないと思いますが,一意に区別できる ID を設定していた方が,後でデータの処理(特に long -> wide の変換を行うとき)の時に便利かなと思います。
また,順序のところを random にしておくと呈示順序をランダム化します。sequential にすると上から順に実行しますので,実施順序が決まっている場合はそちらを選んでください。

1試行の中身を作成する

さて,先ほど配置したループですが,ループで繰り返すことができるのはループの中に挿入した1つのアイテムのみです。今回の場合1試行で複数のアイテムを必要とするので,ここで1試行の流れをシーケンスにして,ループに挿入します。

画像6

ループの中にシーケンスを配置したら,そのシーケンスに sketchpad アイテムを4つ,keyboard_response アイテム(赤いアイコン)を1つ,sketchpad を2つの順に配置します。上から,1 = 試行間のブランク,2 = 注視点, 3 = 刺激間のブランク,4 = ターゲット,キーレスポンス,5 = 正解フィードバック,6 = 不正解フィードバック,となっています。

次に,最初に表示される,試行間のブランクのための sketchpad を選択します。

画像7

これはブランクなので中身は必要ありませんが,呈示時間のところのみ keypress から 500 に変更します。keypress だとキー反応があるまで待ちますが,数値を指定すると指定の時間(ミリ秒単位)経過すると次へ進みます。次に2つめの注視点用の sketchpad を選択します。

画像8

注視点は,十字の上に照準点が載っているような形のアイコンですので,そのアイコンをクリックし,タブエリアの画面の中心で再度クリックして配置します。呈示時間と表示色は適当に設定してください。上の例だと,500 ms,色は黄色になっています。注視点は + がイイ!という人は,代わりにテキストボックスを配置して + と入力してもいいでしょう。

3つ目の刺激間のブランクは最初の試行間のブランクと設定する内容は変わらないので説明は省略し,4つ目のターゲット刺激の設定について説明します。

画像9

まずターゲットを表示するため,テキストを画面の中央に配置します。配置すると表示内容を入力するダイアログが現れるので [target] と入力してください。[ ] で囲まれた内容はループ内の変数として扱われますので,先ほどループの設定で target という名前で作成した列の内容が,試行毎に読み込まれて表示されます。
もう一つ重要なのは,キー反応を求める刺激については,キーの処理を次の keyboard_response で処理するので,刺激の呈示時間を 0 にすることです。これを忘れると,反応時間の取得や正誤の判定が出来ませんので注意してください。

次にキーレスポンスの設定をします。

画像10

「正解の応答」には,その試行で正解となるキーを格納した変数を指定します。ここで,先ほどのループの設定で correct_response という変数名で正解キーを指定しておくことで,この欄を空欄にすることができます。
「可能な応答」にはキー入力として受け付けるキーをセミコロンで区切って記述します。この実験では f, h のキーのみ受け付けて (f;h),それ以外のキーでは反応しません。この設定は実験としてどのような反応を受け付けるか次第ですので,適当な設定をしてください。
「タイムアウト」には,キーが押されるまで待つ (infinite) か,回答に時間制限を付けるならその時間をミリ秒単位で入力します。
「キーイベントの種類」では,押したことで反応と見做す keypress か,キーが放されることで反応と見做す keyrelease のいずれかを選びます。
keyboard_response アイテムは,最後に行われたキー操作を記録するようになっていますので,直前の刺激の呈示時間を 0 にしておかないといけません。

ターゲットの呈示時間が keypress の場合
刺激呈示 → 1度目のキー押し → keyboard_response に移行という順序で進行します。つまりここで再度キーレスポンスを待つ(2度目のキー押し)ので,キーが2回押されないと反応が記録されません。しかもキー入力に対する諸々の判定は2回目のキー押しに対して行われますので,反応時間や正誤の判定が正確に行われません。
ターゲットの呈示時間が 0 の場合
刺激を呈示した瞬間に(呈示時間が 0 ミリ秒なので),処理が keyboard_response に移行します。ですので,1度目のキー押しに対して,キー反応の判定が行われます。

続けて,正解,不正解のフィードバックを設定します。上の例では,5個目の sketchpad が正解,6個目が不正解の表示用となっていますので,それぞれでテキストと呈示時間を設定します。下の例では正解フィードバックの設定をしていますが,黄色で 正解! のフィードバックを表示し,その呈示時間は 250 ms となっています。不正解の方も同じように設定しましょう。

画像11

しかし,このままだと毎試行で正解と不正解のフィードバックが順に表示されてしまいます。各試行の正誤に基づいてどちらか一方を表示するようにしないといけませんね。それでは,練習試行のシーケンスを記述した "new_sequence_1" に移動して,タブエリアで以下のような設定を行ってください。

画像12

なお,ここではそれぞれが何を表しているか分かりやすくなるように,各アイテムの名前を変更しています。キー反応の下にあるのが,正解フィードバック用の "fb_correct",2つ目が不正解フィードバック用の "fb_incorrect" という sketchpad になっています。ここで,キー押しの処理の際に,[correct_response] 変数とキー入力 ([response] という変数に格納されます) を照合して,両者が一致すれば [correct] という変数の値に 1 が,一致しなければ 0 がセットされます。ですので,どちらを表示するかは [correct] の値を見て判定すれば良いですね。アイテムの表示・非表示は実行条件の所で指定します。always となっているアイテムは毎回表示されます。ここで "fb_correct" は [correct] = 1 のとき(つまり正解のとき),"fb_incorrect" は [correct] = 0 のときだけ表示されるようにします。これで,練習試行のシーケンスの設定は終了です。

反応の記録

ここまでで,練習を行うことは可能です。しかしながら,今の状態では,実験自体はできるものの反応が記録できません。反応を記録するためにはロガー (logger) というアイテムをキーレスポンスの後に追加する必要があります。

画像19

ロガーは,keyboard_response の下にある青いアイコンです。これを,キーレスポンスの後,フィードバックの前に配置してください。ロガーはデフォルトでは,全ての変数を記録するようになっています。要らない変数を削除することも可能ですが,この辺は実験の挙動を見ながら後々行った方が良いと思いますので,ここではデフォルト通り,全ての変数の値を記録しておきましょう(後から,分析用のソフトで要らない変数を削除してもよいわけですし)。

本試行の作成

続けて本試行の作成を行います。まず,その前に,↑で行った練習試行のシーケンス以外のアイテムも分かりやすい名前にしておきましょうか。

画像13

まあ,名前の変更は必須ではありません。個人的なわかりやすさ追求という理由だけです。ただ,分かりやすい名前を付けておいた方が,後で見直した時にすぐ理解できるので,個人的にはオススメです。

さて,それでは本試行の作成を行います。まずは練習試行と同様に本試行用のシーケンスと本試行の最初に表示する教示(これから本番を始めます,的な)を配置します。

画像14

次に,練習と同様,本試行用のループを配置し,変数の定義を行います。

画像15

ここまでは練習試行と一緒です。今回の実験では本試行が96試行あります。それを一々ループのシートに入力するのは面倒くさいですよね。例えば,その試行の設定内容が Excel などのスプレッドシートにすでにある場合は,それをコピー・ペーストすることができます(1行目に変数名がある場合は,その行は含めないでください)。

画像16

ちなみに,先ほどの練習と同様にそれぞれの刺激には区別のための id を付けています。con_f_m1 と書かれている場合,最初の con は一致条件 (incon = 不一致),二つ目の f は刺激の真ん中の文字 (= 正解キー),3つ目は通し番号です (m は本試行,p は練習試行)。このルールは特に決まりはありませんので,ご自分で分かりやすいルールで付けていただいてよいと思います(そもそもなくても実験の挙動には影響しません)。

では,ループの中身を作っていきましょう。ループの中身には試行のシーケンスが入るわけです。が,ちょっと待ってください。さっき練習試行のシーケンス作りましたよね?で,本試行も基本的に一緒ですよね?だったら,コピー・ペーストでいいんじゃないの?と思ったあなた。

はい,正解です。正解なんですが,OpenSesame ではアイテムのコピーには2種類あります。リンクなしリンク (あり)の2パターンです。リンクなしは,コピーしたアイテムの複製を作ります。リンクありは,いわゆるシンボリックリンクのようは働きをします(実体はオリジナルの方にあって,そちらへの参照のみを行う)。ですので,リンクなし,の場合はペーストした先で内容を変更してもオリジナルの方に影響はしませんが,リンクありの場合は,実質同じ物を見ているので,修正を加えるとオリジナルの方,今回の場合は練習試行の方も変わってしまいます。そこの所は注意が必要です。今回は,全く同じ内容で構わないので,リンクつきのコピー・ペーストにしましょう。

画像17

やり方は簡単。練習試行のシーケンスを右クリックして,メニューからコピー(リンク)を選び,本試行のループに貼り付けるだけです。

最後に,実験終了時のメッセージを追加して終了です。

画像18

インラインスクリプトを使う

ここまでで,実験自体は可能です。反応もきちんと保存されるはずですので,実験は実行できます。まあ,このままでもいいんですが,ちょっと一工夫してみましょうか。フランカー課題に限らず,反応時間 (RT) を取得する実験の場合,不正解の試行は RT の分析の対象外としますよね?まあ,correct の値が 0 となっている行を除いて分析する,でもいいんですけど,例えば long -> wide 変換をした後で,一致条件と不一致条件の個人平均を求めて,それを分析するというような場合(伝統的な分析ではよくありますよね),不正解試行の RT がそのままデータに入ってると邪魔じゃないですか?不正解試行の RT を欠損にした方が,個人平均求めるときに楽じゃないですか?じゃあ,そんな処理をしてみましょう。

このような処理は Python のスクリプトで行うことになりますので,キーレスポンスの後に,inline_script のアイテム(ロガーの隣にある十字の形をしたアイコン)を配置します。

画像20

タブ・エリアに,スクリプトを入力することができますので,処理の内容を記述します。ここで,ひとつだけ注意ですが,スクリプト画面で,準備,実行というタブがありますが,実行側に処理を書いておかないと処理が実行されません。では,ここに,以下のコードを記入します。

if self.get('correct') == 1:
    exp.set('validRT',self.get('response_time'))
else:
    exp.set('validRT,'')
※ 詳しくは Python のマニュアルや,OpenSesame のドキュメントを見てください。

ここで行っている処理を簡単に言うと,最初の self.get('correct') で,正解か不正解かの値を取得し,その値が 1 である場合 (if self.get('correct') == 1:) ,exp.set() という関数を使って,validRT という変数に,response_time 変数の値をコピーします。validRT はここで作った変数ですが,exp.set() することで,全体で利用可能な変数になります(つまり記録されます)。response_time は keyboard_response アイテムで設定される変数で,反応時間を格納しています。correct 変数の値が 1 以外 (else 以降) ,この場合は正解ではない = 不正解,ということになりますが,その場合は validRT 変数には空の値が設定されます。このスクリプトを含めて実験を実行すると,response_time の列には,正解・不正解にかかわらずキー押しの反応時間が記録され,validRT の列には,不正解の場合のみ欠損となった反応時間が記録されます。
インラインスクリプトを使いこなすと,いろいろな処理が可能になります。

さて,長くなりましたが,とりあえず OpenSesame で実験プログラムを作ってみよう,の解説はここで終了します。
次は,この実験をオンラインで実施する方法を解説します。続編はしばらくお待ちください。

この記事が気に入ったらサポートをしてみませんか?