最近機械学習抜きに TensorFlow を触ったので紹介します。
TensorFlow で方程式の近似解を求めたい人の参考になれば幸いです。
- (2018/05/07) 平均預け入れ年数を修正
最初に
- この記事内の人物・会社はフィクションです
- TensorFlow を使いますが、Deep Learning どころか Neural Network も出てきません
- あくまで高次方程式の近似解を求めるために使います
- 定式化のため問題の一部を簡単化してます
- Google Colaboratory で動かしました(Chrome 以外だと上手く表示されないかも)
- 本エントリ内の画像は かわいいフリー素材集 いらすとや 様より拝借しております
導入
たかしくんは 現在 26歳 の Software Engineer です。
ある日、とある青い銀行に勤めるお友達(広義)からこんなお誘いがありました。
「たかしくん!僕と契約して個人積立年金を始めてよ!(=ノルマのための数字になってよ)」
お友達からのお誘いだったので、たかしくんは詳しくお話を聞くことにしました。
聞いてみたところ以下のことが分かりました。
-
毎月 20,000 円 支払う
- 今は 1月とし、もし加入したら今月から支払うとする
- 1年間で 240,000 円支払う
- 65歳 になるまで支払いを続ける(65 - 26 = 39 年間支払いを続ける)
- 65歳になったらすぐもらえる(据え置き期間はなし)
-
9,930,000 円をまとめてもらえる
- 期間を挟みつつ分割払い、ではない
たかしくんはこれがお得(年利が高い)なら始めてみようと思いました。
ですがその場で年利を計算できなかったため、持ち帰り案件とすることにしました。
問題の定式化
年利が高いとは(たかしくんが個人積立年金を始める基準)
年利が高いか否かを判断するために、たかしくんは定期預金の年利と比較してみることにしました。
【定期預金の金利を徹底比較!】定期預金金利の高さで選ぶ!おすすめネット銀行ランキング!|ネット銀行比較|ザイ・オンライン
上記のサイトを確認したところ、一番高い銀行では 5年 預けると 0.30% の年利がつくことがわかりました(2018年5月3日時点)。
たかしくんが今から個人積立年金を始めた場合、39年間支払います(上記参照)。
払った年によって預け入れる年数が変わりますが、平均すると (39 + 1) / 2 = 20 年お金を預け入れることになります($ (\sum_{k=1}^N k) / N = (N + 1)/ 2$ より)。
ですので上記の 5年 の定期預金の年利と比較した結果、
たかしくんは 個人積立年金の年利 が 0.35% 以上 なら始めようと決意しました。
年利と総受け取り額の関係
ここで年利と総受け取り額の関係を表すため、下記のようなパラメータを定義します。
パラメータ | 内訳 |
---|---|
$R$ [%] | 個人積立年金の年利 |
$P$ [円] | 毎年たかしくんが支払う金額 |
$N$ [年] | たかしくんが支払いを続ける期間 |
$S(N)$ [円] | たかしくんが $N$ 年間支払った後にもらえる金額 |
ここで、下記のような $r$ を定義します。
$$
r = 1 + \frac{R}{100}
$$
仮定
ここで少し面倒なのが、年利は 1年間 お金を預けた場合に発生するのに対し、たかしくんは 毎月 積立金を支払っているということです。
例えば、たかしくんが 2018年5月 に支払った $p$ 円は、2019年5月 になって初めて $rp$ 円になるということです。
ここでは計算を簡単にするため、以下の仮定を置きます。
(一ヶ月単位の利率を仮定することでより正確に計算できますが、それは各自でやってみてください)
- 仮定:たかしくんはその年の最後に $P$ 円をまとめて払ったとして、その 1年後に $r$ 倍になる
1年支払った場合の総受取金額
その年の最後に $P$ 円払ったとして、そのお金に年利がかかる前に返金されるので、下記のようになります。
$$
S(1) = P
$$
2年支払った場合の総受取金額
最初の年に支払った金額には年利がついて、2年目に支払った金額には年利がついてないです。
$$
S(2) = P\cdot r + P
$$
N 年支払った場合の総受取金額
\begin{align}
S(N) &= P \cdot r^{N-1} + P \cdot r^{N-2} + ... + P \cdot r + P\\
&= P \cdot \frac{r^N - 1}{r - 1}
\end{align}
年利を求めるには
ここで実際に求めたい(わかっていない)パラメータは $r$ のみです。
他のパラメータはわかっているので実際に代入してみます。
9930000 = 240000 \cdot \frac{r^{39} - 1}{r - 1}
分かりやすいよう両辺を $240000$ で割ります。
\frac{993}{24} = \frac{r^{39} - 1}{r - 1}
あとは上記の式を満たす $r$ を求めるだけです。
ですがたかしくんは精々2次方程式の解法しか覚えていません。
また、勾配法などでこの方程式の近似解を求めようにも、
たかしくんは微分が大の苦手なので上記の式を $r$ について微分することができません。
困ったことにこのままでは $r$ を求めることができません。
TensorFlow による近似解の計算
しかしたかしくんはただのたかしくんではありません。
闇のパワー(TensorFlow)を持った "たかしくん with TensorFlow" なのです。
そこでたかしくんは TensorFlow を使って利率の近似解を求めることにしました。
(近似解はダメ?厳密解じゃないと許さない?そんなやつは理想を抱いて溺死しろ)
コード(TensorFlow)
※ LEARNING_RATE
(学習率)の値によっては収束しなかったり、収束が遅いです。
import numpy as np
import tensorflow as tf
AGE_START = 26 # 支払い開始年齢
AGE_END = 65 # 支払い終了年齢
YEAR = AGE_END - AGE_START # 支払い期間
COST_MONTH = 20000 # 月に支払う額
COST_YEAR = COST_MONTH * 12 # 年間支払い額
PENSION_TOTAL = 9930000 # 最終的にもらえる額(発表されてる額)
R_INIT = 2. # 年利の初期値(%)
EPOCH = 50 # エポック数(パラメータの更新回数)
LEARNING_RATE = 0.000001 # 学習率、0.01 とかだとすぐ nan になって収束しないので注意
# Model parameters
r = tf.Variable([1. + R_INIT/ 100], dtype=tf.float32)
# Model
model = (tf.pow(r, YEAR) - 1) / (r - 1)
# Label
label = tf.constant([PENSION_TOTAL / COST_YEAR], dtype=tf.float32)
# loss
loss = tf.square(model - label)
# optimizer
optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)
train = optimizer.minimize(loss)
# 初期化用
init = tf.global_variables_initializer()
with tf.Session() as sess:
init.run()
print(" init r[%]: ", R_INIT)
# EPOCH 回パラメータ更新
for epoch in range(EPOCH):
sess.run(train)
# print(sess.run(r))
r_estimated = (sess.run(r)[0] - 1.) * 100
pension = sess.run(model)[0] * COST_YEAR
pension = int(pension)
print(" estimated r[%]: ", r_estimated)
print("estimated pension[¥]: ", pension)
print(" diff[¥]: ", PENSION_TOTAL - pension)
init r[%]: 2.0
estimated r[%]: 0.3084421157836914
estimated pension[¥]: 9929992
diff[¥]: 8
上記の結果から、年利は約 $0.308$ % であり、この場合の誤差は $8$ 円であることがわかりました。
993 万円中の 8 円ですので、許しても良いのではないでしょうか。
(許せない人は、コード内の dtype=tf.float32
を dtype=tf.float64
にしてみましょう)
よって年利が $0.308$ %、すなわち $0.35$ % 未満であることが判明したので、
たかしくんは今回は個人積立年金を 始めない ことにしました。
結論
- 個人積立年金するぐらいなら定期預金(5年)で十分
最後に
今回の例では色々仮定をおいたり、メリット・デメリットを無視したので、実際はそんなに簡単には決められないかもですが、このような考え方もあると知ってもらえれば幸いです。
ちなみに私は今年の昇給額がしょっぱかったのでまだ始めません。
そしてこれからは Qiita ポエマーとして稼いでいこうと思います。