準備

Googleドライブのマウント

In [1]:
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive

sys.pathの設定

以下では,Googleドライブのマイドライブ直下にDNN_codeフォルダを置くことを仮定しています.必要に応じて,パスを変更してください.

In [21]:
import sys
sys.path.append('/content/drive/My Drive/DNN_code_colab_ver200425')

simple convolution network

image to column

In [17]:
import pickle
import numpy as np
from collections import OrderedDict
from common import layers
from common import optimizer
from data.mnist import load_mnist
import matplotlib.pyplot as plt

# 画像データを2次元配列に変換
'''
input_data: 入力値
filter_h: フィルターの高さ
filter_w: フィルターの横幅
stride: ストライド
pad: パディング
'''
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    # N: number, C: channel, H: height, W: width
    N, C, H, W = input_data.shape
    # 切り捨て除算
    out_h = (H + 2 * pad - filter_h)//stride + 1
    out_w = (W + 2 * pad - filter_w)//stride + 1

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    #col = col.transpose(0, 4, 5, 1, 2, 3) # (N, C, filter_h, filter_w, out_h, out_w) -> (N, filter_w, out_h, out_w, C, filter_h)    
    
    col = col.reshape(N * out_h * out_w, -1)
    return col

[try] im2colの処理を確認しよう

・関数内でtransposeの処理をしている行をコメントアウトして下のコードを実行してみよう
・input_dataの各次元のサイズやフィルターサイズ・ストライド・パディングを変えてみよう


In [18]:
# im2colの処理確認
input_data = np.random.rand(2, 3, 3, 3)*100//1 # number, channel, height, widthを表す
print('========== input_data ===========\n', input_data)
print('==============================')
filter_h = 2
filter_w = 2
stride = 1
pad = 0
col = im2col(input_data, filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print('============= col ==============\n', col)
print('==============================')
========== input_data ===========
 [[[[36. 17. 28.]
   [28. 64. 34.]
   [70. 47. 46.]]

  [[ 3. 74. 60.]
   [20. 65. 55.]
   [52. 78. 48.]]

  [[ 9. 99. 50.]
   [47. 86. 11.]
   [50. 89. 17.]]]


 [[[22. 78. 39.]
   [68. 91. 47.]
   [52. 49. 71.]]

  [[37. 21. 30.]
   [56. 64. 73.]
   [12. 20. 34.]]

  [[40.  2.  1.]
   [94. 87.  4.]
   [13. 26. 79.]]]]
==============================
============= col ==============
 [[36. 17. 28. 64. 17. 28. 64. 34. 28. 64. 70. 47.]
 [64. 34. 47. 46.  3. 74. 20. 65. 74. 60. 65. 55.]
 [20. 65. 52. 78. 65. 55. 78. 48.  9. 99. 47. 86.]
 [99. 50. 86. 11. 47. 86. 50. 89. 86. 11. 89. 17.]
 [22. 78. 68. 91. 78. 39. 91. 47. 68. 91. 52. 49.]
 [91. 47. 49. 71. 37. 21. 56. 64. 21. 30. 64. 73.]
 [56. 64. 12. 20. 64. 73. 20. 34. 40.  2. 94. 87.]
 [ 2.  1. 87.  4. 94. 87. 13. 26. 87.  4. 26. 79.]]
==============================

column to image

In [19]:
# 2次元配列を画像データに変換
def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    # N: number, C: channel, H: height, W: width
    N, C, H, W = input_shape
    # 切り捨て除算    
    out_h = (H + 2 * pad - filter_h)//stride + 1
    out_w = (W + 2 * pad - filter_w)//stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2) # (N, filter_h, filter_w, out_h, out_w, C)

    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]

[try] col2imの処理を確認しよう

・im2colの確認で出力したcolをimageに変換して確認しよう


