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

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

ただいまの
回答率

88.81%

CNN回帰モデルの学習を収束させたい

受付中

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 2,426

mkuri

score 10

CNN回帰モデルの学習を収束させたい

以下の画像を入力として数値を出力するCNN回帰モデルを作りたい。
画像の下に記載されている数字がラベルになります。
inputs

このように差がはっきりと分かる入力画像に対してだと、ほぼ100%の正解率になると思っているのですが、
学習結果が収束しません。
改善点をアドバイスいただけるとありがたいです。

発生している問題

損失が以下のように推移し、約60epoch移行は精度が向上しません。
losses
200epoch目の結果は以下のような状態で、正解ラベルが0.1から0.9の間だとすると、ほとんど正解とは言えない状況です。
(outputsがモデルの出力結果、labelsが正解データ)

outputs: tensor([[0.4102]], grad_fn=<AddmmBackward>)
labels: tensor([0.9000])
loss: 0.2399456799030304
outputs: tensor([[0.5098]], grad_fn=<AddmmBackward>)
labels: tensor([0.1000])
loss: 0.16796395182609558
outputs: tensor([[0.2965]], grad_fn=<AddmmBackward>)
labels: tensor([0.6000])
loss: 0.09208495169878006
outputs: tensor([[0.3554]], grad_fn=<AddmmBackward>)
labels: tensor([0.3000])
loss: 0.003064962802454829
[200,     4] loss: 0.126

該当のソースコード

  • 画像を一枚ずつロードし、2乗誤差を計算してモデルをアップデート
  • モデルはAlexnetの最後にfc層を付け加えて、回帰モデルにしたもの
  • optimizerはAdamを使用
import pandas as pd
from PIL import Image
import matplotlib.pyplot as plt
import csv

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import models, transforms
from torch.utils.data import Dataset, DataLoader, random_split

from models.alexnet_reg import AlexNet


class ImageDataset(Dataset):
    def __init__(self, csv_file, transform=None):
        self.csv = pd.read_csv(csv_file)
        self.transform = transform

    def __len__(self):
        return len(self.csv)

    def __getitem__(self, idx):
        img_path = self.csv.at[idx.item(), 'img']
        label = self.csv.at[idx.item(), 'mu']
        img = Image.open(img_path)

        if self.transform:
            img = self.transform(img)

        return img, label


def train(model, train_dataset):
    n_epoch = 200
    train_loader = DataLoader(train_dataset, batch_size=1, shuffle=True, num_workers=1)

    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters())
    model.train()
    losses = []

    for epoch in range(n_epoch):
        running_loss = 0.0
        for i, data in enumerate(train_loader, 0):
            inputs, labels = data
            optimizer.zero_grad()
            labels = labels.type(torch.FloatTensor)

            outputs = model(inputs)
            print('outputs:', outputs)
            print('labels:', labels)
            loss = criterion(outputs, labels)
            print('loss:', loss.item())
            loss.backward()
            optimizer.step()

            running_loss += loss.item()
            # if i % 10 == 9:
            #     print('[%d, %5d] loss: %.3f' %
            #           (epoch + 1, i + 1, running_loss / 10))
            #     running_loss = 0.0

        print('[%d, %5d] loss: %.3f' %
              (epoch + 1, i + 1, running_loss / 4))
        losses.append(running_loss/4)

    with open('losses.csv', 'w') as f:
        writer = csv.writer(f, lineterminator='/n')
        writer.writerow(losses)

    plt.plot(losses)
    plt.title('MSELoss')
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()

def main():
    transform = transforms.Compose(
        [transforms.Resize(224),
         transforms.ToTensor()]
    )
    dataset = ImageDataset(csv_file='./data/dataset00/dataset00.csv', transform=transform)

    train_size = int(1.0 * len(dataset))
    valid_size = len(dataset) - train_size
    train_dataset, valid_dataset = random_split(dataset, [train_size, valid_size])
    print('train_size:', train_size, ', valid_size:', valid_size)

    model = AlexNet()
    train(model, train_dataset)


if __name__ == '__main__':
    main()
import torch.nn as nn
from torchvision import models


class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.alexnet = models.alexnet(pretrained=True)
        self.classifier2 = nn.Sequential(
            nn.Dropout(),
            nn.Linear(1000, 256),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(256, 64),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(64, 16),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(16, 1)
        )

    def forward(self, x):
        x = self.alexnet(x)
        x = self.classifier2(x)
        return x

試したこと

ラベルをすべて正の数値にしてみたところ、より発散した。
losses_posi

補足情報(FW/ツールのバージョンなど)

torch==1.0.1.post2
torchvision==0.2.2.post3

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

+1

まず問題設定と入力データがうまく適合していないので、このあたりを改善しないとうまくいかないかと思います。具体的には、①本件を他クラス分類とする、②もっと簡単な図形と数値の関係とする(例えば、四角形に占める黒部分に係数を乗じたものを数値とする)、③もっと多数のデータを入手する、といったことが考えられます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/03/26 11:58

    ①他クラス分類として、最終層の出力を4つにして誤差関数にSoftmax Cross Entropyを使って学習すれば、このモデルでテストデータに関して収束することを確認しています。今回は中間値も出力したいので、回帰問題として扱いたいです。
    ②CNN+FC層で出力しているのですが、このような関係とすればうまくいく理由はありますでしょうか?
    ③まずテストデータだけを使っている状況で収束しません。汎化性能は今は考慮していません。

    キャンセル

  • 2019/03/26 20:58

    中間値が何を意味しているのかわかりませんが、識別モデルの隠れ層の出力値なのであれば、素直に取り出す方法を考えたほうがいいかと思います。
    私はpytorchを使ったことがないので何とも言えませんが、tensorflowだとどのレイヤーの値でも学習過程・予測過程に関係なく取り出すことができるので、おそらくできるのではないでしょうか

    キャンセル

0

PyTorchはよくわかりませんが。
どれくらいの画像を用意しましたか?
変数labelsに入る具体的な値はなんですか?これ、「ラベル」ではなく、「ラベルのクラス番号」のはずです。ラベルが「犬」「猫」なら、計算できないでしょう??

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/03/22 18:43

    汎化性能は求めておらず、上記4画像から回帰分析結果(0.1や0.9などの数値)が出力されるモデルをまずは作ってみることを目的にしているため、多数の画像は必要がないかなと思っています。その分epochを回します。

    labelsに入る具体的な値は0.1, 0.3, 0.6, 0.9です。分類問題ではなく回帰問題としたいので、正解ラベルをそのまま入力しています。
    モデルの最後の全結合層を1つになるまで結合し、損失関数を2乗誤差にすれば回帰問題として扱えると思っています。

    キャンセル

  • 2019/03/27 15:07

    何を正解としていますか。ある程度の幅が出るので、どこまでの幅を許容するか、だと思います。

    VGGは、自然な写真を元に学習しているので、グラフィックツールで作った境界が発揮知りているものから特徴を抽出するのは苦手かもしれません。可視化手法を使って、判断の元を見るのも、ひとつの調査方法だと思います。

    キャンセル

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

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

関連した質問

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