tqdmの使い方を自分なりにまとめてみた

# なぜtqdmを使うのか？それは便利だから！
* 深層学習における進捗状況や処理状況を表示させるときに便利
* 実行にかかるおおよその時間を知ることが出来る

# 流れ
* ライブラリのインポート
* tqdmを使ってみる
* プログレスバーに情報を加える
* 深層学習にtqdmを用いる

# ライブラリのインポート
まずtqdmライブラリをインポートします．

```py
from tqdm import tqdm
```
※ tqdmライブラリをインストールしていない場合は先にインストールします．

## notebookの場合

jupyter notebookやGoogle colabの場合には，インポートするモジュールを少し変更します．

```py
from tqdm.notebook import tqdm
```



# tqdmを使ってみる

## まずtqdmを使わずに以下のコードを実行してみる

```py
epoch = 10000000
e_sum = 0
for e in range(epoch):
    e_sum += e
print(e_sum)

#以下は実行結果
e_sum:49999995000000
```

もちろんこのような結果になりますが，このままでは実行にかかった時間や処理の状況が分かりません．
（この程度の計算であれば数秒で終わることは明らかですが．．．）
そこでtqdmを使って実行時間と処理状況を見てみましょう．

## tqdmを使った場合

```py
epoch = 10000000
e_sum = 0
with tqdm(range(epoch)) as pbar_epoch:
    for e in pbar_epoch:
        e_sum += e 
print(e_sum)
```

上のコードを実行してみると実行途中で以下のようなものが表示されます．

![2021-04-06 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284399/66abec9a-0206-af79-aec2-5bdb5407ca14.png)

これは

```text
"進捗率"|"プログレスバー"| "完了したイテレーション数"/"全体のイテレーション数" ["経過時刻"<"推定残り時刻", "イテレーション速度"]
```

を表しています．

# プログレスバーに情報を加える

次にプログレスバーに情報を加える方法をまとめてみます．
情報を加える場所はプログレスバーの前と後ろの2つがあります．

## プログレスバーの前に情報を加える

深層学習においては，例えば現在のepochの値などをプログレスバーの前に表示させたいです．
（表示させなくても完了したイテレーション数を見ることで確認することもできます）
プログレスバーの前に情報を加える場合は`set_description()`を用います．
では先ほど実行したものに現在のeの値という情報を加えてみます．


```py
epoch = 10000000
e_sum = 0
with tqdm(range(epoch)) as pbar_epoch:
    for e in pbar_epoch:
        pbar_epoch.set_description("e: %d" % e)
        e_sum += e 
print(e_sum)
```

実行途中の結果は以下のようになります．

![2021-04-06 (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284399/3d260a67-fabe-38ce-1314-73e1fba0c5a3.png)

このようにプログレスバーの前に情報を加えることが出来ます．
ここで気づいたのですが，加えたい情報はeの値だったのですがeの値はあまりにも速く変わり，表示するのに時間がかかるせいなのか実行時間が増えてしまいました．
ただし，tqdmを使用するモチベーションはあくまで深層学習にあり，epochが上の例のような速さで切り替わることはあまり考えられませんし，ましてやepoch数を1000000と取るようなことはないと思うのでtqdmを深層学習に用いる場合においては情報を加えることで実行時間が増えてしまうといった心配は必要ありません．

## プログレスバーの後ろに情報を加える

深層学習においては，損失関数の値や精度などをプログレスバーの後ろに表示させたいです．
プログレスバーの後ろに情報を加えたい場合は`set_postfix()`を用います．
しかしここで注意しなければならないことは，`OrderedDict()`を使用しないと表示される順番が変わってしまう可能性があるということです．

```py
from collections import OrderedDict

epoch = 10000
e_sum = 0
with tqdm(range(epoch)) as pbar_epoch:
    for e in pbar_epoch:
        #pbar_epoch.set_description("e: %d" % e)
        e_sum += e 
        pbar_epoch.set_postfix(OrderedDict(loss=e/10000,
                                           acc=e/1000))
print(e_sum)
```

実行途中の結果は以下のようになります．

![2021-04-06 (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284399/3cdf2c4f-48b9-0284-1bf6-4b5bbf709395.png)

このようにして，プログレスバーの後ろに情報を加えることが出来ました．

# 深層学習にtqdmを用いる

では実際に深層学習にtqdmを使ってみたいと思います．
深層学習の流れとして以下のようなよくある流れについて考えます．

```py
#loadersはpytorchのDataloader()を用いデータセットを取得したとする
epoch = 20
for e in range(epoch):
    for i, (data, label) in enumerate(loaders):    
        #出力 = net(data)
        #誤差 = 誤差関数(出力, label)
        #精度 = ...
        #以下省略
```

上のコードはloadersに格納されているバッチサイズだけ取り出し学習を行うという反復（データセット数/バッチサイズ回）を繰り返し，さらにその処理をepoch(20回)繰り返すといった流れです．
ここにtqdmを使ってみます．
まずはバッチサイズだけ取り出す処理の反復に対しtqdmを用います．

```py
epoch = 20
for e in range(epoch):
    with tqdm(enumerate(loaders),
              total=len(loaders)) as pbar_loss:
        for i, (data, target) in pbar_loss: 
            #以下省略
```

以上のようなコードにすることでtqdmを使うことが出来ます．
注意しなくてはならないのはtqdmの引数に`total=len(loaders)`といれなければいけません．
tqdmはデフォルトで自動的にイテレーション回数を`len()`を用いて計算してくれるのですが，enumerateに`len()`は使用することが出来ないので`total=len(loaders)`とすることでイテレーション回数を与えます．
以下が実行結果です．

![2021-04-06 (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284399/00713736-138d-f227-e5be-750d37e5cf8e.png)

ちなみに今回はデータ数を5000，バッチサイズを100としているので全体のイテレーション数は50となります．
これで1epochごとの処理状況を見ることが出来るようになりました．
しかし，このままだとepoch全体での処理の状況がつかめません．
そこでepoch回繰り返す処理にもtqdmを用いてみます．
以下がそのコードです．

```py
with tqdm(range(epoch)) as pbar_epoch:
    for e in pbar_epoch:
        pbar_epoch.set_description("[Epoch %d]" % (e))
        with tqdm(enumerate(loaders),
                  total=len(loaders)) as pbar_loss:
            for i, (data, target) in pbar_loss:
                #以下省略
```

以下が実行結果になります．

![2021-04-06 (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284399/c46be7d8-0da5-63ad-b27d-239c6b8ce89b.png)

これで全体の処理状況をつかむことが出来ました．
しかしepochごとのプログレスバーが残ったままなので見やすさに欠けます．
そこでプログレスバーを残さないようにします．
tqdmの引数に`leave=False`といれることでプログレスバーが残らないようになります．
以下にコードと実行結果の画像を載せます．

```py
with tqdm(range(epoch)) as pbar_epoch:
    for e in pbar_epoch:
        pbar_epoch.set_description("[Epoch %d]" % (e))
        with tqdm(enumerate(loaders),
                  total=len(loaders),
                  leave=False) as pbar_loss:
            for i, (data, target) in pbar_loss:
                #以下省略
```

![2021-04-06 (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284399/81bd0ea5-3716-19f9-5057-01fd1042bf5f.png)


# 参考

- [グレートなtqdmの使い方](https://qiita.com/SeeLog/items/73c054a37722890b17a2)
- [Pythonメモ:tqdmで処理の進捗（プログレスバー）を表示](https://wonderwall.hatenablog.com/entry/2017/07/23/222856#%E3%83%8D%E3%82%B9%E3%83%88)