試験の数理 その１（問題設定とデータの生成）

試験の数理として有名な項目反応理論について、理論的に追うのと、パラメータ推定をある程度スクラッチでかけるようにします。

一つの記事にすると長いので複数の記事にわたって書きます。目次は以下のとおりです。

* 試験の数理 その１（問題設定とデータの生成）
* [試験の数理 その2（項目反応理論の数理モデル）](https://qiita.com/takuyakubo/items/fe245cbc2f0ae8e2db08)
* [試験の数理 その3（3PL modelの最適化）](https://qiita.com/takuyakubo/items/9ec1d3e3a1ae7f1f7594)
* [試験の数理 その4（問題paramter推定の実装）](https://qiita.com/takuyakubo/items/4284279bcd84b34d5415)
* [試験の数理 その5（受験者paramter推定）](https://qiita.com/takuyakubo/items/50429230367c637c3d3a)

今回はその１「問題設定とデータの生成について」です。

## 問題設定

たとえば、TOEICやセンター試験などの試験を考えます。複数人の受験者が複数問の問題を解き、次のような結果が得られたとします。

| |受験者 1|受験者 2|受験者 3|受験者 4|受験者 5|受験者 6|受験者 7|**正答数**|
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|問 1|正|正|正|正|正|誤|正|6|
|問 2|誤|誤|誤|正|誤|誤|誤|1|
|問 3|正|正|正|正|正|正|誤|6|
|問 4|誤|誤|誤|正|誤|正|正|3|
|問 5|誤|誤|誤|正|誤|誤|正|2|
|問 6|正|正|正|正|誤|正|正|6|
|問 7|正|誤|正|正|正|正|正|6|
|問 8|誤|正|誤|誤|正|誤|正|3|
|問 9|正|正|誤|正|正|正|正|6|
|問 10|正|正|正|誤|誤|正|正|5|
|**素点**|6|6|5|8|5|6|8| |

表の見方は、受験者と問題の交点がその受験者のその問題における正誤です。たとえば、「受験者1は問3に正答」しており、「受験者6は問5に誤答」しています。**正答数** は、その問の受験者全体での正答数であり、行毎の正の数。**素点**は正答を等しく1点としたときの合計点数で、列ごとの正の数です。

さてこの状況で、**受験者の能力と問題の難易度を推定したい**とします。受験者の能力の方は、受験などで順位をつけ合否を出すのに使えるでしょうし、問題の難易度のほうは、問題を使いまわしたいときに試験全体の難易度を調整するのに使えそうです。

## どう考えるか
受験者の能力は、素点をそのまま使えば良いでしょうか？そうしてしまうと、難しい問題と易しい問題の得点が同じになってしまいます。難しい問題が解けるのに、易しい問題をたまたまミスしてしまったという状況に対応できません。

それでは、正答数の逆数を得点としてみるのはどうでしょうか？良さそうな気もしますが、この点数の差というのは果たして正当性がどの程度あるのでしょうか？例えば上の表の状況だと、問2の点数は問1の点数の6倍になりますがそれは良いのでしょうか？


## 項目反応理論
この受験者の能力と問題の難易度を推定するというのを目的として発展してきた理論が**項目反応理論** (Item Response Theory, IRT)です。IRTは試験の作成、実施、評価に非常に有用な数理モデルであり、実際にTOEFL(留学用の英語の試験)やITパスポート試験[^1]などに使われているようです。

## データ
以降の記事で項目反応理論について、理論的背景も含めて自分である程度実装できるように述べるつもりではいますが、まずは実データやデータを生成するところをここでは紹介します。実際のデータや生成したデータを使って、どの程度この理論が使えるのか試してみると良いのではないでしょうか？

### 実データ
実データは、たとえば[KDDCUP](https://pslcdatashop.web.cmu.edu/KDDCup/rules_data_format.jsp)[^2]にあるようです。こちらのデータは、単純に受験者と正誤のデータとなっていないので、処理をする必要があります。それについては、このデータが紹介されている記事[pyirtを用いた能力値推定](https://qiita.com/deerto_herajika/items/aa6d89640ebc7bd3bc37)にあるようです。

### 生成データ
少しIRTの知識を使ってしまいますが、例えば次のようにすれば、データを得ることができます。
なお、環境は、

* python 3.8
* numpy 1.19.2

です。

```python
import numpy as np
from functools import partial
# 3 parameter logistic model の定義
def L3P(a, b, c, x):
    return c + (1 - c) / (1 + np.exp(-  a * (x - b)))

# model parameterの定義
# aは正の実数, bは実数, cは0より大きく1未満であれば良い

a_min = 0.3
a_max = 1

b_min = -2
b_max = 2

c_min = 0
c_max = .4

# 何問、何人にするか、下なら10問7人
num_items = 10
num_users = 7

# 問題parameterの生成
item_params = np.array(
    [np.random.uniform(a_min, a_max, num_items),
     np.random.uniform(b_min, b_max, num_items),
     np.random.uniform(c_min, c_max, num_items)]
).T

# 受験者parameterの生成
user_params = np.random.normal(size=num_users)

# 項目反応行列の作成、 要素は1(正答)か0(誤答)
# i行j列は問iに受験者jがどう反応したか
ir_matrix_ij = np.vectorize(int)(
    np.array(
        [partial(L3P, *ip)(user_params) > np.random.uniform(0, 1, num_users) for ip in item_params]
    )
)

```

これで生成すれば、上で書いた表のような1, 0の行列が得られるはずです。$i$行$j$列は問$i$に受験者$j$がどう反応したかを表します。0は誤答1は正答です。以降の記事でも書きますが、ここでは添字$i$は問題を表すのに、添字$j$は受験者を表すのに使うこととします。

なお、素点は

```python
raw_score_j = ir_matrix_ij.sum(axis=0)
```

正答数は

```python
num_correct_i = ir_matrix_ij.sum(axis=1)
```

で得られます。

## 次回

項目反応理論でよく用いられる1, 2, 3 parameter logistic modelについて紹介します。
[試験の数理 その2(項目反応理論の数理モデル)](https://qiita.com/takuyakubo/items/fe245cbc2f0ae8e2db08)

## 参考文献
* Qiita記事 [pyirtを用いた能力値推定](https://qiita.com/deerto_herajika/items/aa6d89640ebc7bd3bc37)
* 書籍 [項目反応理論 (シリーズ〈行動計量の科学〉)(amazon.co.jp)](https://www.amazon.co.jp/dp/4254128282/ref=cm_sw_r_tw_dp_x_8siyFbR86GC44)
* 書籍 [項目反応理論・理論編―テストの数理 (統計ライブラリー)(amazon.co.jp)](https://www.amazon.co.jp/dp/4254126697/ref=cm_sw_r_tw_dp_x_JuiyFb2VA01PX)

[^1]: https://www3.jitec.ipa.go.jp/JitesCbt/html/about/range.html
[^2]: [pyirtを用いた能力値推定](https://qiita.com/deerto_herajika/items/aa6d89640ebc7bd3bc37)で紹介されています。こちらでは、pythonのパッケージであるpyirtで推定しているようです。pyirtも良いツールですが推定できるのが2PLのみと少し自由度に欠くところがあります。