機械学習メモ

# 基礎
## データフォーマット
- NHWC
    - TensorFlow
    - 普通の画像 (OpenCVのmatとか)
- NCHW
    - TensorRT
    - OpenCV(`cv::dnn::blobFromImage`で変換)
    - ncnn(`ncnn::Mat::from_pixels`で変換)

```txt:width=3,height=2,3色の場合
# NHWC
R00 G00 B00 R01 G01 B01 R02 G02 B02
R10 G10 B10 R11 G11 B11 R12 G12 B12

# NCHW
R00 R01 R02
R10 R11 R12
G00 G01 G02
G10 G11 G12
B00 B01 B02
B10 B11 B12
```


# Google Colaboratory

## アイドルタイムアウト回避
### ブラウザ上で回避
ブラウザのデバッグツール->Consoleで以下を実行

```javascript
function ConnectButton(){
    console.log("Connect"); 
    document.querySelector("#top-toolbar > colab-connect-button").shadowRoot.querySelector("#connect").click();
    // document.querySelector("#connect").click()
    // document.querySelector("colab-toolbar-button#connect").click()
    // document.querySelector("colab-connect-button").click()
}
setInterval(ConnectButton,60*1000);
```

https://isolution.pro/q/so70111024/google-colab-ga-setsudansarenai-yo-ni-suru-ni-wa-do-sureba-yoi-desu-ka

### ラズパイなどから定期的にブラウザで開くことで回避
```sh
while true
do
        timeout 60 firefox https://colab.research.google.com/drive/1sdxxxxxx
        sleep 10m
done
```

## Googleドライブマウント、ファイルのアップロードとダウンロード
```python
from google.colab import drive 
drive.mount("/content/drive")

from google.colab import files
files.download( "./test.h5")
uploaded = files.upload()
```

## サンプル
### TensorFlow推論、TensorFlow Lite変換と推論、TensorFlow Lite 量子化変換と推論
https://colab.research.google.com/github/iwatake2222/colaboratory_study/blob/master/sample/inference_tensorflow.ipynb

### Keras(h5) -> tflite (tf2.5)
```py
import tensorflow as tf

loaded_model = tf.keras.models.load_model("model.h5", custom_objects={'tf': tf})
converter = tf.lite.TFLiteConverter.from_keras_model(loaded_model)
tflite_model = converter.convert()
open("model.tflite", "wb").write(tflite_model)
```

`custom_objects={'tf': tf}` がないと`name 'tf' is not defined` というエラー発生

### ONNX (onnx-runtimeとOpenCV(cv2.dnn)) 推論
https://colab.research.google.com/github/iwatake2222/colaboratory_study/blob/master/sample/inference_onnx.ipynb

### モデル変換: TensorFlow(NHWC) to ONNX(NCHW)
https://github.com/iwatake2222/colaboratory_study/blob/master/sample/tf2onnx.ipynb

### モデル変換:  ONNX(NCHW) to Keras(NHWC) to TensorFlow(NHWC)
https://github.com/iwatake2222/colaboratory_study/blob/master/sample/onnx2keras2tf.ipynb

### モデル変換:  ONNX(NCHW) to OpenVINO(NCHW) to TensorFlow(NHWC) using openvino2tensorflow ( https://github.com/PINTO0309/openvino2tensorflow )
https://github.com/iwatake2222/colaboratory_study/blob/master/sample/onnx2openvino2tensorflow.ipynb

※↑はGoogle Colabから使用していますが、Dockerを使用することが推奨されています。

### モデル変換 TensorFlow Hubのモデル -> tflite
```py
import tensorflow as tf
import tensorflow_hub as hub 
model = hub.load("https://tfhub.dev/tensorflow/efficientdet/d0/1")

concrete_func = model.signatures[tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY]
concrete_func.inputs[0].set_shape([1, 320, 320, 3])
concrete_func.outputs[0]

converter = tf.lite.TFLiteConverter.from_concrete_functions([concrete_func])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
open("efficientdet_d0_320x320.tflite", "wb").write(tflite_model)

interpreter = tf.lite.Interpreter("efficientdet_d0_320x320.tflite")
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
```

↑だと一部outputのshapeが[1, 1, ...] になってしまう


