故障予測問題に TensorFlow Probability を利用する(データ生成編)

nagachika
11 min readDec 15, 2018

--

この記事は TensorFlow Advent Calendar 2018 の5日目にすべりこむために書いたものです。

TensorFlow ProbabilityTensorFlow をベースにした統計モデルを構築するための Python ライブラリです。モデル構築に用いる要素として確率変数、分散関数(distributions)、bijectors という確率分布を変形する仕組みを持ち、モデル推定のために MCMC や変分推定(Variational Inference)の機能を持ちます。またいわゆる Bayesian Neural Network を構築するための部品も備えています。

筆者は Bayesian の考えかたにも TensorFlow Probability (以下TFP)の利用法にも不慣れなのですが、Bayesian Methods for Hackers というプログラマ向けに Bayesian をわかりやすく解説した書籍の TFP 版が公開されたのをきっかけにして両方に一度に入門してみました。

長くなりそうなので「データ生成編」と「モデル推定編」に分けようと思います。今回は「データ生成編」です。

問題設定

問題設定として工場の装置の故障予測という仮想のタスクを考えます。元となるアイデアは "A strategy for implementing industrial predictive maintenance" という一連のブログ記事から得ています。工場の装置のメンテナンス計画の最適化のために装置の故障予測を行いたい、というシナリオです。このブログ記事内ではタスクとしてある装置が一定期間内に故障するか否かの判定(分類)、寿命がどのくらい残っているかの予測(回帰)、装置の挙動の異常の検知(異常検知)、装置のパラメーターなどの操作の学習(強化学習)といった手法があるとしていますが、Bayesian Methods を適用するにあたり、最もシンプルな分類のタスクをベースに故障確率の推定というタスクに変更します。
詳しいモデル設計はモデル推定編に譲ります。

抽象的に「装置」というだけだとイメージが湧かないので、この装置というのは配管に使うパイプの連結器具ってことにします。
予測に用いる入力は以下にします。あくまで架空の問題設定なのであまり具体性がないですがそこはご容赦ください。

  • 稼動日数(延べ何日利用したか)
  • 最後にメンテナンスしてからの日数
  • パイプ内の温度センサー情報

架空の工場でありデータは実在しないので、なんとかでっちあげる必要があります。そこでここで一旦神の視点になって、この架空のパイプ連結器具の故障確率をモデル化してデータを生成したいと思います。
なお今回作るのはあくまでデータ生成のためのものであってモデル推定編で使うモデルとは別のものです。

「真のモデル」の設計

さてここからは神の視点で、ダミーのデータ生成のためにこの架空のパイプ連結器具の故障の確率を生成するモデルを作ってみようと思います。
まず故障の有無は2値のイベントなのでベルヌーイ分布に従うとします。つまりあるパラメータp(0≤p≤1)に対して、pの確率で故障する、(1-p)の確率で故障しないとします。
つまり今から作りたいのは「ある入力に対して p (ただし 0 ≤ p ≤ 1)という数値を出力する数式かなにか」です。

まずおさえておきたいのは、今回前提としているモデルはたとえ神であっても完全に故障を予知できない(かもしれない)ようになっているという点です。どういうことかというと、神なので故障確率を左右するpは完全に決めることができますが、その結果この連結器具が故障するかどうかはpをパラメータとするベルヌーイ分布によるため確定しません。神のみぞ知るどころか、神も知らない、です。ただし常に p=0 または p=1 を出力するように設計すれば結果も完全に知ることができます。さすが神ですね、全知になるかどうかも自分で選べます。

それから予測に用いていない別の入力があることにします。実はこの連結器具を製造しているメーカーの製造所は2つラインがあって、そのうち一方は製造精度が良くて同等の条件でも故障しにくい、ということにします。この2つのラインで製造された連結器具は同数ずつ出荷されていて、導入後にはどっちのラインで製造されたかはわからないので予測には使えないことにします。

つまりまとめると、4つのパラメータ

  • 稼動日数(d)
  • 最後にメンテナンスしてからの日数(m)
  • パイプ内の温度(T)
  • 製造ライン(L)

から 0≤p≤1であるようなpを決める式を作ればいいわけです。

もうひとつ、温度センサーの精度も完全なものではないので、連結器具の故障に影響する真の温度に対して小さなノイズが乗ったものが与えられるということにします。ノイズはガウス分布である小さな定数ε

Tsensor = T + ε Normal(0, 1)

で与えられるものとします。予測に用いるためのダミーデータには Tsensor のほうを利用します。

p を与える式は、まず稼動日数(d)も最後にメンテナンスしてからの日数(m)も増えるとそれだけ故障確率が増える(つまりpが大きくなる)のが自然ですね。特に長期的にはdが支配的になりmが小さくても故障率が大きい(いくらメンテナンスしたといっても寿命というものがある)というのもありそうな話です。d に対してはあるしきい値を超えると急に1に近付くのがそれっぽい気がします。sigmoid 関数を用いるのが良さそうですね。

def p_for_d(d):
running_threshold_days = 180
C1 = 0.05
return tf.nn.sigmoid(C1 * (d — running_threshold_days))

