跳转到主要内容

贝叶斯ADAPTive实验设计

项目描述

BADAPTED:贝叶斯ADAPTive实验设计

PyPI

状态:工作代码,但仍在开发中 🔥

运行高效的贝叶斯自适应实验。

此代码与以下预印本相关。但是,当最终发表时,预印本可能以相当不同的形式出现。

Vincent, B. T., & Rainforth, T. (2017, October 20). The DARC Toolbox: automated, flexible, and efficient delayed and risky choice experiments using Bayesian adaptive design. Retrieved from psyarxiv.com/yehjb

badapted之上构建自己的自适应实验工具箱

以下我们将概述如何使用badapted包运行自适应实验。仅凭自身,此badapted包不会做任何事情。它还需要一些类(以及可能的一些辅助函数),开发者必须为其特定的实验范式创建这些类。这形成了一个“工具箱”,将允许在特定的实验领域中运行自适应实验。

这个例子(第一个例子)是我们的DARC工具箱,它允许进行延迟和风险选择任务的适应性实验。

但以下我们将概述如何为您感兴趣的实验领域创建一个新的“工具箱”。

步骤1:定义设计空间

首先,我们使用我们编写的函数创建一个名为designs的pandas dataframe。每一列是一个设计变量。每一行是一个特定的设计。

def build_my_design_space(my_arguments):
    designs = # CREATE PANDAS DATAFRAME OF THE DESIGN SPACE HERE
    return designs

步骤2:定义自定义设计生成器

为了生成自己的设计生成器,该生成器使用贝叶斯自适应设计(在您的实验领域),则需要创建一个继承自badapted.BayesianAdaptiveDesignGenerator的类。您还需要实现一些特定于您的实验领域的方法,特别是

  • 将设计响应添加到数据框
  • df_to_design_tuple

目前,我们将仅提供我们在DARC工具箱中使用的示例。首先,我们的具体设计生成器类定义如下

from badapted.designs import BayesianAdaptiveDesignGenerator

class BayesianAdaptiveDesignGeneratorDARC(DARCDesignGenerator, BayesianAdaptiveDesignGenerator):
    '''This will be the concrete class for doing Bayesian adaptive design
    in the DARC experiment domain.'''

    def __init__(self, design_space,
                 max_trials=20,
                 allow_repeats=True,
                 penalty_function_option='default',
                 λ=2):

        # call superclass constructors - note that the order of calling these is important
        BayesianAdaptiveDesignGenerator.__init__(self, design_space,
                 max_trials=max_trials,
                 allow_repeats=allow_repeats,
                 penalty_function_option=penalty_function_option,
                 λ=λ)

        DARCDesignGenerator.__init__(self)

请注意,这具有多重继承,所以我们还有一个名为DARCDesignGenerator的类,它仅包含DARC特定方法(add_design_response_to_dataframedf_to_design_tuple)。这被定义为

from badapted.designs import DesignGeneratorABC
from darc_toolbox import Prospect, Design


class DARCDesignGenerator(DesignGeneratorABC):
    '''This adds DARC specific functionality to the design generator'''

    def __init__(self):
        # super().__init__()
        DesignGeneratorABC.__init__(self)

        # generate empty dataframe
        data_columns = ['RA', 'DA', 'PA', 'RB', 'DB', 'PB', 'R']
        self.data = pd.DataFrame(columns=data_columns)

    def add_design_response_to_dataframe(self, design, response):
        '''
        This method must take in `design` and `reward` from the current trial
        and store this as a new row in self.data which is a pandas data frame.
        '''

        trial_data = {'RA': design.ProspectA.reward,
                    'DA': design.ProspectA.delay,
                    'PA': design.ProspectA.prob,
                    'RB': design.ProspectB.reward,
                    'DB': design.ProspectB.delay,
                    'PB': design.ProspectB.prob,
                    'R': [int(response)]}
        self.data = self.data.append(pd.DataFrame(trial_data))
        # a bit clumsy but...
        self.data['R'] = self.data['R'].astype('int64')
        self.data = self.data.reset_index(drop=True)
        return

    @staticmethod
    def df_to_design_tuple(df):
        '''User must impliment this method. It takes in a design in the form of a
        single row of pandas dataframe, and it must return the chosen design as a
        named tuple.
        Convert 1-row pandas dataframe into named tuple'''
        RA = df.RA.values[0]
        DA = df.DA.values[0]
        PA = df.PA.values[0]
        RB = df.RB.values[0]
        DB = df.DB.values[0]
        PB = df.PB.values[0]
        chosen_design = Design(ProspectA=Prospect(reward=RA, delay=DA, prob=PA),
                            ProspectB=Prospect(reward=RB, delay=DB, prob=PB))
        return chosen_design

