FC2ブログ

頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


daeファイルはアプリのビルド時に圧縮される?

Blenderで作成したdaeファイルをNSXMLParserでパースしたい

↑これにからんでなんとかdaeファイルのテキストを得ようとしてるんだが、、、
どうやらビルド時にdaeファイルは圧縮されてるようでテキストを得るのが無理っぽい、というか俺にはできそうにない。
順に書いていくと、、、

daeファイル


↑Blenderでdaeファイル(COLLADA)を出力したとする。どこでもいいんだけど、デスクトップにあるとする。

daeファイルをテキストエディタで見る

↑このdaeファイルをテキストエディタで見ると、xmlファイルだ。

Xcodeに持って来たdaeファイル

↑そのファイルをXcodeに持ってくる。参照じゃなくてコピーする。
するとプロジェクトを置いてるところにdaeファイルがコピーされる。

XcodeのShow in Finder

↑XcodeのShow in Finderで確認可能。ただ、アプリ実行時にはこのファイルを直接使うわけじゃない。
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"frog5" ofType:@"dae"] ;
}
↑macOSアプリ内でこのdaeファイルを使おうとこのようにしてパスを得る。
実行時にこのパスを確認すると
/Users/yama/Library/Developer/Xcode/DerivedData/180807Outline-dmdarosyorbzvzennfoggerorzuk/Build/Products/Debug/180807Outline.app/Contents/Resources/frog5.dae
なんて出てくる。さっきShow in Finderで見たのとは別なところにある。.appのさらに下側にバンドルされるみたいでFinderなんかではもう見えない。

ビルドした時点で多分daeファイルは圧縮されてる。
アプリで上記パスのファイルを copyItemAtPath:toPath:error: でコピーして(※1)そのファイルをFinderで確認すると容量が
627,908バイト→227,491 バイト
と大きく圧縮されていた。
この圧縮されてるファイルをテキストエディタで覗いてみると

圧縮されたdaeファイル

↑こんな感じで元のxmlファイルとなにやら違う。APPLE独自のものになってるんだろうか。

つまり、Xcodeに持って来たファイルはビルド時にバンドルされるわけで、そこでdaeファイルは圧縮される。そうなるとそのファイルを「どーせ、xmlファイルだろ?」と思ってパースしようとしても無理!
ということだと思う。まずいな、、、😖


※1
copyItemAtPath:toPath:error: メソッド実行時に圧縮されてるかもと思ったけど、コピーする前のファイルもxmlとしては読めないし、外部のdaeファイルをコピーしても圧縮しない(パースできる)。やっぱりビルド時に圧縮してるんだろう。


Kerasのコールバック関数内で係数にアクセスする

Kerasで係数の保存と読み込み
↑Kerasでの学習結果をファイルに保存はできた(※1)。

それらを強化学習用にmacOSアプリで読み込みたい。
調べるとcoremltoolsなるものを使ってmlmodelに変換できるらしいのだが、、、噂通り難易度が高く、、、pipでインストールするもエラー出まくりで、、、はい、諦めました、、、😭

いや待て、そもそも学習中の係数にアクセスしてそのまま保存してやればいいんじゃね?モデル構成なんかいらんし、実際強化学習となればいちいち変換なんてやってらんねえぞ。
ということで

(1)学習中に呼び出される関数の設定
(2)コールバック関数で係数にアクセス

この二つが可能か調べた。


(1)学習中に呼び出される関数の設定
これは前記事にあるようなコールバック関数でよさそう。リファレンス(※2)にサンプルコードもある。
class TestCallbackFunction(keras.callbacks.Callback) :
    def on_epoch_end(self, epoch, logs={}) :
        print("on_epoch_end [%d]"%epoch)
↑呼び出されてほしい関数を作っておいて
test_cb = TestCallbackFunction()
↑設定して前記事と同様fitで渡してやる。

すると、1epoch終了ごとにこの関数が呼び出されてる!よしよし。


(2)コールバック関数で係数にアクセス
いろいろ調べた結果、、、
class TestCallbackFunction(keras.callbacks.Callback) :
    def on_epoch_end(self, epoch, logs={}) :
        print("on_epoch_end [%d]"%epoch)
        lastLayer = self.model.layers[-1]
        print(lastLayer.get_weights())
↑モデルオブジェクトにはself.modelでアクセスできる。
modelを構成する層はlayersで取得できるので、一番最後の層を
lastLayer = self.model.layers[-1]
として取得してる。-1で最後のを指定できるって最近覚えました。
layerの係数は get_weights() でアクセス。

Kerasの出力

↑実行結果、とりあえずウエイトとバイアスの出力まではできた!
それにしてもKerasの日本語リファレンスは親切だねえ。


※1
前記事ではモデルと学習後の係数を別々に保存してるけど実は一度にできるらしい。
model.save(f_dir + “model_and_weights.h5”)
↑これでモデルも係数もいけた。

※2
コールバック - Keras Documentation


Blenderで作成したdaeファイルをNSXMLParserでパースしたい

macOSアプリでdaeファイルの中身を調べたいことがあった。daeファイルって、基本xmlファイルだからテキストエディターなんかで中身を見ることができる。
NSXMLParserを使えば中身を取得できるのだが

daeをXcodeにインポート

↑このようにdaeファイルをXcodeにインポートした場合はうまくできなかった(外部に置いている場合(※1)はできてた)。

やりたいことは、
(A)SceneKit(※2)で使い、かつ
(B)daeファイルをパース
を両立させたいわけだ。

Bのパースするためのコードはこんな感じ↓
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@“frog5” ofType:@“dae”];
    NSURL *targetURL = [NSURL fileURLWithPath:filePath];//(※3)

    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:targetURL];

}
ファイルの有無をチェックするとちゃんと有る、となるのだがパースができない。
手打ちで拡張子をxmlもしくはscnに変更するとパースは可能になったのだがAのSceneKitで使うことはできなくなる。