メンテナンスの効果をどう表すかは悩みどころですが、メンテナンスを行うことで稼動日数(d)を-30日減らしたのと同じ効果があり、その効果がメンテナンス後1日ずつ減衰していく(つまりメンテナンスから30日後以降、m≥30では効果がない)ということにしてみます。式にすると以下のようなコードで表現できると思います。

def p_for_d_m(d, m):
running_threshold_days = 180
C1 = 0.05
d = d - tf.nn.relu(30. - m)
return tf.nn.sigmoid(C1 * (d - running_threshold_days))

まったくメンテナンスを行わない場合(赤)、30日ごとにメンテナンスを行う場合(青)、10日ごとにメンテナンスを行う場合(緑)の p の値をプロットしてみるとこうなります。

リアリティはともかく、定期的なメンテナンスが寿命を若干伸ばしてくれるというのは表現できそうです。

パイプ内の温度(T)は高いほど故障しやすいということにします。温度もあるしきい値を超えると悪影響が強く出るのがいい感じなので sigmoid 関数を使うとよさそうですが、最終的な p は 0≤p≤1という範囲に収まる必要がありますから、稼動日数とメンテナンスから計算される値とどう組み合わせるのか考えどころです。
今回は「温度があるしきいちを超えて高くなると稼動日数が少なくても故障しやすい」という特性があるということにして、温度の影響で底上げするように以下のような式にしてみました。

def p_for_T(t):
temp_threshold = 120
C = 0.2
return tf.nn.sigmoid(C * (t - temp_threshold))
def p_for_d_m_T(d, m, t):
return (p_for_d_m(d, m) * (1.0 - p_for_T(t))) + p_for_T(t)

メンテナンスなし(d=0固定)でパイプ内の温度が一定で稼動した場合にT=100,120,140のそれぞれで稼動日数による p の変化をプロットしてみると以下のようになります。高温での稼動は新品の連結器具であっても故障の原因になるということがわかります。

最後に製造ライン(L)の影響ですが、L は2つの製造ライン(l1, l2)からの選択になるので離散的になります。製造ラインの違いは連結器具の寿命(稼動日数のしきい値)の違いとして現れることにします。

def p_for_d_m_T_L(d, m, t, l):
# l = 0 なら 140, l = 1 なら 180 日をしきい値とする
running_days_thresholds = tf.map_fn(
lambda x: tf.cond(tf.equal(x, 0.), lambda: 140., lambda: 180.),
tf.cast(l, tf.float32))
# 稼動日数とメンテナンスの影響
C1 = 0.05
d = d - tf.nn.relu(30. - m)
p_for_d_m_L = tf.nn.sigmoid(C1 * (d - running_days_thresholds))
# 温度Tの影響
temp_threshold = 120
C = 0.2
p_for_T = tf.nn.sigmoid(C * (t - temp_threshold))

return (p_for_d_m_L * (1.0 - p_for_T)) + p_for_T

製造ラインの異なる連結器具のpの値は稼動日数に伴なってそれぞれ以下のように変化します(メンテナンスなし、T=100 で稼動した場合)。

製造ラインの違いによる故障率の違い

製造ラインの違い(L)は神ならぬ身には知ることができないパラメータです。モデル推定時にその他の条件が一定でもこの2つの異なる確率分布が存在することも推定できるのでしょうか。

データ生成

「真のモデル」はできたので、これに基づいてダミーデータを生成したいと思います。

まず4つのパラメーターのサンプリングを行うために TFP の distribution を利用したいと思います。

稼動日数(d)は Pareto 分布 を用います。メンテナンスからの日数(m)は平均して20日でメンテナンスがされているという前提として[0, 19]の一様分布とします。ただし d ≥ m という制約があるのでこれを満たさないデータは棄却するものとします。

パイプ内温度(T)はガウス分布からサンプリングします。

製造ラインは 2つのどちらかなので p=0.5 のベルヌーイ分布を用います。

データ生成はこんなコードになります。

dist_d = tfp.distributions.QuantizedDistribution(tfp.distributions.Pareto(5, scale=180), high=500)
dist_m = tfp.distributions.QuantizedDistribution(tfp.distributions.Uniform(-1, 19), low=0)
dist_T = tfp.distributions.Normal(loc=80.0, scale=20.0)
dist_L = tfp.distributions.Bernoulli(probs=0.5)
N = 10000
d = dist_d.sample(N) - 180
m = tf.minimum(dist_m.sample(N), d)
T = dist_T.sample(N)
L = dist_L.sample(N)
data_p = p_for_d_m_T_L(d, m, T, L)fault = tf.distributions.Bernoulli(probs=data_p).sample()T_noise = tfp.distributions.Normal(loc=0., scale=3.).sample(N)
T_sensor = T + T_noise

最後にCSVにデータを出力してダウンロードします。

ダミーデータ生成にはColaboratoryを利用して、NotebookはGistに保存しました。

まとめ

今回は工場のパイプ連結器具の故障予測のモデルを生成するために、まずダミーデータを生成しました。今回はデータ生成のために神の視点から故障確率のパラメータを出力するモデルを構築しました。ここには予測時には利用できない隠しパラメータも含まれています。

次回モデル推定の時にはこの中身は全て忘れて、生成したダミーデータを元に推定を行ってみたいと思います。

--

--