とある大学生の勉強メモ

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

回転・鏡像反転用のデータ拡張用コード Train-Valid分けコード

自分用 画像Pathを指定するとデータ拡張してくれるコードと,
画像群フォルダのPathを指定するとValidとTrainの2つに分けてくれるコード.

下記を使うのが一般的かもしれないが........ albumentations.ai

from glob import glob
import random
import os
import shutil

#指定したデータセットPATHの中にTrainフォルダとValidフォルダを作り,VALID_Ratioで指定した量で分ける
def Split_Train_Valid(DatasetFolderPath, filetype = ".png", VALID_RATIO = 0.2):
    """
    Args:
        DatasetFolderPath (string): 
        filetype (str, optional): 画像の型. Defaults to ".png".
        VALID_RATIO (float, optional): 検証用のデータ数割合. Defaults to 0.2.
    """
    files = glob(os.path.join(DatasetFolderPath, "*" + filetype))
    random.shuffle(files)
    VALID_DATA_NUM = int(len(files) * VALID_RATIO)
    
    #make dir
    Train_Dir = os.path.join(DatasetFolderPath, "Train")
    Valid_Dir = os.path.join(DatasetFolderPath, "Valid")
    if os.path.exists(Train_Dir)==False:
        os.mkdir(Train_Dir)
    if os.path.exists(Valid_Dir)==False:
        os.mkdir(Valid_Dir)
    
    #copy file
    for i, file in enumerate(files):
        if i<=VALID_DATA_NUM:
            shutil.copy2(file, Valid_Dir)
        else:
            shutil.copy2(file, Train_Dir)
#Writer : Yu Yamaoka
#回転と鏡像で8倍水増し用のコード

import cv2

def Augment_ByRotationMirror(ImageFilePath, filetype = ".png"):
        #every 90 degree rotation * mirror = 4 times * 2times = 8times(MAX)
        img = cv2.imread(ImageFilePath)
        width, height, _ = img.shape
        
        #Mirror
        mirror_img = cv2.flip(img, 1)
        cv2.imwrite(ImageFilePath.replace(filetype,"") + "_mirror" + filetype, mirror_img)
        
        #If width and height is NOT equal, can't do aug by 90 and 270 rot.  
        img_180 = cv2.rotate(img, cv2.ROTATE_180)
        cv2.imwrite(ImageFilePath.replace(filetype,"")+"_180" + filetype, img_180)
        cv2.imwrite(ImageFilePath.replace(filetype,"") + "_180mirror.png", cv2.flip(img_180, 1))
        
        if(width == height):
                img_90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
                cv2.imwrite(ImageFilePath.replace(filetype,"")+"_90" + filetype, img_90)
                cv2.imwrite(ImageFilePath.replace(filetype,"") + "_90mirror" + filetype, cv2.flip(img_90, 1))

                img_270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
                cv2.imwrite(ImageFilePath.replace(filetype,"")+"_270" + filetype, img_270)
                cv2.imwrite(ImageFilePath.replace(filetype,"") + "_270mirror" + filetype, cv2.flip(img_270, 1))

SSH WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

以下をクライアント側で叩いてもう一度SSH打ち直す.

クライアント側

ssh-keygen -R IPアドレス

ホスト側

$ sudo rm data/* -rf
$ rm data/* -rf

サーバ再起動すると今まで使っていたVScodeSSH Configで入れなかったので,Config内容を消しもう一度SSHし直した. しかし入れなかったので上記を打って解決しました.

下記記事参考にホスト側でプロセスをKILLしまくればつながる.

ps -ef | grep 6c3e3dba23e8fadc360aed75ce363ba185c49794
kill ID

sabakunotabito.hatenablog.com

Docker pull→run→exit→psからID取得してexec

自分用です.

Docker Image検索リンクから欲しいImageをPull

$ docker images

からREPOSITORYとTAGの部分を探して下記を埋める. -vの<directory_in_server>マウント場所を指定. pはポート番号で使っていないやつを選ぶ.

$ docker run -it --rm \
--gpus '"device=<GPU_ID>"' \
-p 8000:8000 \
-v <directory_in_server>:/workspace \
REPOSITORY:TAG 
(docker)$ exit

で抜けてから,

$ docker ps

で立ち上がっているCONTAINER ID確認,

$ docker exec -it CONTAINER ID /bin/bash

でもう一度は入れる.

学習したモデルの推論結果が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

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