In [20]:
# ここにcol2imでの処理を書こう
img = col2im(col, input_shape=input_data.shape, filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print(img)
[[[[ 36.  81.  34.]
   [ 48. 275.  96.]
   [ 52. 164.  11.]]

  [[ 17.  31.  74.]
   [129. 156. 151.]
   [ 78.  98.  89.]]

  [[ 28. 138.  60.]
   [ 79. 297.  66.]
   [ 47. 175.  17.]]]


 [[[ 22. 169.  47.]
   [124. 206.  72.]
   [ 12. 107.   4.]]

  [[ 78.  76.  21.]
   [155. 270. 151.]
   [ 20.  47.  26.]]

  [[ 68. 112.  30.]
   [ 92. 202.  77.]
   [ 94. 113.  79.]]]]

convolution class

In [23]:
class Convolution:
    # W: フィルター, b: バイアス
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad
        
        # 中間データ(backward時に使用)
        self.x = None   
        self.col = None
        self.col_W = None
        
        # フィルター・バイアスパラメータの勾配
        self.dW = None
        self.db = None

    def forward(self, x):
        # FN: filter_number, C: channel, FH: filter_height, FW: filter_width
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        # 出力値のheight, width
        out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)
        
        # xを行列に変換
        col = im2col(x, FH, FW, self.stride, self.pad)
        # フィルターをxに合わせた行列に変換
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        # 計算のために変えた形式を戻す
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        # dcolを画像データに変換
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx

pooling class

In [ ]:
class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad
        
        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)
        
        # xを行列に変換
        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        # プーリングのサイズに合わせてリサイズ
        col = col.reshape(-1, self.pool_h*self.pool_w)
        
        #maxプーリング
        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)
        
        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,)) 
        
        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)
        
        return dx

sinple convolution network class

