高位合成によるFPGA回路設計を読んで、YOLOv3を試してみた1

## はじめに
件名にすでに1がついているので、最後まで終わらなかったという内容になるんですが、前回のMNISTを試してみたに引き続き、YOLOv3を試してみた結果です。相変わらず、すんなりいかない。色々なトラブル解決を、スキルアップにつながるからいいか、とよく分からないポジティブな感想が出てくる程度には、何かがあります:joy:

## Darknetモデルをkerasモデルに変換

https://github.com/qqwweee/keras-yolo3

> wget https://pjreddie.com/media/files/yolov3.weights
> python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5

こちらのconvert.pyで変換するのですが、いきなりエラーが発生。Vitis-AI 1.2のDockerコンテナ上で実行しているので、環境要因でエラーが出るとは思えないんですが・・・

```shell
(vitis-ai-tensorflow) madrid@madrid-VirtualBox:/workspace/Vitis-AI-Library/keras
-yolo3$ python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5
Traceback (most recent call last):
  File "convert.py", line 14, in <module>
    from keras import backend as K
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/keras/__init__.py", line 25, in <module>
    from keras import models
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/keras/models.py", line 19, in <module>
    from keras import backend
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/keras/backend.py", line 36, in <module>
    from tensorflow.python.eager.context import get_config
ImportError: cannot import name 'get_config'
```
<br>
TensorflowとKerasのバージョンに何かしら問題がある？

```shell
pip list
Package               Version
--------------------- ---------
keras                 2.6.0
tensorflow            1.15.2
```

tensorflowを最新版にupdateすると、解消するらしい。

https://stackoverflow.com/questions/66964492/importerror-cannot-import-name-get-config-from-tensorflow-python-eager-conte

> These commands fixed the issue:
> pip install --upgrade tensorflow
> pip install --upgrade tensorflow-gpu

tensorflowが2.6.2にupdateされると、確かに解消した。keras 2.6.0だと、tensorflow2しかサポートしてないってことなのかな？tensorflowとkerasのcompatibility tableのようなものを探してみたけど、なんかまとまった情報が見つからず、、、:frowning2:

get_configのエラーは解消したんだけど、今度は別のエラーが発生。
BatchNormalizationのimportでエラーが発生している。

```shell
Traceback (most recent call last):
  File "convert.py", line 18, in <module>
    from keras.layers.normalization import BatchNormalization
ImportError: cannot import name 'BatchNormalization'
```

keras.layers.normalizationではなく、tensorflow.keras.layersからimportしろと言っている。

https://stackoverflow.com/questions/69471749/importerror-cannot-import-name-batchnormalization-from-keras-layers-normaliz

> You should import BatchNormalization in following way:
> from tensorflow.keras.layers import BatchNormalization


変換できた。

```shell
__________________________________________________________________________________________________
conv2d_58 (Conv2D)              (None, None, None, 2 261375      leaky_re_lu_57[0][0]             
__________________________________________________________________________________________________
conv2d_66 (Conv2D)              (None, None, None, 2 130815      leaky_re_lu_64[0][0]             
__________________________________________________________________________________________________
conv2d_74 (Conv2D)              (None, None, None, 2 65535       leaky_re_lu_71[0][0]             
==================================================================================================
Total params: 62,001,757
Trainable params: 61,949,149
Non-trainable params: 52,608
__________________________________________________________________________________________________
None
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
  category=CustomMaskWarning)
Saved Keras model to model_data/yolo.h5
Read 62001757 of 62001757.0 from Darknet weights.
```


## kerasモデルをtensorflowに変換

tensorflow2のままで実行したら、下記エラーが発生してしまった。

```shell
Traceback (most recent call last):
  File "./convert_to_tf.py", line 32, in <module>
    _main()
  File "./convert_to_tf.py", line 17, in _main
    inputs = [out.op.name for out in model.inputs]
  File "./convert_to_tf.py", line 17, in <listcomp>
    inputs = [out.op.name for out in model.inputs]
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/keras/engine/keras_tensor.py", line 222, in op
    raise TypeError('Keras symbolic inputs/outputs do not '
TypeError: Keras symbolic inputs/outputs do not implement `op`. You may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model.
```

<br>
うーん、よくわからんけど、keras 2.6.0だと、opに対応してないってこと？

https://github.com/philipperemy/keract/issues/128


一旦Vitis-AIを終了して、dockerコンテナを再起動してみる。これで、元のバージョンに戻るみたい。
モデル変換を再実行してみると、opのエラーは解消した。

