とある大学生の勉強メモ

Python, C#, UWP, WPF, 心理実験関連の開発備忘録

学習したモデルの推論結果がnullになる MaskRCNN(Pytorch),onnxruntime

推論を行ったとき結果がnullとなるケースが多々ある. 今回PreTrainedのresnetをonnx exportし,onnx runtimeで推論すると結果が返るが, pytorchで学習を進めたモデルをexportした場合,推論結果がnullとなった際の備忘録.

結論

画像を配列にしたとき正規化を行えば推論結果が返るようになった.

input_image = cv2.imread(file_path)/255

序論

推論時はモデルの入出力の型に注意しなくてはいけない. 今回画像をTensorFloat配列に変換しモデルに入力する.

UWPにonnxモデルを取り込むとcode generatorが自動でラッパーファイルをonnx_model_name.csで生成する.
その際にInputの方をImageFeatureValueまたはTensorFloatにすると思うが,
入力配列の中身の値が0~255で構成されているならばImageFeatureValue,0~1ならTensorFloatを用いれば良い(と思う).
余談だが配列の中身の値が-1~1の範囲を求めるならば,onnxexportの際にtorch.randnを用いる.

modelのexportと推論

学習後のモデルをonnx exportする.
(BatchNorm層のあるモデルでもtorchの最新版であれば対応しています)

import torch
import torchvision
model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)
x = torch.rand(1,3,2048, 1084,device=torch.device("cpu"))
torch.onnx.export(model.cpu(), x, 
                  "detection_resnet_op11.onnx",
                  export_params=True,#モデルに訓練した重みも保存するか否か
                  verbose=False, #変換の途中経過を見るかどうか
                  opset_version = 11)

上記のように学習済みのResnet50であれば,onnx sessionで推論した画像をcv2で読み込んでInputに指定すれば結果が返る.
今回はtrain_one_epoch(model, optimizer, data_loader, device, epoch, print_freq=100)後のmodelだと推論結果がnullになった.

そこでevaluate(model.cpu(), data_loader_test, device=device)の際にモデルに入力される画像の中身を確認した.
model(images)で推論されているのだが,そのimagesをprint
・Resnet
f:id:amakazeryu:20220115115359p:plain

・自前データセットでの学習後の推論
f:id:amakazeryu:20220115115532p:plain

ここで自分の愚に気付く!!
画像を正規化すれば解決しました.
以下推論コード

# Writer : Yu Yamaoka
import onnxruntime
import numpy as np
import torch
import cv2

#モデルの読み込み
onnx_session = onnxruntime.InferenceSession(model_path)# ex:) model_path = "model.onnx"

#入力画像の用意
input_image = cv2.imread(file_name) # file_name = "picture.png"
input_image = input_image/255 #正規化(笑)
input_image = input_image.transpose(2, 1, 0).astype('float32') #モデルのInputの型と合うように転置すればいいです
input_image = input_image.reshape(1,3,2048, 1084)#Ajust model input shape

#モデルへのInputとOutputを規定
input_name = onnx_session.get_inputs()[0].name#NetronでInputの名前を確認してもいい.
output_name = onnx_session.get_outputs()[1].name#欲しい出力を決める,Netronで確認するのが楽.

#推論
result = onnx_session.run([output_name], {input_name: input_image})

#推論結果整形
result = np.array(result).squeeze()
print(result)

本日はここまでです.お疲れ様でした.