Convert to dae to scn

↑ならば、とdae→scnにちゃんと(?)Xcodeの機能
Editor / Convert to SceneKit scene file format (.scn)
で変換してやると、、、当然SceneKitで使えるが、パースができなくなった。

ならば、とdae→NSStringにすればNSXMLParserの
initWithData:
でパースできんじゃね?と思ったけどdaeファイルの文字列を取得できなかった(※4)。


結局、ABを両立させることはできなかった。
回避策としては、一度どこかにdaeファイルを保存してからそれを読み込むか、Xcodeインポート時にdaeとxmlの二つ作成するか(試してないので動くか不明)。まあどちらにせよダサい、ダサすぎる、、、



※1
外部のdaeファイルを指定するコード。
↓これだと問題ない(ABともいける)。
{
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:targetURL];//パース可能
}
※2
daeファイルをSceneKitで使うのに↓のようにしてSCNSceneオブジェクトを得る必要がある。
{
    SCNScene *daeScene = [SCNScene sceneNamed:daeFileName];
}
しかしこの形式で使えるファイル形式は定められていてリファレンスによると↓のようになってる。
SCNSceneリソース形式

※3
↓この方法でNSStirng→NSURLに変換すると動かないってのは何回もはまってきたんだけど今回の問題はこれじゃない。
{
    NSURL *targetURL = [NSURL URLWithString:filePath];
}

※4
↓これだとdaeファイルの文字列を取得できなかった。
{
    NSString *content = [NSString stringWithContentsOfFile:filePath
                                                  encoding:NSUTF8StringEncoding
                                                     error:&error];
}



macOS High Sierra 10.13.6
Xcode 9.4.1


KerasでのTensorBoard関連の設定をする

Kerasで係数の保存と読み込み
↑この続きというわけでもないけど似たようなコードになる。
KerasでもやっぱりTensorBoardに記録したいよねってこともあるだろう、ということでそのあたりのメモ。
tb_cb = keras.callbacks.TensorBoard(log_dir=log_filepath, histogram_freq=1)
↑コールバックでTensorBoard用にデータ保存せえよって指定できる。

initial_epoch = 0
epochs=5
とすれば

Kerasでの計算結果出力

↑このように1〜5の5エポック分の計算を行う。

TensorBoardの画像1

↑ただ、TensorBoardだと0から表示されるっぽい。

TensorBoardの画像2

↑係数ファイルを読み込んで継続した場合もOK。よしよし。
それにしても記述がめっちゃ少なくなるな、、、恐るべし、Keras!



ところで、ここまでやってみた内容(モデルと係数の保存・読み込み、TensorBoardの使用)だと
import keras.backend.tensorflow_backend as KTF

old_session = KTF.get_session()

with tf.Graph().as_default():
    session = tf.Session('')
    KTF.set_session(session)
といったTensorFlowに関係するらしき記述が必要なくても意図通りに動いてるみたいなんだけどいいんかな?
[TF]KerasからTensorboardを使用する方法
↑例えばこちらなど、調べるとそういう記述がされてるのをよく見るんだがなぜ必要なのだろう、、、?


Kerasで係数の保存と読み込み

[TF]KerasでModelとParameterをLoad/Saveする方法
Kerasのcallbackを試す(modelのsave,restore/TensorBoard書き出し/early stopping)

↑この辺りを参考に係数の保存、読み込みを試してる。
というのも機械学習はMacが空いてる時間に計算させておく、、、ってのを何日もやったりするので、どうしても適当なタイミングで保存させ、起動時には最新の係数を読み込めるようにしたいのだ。
{
    json_string = open(os.path.join(f_model, model_filename)).read()
    model = model_from_json(json_string)
}
↑モデルの保存、読み込みもやってみたけどモデルの構成とかなので自分には必要性低そう。必要なのは係数の方なので。
{
    model.load_weights(os.path.join(f_model,weights_filename))
}
↑係数を読み込む。
{
    fileName = 'keras_weights.{epoch:02d}-{loss:.2f}-{val_loss:.2f}.hdf5'
    cp_cb = ModelCheckpoint(filepath = f_model+fileName, save_weights_only=True)
}
↑計算途中に、係数を保存するためのコールバックの設定。ファイル名にエポックの番号を使えるのは便利かもしれない。
{
    history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    initial_epoch = 0,
                    epochs=3,
                    callbacks=[cp_cb],
                    verbose=2, 
                    validation_data=(x_test, y_test))
}
↑訓練を実行する部分の記述。

initial_epochと組み合わせると,epochsは「最終エポック」として理解されることに注意してください. このモデルはepochsで与えられた反復回数の訓練をするわけではなく,単にepochsという指標に試行が達するまで訓練します.
↑リファレンスに書いてるようにinitial_epochとepochsを設定すればいいんだけど、ちょっとややこしいのが、例えば
initial_epoch = 0
epochs = 3
で実行したら上記の係数保存方法だとepochは1,2,3として保存されてる。

Kerasで生成した係数ファイル1

↑こんな感じ。

initial_epoch = 1
epochs = 3
にすると

Kerasで生成した係数ファイル2

↑epochは2,3の二つの係数ファイルが保存されてる(リファレンス通りepochsは回数じゃないね)。

initial_epoch + 1 の値から保存されるのでさらに継続する場合は
initial_epoch = 3
epochs = 7
とすると、、、

Kerasで生成した係数ファイル3

↑4〜7のファイルができた。

ということで、読み込んだ最新の係数ファイルのエポックの数値をinitial_epochとして与えればいいな。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad