メインコンテンツへスキップ
見出し画像

書記が数学やるだけ#823 項目反応理論モデルの推定

    項目反応理論モデルの推定について,手順を追って説明する。


    問題


    画像


    説明

    前回示したロジスティック回帰モデルは,最尤法やベイズ統計などを用いて解析することができる:


    画像



    画像


    異なるテスト同士の比較では,等化という手法を用いる。

    画像


    解答

    まず,能力パラメータの最尤推定を導出する。

    画像


    テストパラメータを推定する方法として,能力パラメータとの同時最尤推定や,能力パラメータを周辺化して除いた周辺最尤推定などが挙げられる。また,これを
    応用してベイズ統計により解くことも可能である。

    画像


    項目反応理論におけるフィッシャー情報量をテスト情報量といい,さらに項目ごとに項目情報量が定義できる。

    画像


    ここでは,PyMC3によるベイズモデルを実装する,ロジスティック回帰モデルの
    応用と考えれば良い。

    import pymc3 as pm
    import numpy as np
    
    # データの準備
    responses = np.array([[1, 1, 1, 1, 0],
                          [1, 1, 1, 0, 0],
                          [1, 1, 0, 0, 0],
                          [1, 0, 0, 0, 0]])
    
    num_items = len(responses[0])
    num_people = len(responses)
    
    # モデルの構築
    with pm.Model() as irt_model:
        # 能力パラメータを事前分布を指定して定義
        ability = pm.Normal('ability', mu=0, sd=1, shape=num_people)
        
        # 項目パラメータ(難易度および識別力)を事前分布を指定して定義
        item_difficulty = pm.Normal('item_difficulty', mu=0, sd=1, shape=num_items)
        item_discrimination = pm.HalfNormal('item_discrimination', sd=1, shape=num_items)
        
        # 項目特性曲線(Item Characteristic Curve, ICC)を定義
        theta = ability[:, np.newaxis] - item_difficulty
        logits = item_discrimination * theta
        
        # 応答確率のロジスティック関数
        p = pm.math.invlogit(logits)
        
        # データ生成分布を指定
        observed = pm.Bernoulli('observed', p=p, observed=responses)
    
    # 推定
    with irt_model:
        trace = pm.sample(1000, tune=1000)
    
    # 推定結果の確認
    pm.summary(trace)
    pm.traceplot(trace)
    pm.plot_posterior(trace)


    画像


    テスト情報関数の実装は以下の通り。

    #テスト情報関数を計算する関数
    def compute_test_information(ability_values, item_difficulty, item_discrimination):
        theta = ability_values[:, np.newaxis] - item_difficulty
        logits = item_discrimination * theta
        p = 1 / (1 + np.exp(-logits))
        information = item_discrimination**2 * np.exp(-logits) / (1 + np.exp(-logits))**2
        test_information = np.sum(information, axis=1)
        return test_information
    
    # モデルからサンプリングされたパラメータの事後分布からランダムサンプルを抽出
    sampled_ability = trace['ability'][::10]
    sampled_item_difficulty = trace['item_difficulty'][::10]
    sampled_item_discrimination = trace['item_discrimination'][::10]
    
    # テスト情報関数の計算
    ability_values = np.linspace(-3, 3, 1000)
    test_information_values = np.zeros((len(sampled_ability), len(ability_values)))
    
    for i in range(len(sampled_ability)):
        test_information_values[i, :] = compute_test_information(
            ability_values, sampled_item_difficulty[i, :], sampled_item_discrimination[i, :]
        )
    
    # テスト情報関数の可視化
    plt.figure(figsize=(10, 6))
    plt.plot(ability_values, np.mean(test_information_values, axis=0), label='Mean Test Information')
    plt.fill_between(
        ability_values,
        np.percentile(test_information_values, 2.5, axis=0),
        np.percentile(test_information_values, 97.5, axis=0),
        alpha=0.3,
        label='95% CI'
    )
    plt.title('Test Information Function')
    plt.xlabel('Ability')
    plt.ylabel('Test Information')
    plt.legend()
    plt.show()


    画像


    同様に項目反応関数も実装する。

    #項目情報関数を計算する関数
    def compute_item_information_function(ability_values, item_difficulty, item_discrimination):
        theta = ability_values[:, np.newaxis] - item_difficulty
        logits = item_discrimination * theta
        information = item_discrimination**2 * np.exp(-logits) / (1 + np.exp(-logits))**2
        return np.sum(information, axis=1)
    
    # 計算と可視化
    ability_values = np.linspace(-3, 3, 1000)  # ポイントの数を増やす
    
    plt.figure(figsize=(12, 8))
    
    for i in range(num_items):
        plt.subplot(2, 3, i + 1)
        
        # 寸法を一致させるためにability_valuesを再計算
        computed_ability_values = np.linspace(-3, 3, 1000)
        
        plt.plot(computed_ability_values, compute_item_information_function(
            computed_ability_values, sampled_item_difficulty[:, i], sampled_item_discrimination[:, i]
        ), label=f'Item {i + 1}')
        
        plt.title(f'Item Information Function - Item {i + 1}')
        plt.xlabel('Ability')
        plt.ylabel('Information')
        plt.legend()
    
    plt.tight_layout()
    plt.show()


    画像


    異なるテスト間における尺度変換と,mean-sigma法における等化係数を導出する。

    画像


    本記事のもくじはこちら:


    あなたへのおすすめ