```shell
conv2d_58 (Conv2D)              (None, None, None, 2 261375      leaky_re_lu_57[0][0]             
__________________________________________________________________________________________________
conv2d_66 (Conv2D)              (None, None, None, 2 130815      leaky_re_lu_64[0][0]             
__________________________________________________________________________________________________
conv2d_74 (Conv2D)              (None, None, None, 2 65535       leaky_re_lu_71[0][0]             
==================================================================================================
Total params: 62,001,757
Trainable params: 61,949,149
Non-trainable params: 52,608
__________________________________________________________________________________________________
inputs = ['input_1']
outputs = ['conv2d_58/BiasAdd', 'conv2d_66/BiasAdd', 'conv2d_74/BiasAdd']
WARNING:tensorflow:From ./convert_to_tf.py:27: The name tf.keras.backend.get_session is deprecated. Please use tf.compat.v1.keras.backend.get_session instead.
```

## tensorflowモデルの凍結
モデルを訓練対象から除外することを「凍結する」というらしいですが、凍結したモデルでないと、次ステップの量子化を実施できないようです。凍結用のスクリプトを実施してみたが、またエラー:joy:

```shell
Traceback (most recent call last):
  File "./freeze_graph.py", line 491, in <module>
    run_main()
  File "./freeze_graph.py", line 487, in run_main
    app.run(main=my_main, argv=[sys.argv[0]] + unparsed)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/tensorflow_core/python/platform/app.py", line 40, in run
    _run(main=main, argv=argv, flags_parser=_parse_flags_tolerate_undef)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/absl/app.py", line 312, in run
    _run_main(main, args)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/absl/app.py", line 258, in _run_main
    sys.exit(main(argv))
  File "./freeze_graph.py", line 486, in <lambda>
    my_main = lambda unused_args: main(unused_args, flags)
  File "./freeze_graph.py", line 378, in main
    flags.saved_model_tags, checkpoint_version)
  File "./freeze_graph.py", line 361, in freeze_graph
    checkpoint_version=checkpoint_version)
  File "./freeze_graph.py", line 226, in freeze_graph_with_def_protos
    variable_names_blacklist=variable_names_blacklist)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/tensorflow_core/python/util/deprecation.py", line 324, in new_func
    return func(*args, **kwargs)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/tensorflow_core/python/framework/graph_util_impl.py", line 277, in convert_variables_to_constants
    inference_graph = extract_sub_graph(input_graph_def, output_node_names)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/tensorflow_core/python/util/deprecation.py", line 324, in new_func
    return func(*args, **kwargs)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/tensorflow_core/python/framework/graph_util_impl.py", line 197, in extract_sub_graph
    _assert_nodes_are_present(name_to_node, dest_nodes)
  File "/opt/vitis_ai/conda/envs/vitis-ai-tensorflow/lib/python3.6/site-packages/tensorflow_core/python/framework/graph_util_impl.py", line 152, in _assert_nodes_are_present
    assert d in name_to_node, "%s is not in graph" % d
AssertionError: conv2d_59/BiasAdd is not in graph
```

スクリプトで指定している出力層と、実際のモデルの層構成が違うみたい・・・何故だろう？

スクリプト
```shell 
  --output_node_names conv2d_59/BiasAdd,conv2d_67/BiasAdd,conv2d_75/BiasAdd \
```

実際のモデル
```shell
conv2d_58 (Conv2D)              (None, None, None, 2 261375      leaky_re_lu_57[0][0]             
__________________________________________________________________________________________________
conv2d_66 (Conv2D)              (None, None, None, 2 130815      leaky_re_lu_64[0][0]             
__________________________________________________________________________________________________
conv2d_74 (Conv2D)              (None, None, None, 2 65535       leaky_re_lu_71[0][0]             
==================================================================================================
Total params: 62,001,757
Trainable params: 61,949,149
Non-trainable params: 52,608
__________________________________________________________________________________________________
inputs = ['input_1']
outputs = ['conv2d_58/BiasAdd', 'conv2d_66/BiasAdd', 'conv2d_74/BiasAdd']
```

<br>
スクリプトを修正してみたら、正常に変換できた。

```shell
  --output_node_names conv2d_58/BiasAdd,conv2d_66/BiasAdd,conv2d_74/BiasAdd \
```

```shell
INFO:tensorflow:Froze 366 variables.
I0329 06:49:34.823935 140063022876480 graph_util_impl.py:334] Froze 366 variables.
INFO:tensorflow:Converted 366 variables to const ops.
I0329 06:49:35.491246 140063022876480 graph_util_impl.py:394] Converted 366 variables to const ops.
```

## tensoflowモデルの量子化
モデルの重みを32bit浮動小数点から、8bit固定小数点に変換する。これも、スクリプトでの出力層の指定の修正が必要だった。

```shell
  --output_nodes       conv2d_58/BiasAdd,conv2d_66/BiasAdd,conv2d_74/BiasAdd \
```

量子化できた？
```shell
********************* Quantization Summary *********************      
INFO: Output:       
  quantize_eval_model: ./outputs/quantize/quantize_eval_model.pb       
  deploy_model: ./outputs/quantize/deploy_model.pb
```

## tensorflowモデルのコンパイル

コンパイルできた。