### PyTorch(pkl) -> onnx
https://github.com/iwatake2222/study_colaboratory/blob/master/sample/pytorch_pkl_2_onnx_LapDepth.ipynb


## Object Detection モデル再学習の環境 (as of 2020/03/27)
- `AttributeError: module 'tensorflow' has no attribute 'contrib'` 対策
    - Tensorflow 1.15を使用する
    - Pythonパス設定 (`os.environ["PYTHONPATH"] = "/content/models/research:/content/models/research/slim:" + os.environ["PYTHONPATH"]`)
        - https://github.com/googlecolab/colabtools/issues/1070

```python
%tensorflow_version 1.x
import tensorflow as tf
print(tf.__version__)
!pip install pycocotools

!git clone --depth 1 https://github.com/tensorflow/models
%cd models/research/
!protoc object_detection/protos/*.proto --python_out=.
!pip install .
%cd /content

# Modify python path and check script
# %set_env PYTHONPATH=$PYTHONPATH:models/research:models/research/slim
import os
os.environ["PYTHONPATH"] = "/content/models/research:/content/models/research/slim:" + os.environ["PYTHONPATH"]
!echo $PYTHONPATH
!python models/research/object_detection/builders/model_builder_test.py
```

# その他
## int8量子化モデルへの入力
推奨は↓らしいけど、値範囲が-128～127を超える可能性があるので単に-128するだけでいいかも。

```c
for(uint i=0; i< input->bytes;i++){
    input->data.int8[i] = input->data.uint8[i] + input->params.zero_point - 128; 
}
```

https://github.com/tensorflow/tflite-micro/issues/342#issuecomment-892847267


# 法律
- http://www.bunka.go.jp/seisaku/chosakuken/hokaisei/h30_hokaisei/pdf/r1406693_02.pdf
- https://storialaw.jp/blog/7609

# OpenVINO
## ダウンロード
- https://www.intel.com/content/www/us/en/developer/tools/openvino-toolkit-download.html
    - Linux / Windows
    - Online & Offline

## Linux
### インストール
```sh
# On Host
docker create -it -v /mnt/c/iwatake/devel:/root/devel -e DISPLAY="192.168.1.2:0" -e TZ="Asia/Tokyo" -w /root/ -p 8888:8888  --name=ubuntu20_openvino_01 ubuntu:20.04
docker start ubuntu20_openvino_01
docker exec -it ubuntu20_openvino_01 bash

# In Docker
tar xzvf l_openvino_toolkit_p_2021.4.752_online.tgz
cd l_openvino_toolkit_p_2021.4.752_online
./install_openvino_dependencies.sh
./install.sh

# For model conversion
pip3 install -r /opt/intel/openvino_2021/deployment_tools/model_optimizer/requirements.txt
```

### モデル変換
```sh
python3 /opt/intel/openvino_2021/deployment_tools/model_optimizer/mo.py --input_model aaa.onnx
```

### 使う(C++)
```sh
source /opt/intel/openvino_2021/bin/setupvars.sh
# LD_LIBRARY_PATH=/opt/intel/openvino_2021/opencv/lib/:$LD_LIBRARY_PATH
# PATH=/opt/intel/openvino_2021/opencv/lib/:$PATH
# LD_LIBRARY_PATH=/opt/intel/openvino_2021.4.752/deployment_tools/inference_engine/lib/intel64/:/opt/intel/openvino_2021.4.752/deployment_tools/ngraph/lib/:$LD_LIBRARY_PATH
# PATH=/opt/intel/openvino_2021.4.752/deployment_tools/inference_engine/lib/intel64/:/opt/intel/openvino_2021.4.752/deployment_tools/ngraph/lib/:$PATH
# ldconfig
cd 自分のプロジェクト && mkdir -p build && cd build
cmake .. -DOpenCV_DIR=/opt/intel/openvino_2021/opencv/cmake/
```

### 参考:通常のOpenCV(セルフビルド)を使う
- OpenVINOを使う場合には不要
    - 比較したい場合など

