質問編集履歴
1
書式の改善
title
CHANGED
File without changes
|
body
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
SSDを用いた物体検出を現在行っています。(初心者です)
|
3
3
|
コードは「物体検出とGAN、オートエンコーダー、画像処理入門」という本のダウンロードサービスから拝借しています。
|
4
4
|
#エラーが出たコード
|
5
|
+
```Python
|
5
6
|
%%time
|
6
7
|
'''
|
7
8
|
8. VOC2012データセットをSSDモデルで学習
|
@@ -14,7 +15,9 @@
|
|
14
15
|
optimizer, # オプティマイザー
|
15
16
|
num_epochs=num_epochs) # エポック数
|
16
17
|
|
18
|
+
```
|
17
19
|
ここでtrainは以下のコードで定義しています。
|
20
|
+
```Python
|
18
21
|
'''
|
19
22
|
7. SSDモデルの学習(パラメーターの更新)を行う関数
|
20
23
|
'''
|
@@ -30,10 +33,10 @@
|
|
30
33
|
optimizer(object): オプティマイザー
|
31
34
|
num_epochs(object): 学習回数
|
32
35
|
'''
|
33
|
-
SSDモデルでGPUを使用
|
36
|
+
# SSDモデルでGPUを使用
|
34
37
|
net.to(device)
|
35
38
|
|
36
|
-
ネットワークの構成に対して最適なアルゴリズムを見つけて高速化させる
|
39
|
+
# ネットワークの構成に対して最適なアルゴリズムを見つけて高速化させる
|
37
40
|
torch.backends.cudnn.benchmark = True
|
38
41
|
|
39
42
|
iteration = 1 # イテレーション(ステップ)カウンター
|
@@ -41,124 +44,203 @@
|
|
41
44
|
epoch_val_loss = 0.0 # 検証1エポックごとの損失和
|
42
45
|
logs = [] # 損失のログを記録するリスト
|
43
46
|
|
44
|
-
学習、または検証のエポックごとのループ
|
47
|
+
# 学習、または検証のエポックごとのループ
|
45
48
|
for epoch in range(num_epochs):
|
46
|
-
|
49
|
+
# 開始時刻を保存
|
47
50
|
t_epoch_start = time.time()
|
48
51
|
t_iter_start = time.time()
|
49
52
|
|
50
|
-
現在のエポック数を出力
|
53
|
+
# 現在のエポック数を出力
|
51
54
|
print('---------------------------------------')
|
52
55
|
print('Epoch {}/{}'.format(epoch+1, num_epochs))
|
53
56
|
print('---------------------------------------')
|
54
57
|
|
55
|
-
エポック10回につき検証を1回行う
|
58
|
+
# エポック10回につき検証を1回行う
|
56
59
|
for phase in ['train', 'val']:
|
57
|
-
エポックが10回に達するまではモデルを訓練モードにする
|
60
|
+
# エポックが10回に達するまではモデルを訓練モードにする
|
58
61
|
if phase == 'train':
|
59
62
|
net.train() # モデルを訓練モードにする
|
60
63
|
else:
|
61
|
-
|
64
|
+
# エポックが10回に達していたらモデルを検証モードにして検証開始
|
62
65
|
if((epoch+1) % 10 == 0):
|
63
66
|
net.eval() # モデルを検証モードにする
|
64
67
|
print('---------------------------------------')
|
65
68
|
print('(validation)')
|
66
69
|
else:
|
67
|
-
|
70
|
+
# 10回に達していなければ次のエポックに進む
|
68
71
|
continue
|
69
72
|
|
70
|
-
|
73
|
+
# 1ステップにおけるミニバッチを使用した学習または検証
|
71
|
-
|
74
|
+
# データローダーをイテレートしてミニバッチを抽出
|
72
75
|
for images, targets in dataloaders_dict[phase]:
|
73
|
-
画像データにデバイスを割り当てる
|
76
|
+
# 画像データにデバイスを割り当てる
|
74
77
|
images = images.to(device)
|
75
|
-
教師データ(正解BBoxのアノテーション情報)
|
78
|
+
# 教師データ(正解BBoxのアノテーション情報)
|
76
|
-
(バッチサイズ, 物体数, 5[xmin, ymin, xmax, ymax, label_index])
|
79
|
+
# (バッチサイズ, 物体数, 5[xmin, ymin, xmax, ymax, label_index])
|
77
|
-
|
80
|
+
# にデバイスを割り当てる
|
78
81
|
targets = [ann.to(device) for ann in targets]
|
79
82
|
|
80
|
-
|
83
|
+
# optimizerが保持する勾配を0で初期化(累積しないように)
|
81
84
|
optimizer.zero_grad()
|
82
85
|
|
83
|
-
|
86
|
+
# 順伝搬(forward)とバックプロパゲーション(訓練時のみ)
|
84
87
|
with torch.set_grad_enabled(phase == 'train'):
|
85
|
-
順伝搬(forward)を行って(loc, conf, dbox_list)を取得
|
88
|
+
# 順伝搬(forward)を行って(loc, conf, dbox_list)を取得
|
86
|
-
|
89
|
+
# ・locの出力(バッチサイズ, 8732, 4[Δcx, Δcy, Δw, Δh])
|
87
|
-
|
90
|
+
# ・confの出力(バッチサイズ, 8732, 21)
|
88
|
-
|
91
|
+
# ・DBoxの情報(8732, 4[cx, cy, width, height])
|
89
92
|
outputs = net(images)
|
90
93
|
|
91
|
-
|
94
|
+
# Positive DBoxのオフセット情報の損失平均
|
92
|
-
|
95
|
+
# ミニバッチにおけるPositive DBoxの確信度の損失平均
|
93
96
|
loss_l, loss_c = criterion(outputs, targets)
|
94
|
-
|
97
|
+
# 2つの損失を合計する
|
95
98
|
loss = loss_l + loss_c
|
96
99
|
|
97
|
-
|
100
|
+
# 訓練時はバックプロパゲーションによるパラメーター更新を行う
|
98
101
|
if phase == 'train':
|
99
102
|
loss.backward() # バックプロパゲーション
|
100
103
|
|
101
|
-
|
104
|
+
# 勾配が大きすぎると不安定になるので
|
102
|
-
|
105
|
+
# clipで勾配の上限を2.0に制限する
|
103
106
|
nn.utils.clip_grad_value_(net.parameters(),
|
104
107
|
clip_value=2.0)
|
105
|
-
|
108
|
+
# 勾配降下法の更新式を適用してバイアス、重みを更新
|
106
109
|
optimizer.step()
|
107
110
|
|
108
|
-
|
111
|
+
# ミニバッチを10個処理(10ステップ)ごとに損失を出力
|
109
112
|
if (iteration % 10 == 0):
|
110
|
-
|
113
|
+
# 10ステップの所要時間を取得
|
111
114
|
t_iter_finish = time.time()
|
112
115
|
duration = t_iter_finish - t_iter_start
|
113
|
-
ステップ数、損失、所要時間を出力
|
116
|
+
# ステップ数、損失、所要時間を出力
|
114
117
|
print('ステップ( {} ) loss: {:.4f} -- time: {:.4f} sec.'.format(
|
115
118
|
iteration, loss.item(), duration))
|
116
119
|
t_iter_start = time.time()
|
117
120
|
|
118
|
-
|
121
|
+
# エポックの損失をepoch_train_lossに加算する
|
119
122
|
epoch_train_loss += loss.item()
|
120
|
-
|
123
|
+
# ステップ数を1増やす
|
121
124
|
iteration += 1
|
122
125
|
|
123
|
-
|
126
|
+
# 検証モードでは順伝播後の損失の記録のみを行う
|
124
127
|
else:
|
125
128
|
epoch_val_loss += loss.item()
|
126
129
|
|
127
|
-
epochのphaseごとのlossと正解率
|
130
|
+
# epochのphaseごとのlossと正解率
|
128
|
-
|
131
|
+
# エポック終了時の時刻を取得
|
129
132
|
t_epoch_finish = time.time()
|
130
133
|
print('---------------------------------------')
|
131
|
-
|
134
|
+
# 訓練データの損失と検証データの損失を出力
|
132
135
|
print('train_loss: {:.4f} - val_loss(Every 10 epochs): {:.4f}'.format(
|
133
136
|
epoch_train_loss, epoch_val_loss))
|
134
|
-
エポック終了までに要した時間を取得
|
137
|
+
# エポック終了までに要した時間を取得
|
135
138
|
print('time: {:.4f} sec.'.format(t_epoch_finish - t_epoch_start))
|
136
|
-
次のエポックの開始時刻を取得
|
139
|
+
# 次のエポックの開始時刻を取得
|
137
140
|
t_epoch_start = time.time()
|
138
141
|
|
139
|
-
エポックごとに損失をdictオブジェクトに保存
|
142
|
+
# エポックごとに損失をdictオブジェクトに保存
|
140
143
|
log_epoch = {'epoch': epoch+1,
|
141
144
|
'train_loss': epoch_train_loss,
|
142
145
|
'val_loss': epoch_val_loss}
|
143
|
-
ログのリストに追加
|
146
|
+
# ログのリストに追加
|
144
147
|
logs.append(log_epoch)
|
145
|
-
ログのリストをデータフレームに変換
|
148
|
+
# ログのリストをデータフレームに変換
|
146
149
|
df = pd.DataFrame(logs)
|
147
|
-
ログファイルに保存
|
150
|
+
# ログファイルに保存
|
148
151
|
df.to_csv('/content/drive/MyDrive/Colab Notebooks/ObjectDetection/epoch_loss.csv')
|
149
152
|
|
150
|
-
|
153
|
+
# 訓練時の損失和を0で初期化
|
151
154
|
epoch_train_loss = 0.0
|
152
|
-
|
155
|
+
# 検証時の損失和を0で初期化
|
153
156
|
epoch_val_loss = 0.0
|
154
157
|
|
155
|
-
|
158
|
+
# 1エポック終了ごとにモデルのパラメーター値を保存
|
156
159
|
if ((epoch+1) % 10 == 0):
|
157
160
|
torch.save(
|
158
161
|
net.state_dict(),
|
159
162
|
'/content/drive/MyDrive/Colab Notebooks/ObjectDetection/weights/ssd_weights' +
|
160
163
|
str(epoch+1) + '.pth')
|
161
164
|
print('--saved weights--')
|
165
|
+
```
|
166
|
+
#出たエラー
|
167
|
+
```Python
|
168
|
+
/usr/local/lib/python3.7/dist-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
|
169
|
+
2115 magic_arg_s = self.var_expand(line, stack_depth)
|
170
|
+
2116 with self.builtin_trap:
|
171
|
+
-> 2117 result = fn(magic_arg_s, cell)
|
172
|
+
2118 return result
|
173
|
+
2119
|
174
|
+
|
175
|
+
<decorator-gen-53> in time(self, line, cell, local_ns)
|
176
|
+
|
177
|
+
/usr/local/lib/python3.7/dist-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
|
178
|
+
186 # but it's overkill for just that one bit of state.
|
179
|
+
187 def magic_deco(arg):
|
180
|
+
--> 188 call = lambda f, *a, **k: f(*a, **k)
|
181
|
+
189
|
182
|
+
190 if callable(arg):
|
183
|
+
|
184
|
+
/usr/local/lib/python3.7/dist-packages/IPython/core/magics/execution.py in time(self, line, cell, local_ns)
|
185
|
+
1191 else:
|
186
|
+
1192 st = clock2()
|
187
|
+
-> 1193 exec(code, glob, local_ns)
|
188
|
+
1194 end = clock2()
|
189
|
+
1195 out = None
|
190
|
+
|
191
|
+
<timed exec> in <module>()
|
192
|
+
|
193
|
+
<ipython-input-8-dd6592475800> in train(net, dataloaders_dict, criterion, optimizer, num_epochs)
|
194
|
+
53 # 1ステップにおけるミニバッチを使用した学習または検証
|
195
|
+
54 # データローダーをイテレートしてミニバッチを抽出
|
196
|
+
---> 55 for images, targets in dataloaders_dict[phase]:
|
197
|
+
56 # 画像データにデバイスを割り当てる
|
198
|
+
57 images = images.to(device)
|
199
|
+
|
200
|
+
/usr/local/lib/python3.7/dist-packages/torch/utils/data/dataloader.py in __next__(self)
|
201
|
+
519 if self._sampler_iter is None:
|
202
|
+
520 self._reset()
|
203
|
+
--> 521 data = self._next_data()
|
204
|
+
522 self._num_yielded += 1
|
205
|
+
523 if self._dataset_kind == _DatasetKind.Iterable and \
|
206
|
+
|
207
|
+
/usr/local/lib/python3.7/dist-packages/torch/utils/data/dataloader.py in _next_data(self)
|
208
|
+
559 def _next_data(self):
|
209
|
+
560 index = self._next_index() # may raise StopIteration
|
210
|
+
--> 561 data = self._dataset_fetcher.fetch(index) # may raise StopIteration
|
211
|
+
562 if self._pin_memory:
|
212
|
+
563 data = _utils.pin_memory.pin_memory(data)
|
213
|
+
|
214
|
+
/usr/local/lib/python3.7/dist-packages/torch/utils/data/_utils/fetch.py in fetch(self, possibly_batched_index)
|
215
|
+
42 def fetch(self, possibly_batched_index):
|
216
|
+
43 if self.auto_collation:
|
217
|
+
---> 44 data = [self.dataset[idx] for idx in possibly_batched_index]
|
218
|
+
45 else:
|
219
|
+
46 data = self.dataset[possibly_batched_index]
|
220
|
+
|
221
|
+
/usr/local/lib/python3.7/dist-packages/torch/utils/data/_utils/fetch.py in <listcomp>(.0)
|
222
|
+
42 def fetch(self, possibly_batched_index):
|
223
|
+
43 if self.auto_collation:
|
224
|
+
---> 44 data = [self.dataset[idx] for idx in possibly_batched_index]
|
225
|
+
45 else:
|
226
|
+
46 data = self.dataset[possibly_batched_index]
|
227
|
+
|
228
|
+
/content/drive/MyDrive/Colab Notebooks/ObjectDetection/voc.py in __getitem__(self, index)
|
229
|
+
256 # pull_item()にイメージのインデックスを渡して前処理
|
230
|
+
257 # 処理後のイメージデータとBBoxとラベルの2次元配列を返す
|
231
|
+
--> 258 im, bl, _, _ = self.pull_item(index)
|
232
|
+
259 return im, bl
|
233
|
+
260
|
234
|
+
|
235
|
+
/content/drive/MyDrive/Colab Notebooks/ObjectDetection/voc.py in pull_item(self, index)
|
236
|
+
275 img_path = self.img_list[index] # インデックスを指定してイメージのパスを取得
|
237
|
+
276 img = cv2.imread(img_path) # OpenCV2でイメージの[高さ,幅,[B,G,R]]を取得
|
238
|
+
--> 277 height, width, _ = img.shape # 配列要素数を数えて高さ,幅のみを取得
|
239
|
+
278
|
240
|
+
279 # アノテーションデータのリストを取得
|
241
|
+
|
242
|
+
AttributeError: 'NoneType' object has no attribute 'shape'
|
243
|
+
```
|
162
244
|
#知りたいこと
|
163
245
|
このエラーはどういう意味か。
|
164
246
|
どうすれば動くようになるか。
|