```shell
**************************************************
* VITIS_AI Compilation - Xilinx Inc.
**************************************************

Kernel topology "yolov3_kernel_graph.jpg" for network "yolov3"
kernel list info for network "yolov3"
                               Kernel ID : Name
                                       0 : yolov3

                             Kernel Name : yolov3
--------------------------------------------------------------------------------
                             Kernel Type : DPUKernel
                               Code Size : 1.41MB
                              Param Size : 59.05MB
                           Workload MACs : 65864.08MOPS
                         IO Memory Space : 8.43MB
                              Mean Value : 0, 0, 0, 
                      Total Tensor Count : 80
                Boundary Input Tensor(s)   (H*W*C)
                            input_1:0(0) : 416*416*3

               Boundary Output Tensor(s)   (H*W*C)
                   conv2d_58_Conv2D:0(0) : 13*13*255
                   conv2d_66_Conv2D:0(1) : 26*26*255
                   conv2d_74_Conv2D:0(2) : 52*52*255

                        Total Node Count : 79
                           Input Node(s)   (H*W*C)
                        conv2d_Conv2D(0) : 416*416*3

                          Output Node(s)   (H*W*C)
                     conv2d_58_Conv2D(0) : 13*13*255
                     conv2d_66_Conv2D(0) : 26*26*255
                     conv2d_74_Conv2D(0) : 52*52*255
```

## YOLOv3の実行
VARTのサンプルプログラムのコンパイルを実施し生成されたバイナリと、コンパイルしたモデルを、Ultra96v2にコピーし実行した。あれ・・・DPUが動き出した途端、リブートする・・・なにこれ:joy:

```shell
root@ultra96v2_v2020_1:~/YOLOv3# ./YOLOv3 dpu_yolov3.elf
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0325 17:23:02.551630   611 main.cc:14] create running for subgraph: yolov3
conv2d_58_Conv2D_60
conv2d_66_Conv2D_70
conv2d_74_Conv2D_79
in_dims   : 1, 416, 416, 3
out_dims0 : 1, 13, 13, 255
out_dims1 : 1, 26, 26, 255
out_dims2 : 1, 52, 52, 255
Infering COCO_train2014_000000000078.jpg...
Xilinx Zynq MP First Stage Boot Loader
Release 2020.1   Mar 25 2022  -  16:03:34
NOTICE:  ATF running on XCZU3EG/silicon v4/RTL5.1 at 0xfffea000
NOTICE:  BL31: v2.2(release):xilinx_rebase_v2.2_2020.1
NOTICE:  BL31: Built : 15:43:28, Mar 25 2022


U-Boot 2020.01 (Mar 25 2022 - 15:59:59 +0000)

Model: Avnet Ultra96 Rev1
Board: Xilinx ZynqMP
DRAM:  2 GiB
PMUFW:  v1.1
EL Level:       EL2
Chip ID:        zu3eg
```

エラーログもなにも吐かずに、いきなりリブートして、Boot Loaderが起動しているので、なんかハードウェアの問題っぽい挙動ですね、これ。熱とか電源系の問題？と思い、扇風機でめっちゃ冷やしてみたけど、症状は変わらない。

うーん・・・なんかボードの既知の不具合でもあるんかなあ、と思ったら、まさにそれっぽい情報を発見。
Date Code見ると、エラッタ持ちのボードみたい。

https://signate.jp/competitions/537#Ultra96board

> 特に、エラッタのうち以下の既知問題があります。
> ・Ultra96-V2のリブート問題
> 　- Ultra96-V2　PL部の負荷が高くなった場合にMPSoCがリブートするという現象が発生しています。
> 　- Ultra96ーV2 エラッタ報告ページに掲載されています。
> 　　　Vccintの電圧をPMICというICが監視しており、特定の閾値を超えた場合にPowerGood信号が落ちることで、MPSoCにリセットが入ると>いう動作によるものです。
>　- 現在のV2ボードでは、PL部の消費電流増加により設定閾値を超えた場合にリセットがかかるという事象が発生します。

PMIC FWの更新すれば、エラッタ回避できるらしいけど・・・ライタ持ってないし1万円もするから、わざわざこのために買うのもなあ:unamused:

> ・なお、電力制限が3.5A, 4.0Aのボードも、基板改版とPMICの設定変更を行う事で5.5Aの制限に緩和できます。Avunet社に準備いただいた対> 策手順一式のダウンロードが、以下から可能です
>　　20191114_Ultra96-V2 PMIC update procedure.zip　（本サイトのデータタブより取得）
>　　本問題対応に必要となるInfinion USB005書き込みケーブルの販売先情報は以下になります

さてどうしよう(続く) 
 * Infinion USB005を購入して、PMICのFWを更新
 * 誰かに更新してもらう(他力本願)
 * PMICの電流制限に引っかからない程度に、DPUの規模と周波数を落とす。
 * PGOODの回路を改造