我们之所以这样做多重继承,是因为我们想要其他(非贝叶斯自适应)设计生成器,这些生成器在DARC领域中工作,但没有贝叶斯自适应设计组件。在大多数情况下,仅关注贝叶斯自适应设计,您只需在单个具体设计生成器类中定义add_design_response_to_dataframedf_to_design_tuple类。

步骤 3:定义一个模型

您必须提供一个继承自Model的模型类。您还必须提供以下方法

  • __init__
  • predictive_y

以下是一个用户定义的最小模型实现的示例

from badapted.model import Model
from badapted.choice_functions import CumulativeNormalChoiceFunc, StandardCumulativeNormalChoiceFunc
from scipy.stats import norm, halfnorm, uniform
import numpy as np


class MyCustomModel(Model):
    '''My custom model which does XYZ.'''

    def __init__(self, n_particles,
                 prior={'logk': norm(loc=-4.5, scale=1),
                        'α': halfnorm(loc=0, scale=2)}):
        '''
        INPUTS
        - n_particles (integer).
        - prior (dictionary). The keys provide the parameter name. The values
        must be scipy.stats objects which define the prior distribution for
        this parameter.

        We provide choice functions in `badapted.choice_functions.py`. In this
        example, we define it in the __init__ but it is not necessary to happen
        here.
        '''
        self.n_particles = int(n_particles)
        self.prior = prior
        self.θ_fixed = {'ϵ': 0.01}
        self.choiceFunction = CumulativeNormalChoiceFunc

    def predictive_y(self, θ, data):
        '''
        INPUTS:
        - θ = parameters
        - data =

        OUTPUT:
        - p_chose_B (float) Must return a value between 0-1.
        '''

        # Step 1 - calculate decision variable
        k = np.exp(θ['logk'].values
        VA = data['RA'].values * 1 / (1 + k * data['DA'].values)
        VB = data['RB'].values * 1 / (1 + k * data['DB'].values)
        decision_variable = VB - VA

        # Step 2 - apply choice function
        p_chose_B = self.choiceFunction(decision_variable, θ, self.θ_fixed)
        return p_chose_B

步骤 4:构建实验试验循环

这相当直接,这里不需要进行任何重大定制。

def run_experiment(design_generator, model, max_trials):
    '''Run an adaptive experiment
    INPUTS:
    - design_generator: a class
    '''

    for trial in range(max_trials):
        design = design_generator.get_next_design(model)
        if design is None:
            break
        response = get_response(design)
        design_generator.enter_trial_design_and_response(design, response)
        model.update_beliefs(design_generator.data)

    return model

请注意,response = get_response(design)这一行由您来实现。您在这里所做的工作取决于您是在模拟响应还是从PsychoPy等获取真实响应。run_experiment函数只是代码各部分如何协同工作的一个示例。当使用PsychoPy运行实际实验时,最好参考我们提供的DARC工具箱中的示例,以了解如何进行操作。

步骤 5:设置和运行实验

designs = build_my_design_space(my_arguments)
design_generator = MyCustomDesignGenerator(designs, max_trials=max_trials)
model = MyCustomModel()

model = run_experiment(design_generator, model, max_trials)

请注意,run_experiment函数的使用只是展示事物如何组合的逻辑。如前所述,请参考DARC工具箱中的PsychoPy示例实验,以了解如何在PsychoPy实验中将其组合在一起。

使用badapted的工具箱

项目详情


下载文件

下载适用于您的平台的文件。如果您不确定选择哪一个,请了解更多关于安装包的信息。

源分布

badapted-0.0.3.tar.gz (22.4 kB 查看哈希值)

上传时间

构建分布

badapted-0.0.3-py3-none-any.whl (22.2 kB 查看哈希值)

上传时间 Python 3

支持者