

以下のプログラムは手書き数字データセットMNISTを10クラスに識別するものです。  
正解数と識別率をevaluate関数で求めているのですが、どのデータがどのクラスに分類されたかを調べるために混同行列を用いてみました。しかし、このプログラムを実行すると、ncorrectの値と混同行列の体格成分の和が一致しません。どうすれば一致するでしょうか。

* * *

**学習用プログラム**

[code]

    import os
    import sys
    import numpy as np
    import datetime
    
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.optim as optim
    
    import getdata
    
    PATH_MNIST = './mnist'
    PATH_RESULT = 'result_mnist'
    
    ### definition of the network
    #
    class NN(nn.Module):
    
        def __init__(self):
            super(NN, self).__init__()
            Hin1, Win1 = 28, 28
            self.conv1 = nn.Conv2d(1, 32, kernel_size = 5)
            Hout1, Wout1 = _output_shape(Hin1, Win1, self.conv1) # 24 x 24
            self.conv2 = nn.Conv2d(self.conv1.out_channels, 64, kernel_size = 5) 
            Hout2, Wout2 = _output_shape(Hout1//2, Wout1//2, self.conv2) # 8 x 8
            self.fc1 = nn.Linear(64 * (Hout2//2) * (Wout2//2), 1024) # 64 x 4 x 4
            self.fc2 = nn.Linear(self.fc1.out_features, 10)
    
        def forward(self, X):
            X = F.relu(F.max_pool2d(self.conv1(X), 2))
            X = F.relu(F.max_pool2d(self.conv2(X), 2))
            X = X.view(-1, self.fc1.in_features)
            X = F.relu(self.fc1(X))
            X = self.fc2(X)
            return F.log_softmax(X, dim = 1)
    
    def _output_shape(Hin, Win, conv2d):
            Hout = int(np.floor((Hin + 2 * conv2d.padding[0] - conv2d.dilation[0] * (conv2d.kernel_size[0] - 1) - 1) / conv2d.stride[0] + 1))
            Wout = int(np.floor((Win + 2 * conv2d.padding[1] - conv2d.dilation[1] * (conv2d.kernel_size[1] - 1) - 1) / conv2d.stride[1] + 1))
            return Hout, Wout
    
    def evaluate(model, X, Y, bindex):
        A = np.zeros((10,10))
        nbatch = bindex.shape[0]
        loss = 0
        ncorrect = 0
        with torch.no_grad():
            for ib in range(nbatch):
                ii = np.where(bindex[ib, :])[0]
                output = model(X[ii, ::])
                
                #loss += F.nll_loss(output, Y[ii], size_average=False).item()
                loss += F.nll_loss(output, Y[ii], reduction='sum').item()
                pred = output.max(1, keepdim=True)[1] # argmax of the output #ここで分類結果を求める
                for i in range(100):
                    A[Y[i]][pred[i]] += 1
                ncorrect += pred.eq(Y[ii].view_as(pred)).sum().item()
                
    
        loss /= X.shape[0]
        acc = ncorrect / X.shape[0]
        print(A)
        print(np.trace(A),np.sum(A))
        return loss, acc, ncorrect
    
        
    
    if __name__ == '__main__':
    
        ### device
        #
        use_gpu_if_available = True
        if use_gpu_if_available and torch.cuda.is_available():
            device = torch.device('cuda')
        else:
            device = torch.device('cpu')
        print('# using', device)
        
        ### reading and preparing the training data
        #
        data = getdata.Data(PATH_MNIST, nV = 10000)
    
        D = data.nrow * data.ncol
        K = data.nclass
        datLraw, labL = data.getData('L')
        datL = datLraw.reshape((-1, 1, data.nrow, data.ncol))
        datVraw, labV = data.getData('V')
        datV = datVraw.reshape((-1, 1, data.nrow, data.ncol))
        NV = datV.shape[0]
        NL = datL.shape[0]
    
        ### to torch.Tensor
        #
        XL = torch.from_numpy(datL.astype(np.float32)).to(device)
        YL = torch.from_numpy(labL).to(device)
        XV = torch.from_numpy(datV.astype(np.float32)).to(device)
        YV = torch.from_numpy(labV).to(device)
        
        ### initializing the network
        #
        Seed = 20
        torch.manual_seed(Seed)
        nn = NN()
        model = nn.to(device)
        print(nn)
        optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum = 0.9)
        print(optimizer)
    
        ### training
        #
        batchsize = 100
        bindexL = getdata.makeBatchIndex(NL, batchsize)
        nbatchL = bindexL.shape[0]
        bindexV = getdata.makeBatchIndex(NV, batchsize)
        nbatchV = bindexV.shape[0]
    
        nitr = 10001
        nd = 0
        start = datetime.datetime.now()
    
        for i in range(nitr):
    
            if (i != 0) and (i % 500 == 0):
                model.eval()  # setting the module in evaluation mode
                lossL, accL, ncorrectL = evaluate(model, XL, YL, bindexL)
                #lossV, accV = evaluate(model, XV, YV, bindexV)
                print('#epoch{}'.format(nd/NL), end = ' 　　')
                print('{:.4f} {:.2f} {}'.format(lossL, accL*100, ncorrectL))
                fnModel = os.path.join(PATH_RESULT, os.path.splitext(sys.argv[0])[0] + 'seed{}-{}'.format(str(Seed),str(int(nd/NL))) + '-params.pickle')
                with open(fnModel, mode = 'wb') as f:
                    torch.save(model.state_dict(), f)
            model.train()  # setting the module in training mode
            ib = np.random.randint(0, nbatchL)
            ii = np.where(bindexL[ib, :])[0]
            optimizer.zero_grad()
            output = model(XL[ii, :])
            loss = F.nll_loss(output, YL[ii])
            loss.backward()
            optimizer.step()
    
            nd += ii.shape[0]
    
        print('# elapsed time: ', datetime.datetime.now() - start)
        
        ### saving the model
        #
        fnModel = os.path.join(PATH_RESULT, os.path.splitext(sys.argv[0])[0] + 'seed{}-params.pickle'.format(Seed))
        with open(fnModel, mode = 'wb') as f:
            torch.save(model.state_dict(), f)
            print('# The model is saved to ', fnModel)
    
[/code]

* * *

**テスト用プログラム**

[code]

    import os
    import sys
    import pickle
    import numpy as np
    import datetime
    
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    import getdata
    import cnnL
    
    
    if __name__ == '__main__':
    
        ### device
        #
        use_gpu_if_available = True
        if use_gpu_if_available and torch.cuda.is_available():
            device = torch.device('cuda')
        else:
            device = torch.device('cpu')
        print('# using', device)
    
        ### initializing the network
        #
        fnModel = os.path.join(cnnL.PATH_RESULT,'cnnL-params.pickle')
        torch.manual_seed(0)
        nn = cnnL.NN()
        with open(fnModel, mode = 'rb') as f:
            nn.load_state_dict(torch.load(f))
        model = nn.to(device)
    
        ### reading and preparing the training data
        #
        data = getdata.Data(cnnL.PATH_MNIST, nV = 10000)
    
        D = data.nrow * data.ncol
        K = data.nclass
        datTraw, labT = data.getData('T')
        datT = datTraw.reshape((-1, 1, data.nrow, data.ncol))
        NT = datT.shape[0]
    
        ### to torch.Tensor
        #
        XT = torch.from_numpy(datT.astype(np.float32)).to(device)
        YT = torch.from_numpy(labT).to(device)
        
        ### evaluation
        #
        batchsize = 100
        bindexT = getdata.makeBatchIndex(NT, batchsize)
        model.eval()  # setting the module in evaluation mode
        start = datetime.datetime.now()
        lossT, accT = cnnL.evaluate(model, XT, YT, bindexT)
        print('# elapsed time: ', datetime.datetime.now() - start)
        print('{:.4f} {:.2f}'.format(lossT, accT*100))
    
    
[/code]


CC BY-SA 4.0
2020-12-21T07:11:54.127
72791
2021-01-23T22:13:54.990
43170
question
python 機械学習 numpy pytorch
pytorchのoutputについて


コードにキャッチアップできず申し訳ないのですが、問題を切り分けるための提案をさせてください。

まず、原因はevaluateの実装にある可能性が高いので、nnの実装からこれを切り離して考えます。

関数evaluateの引数X, Y, bindex、および関数の中のoutput = model(X[ii,
::])を手で作った簡単な配列に置き換え、混合行列とncorrectの出力を見てみます。どちらが期待される値と異なっていますか？

* * *

よりよい回答を得るために、こちらも参考にしてみてください。

  * [再現可能な短いサンプルコードの書き方 - stackoverflow](https://ja.stackoverflow.com/help/minimal-reproducible-example)


CC BY-SA 4.0
2021-01-23T22:13:54.990
73536
2021-01-23T22:13:54.990
41038
72791
answer