```sh
apt -y install git libgtk-3-dev libdc1394-22 libdc1394-22-dev ffmpeg libavcodec-dev libavformat-dev libswscale-dev libavresample-dev
git clone https://github.com/opencv/opencv.git --branch 4.5.5 --depth 1
cd opencv && mkdir -p build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=./install
make -j4
make install

source /root/opencv/build/install/bin/setup_vars_opencv4.sh
cd 自分のプロジェクト && mkdir -p build && cd build
cmake .. -DOpenCV_DIR=/root/opencv/build
```

## Windows
### インストール
- Pythonのインストール
    - 無くても大丈夫そう
    - pyenvを使っている場合は、`pyenv global 3.8.9` などでpythonのバージョンを指定しておく
- w_openvino_toolkit_p_2021.4.752_online.exe
    - 全部デフォルト

### 使う(C++)
- cmake-guiでプロジェクト(ソリューション)を作る
    - OpenCV_DIR = `C:\Program Files (x86)\Intel\openvino_2021.4.752\opencv\cmake`
- コマンドプロンプト上で、環境設定をしてからそのまま生成したソリューションを開く
    - PowerShellは使わない方がよいらしい

```cmd:コマンドプロンプト
"C:\Program Files (x86)\Intel\openvino_2021\bin\setupvars.bat"
"<path-to-solution>\main.sln"
```


# PyTorch
## エラー: `Expected a 'cuda' device type for generator but found 'cpu'`
`torch.set_default_tensor_type('torch.cuda.FloatTensor')` が原因という記述もあるが、これだとすべての個所に`.cuda()` や、`.to(device)` が必要になってしまう。`data.DataLoader` に`generator=torch.Generator(device='cuda')` オプションを追加することで解決するかも。


# ONNX
## モデル作成と実行
- out_0 = in_0 + in_1
- out_1 = [0, 1, ..., 9]

```py
import numpy as np
import onnx
import onnxruntime

def make_onnx_model(model_file_path):
    '''
    out_0 = in_0 + in_1
    out_1 = [0, 1, ..., 9]
    '''

    # Create one input
    in_0 = onnx.helper.make_tensor_value_info('in_0', onnx.TensorProto.FLOAT, [1, 2])
    in_1 = onnx.helper.make_tensor_value_info('in_1', onnx.TensorProto.FLOAT, [1, 2])

    # Create one output
    out_0 = onnx.helper.make_tensor_value_info('out_0', onnx.TensorProto.FLOAT, [1, 2])
    out_1 = onnx.helper.make_tensor_value_info('out_1', onnx.TensorProto.FLOAT, [1, 10])

    # Create a node
    node_add_0 = onnx.helper.make_node(
        'Add',
        inputs=['in_0', 'in_1'],
        outputs=['out_0'],
    )

    const_values_0 = np.arange(10, dtype=np.float32)
    node_const_0 = onnx.helper.make_node(
        'Constant',
        inputs=[],
        outputs=['out_1'],
        value=onnx.onnx.helper.make_tensor(
            name='const_tensor',
            data_type=onnx.TensorProto.FLOAT,
            dims=const_values_0.shape,
            vals=const_values_0.flatten().astype(float),
        ),
    )

    graph_def = onnx.helper.make_graph(
        [node_add_0, node_const_0],
        'test-model',
        [in_0, in_1],
        [out_0, out_1],
    )

    op = onnx.OperatorSetIdProto()
    op.version = 11
    model_def = onnx.helper.make_model(graph_def, opset_imports=[op])
    # model_def.opset_import[0].version = 11

    # print('The model is:\n{}'.format(model_def))
    onnx.checker.check_model(model_def)
    print('The model is checked!')

    with open(model_file_path, "wb") as f:
        f.write(model_def.SerializeToString())


def run_onnx_model(model_file_path):
    sess = onnxruntime.InferenceSession(model_file_path)
    in_0_name = sess.get_inputs()[0].name
    in_1_name = sess.get_inputs()[1].name
    in_0_val = np.array([[1, 2]], dtype=np.float32)
    in_1_val = np.array([[3, 4]], dtype=np.float32)
    out = sess.run(None, {in_0_name: in_0_val, in_1_name: in_1_val})
    print(out[0])
    print(out[1])

def main():
    make_onnx_model("model.onnx")
    run_onnx_model("model.onnx")

if __name__ == "__main__":
    main()
```