頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


KerasとMPSで同じ計算をする(1)

Kerasのコールバック関数内で係数にアクセスする
↑ここで学習時の係数を得る事はできた。この続き。

保存したKeras(Python)の係数がMetal Performance Shaders(macOSアプリ)で使えるかを確認する。単にMPS側でロードできます、だけじゃ不十分なので簡単な計算をして同じになるかを確認する。

(1)Keras側
まずはKeras側で計算する。
# モデル作成
model = Sequential()

model.add(Dense(4, activation='relu', input_shape=(2,)))
model.add(Dense(3, activation='relu'))

layer1 = model.layers[0]
layer2 = model.layers[1]

print("%s -> %s"%(layer1.input_shape , layer1.output_shape)) # (None, 2) -> (None, 4)
print("%s -> %s"%(layer2.input_shape , layer2.output_shape)) # (None, 4) -> (None, 3)
↑入力が2個で→4→出力3個になる。どちらも全結合層で構成。活性化関数はどちらもReLUにしとく。

学習は行わず単に行列の計算なので、係数の初期値を与えておく。
w1 = np.array([[-0.40,  0.96,  0.57, -0.40], [ 0.20 , -0.03, -0.09,  0.30]] )
b1 = np.array([0.1 , 0.2, 0.3,  0.4])
a1 = [w1,b1] 
layer1.set_weights(a1)

w2 = np.array([[-0.72,  0.36  , -0.34 ],
                       [ 0.18, -0.35,  0.85  ],
                       [ 0.48, -0.68, -0.48],
                       [ 0.13, -0.48,  0.64]])
b2 = np.array([0.5 , 0.6, 0.7])
a2 = [w2,b2]
layer2.set_weights(a2)
↑これで係数の初期値を与える。
# 入力を設定
x_train = [ [[0.1 , 0.2 ] ]]

# 実行
result = model.predict_on_batch(x_train)
print("resutl=%s"%result)
↑入力を設定して、実行する。

resutl=[[0.69752 0.10237998 1.01858 ]]

↑結果がこのように表示された。
w1_save = layer1.get_weights()[0].transpose(1, 0)
b1_save = layer1.get_weights()[1]

with open(fileName_w1, 'wb') as f:
    f.write(w1_save.tobytes())
with open(fileName_b1, 'wb') as f:
    f.write(b1_save.tobytes())
↑この時の係数保存部分はこんな感じでウエイトの方は転置行列にしてから保存。そういうもんらしい。TensorFlowの時もそうだったな。
二層目も同様に保存する。

続く。





休暇村近江八幡のキャンプ場に行ってきた

キャンプ場 | 休暇村近江八幡【公式】
↑滋賀県にある近江八幡休暇村のキャンプ場に行ってきたのでメモ。
ちなみに二泊目は休暇村のホテルに宿泊。


【キャンプ場についてのメモ】

フリーサイトが予約できなかったので、テント付きの手ぶらプランで行った。

タープ付きテント

↑こういうデラックステント(タープ付き)だった(写真撮ってなかったので公式より)。

テントの床

↑中はこんな感じで板間に薄い断熱板のようなものがひいてある。
手ぶらプランでは毛布が1枚あってそれで寝ることになる(まくら、シュラフとかなし)。これがなかなか難易度が高くて、、、あまり寝心地が良いとは言えなかった。

テントの準備や後片付けをしなくていいのは楽なんだけど、つまりずっとテントが張られているわけで、蜘蛛の巣があっちこっちに張られていたりする。備え付けのテーブル、椅子も同様。虫嫌いの息子が自分で張るテントの方がいいとずっと言ってた。

BBQ

↑夕食は手ぶらプランのBBQ(お肉以外にも焼きそばもあった)だったんだけど、炭の火力が弱く支給された着火剤を使い切っても炭がいこってこない。かろうじてカルビを食べてる間は落ちる油のおかげか食べることができたけど、他のお肉の時には全然だめだった。仕方なく、持参してた焚き火セットを使って、残りのお肉と焼きそばを作った。なお、焼きそば用の鉄板も支給される。

ホットサンドを焼いているところ

↑朝食はホットサンドを選択。ホットサンドにはポテトサラダを挟む。焼くためのコンロ一式も貸してくれる。これで焼くこと数分、、、

