kerasを使ってT-fal のお湯の温度を測ってみる(音声→温度)　In-situでやる。

# お湯の温度を音声から測る

[背景と手法は前回の記事を参照](https://qiita.com/ac-yasushi/items/19bab66abc13a1c2011b)
[リポジトリはこれ](https://github.com/ac-yasushi/measure_temp_Tfal)

　今回は前回の内容に加えて、

- kerasのhistoryを使って学習のlossとaccuracyを可視化した。
- 学習曲線を元に最適な学習条件を求めた。
- 学習モデルを保存し、リアルタイムで温度測定をするプログラムを作成。評価した。

　学習のhistoryを求めるには学習データの一部を評価に使う必要がある。前回の26サンプルでは小さすぎるので、新たにデータを追加。
　40サンプルと10のテストデータを用意した。


# lossとaccuracyの可視化

　この辺はkerasのドキュメントや記事が多いので特に問題はなかった。1つだけ、   

**Sequentialモデルではmetric=["accuracy"]が0になる**   

　softmaxなんかの出力ではうまくいくらしいがよく分からない。mean_absolute_errorもうまい値にならないのでkerasのドキュメントを漁ったら自分で評価関数を作れるようなので、誤差を%で表示するようにした。

```python:data_analysis_by_keras_Sequential
import tensorflow.keras.backend as K

def abs_percent(y_ture, y_pred):
    return 1-abs((y_pred-y_ture)/y_ture)

model.compile(loss='mse',
            optimizer=optimizer,
            metrics=[abs_percent])
```
　これで誤差の絶対値を精度の評価として使える。ただし、モデルの保存の時、abs_percentが定義されていないと言われるので、保存時だけmetricsを消しておいた。(モデル呼び出し側で再定義したらいける？)


# 最適な学習条件の探索

　40サンプルしかないのでモデルの評価にサンプルデータの一部を用いると逆に精度が落ちるというジレンマがある。結局データの10%、20% = 4～8サンプルを評価に用いた。
　4096点の振幅を温度1点に学習させる。層の数はよく分からないので5層で変えていない。一番シンプルなモデル。   

　結局、学習部分はこんな感じ。

```python:data_analysis_by_keras_Sequential
import numpy as np
import tensorflow as tf
import keras
import matplotlib.pyplot as plt
import tensorflow.keras.backend as K


model=keras.Sequential([
    keras.layers.Dense(4096, activation=tf.nn.relu, input_dim=4096),
    keras.layers.Dense(512, activation=tf.nn.relu),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=tf.nn.relu),
    keras.layers.Dense(1)
])

optimizer = keras.optimizers.Adadelta()

def abs_percent(y_ture, y_pred):
    return 1-abs((y_pred-y_ture)/y_ture)

model.compile(loss='mse',
            optimizer=optimizer,
            metrics=[abs_percent])

print(model.summary())

epochs=50

fit=model.fit(sound_data, temp_data, epochs=epochs, callbacks=None, validation_split=0.2)


plt.figure(figsize=(20,10))

plt.subplot(1, 2, 1)
plt.xlim(0,epochs)
plt.ylim(0, 0.5)
plt.plot(fit.history['loss'], label="loss of training")
try:
    plt.plot(fit.history['val_loss'], label="loss of validation")
except:
    pass
plt.title("model loss")
plt.xlabel("epoch")
plt.ylabel("loss")
plt.legend()

plt.subplot(1, 2, 2)
plt.xlim(0,epochs)
plt.ylim(0, 1)
plt.plot(fit.history['abs_percent'], label="acc of training")
try:
    plt.plot(fit.history['val_abs_percent'], label="acc of validation")
except:
    pass
plt.title("model accuracy")
plt.xlabel("epoch")
plt.ylabel("accuracy")
plt.legend()

plt.show()
```
これで学習させた過程が下になる。
![学習曲線.png](https://qiita-image-store.s3.amazonaws.com/0/371781/2e75644a-1086-3989-0e23-828704d6629a.png)
validation_split=0.2 に設定したので8サンプルが学習データから外れるので以上のようになる。   

　ひとまず過学習はなさそう？
　実際の学習は40サンプルすべてで学習させる。これだと大体90%以上の精度が出る。

```
40/40 [==============================] - 0s 4ms/step
Test_loss: 0.002086775377392769
Test_accuracy: 0.9321275949478149
```
　学習データではうまくいくがテストデータのevaluateはなぜか数値が吹っ飛ぶ。

## modelの保存
　以上の条件でkerasのモデルを保存する。   

　簡単かと思いきや意外と苦戦した。コード自体は

```
model.save("ディレクトリ")
```

で済むが、

- まずtensorflowのOptimizerが使えないと言われたのでkerasのOptimizerに置き換えた。Adadeltaの部分が該当する。
- tensorflowのkerasとkeras本体のコードが混在していると保存できないと言われる。これってkeras単品で使う以外の選択肢がないのでは？？？
- 保存したモデルを読みだす段階で、上で定義したabs_percentが使えないと言われる。これは評価用の関数なのでmetricsを省いてコンパイルすればいい。

というわけでやたら苦労してモデルを保存した。

# リアルタイム(In-situ)測定

~~In-situって書くと工学者っぽくてかっこいいよね。~~   

　リアルタイムと言っても別に今までのプログラムを結合すればいいだけなので特に工夫はない。5秒に1度ほど測定する設定で実際にT-falの温度を測ってみる。

<img src=https://qiita-image-store.s3.amazonaws.com/0/371781/09f286d2-9128-dd49-639b-17b11edc8706.png width=80%>

　う～ん。予測できているようなできていないような……
　温度初期では音が小さく誤差が大きいためそこまで気にならないが、1.5 minと4.0 minのあたりが誤差が大きいように思える。しかし、学習部分だけが精度よく一致しているわけではないので過学習はないだろう。(切片：初期の水温は15 ℃としている)  

　目で見て明らかな外れ値を取り除いてみる。
<img src=https://qiita-image-store.s3.amazonaws.com/0/371781/78d3d49c-3678-90b5-2c3c-5c9aa1eeeee8.png width=80%>
 うん！それっぽい！！(投げやり)

# 結論
　結論としてそこまで精度のいい学習結果は得られなかったが、データセットから自分で揃えたにしてはなかなか面白い結果になったと言える。製作途中から別に機械学習ではなく別手法の回帰でいいのではないのかと思えてきたが、まあ機械学習の勉強ということで有意義だった。  

　もう一つ、学習結果が上下に振れているので学習結果の誤差の算術平均を取ると3.0%付近に収まる。今回のような短時間で可変的な温度変化を捉える場合、誤差の処理は少し面倒になるが、例えば一定温度をモニタリングする場合などは多少予測温度が荒くても移動平均などで誤差を小さくできる。はず
　その点でも全くの無駄というわけではない。

# 感想
　もっとちゃちゃっとコードを書けるようになりたい。