In [ ]:
class SimpleConvNet:
    # conv - relu - pool - affine - relu - affine - softmax
    def __init__(self, input_dim=(1, 28, 28), conv_param={'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
        filter_num = conv_param['filter_num']        
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']
        input_size = input_dim[1]
        conv_output_size = (input_size - filter_size + 2 * filter_pad) / filter_stride + 1
        pool_output_size = int(filter_num * (conv_output_size / 2) * (conv_output_size / 2))

        # 重みの初期化
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_init_std * np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)

        # レイヤの生成
        self.layers = OrderedDict()
        self.layers['Conv1'] = layers.Convolution(self.params['W1'], self.params['b1'], conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = layers.Relu()
        self.layers['Pool1'] = layers.Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = layers.Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = layers.Relu()
        self.layers['Affine2'] = layers.Affine(self.params['W3'], self.params['b3'])

        self.last_layer = layers.SoftmaxWithLoss()

    def predict(self, x):
        for key in self.layers.keys():
            x = self.layers[key].forward(x)
        return x
        
    def loss(self, x, d):
        y = self.predict(x)
        return self.last_layer.forward(y, d)

    def accuracy(self, x, d, batch_size=100):
        if d.ndim != 1 : d = np.argmax(d, axis=1)
        
        acc = 0.0
        
        for i in range(int(x.shape[0] / batch_size)):
            tx = x[i*batch_size:(i+1)*batch_size]
            td = d[i*batch_size:(i+1)*batch_size]
            y = self.predict(tx)
            y = np.argmax(y, axis=1)
            acc += np.sum(y == td) 
        
        return acc / x.shape[0]

    def gradient(self, x, d):
        # forward
        self.loss(x, d)
        
        # backward
        dout = 1
        dout = self.last_layer.backward(dout)
        layers = list(self.layers.values())
        
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 設定
        grad = {}
        grad['W1'], grad['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db
        grad['W2'], grad['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grad['W3'], grad['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grad
In [ ]:
from common import optimizer

# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(flatten=False)

print("データ読み込み完了")

# 処理に時間のかかる場合はデータを削減 
x_train, d_train = x_train[:5000], d_train[:5000]
x_test, d_test = x_test[:1000], d_test[:1000]


network = SimpleConvNet(input_dim=(1,28,28), conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                        hidden_size=100, output_size=10, weight_init_std=0.01)

optimizer = optimizer.Adam()

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10



for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]
    
    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)

    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)
        
        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()
データ読み込み完了
Generation: 10. 正答率(トレーニング) = 0.368
                : 10. 正答率(テスト) = 0.327
Generation: 20. 正答率(トレーニング) = 0.6062
                : 20. 正答率(テスト) = 0.576
Generation: 30. 正答率(トレーニング) = 0.7726
                : 30. 正答率(テスト) = 0.739
Generation: 40. 正答率(トレーニング) = 0.8066
                : 40. 正答率(テスト) = 0.768
Generation: 50. 正答率(トレーニング) = 0.8324
                : 50. 正答率(テスト) = 0.803
Generation: 60. 正答率(トレーニング) = 0.8752
                : 60. 正答率(テスト) = 0.845
Generation: 70. 正答率(トレーニング) = 0.8792
                : 70. 正答率(テスト) = 0.842
Generation: 80. 正答率(トレーニング) = 0.8968
                : 80. 正答率(テスト) = 0.858
Generation: 90. 正答率(トレーニング) = 0.8876
                : 90. 正答率(テスト) = 0.865
Generation: 100. 正答率(トレーニング) = 0.8958
                : 100. 正答率(テスト) = 0.869
Generation: 110. 正答率(トレーニング) = 0.9102
                : 110. 正答率(テスト) = 0.876
Generation: 120. 正答率(トレーニング) = 0.9124
                : 120. 正答率(テスト) = 0.89
Generation: 130. 正答率(トレーニング) = 0.9202
                : 130. 正答率(テスト) = 0.89
Generation: 140. 正答率(トレーニング) = 0.9178
                : 140. 正答率(テスト) = 0.889
Generation: 150. 正答率(トレーニング) = 0.9236
                : 150. 正答率(テスト) = 0.884
Generation: 160. 正答率(トレーニング) = 0.9248
                : 160. 正答率(テスト) = 0.886
Generation: 170. 正答率(トレーニング) = 0.9318
                : 170. 正答率(テスト) = 0.902
Generation: 180. 正答率(トレーニング) = 0.9262
                : 180. 正答率(テスト) = 0.904
Generation: 190. 正答率(トレーニング) = 0.931
                : 190. 正答率(テスト) = 0.896
Generation: 200. 正答率(トレーニング) = 0.931
                : 200. 正答率(テスト) = 0.907
Generation: 210. 正答率(トレーニング) = 0.9354
                : 210. 正答率(テスト) = 0.894
Generation: 220. 正答率(トレーニング) = 0.9324
                : 220. 正答率(テスト) = 0.91
Generation: 230. 正答率(トレーニング) = 0.9446
                : 230. 正答率(テスト) = 0.91
Generation: 240. 正答率(トレーニング) = 0.9376
                : 240. 正答率(テスト) = 0.914
Generation: 250. 正答率(トレーニング) = 0.9496
                : 250. 正答率(テスト) = 0.919
Generation: 260. 正答率(トレーニング) = 0.9482
                : 260. 正答率(テスト) = 0.919
Generation: 270. 正答率(トレーニング) = 0.9502
                : 270. 正答率(テスト) = 0.915
Generation: 280. 正答率(トレーニング) = 0.9506
                : 280. 正答率(テスト) = 0.921
Generation: 290. 正答率(トレーニング) = 0.9556
                : 290. 正答率(テスト) = 0.923
Generation: 300. 正答率(トレーニング) = 0.9466
                : 300. 正答率(テスト) = 0.923
Generation: 310. 正答率(トレーニング) = 0.9584
                : 310. 正答率(テスト) = 0.922
Generation: 320. 正答率(トレーニング) = 0.9572
                : 320. 正答率(テスト) = 0.924
Generation: 330. 正答率(トレーニング) = 0.9582
                : 330. 正答率(テスト) = 0.927
Generation: 340. 正答率(トレーニング) = 0.9584
                : 340. 正答率(テスト) = 0.922
Generation: 350. 正答率(トレーニング) = 0.9598
                : 350. 正答率(テスト) = 0.928
Generation: 360. 正答率(トレーニング) = 0.96
                : 360. 正答率(テスト) = 0.921
Generation: 370. 正答率(トレーニング) = 0.9652
                : 370. 正答率(テスト) = 0.939
Generation: 380. 正答率(トレーニング) = 0.9658
                : 380. 正答率(テスト) = 0.925
Generation: 390. 正答率(トレーニング) = 0.967
                : 390. 正答率(テスト) = 0.935
Generation: 400. 正答率(トレーニング) = 0.97
                : 400. 正答率(テスト) = 0.937
Generation: 410. 正答率(トレーニング) = 0.9674
                : 410. 正答率(テスト) = 0.94
Generation: 420. 正答率(トレーニング) = 0.971
                : 420. 正答率(テスト) = 0.937
Generation: 430. 正答率(トレーニング) = 0.973
                : 430. 正答率(テスト) = 0.939
Generation: 440. 正答率(トレーニング) = 0.9704
                : 440. 正答率(テスト) = 0.945
Generation: 450. 正答率(トレーニング) = 0.9726
                : 450. 正答率(テスト) = 0.944
Generation: 460. 正答率(トレーニング) = 0.9738
                : 460. 正答率(テスト) = 0.948
Generation: 470. 正答率(トレーニング) = 0.9684
                : 470. 正答率(テスト) = 0.938
Generation: 480. 正答率(トレーニング) = 0.9686
                : 480. 正答率(テスト) = 0.936
Generation: 490. 正答率(トレーニング) = 0.9714
                : 490. 正答率(テスト) = 0.943
Generation: 500. 正答率(トレーニング) = 0.9762
                : 500. 正答率(テスト) = 0.94
Generation: 510. 正答率(トレーニング) = 0.9762
                : 510. 正答率(テスト) = 0.953
Generation: 520. 正答率(トレーニング) = 0.9748
                : 520. 正答率(テスト) = 0.941
Generation: 530. 正答率(トレーニング) = 0.9732
                : 530. 正答率(テスト) = 0.944
Generation: 540. 正答率(トレーニング) = 0.9732
                : 540. 正答率(テスト) = 0.945
Generation: 550. 正答率(トレーニング) = 0.9766
                : 550. 正答率(テスト) = 0.954
Generation: 560. 正答率(トレーニング) = 0.979
                : 560. 正答率(テスト) = 0.946
Generation: 570. 正答率(トレーニング) = 0.9816
                : 570. 正答率(テスト) = 0.957
Generation: 580. 正答率(トレーニング) = 0.9806
                : 580. 正答率(テスト) = 0.958
Generation: 590. 正答率(トレーニング) = 0.981
                : 590. 正答率(テスト) = 0.952
Generation: 600. 正答率(トレーニング) = 0.9798
                : 600. 正答率(テスト) = 0.957
Generation: 610. 正答率(トレーニング) = 0.9798
                : 610. 正答率(テスト) = 0.944
Generation: 620. 正答率(トレーニング) = 0.9846
                : 620. 正答率(テスト) = 0.952
Generation: 630. 正答率(トレーニング) = 0.985
                : 630. 正答率(テスト) = 0.949
Generation: 640. 正答率(トレーニング) = 0.9846
                : 640. 正答率(テスト) = 0.958
Generation: 650. 正答率(トレーニング) = 0.9876
                : 650. 正答率(テスト) = 0.958
Generation: 660. 正答率(トレーニング) = 0.9852
                : 660. 正答率(テスト) = 0.948
Generation: 670. 正答率(トレーニング) = 0.9864
                : 670. 正答率(テスト) = 0.951
Generation: 680. 正答率(トレーニング) = 0.9772
                : 680. 正答率(テスト) = 0.942
Generation: 690. 正答率(トレーニング) = 0.9872
                : 690. 正答率(テスト) = 0.956
Generation: 700. 正答率(トレーニング) = 0.9898
                : 700. 正答率(テスト) = 0.957
Generation: 710. 正答率(トレーニング) = 0.99
                : 710. 正答率(テスト) = 0.955
Generation: 720. 正答率(トレーニング) = 0.9912
                : 720. 正答率(テスト) = 0.958
Generation: 730. 正答率(トレーニング) = 0.9888
                : 730. 正答率(テスト) = 0.96
Generation: 740. 正答率(トレーニング) = 0.9916
                : 740. 正答率(テスト) = 0.957
Generation: 750. 正答率(トレーニング) = 0.9896
                : 750. 正答率(テスト) = 0.959
Generation: 760. 正答率(トレーニング) = 0.99
                : 760. 正答率(テスト) = 0.961
Generation: 770. 正答率(トレーニング) = 0.9908
                : 770. 正答率(テスト) = 0.955
Generation: 780. 正答率(トレーニング) = 0.9904
                : 780. 正答率(テスト) = 0.959
Generation: 790. 正答率(トレーニング) = 0.9892
                : 790. 正答率(テスト) = 0.956
Generation: 800. 正答率(トレーニング) = 0.9916
                : 800. 正答率(テスト) = 0.957
Generation: 810. 正答率(トレーニング) = 0.9928
                : 810. 正答率(テスト) = 0.966
Generation: 820. 正答率(トレーニング) = 0.9924
                : 820. 正答率(テスト) = 0.963
Generation: 830. 正答率(トレーニング) = 0.9892
                : 830. 正答率(テスト) = 0.956
Generation: 840. 正答率(トレーニング) = 0.9916
                : 840. 正答率(テスト) = 0.964
Generation: 850. 正答率(トレーニング) = 0.992
                : 850. 正答率(テスト) = 0.964
Generation: 860. 正答率(トレーニング) = 0.9914
                : 860. 正答率(テスト) = 0.961
Generation: 870. 正答率(トレーニング) = 0.9934
                : 870. 正答率(テスト) = 0.958
Generation: 880. 正答率(トレーニング) = 0.9932
                : 880. 正答率(テスト) = 0.957
Generation: 890. 正答率(トレーニング) = 0.9934
                : 890. 正答率(テスト) = 0.961
Generation: 900. 正答率(トレーニング) = 0.995
                : 900. 正答率(テスト) = 0.96
Generation: 910. 正答率(トレーニング) = 0.9942
                : 910. 正答率(テスト) = 0.961
Generation: 920. 正答率(トレーニング) = 0.9952
                : 920. 正答率(テスト) = 0.959
Generation: 930. 正答率(トレーニング) = 0.994
                : 930. 正答率(テスト) = 0.963
Generation: 940. 正答率(トレーニング) = 0.9946
                : 940. 正答率(テスト) = 0.964
Generation: 950. 正答率(トレーニング) = 0.9956
                : 950. 正答率(テスト) = 0.962
Generation: 960. 正答率(トレーニング) = 0.9952
                : 960. 正答率(テスト) = 0.958
Generation: 970. 正答率(トレーニング) = 0.997
                : 970. 正答率(テスト) = 0.971
Generation: 980. 正答率(トレーニング) = 0.9976
                : 980. 正答率(テスト) = 0.964
Generation: 990. 正答率(トレーニング) = 0.996
                : 990. 正答率(テスト) = 0.961
Generation: 1000. 正答率(トレーニング) = 0.9952
                : 1000. 正答率(テスト) = 0.961