Pytorchで学習・保存したonnx(format version6,opset=11)をVisual Studio2019(C#)のUWPで読み込む

備忘録.
Pytorchで学習したモデルをtorch.onnx.exportで保存後,Visual Studioでmodel Outputが返ってくるまでのお話.

環境:

Windows 10 2004 (OSビルド 19041.1052)
onnx opset =11
onnx format version = 6
Visual Studio 2019(UWP)
CUDA : 10.0
cuDNN : 7.4.1
torch 1.8.1 + cu102
torchvision 0.9.1+cu102

onnxの保存

色々とハマる点がある.
・ModelのInput
・opsetの指定
・Model,InputをCPU上で扱うかGPU上で扱うか
今回モデルの入力にはカラー画像を用いたので,縦横(width*heightピクセル)の画像をinputにし,modelはcpu()上に乗せ,opsetは11でexportした.cuda()にすればGPUで使えるようになるが,Inputとmodelは同じハードウェア上で用いなければErrorを吐かれるので注意.

import torch
input = torch.rand(1, 3, width, height)
torch.onnx.export(model.cpu(), input, "model_name.onnx", opset_version = 11)

ひとまずこのonnxをNetronで確認してみよう. f:id:amakazeryu:20210706120748p:plain f:id:amakazeryu:20210706120730p:plain ここまで確認できればonnx出力はお終い.

モデルのラッパーファイル作成

色々と沼るポイントにモデルのインターフェースを合わせる(InputとOutputを合わせる)作業がある.こちらをショートカットさせてくれるのがWindows Machine Learning Code Generatorである.
ツールバー拡張機能から,インストールして用いるのが手っ取り早い.
f:id:amakazeryu:20210706121355p:plain f:id:amakazeryu:20210706121440p:plain

自分が半日死んだポイントはVisual StudioのプロジェクトファイルのPath問題である.一応参考リンクを.

amakazeryu.hatenablog.com

さて,ラッパーファイルであるmodel_name.csがVisual Studio上に無事出来上がったら覗こう.
とりあえずInputとOutputを見る.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Media;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.AI.MachineLearning;
namespace MNIST_Demo
{
    
    public sealed class mrcnn_0702Input
    {
        public TensorFloat images; // shape(1,3,width, height)
    }
    
    public sealed class mrcnn_0702Output
    {
        public TensorFloat output3550; // shape(-1,4)
        public TensorInt64Bit output3213; // shape(-1)
        public TensorFloat output3211; // shape(-1)
        public TensorFloat output3788; // shape(0,1,width, height)
    }

変更ポイントは主に二つ.Inputの型をイメージに,opset=11を読み込めるようにMicrosoft.AI version≧1.8.0を使うこと.

//using Windows.AI.MachineLearning;
using Microsoft.AI.MachineLearning;
public sealed class mrcnn_0702Input
{
    //public TensorFloat images; // shape(1,3,2448,1920)
    public ImageFeatureValue images; // shape(1,3,2448,1920)
}

とまあ公開できる情報は現状ここまで.具体的なコードはオープンにできないが,時間が経ったらできるかもしれない. 引っかかるポイントをクリアさせられていたら嬉しい.

"ModelGenがNull" UWP(C#,Visual Studio2019)でopset≧10,format version≧6のonnxモデルを読み込むときの対処

環境
Windows10 2004 OSビルド19041,1052
onnx opset = 11
onnx format version = 6
Visual Studio 2019

問題

Pytorchで作成したonnxをVisual Studio(C#)で動かすチュートリアルを試していた.

github.com

こちらのWindows-Machine-Learning-master\Samples\Tutorial Samples\MLNET and Windows ML
が今回の主人公である.

実行すると, f:id:amakazeryu:20210703144057p:plain NullReferenceExceptionを頂戴したときのおはなし.

調べると読み込んだはずのModelがNullとなっている.
f:id:amakazeryu:20210702145134p:plain
理由はonnxを作成したときのformat versionopsetの違いである.
NugetのライブラリやOSのバージョンによっては対応していないopsetとformat versionがある.
こちらのエラーメッセージもvisual studio2019のコンソールに表示される筈.   WinRT Failed to load model with error: Unknown model file format version.

解決策

NugetパッケージでMicrosoft.AI.MachineLearning 1.8.0を入れて,
f:id:amakazeryu:20210702145914p:plain MainPagexaml.csとMNIST.cs(モデルのラッパーファイル)の両方で,
using Windows.AI.MachineLearningをコメントアウトし,
using Microsoft.AI.MachineLearning;を入れる. f:id:amakazeryu:20210702150009p:plain

Windows.AIとMicrosoft.AIは各種変数の名前が同じなので競合します.
ですのでwindowsの方を消しましょう.

補足

onnxの各バージョンの確認をするにはこちらのNetronを使うのが手っ取り早い github.com

モデルを読み込んだら赤丸でopsetとformat versionをチェックできる

f:id:amakazeryu:20210702145604p:plain

確認欄は下図のかんじ.

f:id:amakazeryu:20210702145744p:plain
MNIST.onnx
こちらのformat versionと,Importsのai.onnx v○○の○○がopsetに相当している.

MLNET and Windows ML にあるonnxモデルは

f:id:amakazeryu:20210703144316p:plain
bestModel.onnx
となっており,opset=9,format version = 6で,windows.AIでは読み込めない.

UWPの知見と人材が少なすぎて泣けてくる.

Windows Machine Learning Code GeneratorでAssetに追加したonnxファイルからcsファイルが生成されないことの解決策

環境

Windows OS 1803以降
・Windows10 SDK 10.0.17763.0以降

developer.microsoft.com

Windows Machine Learning Code Generator (VS2019 version)

marketplace.visualstudio.com

Visual studio 2019以降

問題

Assetにonnxファイルを入れても,csファイルが生成されない. f:id:amakazeryu:20210528190930p:plain
プロパティをコンテンツにしても駄目.

解決策

プロジェクトの保存場所にアルファベット以外の文字が入っている場合,ジェネレーターが起動しない. したがってCドライブ直下などに置けばonnxの取り込みと同時にcsファイルが生成される.

f:id:amakazeryu:20210528191337p:plain
自分の場合はD直下に置いた.

そしてAssetにonnxを入れると自動生成される. f:id:amakazeryu:20210528191425p:plain

以上.
このバグで半日溶けたので,同じ悩みを持った人がいたら救いになって欲しい. アメリカに開発中心があると,こういったバグになかなか気付けないものですね.

MaskRCNNの学習で複数のGPUを用いるときの対処 備忘録 

MaskRCNNでconfig.pyのGPU_COUNT>1にすると,バグるのでその対処方法の自分用備忘録.

バージョン・環境
OS : windows 10
GPU : GeForce GTX 1070
CUDA : 10.0 CUDA Toolkit 10.0 Archive | NVIDIA Developer
cuDNN : 7.4.1 cuDNN Archive | NVIDIA Developer

TensorFlow_version(1.14.0)
python version(3.6.5)
keras version(2.2.4)
numpy version(1.16.4)
cv2 version(4.5.1)

問題

Mask_RCNN/mrcnn/config.py
GPU_COUNT=1→GPU_COUNT=x(x>1)とすると,

RuntimeError: It looks like you are subclassing Model and you forgot to call super(YourClass, self).__init__(). Always start with this line.

に出会える.
結論,mrcnn/parallel_model.pyのclass ParallelModel(KM.Model)を下記のように書き換える.

class ParallelModel(KM.Model):
    def __init__(self, keras_model, gpu_count):
        super(ParallelModel, self).__init__()
        self.inner_model = keras_model
        self.gpu_count = gpu_count
        merged_outputs = self.make_parallel()
        super(ParallelModel, self).__init__(inputs=self.inner_model.inputs,
                                            outputs=merged_outputs)

実際のところ,関数定義の下にsuper(ParallelModel, self).init()を挿入しているだけ.
お次は学習器のコードに戻り,学習コード前にTensorFlowが使用するGPU領域の制限を解除するコードを付け加える.

#GPUの制限解除コード
import tensorflow as tf
import keras.backend.tensorflow_backend as KTF
 
config = tf.ConfigProto()
config.allow_soft_placement=True
session = tf.Session(config=config)
KTF.set_session(session)


#学習コード
model.load_weights(COCO_MODEL_PATH, by_name=True,
                       exclude=["mrcnn_class_logits", "mrcnn_bbox_fc",
                                "mrcnn_bbox", "mrcnn_mask"])

model.train(dataset_train, dataset_val,
                learning_rate=0.01,
                epochs=1,
                layers='heads')

以上.

麻雀 点棒自動計算器を制作

最近では主流となっている麻雀の点棒自動計算機を作ってみた備忘録を残す.
ロードセルを使った重さで計測する手法,抵抗値を計測する手法,磁気センサを用いた手法,ラズパイとカメラによる画像認識などを考えた末,
抵抗を使ったやり方が手軽でノイズに強く,精密な計算ができると考え,抵抗案を採用.
問題の骨子は並列に配置された抵抗を如何に計測するかと,計測した抵抗を如何にLEDで出力するかであろう.

環境
windows10
mbed LPC1768(https://os.mbed.com/)

ステップ1 点棒の数をカウントする機構

まず抵抗をマイコンで計測できなければ話にならない.
用いたマイコンはmbed LPC1768.
f:id:amakazeryu:20210216054624p:plain

抵抗値をマイコンで計測

まずテストとして適当にアナログピンp20を指定する.以下のコードで指定したピン20の電圧を計測する.

#include "mbed.h"
 
AnalogIn myInput(p20);
Serial pc(USBTX, USBRX);
 
int main() {
    while(1) {
        pc.printf("%f\n", myInput.read());//PCでシリアル出力
        wait(1);//1s待つ
    }
}

配線はこんな感じ.1MΩをp20と3.3V-Outに割り込ませて,p20の電圧を計測する.

f:id:amakazeryu:20210216065634p:plain

USBでPCと接続し,値をチェックする.
USBポートを使ったシリアル通信用のドライバを入れることを忘れないようにしよう.

mbed.org

tera termでシリアルの返り値を見ると,まあ取れているっぽいゾ.

f:id:amakazeryu:20210216070441p:plain

こちらの値が1になると,指定したpinはグラウンドに対して3.3Vの電圧を有することになる.

分圧で抵抗を求める回路の作成

マイコンは電圧計測を主に行えるため,分圧の式から抵抗xを求める.
回路図は以下の通り.

f:id:amakazeryu:20210216155738p:plain

回路から,抵抗にかかる電圧V_rは,V-outから出力された電圧V_oと,定格抵抗A[Ω]を用いて,

V_r = \dfrac{x}{x+A}*V_o

と求まる.
したがってpin20で計測される電圧V_p

V_p = V_o - V_r

となるであろう.xを可変抵抗とみなしたときV_pが0~1まで満遍なく散らばれば,より多くの本数の分類が可能となる.
手持ちの100k[Ω]を使って10本の本数のカウントを想定してみた.

f:id:amakazeryu:20210216163827p:plain

これでは不安という方には数値解析を行おう.
解析的に解かねばならないのは,0~0.01の範囲でもっとも増加幅の良いグラフf(x)= \dfrac{x}{x+a}のパラメータaを決定すればよい.
Grapesというソフトが無料で素晴らしい.パラメータaをいじいじして,よさそうなグラフを見つけよう.(定格抵抗aを0から1まで)

f:id:amakazeryu:20210217210800g:plain

おそらく100kを点棒に使う場合,定格抵抗は25k[Ω]が良さそうだ.無論総当たりで数値解も出せるわけだが,評価関数の用意などしなくてはならず,今回はそこまでやらん!

次回予告

それでは次に抵抗付き点棒を用意しどこまで本数を区別できるのかのテストをしてみる.
計測した電圧値はもちろんノイズの影響を受けてばらつく.その平均を取って,うまく抵抗本数を推定できるかが勝負だ.
あまりにノイズが厳しい場合,オペアンプなどの増幅回路を投入して,抵抗の変化に対して電圧が反応しやすいようにする必要がある.

入力側が終わればあとは7セグにいかに出力するかだ.

セーリングのコース理論 タクティクス編 戦術と戦略の違いから

前置き:コース理論における2つの視点

コースを勉強するといっても実は二通りある.ずばりストラテジー(戦略)とタクティクス(戦術)である.ストラテジーとは戦略を意味し,マクロな視点からコースを組み立てることを指す.逆にタクティクスとは戦術を意味し,ミクロな要所要所の視点でコースを組み立てることを指す.また他艇という言葉を使う際に,ストラテジーでは往々にして艇団を想定するが,タクティクスでは艇一つないし二つ程度を想定する. f:id:amakazeryu:20200907151926p:plain

まず最も大切なこととして「タクティクスはストラテジーに勝てない」ということを断っておく.戦略でまさっていたのに戦術で負けたというのはコードギアスのスザク&ランスロットほどのぶっ壊れ戦術兵器が登場しない限りあり得ない.またぶっ壊れ性能がある敵とは基本的に戦わないでいいと思う.

さてストラテジーでは「どちらの海面にいくのか?」や「アプローチはどちらからするのか?」といったスタート前に組み立てる要素が多い.スタート前に「ブローがどちらにあるのか?」は当然ペアで協議すべき内容だろう.また当日の海上における観察だけでなく,陸にいるときの天候の予想やパターンの把握なども必要になってくる.ストラテジーのほとんどは海の上ではなく,陸で完結することの方が多い.そしてこれは例えば予選突破がかかったレガッタなどで,どの艇をマークするのかといったことも含まれる.従ってストラテジーとは「スタート前に決まっている予定のコース」と言い替えてもよい. ストラテジーを組み立てるのは基本的にスタート前,二上ならば一下ランニングの暇な時間帯である.決してクローズ帆走中にストラテジーは決まらない.常にスタート前あるいはランニング中に次のクローズのストラテジーを組み立てる癖をつけてほしい.

上記を理解した上でさらに順位を一つ一つ伸ばそうという考え方がタクティクスである.一点で泣くことはよくある.特に実力が伯仲するときは大きなコースミスが出にくく,タクティクスで差が出ることが多い.小さな勝利を積み上げていくのは本当に大切だ.しかしウインドストラテジーなどを見てもセーリングのタクティクスを言語化している文書はほとんどない.これはうまい人は意識せずに行っている代表格だからだと思う.「こういう時はこうする」というのが何度とないレース経験から導かれているのだ.しかし学生の時間は有限で,レース経験も稼げない.そこで今回知識化しようという試みを行いたい.

・クローズタクティクス

代表的なクローズタクティクスを紹介する.中級者ならだれでも知っているがおさらいとして見てほしい.

1.リーバウ

クローズのタクティクスとして最も有名なのがリーバウ(下受け)タックだろう.並んで走る際に,先行している下の艇が風をセールでベンドさせることで,後ろの艇がヘダーの風を受けることを利用している.まず最初に知っておくべき戦術だ.

f:id:amakazeryu:20200907144504p:plain

2.ドライブ⇒ブランケット

これも基本的な戦術.470は基本的にドライブメインな艇種だと思うが,特に潰したい下艇がいるときはドライブで被せていくという戦術がある.これは普通に帆走していても起こることなので特段戦術感はない.行きがかり上被せてしまうことの方が多い.通称ごめんなさいね~ブランケット. f:id:amakazeryu:20200907145052p:plain

3.ピンチング⇒リーバウ

スタート直後やレイライン,そして逃げのタックを打ちたくないときに出てくる.ドライブメインな470だが,あえてピンチングして上艇を潰さなくては自艇がブランケットポジションに追いやられてしまうことが多い.ボディアクションと合わせることで速度を落とさず角度だけ稼ぐこともできるので練習しよう.通称上殺し.なんかかっこいい.

f:id:amakazeryu:20200907145231p:plain

4.上マークアプローチでのドライブ⇒ゾーン内タックへのプロテスト

これも学生の大会だとよく見かける.上マークにオーバーセール状態でフェッチングしている際,余裕をもってドライブ目でアプローチし,タックした相手艇をラフして避けて(avoid)プロテストコール.ゾーン内でタックした艇は後続艇をラフさせてはいけないルールを逆手に用いる手段だ.ジャッジの人曰く95%でスタボ艇が勝つというぐらい注意しなくてはいけない審問案件なのでポートアプローチ時は注意しなくてはならない. f:id:amakazeryu:20200907150538p:plain

箸休め

以上4つは基本的なのでほとんどの中級セーラーは知っていると思う.ほかにもミートするポート艇に「前通っていいよコール」やサンドイッチ下受けなどあるがとりあえずクローズはこのあたりでおしまいにしたい.本番はランニングである.なぜかランニングになると「戦術なんてブランケット以外ないよ」となる人が多い.全くの的外れである.ランニングの方が戦術でやることは多い.そして上手い人はこの戦術を意識下で行ってしまうので,経験者とランニングで差がついてしまうわけだ.

・ランニングタクティクス

とはいえ一にも二にもブランケットである.そしてブランケットを気にせず安心できるポジションはクリアアヘッド・アスターンしかない.絶対にクリアアヘッド・アスターンになるまで他艇へのアテンションを下げてはいけない.これを基本としてほしい.

1.ブランケット回避(avoid)のジャイブ

ランニングのブランケットを被せる旨みを覚えた中級者は,上り目で被せてくることが非常に多い.そのときに相手が上ったタイミング(あるいは自分も少し上って相手にさらに上らせることもある)で,ジャイブすることで「はい,さようならー」とする戦術である.VMGを稼げない上りをしている艇はジャイブしても絶対にこちらをブランケットに入れられない.加えて相手艇に対してポテンシャルも獲得できるというブランケット症候群(ブランケットを入れて抜きたくて仕方のない人たち)対策にはうってつけの戦術である.通称さよならジャイブ. f:id:amakazeryu:20200907155703p:plain

2.権利主張⇒下突破

下から相手艇をまず突き上げる.そのあとパンピングによってサーフィングして速度を得て波の力でブランケットを外していく戦術.同じ艇種で何もせず下突破はほぼできない.そこで相手を上らせることでまずポテンシャルを稼ぎ,ブランケットコーンの幅を小さくする.最小限のブランケット領域を帆走する必要はあるので,風の力よりもその時間では波の力を有効活用する.多少無理やりにでもサーフィングすればブランケット内でも速度を維持できるため下突破を行えるのだ.わざと上らせることで相手艇に対してポテンシャルを稼ぎジャイブする戦術もある(筆者もよくやられました涙).通称突き上げ下突破. f:id:amakazeryu:20200907155142p:plain

終わりに

このほかにもマークアプローチの際に3万個ぐらい戦術があるのだが,正確なレイラインの読みを必要とするのでまた後日にしたい.クローズでも残り一万個ぐらい戦術があるが,お声があれば出していきたいと思う.それでは本日は以上です,お疲れさまでした.