完成したホットサンド

↑ホットサンドって初めてだったんだが、これはおいしいもんだな。ホットサンドを作るやつが欲しくなったわ。


写真はないけど、トイレも年季が入っていて大は和式だった(女性用は不明)。虫もそれなりに。でも、少し歩けば休暇村の西館があるので、そこだとウォシュレット付き洋式トイレがある。

テント横に車を停められるわけではなく、荷物を台車で運ぶパターン。台車は数台ある。

キャンプ場のお客も休暇村の温泉に入れる。西館の方には露天風呂はなかった。



【近くで遊べるところ】

宮ヶ浜湖水浴場

↑琵琶湖(宮ヶ浜水泳場)で湖水浴、いいね!海とは違う体験。休暇村から歩いてすぐ。シャワーや売店などあり。

沖島

↑近くに見える、沖島へも行ってみた。猫がいっぱいいる島らしい。

港の猫

↑船で行くんだけど、船を待っている港にすでに猫がいて逃げない!これは期待できる?!

沖島の猫

↑でも島で会えたのはこの1匹だけでした。これも逃げなかったなあ。
島はレトロな雰囲気を楽しめるのかもしれないが、俺には親の実家みたいだなと思いながら散策してた。

彦根城

↑休暇村から車で30分ほどの、国宝彦根城。
彦根城の中には食べる場所がないようで、京橋付近へ行ってみたら観光客向けにいろいろとお店が並ぶところがあって楽しめた。

彦根京橋の鮒寿司の自動販売機

↑京橋近辺の自動販売機に鮒寿司があった!ここでは食べなかったけど。

彦根城のひこにゃん

↑彦根城にひこにゃんもいました。1日2回どこぞに登場するって案内があった。

八幡堀めぐり

↑八幡堀めぐり。大人1000円。何時に出発する、という時刻表があるようには思えない感じだったが、、、とりあえず着いたらレストランのように名前書くとこがあった。そこに名前を書いておき、そこを離れて周囲を散歩するなどできた。

水郷めぐり・八幡堀めぐり(貸切船・定期船) 近江八幡観光物産協会
↑最初、水郷めぐりとごっちゃになってて、どれも同じようなコースなんだろうと思ってたけど違う。水郷めぐりの船は八幡堀に来ない。八幡堀めぐりをしたかったらエンジン船で1000円のしかない。

鮒寿司

↑乗り場すぐ側のお店では200円で二切れの鮒寿司を売ってた!そうそう、味見したかったのでちょうどいい量&価格だよ。なんだかチーズみたいだったな。

琵琶湖博物館のニゴロブナ

↑琵琶湖博物館。休暇村から車で1時間弱くらいか。画像は鮒寿司に使われるニゴロブナ。
新種のナマズを発見しました!! | びわ博ホームページ
↑休暇村のテレビでこのニュースを聞いたのでじゃあ行ってみるか、となった。
行く前に電話で確認すると展示はまだ(9月8日かららしい)、とのことだったけどまあいいか、ってことで行ってみたが、これが意外に充実してて小5の息子も楽しんでいた。

ブラックバスの天丼

↑博物館内のレストランでブラックバスとビワマスの天丼。美味しかったです。



【私的メモ】
キャンプ場の手ぶらプランは支給物資を事前によく確認しとかないとダメだな。
ホットサンドメーカー探そう!


買ってよかったコストコのワゴン

壊れた台車をだましだまし使っていたんだけど、キャンプにも使えるしってことで

コストコのワゴン(ケース)

↑このワゴンをコストコで購入。税込5,780円と妙に安い。
コストコなのに3個まとめて、とかじゃなくて1個から買えました!

コストコのワゴン(畳んだ状態)

↑箱から出すとこんな感じ。広げる時もたたむ時も簡単なのがいい。

コストコのワゴン(使用中)

↑後日コストコで買い物して駐車場からマンションの部屋まで運ぶのにも活躍。これ、今までは手に持って運んでいたわけだし、二往復しないとダメな量だったろうけど、このワゴンのおかげで一回でラクラク運べた〜!感動。
これはいいものを買った。

でも、たたんでも大きいので部屋の置き場所に困っていたりする。何か考えないとな、、、


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







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