🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Jupyter

Jupyter (旧IPython notebook)は、Notebook形式でドキュメント作成し、プログラムの記述・実行、その実行結果を記録するツールです。メモの作成や保存、共有、確認などもブラウザ上で行うことができます。

CUDA

CUDAは並列計算プラットフォームであり、Nvidia GPU(Graphics Processing Units)向けのプログラミングモデルです。CUDAは様々なプログラミング言語、ライブラリ、APIを通してNvidiaにインターフェイスを提供します。

PyTorch

PyTorchは、オープンソースのPython向けの機械学習ライブラリ。Facebookの人工知能研究グループが開発を主導しています。強力なGPUサポートを備えたテンソル計算、テープベースの自動微分による柔軟なニューラルネットワークの記述が可能です。

メモリリーク

メモリリークは、プログラムファイルがメモリの解放に失敗した時に起こります。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Q&A

解決済

1回答

6721閲覧

[Pytorch / ResNet画像分類 / テストデータ予測] RAMメモリがfold毎に増加する・メモリが解放されない

nanoseeing

総合スコア133

Jupyter

Jupyter (旧IPython notebook)は、Notebook形式でドキュメント作成し、プログラムの記述・実行、その実行結果を記録するツールです。メモの作成や保存、共有、確認などもブラウザ上で行うことができます。

CUDA

CUDAは並列計算プラットフォームであり、Nvidia GPU(Graphics Processing Units)向けのプログラミングモデルです。CUDAは様々なプログラミング言語、ライブラリ、APIを通してNvidiaにインターフェイスを提供します。

PyTorch

PyTorchは、オープンソースのPython向けの機械学習ライブラリ。Facebookの人工知能研究グループが開発を主導しています。強力なGPUサポートを備えたテンソル計算、テープベースの自動微分による柔軟なニューラルネットワークの記述が可能です。

メモリリーク

メモリリークは、プログラムファイルがメモリの解放に失敗した時に起こります。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

0グッド

1クリップ

投稿2021/03/15 11:23

前提

Pytorchで画像分類をしています。(ResNet200Dモデル)

交差検証で事前に訓練したモデルが5fold分あり、
それらのモデルを使ってテストデータの予測を5つ分行い、
アンサンブルをしようとしています。

発生している問題

下記ソースコード中の、inference()を実行すると、
各fold毎に、RAMメモリが増加していき、
最終的にメモリが解放されないまま終了して困っています。

具体的には、RAMメモリの使用状況が、

(実行前) 0.2 GB
(1ループ目)2.5 GB
(2ループ目) 3.3 GB
・・・
(5ループ目) 6.0 GB
(実行後) 5.5 GB

のように遷移しています。

GPUメモリについても、2GBほど増えたまま解放されません。

・fold毎にRAMメモリ使用量が増加するのはなぜか。また、その解決策。
・実行後に、RAMメモリ・GPUメモリが解放されないのはなぜか。また、その解決策。

以上、ご教示いただければ幸いです。

該当のソースコード

python

1 2device = torch.device('cuda') 3df_test = pd.read_csv(CSV_PATH) 4 5def inference_one_loop(model, loader, device): 6 7 model.to(device) 8 model.eval() 9 pred_list = [] 10 with torch.no_grad(): 11 for x in tqdm(loader): 12 y = model(x.to(device)) 13 pred_list.append(y.sigmoid().detach().cpu().numpy()) 14 15 pred_arr = np.concatenate(pred_list) 16 17 # del pred_list 18 # torch.cuda.empty_cache() 19 # gc.collect() 20 21 return pred_arr 22 23 24def inference(): 25 26 n_fold = 5 27 n_class = 11 28 batch_size = 32 29 image_size = 512 30 31 Model = ResNet200D 32 model_name = 'resnet200d' 33 model_paths = ['./fold0.pth','./fold1.pth','./fold2.pth','./fold3.pth','./fold4.pth','./fold5.pth'] 34 35 test_dataset = TestDataset(df_test, transform=get_transforms(image_size=image_size)) 36 test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4 , pin_memory=True, drop_last=False) 37 38 ### 各foldごとにメモリが増加する ### 39 40 test_preds_arr = np.zeros((n_fold, len(df_test), n_class)) 41 for fold in range(n_fold): 42 logger.info(f"[fold {fold}]") 43 44 model = Model(model_name=model_name) 45 model.load_state_dict(torch.load(model_paths[fold], map_location=device)) 46 47 test_pred = inference_one_loop(model, test_loader, device) 48 test_preds_arr[fold] = test_pred 49 50 # del model 51 # torch.cuda.empty_cache() 52 # gc.collect() 53 54 return test_preds_arr.mean(axis=0) 55 56 57### 関数を抜けてもメモリが解放されない。 58preds = inference() 59

試したこと

ソースコード中でコメントアウトしているように、
del による変数の参照の削除や、
torch.cuda.empty_cache()を実施しましたが、
結果は変わりませんでした。

また、下記の記事を参考にしましたが、
メモリを多く占有している変数は見つかりませんでした。
https://qiita.com/AnchorBlues/items/883790e43417640140aa

inference()の実行後は、
メソッド内のローカル変数を参照することは当然ながらできないようです。

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

環境: Jupyter Notebook (Kaggle上のnotebook環境)

ライブラリ:
・torch 1.7.0
・timm (https://github.com/rwightman/pytorch-image-models)

モデルやDatasetの定義:

python

1class ResNet200D(nn.Module): 2 def __init__(self, model_name): 3 super().__init__() 4 self.model = timm.create_model(model_name, pretrained=False) 5 n_features = self.model.fc.in_features 6 self.model.global_pool = nn.Identity() 7 self.model.fc = nn.Identity() 8 self.pooling = nn.AdaptiveAvgPool2d(1) 9 self.fc = nn.Linear(n_features, 11) 10 11 def forward(self, x): 12 bs = x.size(0) 13 features = self.model(x) 14 pooled_features = self.pooling(features).view(bs, -1) 15 output = self.fc(pooled_features) 16 return output 17 18def get_transforms(image_size=640): 19 return Compose([ 20 Resize(image_size, image_size), 21 Normalize(), 22 ToTensorV2(), 23 ]) 24 25class TestDataset(Dataset): 26 def __init__(self, df, transform=None): 27 self.df = df 28 self.file_names = df['StudyInstanceUID'].values 29 self.transform = transform 30 31 def __len__(self): 32 return len(self.df) 33 34 def __getitem__(self, idx): 35 file_name = self.file_names[idx] 36 file_path = f'{TEST_DIR}/{file_name}.jpg' 37 image = cv2.imread(file_path) 38 image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 39 if self.transform: 40 augmented = self.transform(image=image) 41 image = augmented['image'] 42 return image

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

tqdmへのイテレータの渡し方が問題でした。

python

1tqdm(loader)

ではなく、

python

1tqdm(enumerate(loader), total=len(loader))

とするのが正しいです。

こちらの質問が参考になりました。
https://github.com/tqdm/tqdm/issues/746

また、問題の発見には、
sys.getrefcount()で、objectの参照数を調べることが役に立ちました。

inference()関数を抜ける直前に、すべてのローカル変数について参照数を調べたところ、
test_loader変数のみ参照が残っていることがわかりました。

投稿2021/03/17 12:45

nanoseeing

総合スコア133

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問