質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.34%

state_dictを利用してモデルのパラメータを取得し評価(accuracy)を求めたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 313

oinari03

score 55

やりたいこと

犬と猫を見分ける画像分類問題を取り扱うために、pytorchで実装を試みています。

このとき、データセットの作成、モデルの作成、lossの作成から学習部分の実装は大体終わっていて、次にaccuracyを求めたいと考えています。いわゆる精度&推論というところだと思います。
このときのデータセット(256x256x3)として、少ないですが、以下のようになっています。

├─animal_dataset
   ├─train
   │  ├─cat(70枚くらい)
   │  └─dog(70枚くらい)
   └─val
       ├─cat(30枚くらい)
       └─dog(30枚くらい)


また、lossの収束としては、batchsize=4,epoch=100で以下のようなグラフに収束しました。
イメージ説明

本当にやりたいこと
ここからが本当にやりたいことです。
lossの値を出したように精度(accuracy)を確認したいです。

ただ、チュートリアルにあるようなコードを使っても正しいかどうかわからないという点にくろうしています。

そもそも評価ってどうすればいいのか。何をもとに評価しているのか...
そのため、以下に示すコードから間違っている点を指摘していただきたいです。

ソースコード

eval.py(実行コード)

import torch    
import torch.nn as nn
import torch.nn.functional as F

import torch.optim as optim
import model,dataset
from model import *

from tqdm import tqdm
from torch.autograd import Variable


import numpy as np
from matplotlib import pyplot as plt


device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

if __name__ == "__main__":
    # modelの定義
    model = model.Net().to(device)
    # 推論モード
    model.eval()

    # optimizerの定義
    optimizer = torch.optim.SGD(
         model.parameters(), lr=0.0001, momentum=0.9, weight_decay=0.0001)
    #パラメータの読み込み 
    model.load_state_dict(torch.load("model_cpu.pth", map_location=device))

    # validationのデータセット
    valid_dataset = dataset.MyDatasets(
        root_dir="./animal_dataset", 
        key="val", 
    )

    valid_loader = torch.utils.data.DataLoader(
        valid_dataset,
        batch_size=4, 
        shuffle= True
    )
    # 評価関数の作成

    correct = 0
    total = 0

    acc = []
    with torch.no_grad():
        for data in valid_loader:
            inputs, labels = data[0].to(device), data[1].to(device)
            # print("label={}".format(labels))
            # print("inputs={}".format(inputs))

            outputs = model(inputs).to(device)
            # print(outputs.data)
            # 確率
            _, predicted = torch.max(outputs.data, 1)
            # print(predicted)
            total += labels.size(0)
            # print(total)
            correct += (predicted == labels).sum().item()
            # print(correct)
        acc.append(float(correct/total))

    print("Accuracy of the network on the 100 test images: %d %%" % (100*correct/total))


    def plot_acc(acc):
        fig, ax = plt.subplots()
        epochs = np.arange(1,len(acc)+1)

        ax.set_title("accuracy")
        ax.plot(epochs, acc)
        ax.set_xlabel("Epoch")
        print("epochs: {}".format(epochs))
        print("acc: {}".format(acc))

        plt.savefig("acc.png")


    plot_acc(acc)

出力結果

様々な点にprintを入れて出力を見ています。

・精度画像の出力
なかなかにひどいです。(確実に間違っているのですがどこが違うのかわかりません。)
イメージ説明

・printの結果
eval.pyの以下にprintを入れた時の出力結果です。

_, predicted = torch.max(outputs.data, 1)
print(predicted)

torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([4, 984064])
tensor([0, 0, 0, 0], device='cuda:0')
torch.Size([2, 984064])
tensor([0, 0], device='cuda:0')
Accuracy of the network on the 100 test images: 50 %


なんかこの0が並んでるのが違和感です。

追記

プログラムにepochsとaccの内容をprintしました。

.
.
.
torch.Size([2, 984064])
Accuracy of the network on the 100 test images: 50 %
epochs: [1]
acc: [0.5]


このようになっていました。

追記2

lossを収束させたコード(train.py)

import torch    
import torch.nn as nn
import torch.nn.functional as F

import torch.optim as optim
import model,dataset
from model import *

from tqdm import tqdm
from torch.autograd import Variable


import numpy as np
from matplotlib import pyplot as plt





device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')


# 上でgpuの設定device
if __name__ == "__main__":
    # modelの定義
    model = model.Net().to(device)
    model.train()

    # optimizerの定義
    optimizer = torch.optim.SGD(
        model.parameters(), lr=0.0001, momentum=0.9, weight_decay=0.0001)

    # datasetの定義
    # training
    train_dataset = dataset.MyDatasets(
        root_dir="./animal_dataset", 
        key="train", 
    )
    train_loader = torch.utils.data.DataLoader(
        train_dataset,
        batch_size=4, 
        shuffle= True
    )

    # validation
    valid_dataset = dataset.MyDatasets(
        root_dir="./animal_dataset", 
        key="val", 
    )

    valid_loader = torch.utils.data.DataLoader(
        valid_dataset,
        batch_size=4, 
        shuffle= True
    )

    # batch = len(next(iter(train_loader))) #2
    # for i in train_loader:
    #     print(i)
    # for i in valid_loader:
    #     print(i)

    # iterationの確定
    sample_size = len(train_dataset) #129
    # num_iters = sample_size // 4  

    #loss 
    criterion = nn.CrossEntropyLoss().to(device)

    losses = [] 
    #start epoch
    epoch_num = 100
    for epoch in range(epoch_num):  # loop over the dataset multiple times
        epoch_loss = 0
        for i, data in enumerate(train_loader, 0):
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data[0].to(device), data[1].to(device)
            # print("label={}".format(labels))
            # print("inputs={}".format(inputs))

            outputs = model(inputs).to(device)
            # print(model)
            # with torch.no_grad(): 
            loss = criterion(outputs, labels)
            # loss.requires_grad = True
            # print(loss)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            # print(loss)
            epoch_loss += loss


        losses.append(np.mean(float(epoch_loss)))

    print('Finished Training')

    """
    層を出力
    おそらくtorch.save
    torch.loadをつかう??
    """
    # for param_tensor in model.state_dict():
    print(model.state_dict()['conv1.weight'])
    model = model.to('cpu')
    torch.save(model.state_dict(), 'model_cpu.pth')

    # lossグラフ描画
    def plot_history(losses):
        fig, ax = plt.subplots()

        epochs = np.arange(1, len(losses) + 1)

        # 損失の推移
        ax.set_title("Loss")
        ax.plot(epochs, losses)
        ax.set_xlabel("Epoch")

        plt.savefig('loss.png')


    plot_history(losses)


