実験やシミュレーションには理想的な信号を用いることがよくあります。ここでは1つの例として、滑らかに振幅が増加する正弦波をPythonで生成する方法を紹介します。
こんにちは。wat(@watlablog)です。
科学技術系のプログラムを作っていると、信号の生成は頻繁にやりますよね。ここでは、Pythonを使った滑らかに振幅増加する正弦波の生成方法を紹介します!
漸近し滑らかに増加する関数の色々
どんな信号が欲しいか?
「滑らかに振幅が増加する」という目的では、指数関数がまず思い浮かびます。
但し指数関数では増加する一方です。ここでは理想波形として振幅がある一定値に漸近し、そこまでの間滑らかに振幅が増加していく、といった波形を目指します。
実はそういった波形は「ディープラーニングにおける活性化関数をPythonで作る!」で紹介した活性化関数に多々ありました。以下に再掲します。
シグモイド関数
シグモイド関数は0から1の間を滑らかに増加していく関数です。上昇する時も漸近する時も指数的な曲線になっていることがわかりますね。
シグモイド関数をPythonで作るコードは以下になります。
1 2 3 4 5 |
import numpy as np #シグモイド関数 def sigmoid(x): y = 1 / (1 + np.exp(-x)) return y |
しかし、シグモイド関数は縦軸0近傍は横軸が負値です。時間波形等横軸は0から始まってほしい場合にはちょっと不都合があります。
そこで、シグモイド関数の分母\(e\)に\(a\)と係数をかけてみます。
$$y=\frac{1}{1+ae^{-x}}$$
\(a=30\)としたところ縦軸0近傍が横軸0に近づいてきました。
tanh(ハイパボリックタンジェント)
続いてtanh(ハイパボリックタンジェント)関数を紹介します。
この関数は-1から1の間を滑らかに増加する関数です。特にこの関数は横軸0の時に縦軸0になっており、時間波形の0秒時を0にしたい時に便利そうです。
tanh関数のコードを以下に示します。
1 2 3 4 5 |
import numpy as np #ハイパボリックタンジェント関数 def tanh(x): y = np.tanh(x) return y |
Pythonで滑らかに振幅増加する正弦波を作ってみた
今回生成した波形の考え方
滑らかに振幅増加する波形、というと、もしかしたらそれらを駆使しているプロの方からは「〇〇を使う方が圧倒的に良いよ!」とアドバイスをお持ちの方もいらっしゃると思いますが、その場合は是非当記事へのコメント、またはTwitter(@watlablog)にてご教示頂けると勉強になるので助かります!
ここでは、あくまで僕個人が「どんなことをしたら滑らかな関数ができるか?」を考えて作った式を使うので、これが定石ということはありません。その点ご注意を!
それでは本題です。
今回の関数は係数を導入したシグモイド関数と純粋なtanh関数の2つを使います。もう一度二つの関数を見てみましょう。
シグモイド関数の増加と漸近で2回の変曲点を持つ特徴と、係数によってシグモイド関数が横にシフトしたとしても、横軸0の時は縦軸0と強制的に波形を補正するtanh関数の特徴を合わせて使います。
Pythonコードと生成した波形の紹介
作成した関数は以下の式です。あまりひねりはありませんが、単純に係数考慮のシグモイド関数にtanhをかけただけです。これを好きな関数にかけることで初期値が(0, 0)の滑らかに振幅増加する波形の出来上がりです。
$$y=\frac{\tanh x}{1+ae^{-x}}$$
グラフ処理まで含めた全コードを以下に示します。def smooth(x)の部分が自作したスムース関数です。正弦波の周波数は10[Hz]にしていますが、色々な周波数、もしくは波形の種類を変えたりしてお試し下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import numpy as np from matplotlib import pyplot as plt def smooth(x): a = 30 y = np.tanh(x) / (1 + a * np.exp(- x)) return y A = 1 # 振幅 t0 = 0 # 初期時間[s] tf = 10 # 終了時間[s] dt = 0.005 # 時間刻み[s] f = 10 # 周波数[Hz} t = np.arange(t0, tf + dt, dt) # 時間軸 # 滑らかに振幅増加する正弦波 y = smooth(t) * A * np.sin(2 * np.pi * f * t) # ここからグラフ描画 # フォントの種類とサイズを設定する。 plt.rcParams['font.size'] = 14 plt.rcParams['font.family'] = 'Times New Roman' # 目盛を内側にする。 plt.rcParams['xtick.direction'] = 'in' plt.rcParams['ytick.direction'] = 'in' # グラフの上下左右に目盛線を付ける。 fig = plt.figure() ax1 = fig.add_subplot(111) ax1.yaxis.set_ticks_position('both') ax1.xaxis.set_ticks_position('both') # 軸のラベルを設定する。 ax1.set_xlabel('Time [s]') ax1.set_ylabel('y') # 軸の範囲設定 ax1.set_xticks(np.arange(0, 20, 1)) ax1.set_yticks(np.arange(-2, 2, 0.5)) ax1.set_xlim(0, 10) ax1.set_ylim(-1.5, 1.5) # データプロット ax1.plot(t, y, label="sigmoid(a=30)+tanh") # レイアウトと凡例 fig.tight_layout() plt.legend() # グラフを表示する。 plt.show() plt.close() |
以下の画像が実行結果です。非常に滑らか!
ちなみに、シグモイド関数内にかかっている係数\(a\)の値を小さくすることで、tanhの特徴がより強く出るようになります。同一の式を使ってパラメータ違いで滑らかさの度合いを変更できる関数が出来た、と捉えればちょっとは意味のあるものになったでしょうか。
今回の式はシグモイド関数の横移動による変化しか与えられないけど、2つの変曲点の位置をパラメータで持てればもう少し使い勝手の良さそうな関数になりそう。それはまた今度検討しよう。
まとめ
ディープラーニングの分野で使われている活性化関数の特徴を使って、滑らかに振幅が増加する波形を作ることができました。
作成した関数は増加する時と漸近していく時で2回の変曲点を持つ特徴を持ちました。
今回は波形生成の1つの例を示しました!実用例は別記事で紹介する予定です!
⇒実用例の記事を書きました!「Pythonの過渡応答解析で力を滑らかにかけて応答の違いを見る」
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
コメント