前提
研究で画像からその物体の固有値を算出するCNNの構築を試みています.入力として224*224ixelの画像を,出力として一次モードの固有値をもつネットワークです.構造はVGG19を参考に作り,損失関数にはMAEを使用しています.損失関数のhistoryを見る限り学習が進んでいるようにみえるのですが,学習終了後にいくつかのデータを与えても同じ出力しか返ってきません.おそらく重みパラメータが更新されていないorどこかでリセット(すべての重みが0でその先の出力が同じになっている)のではないかと疑っています.
試しに
model.state_dict()
でパラメータの中身を確認してみましたが,原因が見つかりませんでした...
実現したいこと
各入力に対して異なる出力値を受け取ること.
発生している問題・エラーメッセージ
以下はいくつかのデータを使って推論した出力です.複数のデータに対して同じ出力となっています.
>>> y_eval tensor([[-0.0040], [-0.0040], [-0.0040], [-0.0040], 中略 [-0.0040], [-0.0040], [-0.0040], [-0.0040]], device='cuda:0', grad_fn=<AddmmBackward0>)
該当のソースコード
ベタ付ですみません..
Python
1import numpy as np 2import torch 3import torch.nn as nn 4import torch.nn.functional as F 5import torch.optim as optim 6from torchsummary import summary 7import torchvision 8from sklearn.model_selection import train_test_split 9import matplotlib.pyplot as plt 10import time 11 12#Parameter 13DATA_DIR = "../../../pinn/data/supervised/" 14TEST_SIZE = 0.2 15BATCH_SIZE = 1 16LEARNING_RATE = 0.001 17EPOCH = 200 18PIXEL = 224 19 20#Select device 21def get_device(use_gpu): 22 if use_gpu and torch.cuda.is_available(): 23 torch.backends.cudnn.deterministic = True 24 return torch.device("cuda") 25 26 else: 27 return torch.device("cpu") 28device = get_device(use_gpu=True) 29 30 31#Input & Output 32X = np.load(DATA_DIR + "input.npy") 33X = np.reshape(X, (X.shape[0], 1, PIXEL, PIXEL)) 34X = torch.tensor(X, dtype=torch.float) 35y = np.load(DATA_DIR + "output.npy") 36y = np.reshape(y, (y.shape[0], 1)) 37y = torch.tensor(y, dtype=torch.float) 38X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=0) 39 40train_dataset = torch.utils.data.TensorDataset(X_train, y_train) 41train_dataset = torch.utils.data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True) 42test_dataset = torch.utils.data.TensorDataset(X_test, y_test) 43test_dataset = torch.utils.data.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=True) 44 45#Make Model 46class VGG19(nn.Module): 47 def __init__(self, input_image_channels): 48 super(VGG19, self).__init__() 49 self.conv1 = nn.Conv2d(input_image_channels, 64, kernel_size=(3, 3), stride=(1, 1), padding=1) 50 self.conv2 = nn.Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=1) 51 self.conv3 = nn.Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=1) 52 self.conv4 = nn.Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=1) 53 self.conv5 = nn.Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=1) 54 self.conv6_1 = nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=1) 55 self.conv6_2 = nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=1) 56 self.conv6_3 = nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=1) 57 self.conv7 = nn.Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 58 self.conv8_1 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 59 self.conv8_2 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 60 self.conv8_3 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 61 self.conv8_4 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 62 self.conv8_5 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 63 self.conv8_6 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 64 self.conv8_7 = nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=1) 65 66 self.max_pool_1 = nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) 67 self.max_pool_2 = nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) 68 self.max_pool_3 = nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) 69 self.max_pool_4 = nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) 70 self.max_pool_5 = nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) 71 72 # self.bn1 = nn.BatchNorm2d(64) 73 # self.bn2 = nn.BatchNorm2d(128) 74 # self.bn3 = nn.BatchNorm2d(256) 75 # self.bn4 = nn.BatchNorm2d(512) 76 77 self.fc1 = nn.Linear(56*56*128, 1000) 78 self.fc2 = nn.Linear(1000, 100) 79 self.fc3 = nn.Linear(100, 10) 80 self.fc4 = nn.Linear(10, 1) 81 # self.fc5 = nn.Linear(16, 1) 82 83 def forward(self, x): 84 x = F.relu(self.conv1(x)) 85 x = F.relu(self.conv2(x)) 86 x = self.max_pool_1(x) 87 88 x = F.relu(self.conv3(x)) 89 x = F.relu(self.conv4(x)) 90 x = self.max_pool_2(x) 91 92 x = F.relu(self.conv5(x)) 93 x = F.relu(self.conv6_1(x)) 94 x = F.relu(self.conv6_2(x)) 95 x = F.relu(self.conv6_3(x)) 96 x = self.max_pool_3(x) 97 98 x = F.relu(self.conv7(x)) 99 x = F.relu(self.conv8_1(x)) 100 x = F.relu(self.conv8_2(x)) 101 x = F.relu(self.conv8_3(x)) 102 x = self.max_pool_4(x) 103 104 x = F.relu(self.conv8_4(x)) 105 x = F.relu(self.conv8_5(x)) 106 x = F.relu(self.conv8_6(x)) 107 x = F.relu(self.conv8_7(x)) 108 x = self.max_pool_5(x) 109 110 x = x.view(-1, 56*56*128) 111 x = F.relu(self.fc1(x)) 112 x = F.relu(self.fc2(x)) 113 x = F.relu(self.fc3(x)) 114 x = self.fc4(x) 115 return x 116 117class MAELoss(nn.Module): 118 def __init__(self): 119 super(MAELoss, self).__init__() 120 121 def forward(self, outputs, targets): 122 123 loss = torch.mean(torch.abs(outputs - targets)) 124 return loss 125 126# Model Check 127model = VGG19(1).to(device) 128summary(model, input_size=(1, 224, 224)) 129 130# Loss & Optimizer 131loss_fn = MAELoss() 132optimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE) 133 134train_loss_value=[] 135train_eval_value=[] 136test_loss_value=[] 137test_eval_value=[] 138 139# Compile 140for epoch in range(EPOCH): 141 model.train() 142 running_train_loss = 0.0 143 144 torch.cuda.synchronize() 145 start = time.time() 146 with torch.set_grad_enabled(True): 147 for (input, true) in train_dataset: 148 input, true = input.to(device), true.to(device) 149 optimizer.zero_grad() 150 output = model(input) 151 loss = loss_fn(output, true) 152 running_train_loss += loss.item() 153 loss.backward() 154 optimizer.step() 155 156 train_loss_value.append(running_train_loss * BATCH_SIZE / len(train_dataset)) 157 158 model.eval() 159 running_test_loss = 0.0 160 with torch.set_grad_enabled(False): 161 for (input, true) in test_dataset: 162 input, true = input.to(device), true.to(device) 163 optimizer.zero_grad() 164 output = model(input) 165 loss = loss_fn(output, true) 166 running_test_loss += loss.item() 167 168 test_loss_value.append(running_test_loss * BATCH_SIZE / len(test_dataset)) 169 170 torch.cuda.synchronize() 171 elapsed_time = time.time() - start 172 y_eval = model(X.to(device)) 173 print(y_eval) 174 print('#EPOCH:{}\ttrain loss: {}\tvalid loss: {}\ttime: {}'.format(epoch, running_train_loss * BATCH_SIZE / len(train_dataset), running_test_loss * BATCH_SIZE / len(test_dataset), elapsed_time)) 175 176model.eval() 177y_eval = model(X.to(device)) 178print(y_eval)
試したこと
構造を2層の全結合層からなる簡単なNNに変更したり,最適化手法・損失関数等を変更したりさまざまなパラメータをいじりましたが,出力値はすべてのデータに対して同じになってしまいました.そのときの重みやバイアスを見たところ最後のバイアスのみ値が変化し,それ以外の重みやバイアスは全て0となっていました.おそらく学習の過程でパラメータの更新がうまく行っていないのだと予測してはいますがどこで問題が起きているのか皆目検討も付きません.よろしくお願いいたします.
補足情報(FW/ツールのバージョンなど)
PyTorch 1.12.0+cu116
Python 3.8.10