上記で、lossを収束させています。modelはconv1,conv2の2層です。

ただ、model_cpu.pthの容量が大きすぎて中身見れないみたいです。

追記3

eval.pyとtrain.pyでパラメータの保存とloadを書いた時の出力accを表示します。

epochs: [1]
acc: [0.5740740740740741]

困っていること

以上の結果から正しい精度&推論を出すためのプログラムの書き方を教えてほしいです。
plot_accのグラフを書く関数がおかしいのか。それとももともと評価するところのプログラムがおかしいのかわかりません。どうかご指摘下ればと思います。

また、初心者故にわからないところ、投稿に不備があるかと思います。
その時はご指摘していただけると助かります。

どうか、今回の精度を出す実装をお手伝いくださればと思います。
よろしくお願いいたします。

参考

ciefer10のチュートリアル。精度のほとんどの部分を参考にしました。
torch.maxの使い方について
グラフの描画から精度まで参考にした記事

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • oinari03

    2020/09/11 14:08 編集

    ご指摘ありがとうございます。
    精度が0.5というのはなかなか悲しいものなのでもう少し上げた精度が欲しいです。
    そのために、モデルのパラメータを入れる必要があるという解釈で大丈夫でしょうか?

    投稿の質問タイトルの内容の処理をかいてないのが申し訳ないのですが、

    今回投稿した意図は、state_dictでモデルのパラメータをloadすれば推論ができる。と見たが、具体的なやり方がわからない。

    いったん、モデルパラメータを加えず、チュートリアル通りに推論の処理を書いた次第です。

    例えば、state_dictに関しては、以下のコードで
    for param_tensor in model.state_dict():
    print(param_tensor, "\t", model.state_dict()[param_tensor].size())

    以下の出力が得られました。

    ```
    conv1.weight torch.Size([128, 3, 3, 3])
    conv1.bias torch.Size([128])
    conv2.weight torch.Size([256, 128, 3, 3])
    conv2.bias torch.Size([256])
    fc1.weight torch.Size([120, 984064])
    fc1.bias torch.Size([120])
    fc2.weight torch.Size([84, 120])
    fc2.bias torch.Size([84])
    fc3.weight torch.Size([2, 84])
    fc3.bias torch.Size([2])
    ```
    これはモデルのパラメータを表していることかと思いますが、どのように推論に落とし込むのかに困っています。

    結果的に精度向上になるよな改善点があればご指摘願えればと思います。

    キャンセル

  • tiitoi

    2020/09/11 14:26

    > モデルのパラメータを入れる必要があるという解釈で大丈夫でしょうか?

    パラメータを入れるというか、質問のコードはモデルだけ作って学習されていない状態です。
    Loss のグラフを作ったということは他のコードで学習したのだと思いますが、だとしたら、そのときに重みを保存して、推論するときはその重みを読み込むという処理が必要です。
    質問のコードにはそれが見当たりません。

    キャンセル

  • oinari03

    2020/09/11 15:18 編集

    根本的にモデルのパラメータをどう処理すればいいかというのが頭に入ってなかったです。

    いろいろご指摘いただけたおかげで多分ですが、やっとやろうとしていることを理解しました。
    (見当違いだった気がします....)

    自分がわからないのはlossを作ったコードから、モデルのパラメータを保存して、今回のeval.pyに引継ぎをする方法ということでした。

    失礼かと思いますが、
    train.py(lossを収束させたコード)と、自分なりにパラメータを保存するコードをtrain/evalそれぞれに追記いたしましたので、学習後のパラメータを保存して、evalに引き継ぎそれをもとに推論する方法を教えていただけないでしょうか...

    キャンセル

回答 1

checkベストアンサー

0

以下をコードに組み込んでください。

学習時に重みを保存するコード

# モデルを保存する。
torch.save(model.state_dict(), "model.pth")

推論時に重みを読み込むコード

# 保存したモデルを読み込む。
model.load_state_dict(torch.load("model.pth"))

Pytorch - モデルをファイルに保存する方法

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/09/11 17:50

    お答えいただきありがとうございます。
    添付ファイルと解答通り修正して、追記いたしました。
    修正後のコードを質問にも追記していますが、手元で確認したところ、精度が
    ```
    Accuracy of the network on the 100 test images: 57 %
    epochs: [1]
    acc: [0.5740740740740741]
    ```
    57%と低い値でした、こちらをもう少し精度よくするにはどんな工夫がありますでしょうか?

    キャンセル

  • 2020/09/11 18:22

    * 学習データを増やす
    * オーグメンテーションする
    * 学習済みモデルを使う
    etc...

    一概にこれをすれば良くなるというのはないので、いろいろ試すしかないですね

    キャンセル

  • 2020/09/11 21:27

    なるほどです。
    いつもありがとうございます。会話の中からいろいろ勉強させていただいています。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る