PyTorchで色々な非線形関数を回帰してみたらすごかった

  • このエントリーをはてなブックマークに追加

ニューラルネットワークは複雑な非線形関数を近似する事ができるため、回帰問題を解いてみると効果がわかりやすいです。ここではPyTorchのネットワークモデルで色々な非線形関数を回帰してみた結果とそのコードを紹介します。

こんにちは。wat(@watlablog)です。ここではPyTorchを使って非線形関数を回帰していきます!効果がすごいので是非ご覧ください!

本記事の対象者

ニューラルネットワーク初心者

本記事はニューラルネットワーク初心者を対象としています。

当WATLABブログではPyTorchを使ってニューラルネットワークモデルを定義し回帰問題を解いていきますが、まだPyTorchの事がよくわからないという方は是非以下の記事を読んでみて下さい。

まだPyTorchに触れた事がない人でも、順番に読んでいく事できっとこの記事を読み進める事ができるようになるはずです。

PyTorchの概要とインストール

以下の記事でPyTorchの概要とインストールについて説明しています。ここまでの一連の記事で紹介している内容は全てローカルPCで動作させるレベルのものなので、是非お手持ちのPC環境に合わせてPyTorchをインストールしてみて下さい。

ディープラーニング初心者がPyTorchを選んだ3つの理由

PyTorchチュートリアル

PyTorchではテンソル演算を行います。tensor型という聞き慣れない型を使って演算していきますが、使用感はNumpyの行列演算とほぼ同じです。まずは以下の記事でテンソル演算に慣れてみましょう。

「What is PyTorch?」チュートリアルをやってみた

線形ネットワークによる線形回帰とモジュール化

PyTorchのネットワークモデル構築にはひとクセあります。まずは手計算やExcelでもできそうな線形回帰問題(データを直線で近似する問題)を行なって、ネットワークを作って学習するという事がどういう流れなのかを以下の記事で理解しましょう。

PyTorchのネットワークモデルを使って線形回帰をする方法

上記の線形回帰問題ができるようになったら、次はネットワークをモジュール化(クラスで表現してオブジェクト指向で使う)しましょう。ここを理解する事で、他の人が書いたコードが少し読めるようになってくると思います。

以下の記事でPyTorchのネットワークモデルをクラスで書く時のメモをまとめました。

PyTorchのネットワークモデルをクラスで書く時のメモ

そもそも機械学習やニューラルネットをよく知らない場合

実は機械学習とかニューラルネットワークとかよくわからん。おいしいの?でも非線形回帰はどうしてもしたいの…。

…という方も大丈夫です。僕もその状態から学習を始めており、過去記事に内容を全てまとめていますのでフォロー可能です。

以下のリンクは当ブログの「AI」カテゴリです。リンク先の「G検定」の項目に機械学習、ディープラーニングの分野を体系的にまとめていますので、一通り読む事で本記事の内容にスムーズに繋がると思います。

WATLABブログ:AIカテゴリ

非線形回帰を行いたい人

本記事では特に非線形回帰を行いたい人に向けて書いています。

非線形回帰とは、正弦波や指数関数といった直線で近似できない類の関数に対して回帰分析を行う事です。

Excel等の近似曲線でもフィッティングが難しいような関数を回帰するためには、通常は何かモデル(数式)を定義してから最小二乗法により誤差を最小にするような最適化をかけます。

今回のネットワークモデルを用いた非線形回帰は、全て同じ層構造で学習しますので、モデルを人が定義しないというメリットがあります。

では以下よりニューラルネットワークを用いた非線形回帰コードを説明していきます。

色々な非線形関数を回帰するコード

共通項目の解説

非線形関数を回帰する前に、今回使用するコードの共通項目の解説を行なっていきます。

層/Unitの個数と活性化関数

これまでの線形回帰では、入力層と出力層を直接繋ぐ事が回帰式をそのまま表現する事になっていましたが、それだと直線近似しかできません。

今回は一度線形回帰の式を忘れ、表現力を上げるために中間層を2つ追加してみます。

モデルのコードは以下です。
PyTorchのネットワークモデルをクラスで書く時のメモ」に対してlinear2, linear3が増えていますが全てnn.Linearで作成しているので、それぞれは線形ネットワークです。

上記linear1〜3の構造を図解したのが下で、nn.Linear(in_features, out_features)に各層における入力Unit数、出力Unit数を設定します。

例えば、横軸時間、縦軸振幅で表現されるデータを回帰する場合、データは線形回帰の時と同じ形だとすると入力層は定数項である1と横軸成分の計2つになります。

そして横軸の成分が与えられた時にただ一つの縦軸値を求めたいので、出力層は1になります。

今回は複雑な非線形関数を近似するため、Unit数32と16の中間層を2層入れています(中間数のUnit数は適当)。

コードとネットワーク構造の対比

linear1, linear2には活性化関数としてReLUを設定しています。ReLUは下図の関数で、勾配消失問題が発生しにくいためニューラルネットワークの活性化関数に最もよく使われています。

ReLU

最適化アルゴリズムと損失関数

線形回帰はSGD(確率的降下法)を最適化アルゴリズムに設定していましたが、今回色々試した結果、非線形関数の回帰学習においてはRMSpropが優秀だったので変更しています。

RMSpropは勾配降下法の一種ですが、「学習の停滞を改善するRMSPropをPythonで書いてみた」にその他勾配降下法派生種との比較を示していますので、是非ご確認下さい。

正弦波:\(\sin x\)

まずは非線形関数の代表格とも言えるサイン波で回帰してみましょう。

トレーニングデータは以下のコードで生成します。前回と同様に汎用的にNumpyで波形を作り、テンソルへの変換と1ベクトルの追加といったデータ整形を行なっています。

全コード

コピペで動作させる用の全コードを以下に示します。

下図が結果です。

5000エポック(エポック=学習回数)とっていましたが、1000あたりでほぼフィッティングされているようです。

トレーニングデータのある範囲においてはよくフィットしていると思います。

全コード(動画生成付き)

以下はおまけです。

学習の過程でどのように回帰されていくかを可視化してみました(面白い!)。

正弦波が回帰されていく過程の動画

GIF動画生成を行うバージョンのコードを以下に示します。
※こちらは結構コード汚いですので、あくまで参考までに。

正弦波:\(\sin x\)(データを増やした場合)

先ほどはデータ範囲の回帰は十分できていましたが、正弦波がさらに繰り返している場合はどうなるか確かめてみます。

以下のようにxの範囲を-5から15までに拡大したトレーニングデータを使います。

以下が結果です。やはりデータ範囲がある部分はよく回帰できています(すごい!)。

横軸長を長くした場合の正弦波

動画はこちら。

横軸長を長くした場合の正弦波の動画

指数関数:\(e^{x}\)

次は指数関数で試してみます。といってもネットワーク構造や最適化アルゴリズムに変化はないので、トレーニングデータを以下にするだけです。

下図が結果です。指数関数であってもフィットできているようです。
但し、値自体が大きいので、損失関数の値も大きな値で推移しています。

動画はこちら。

指数関数の回帰

ハイパボリックタンジェント関数:\(\tanh x\)

ハイパボリックタンジェント関数(\(\tanh x\))も試してみます。

ハイパボリックタンジェントは-1〜1の範囲に値をとりますが、こちらも問題無くフィットできています。

tanhの回帰

動画はこちら。この関数はすぐ収束しましたね。

tanhの回帰動画

非線形関数を回帰するコード(3Dデータセット)

先ほどまでは1つのxに対して1つのyが決まるような関数の回帰でしたが、3Dのデータセットに対しても適用できるか試してみます。

トレーニングデータはこちら。ちょっとコード汚いですが許してください。

結果はこちら。中間層2個にしては結構良い感じ!

動画はこちら。RMSpropの特徴で解が振動していますが、うねうねと曲面がデータセットに適合していこうとしているのが本当に面白いです!これがやりたかった。

2Dの非線形関数を回帰した結果

まだ3Dデータセットへの回帰はデータの形合わせに試行錯誤感があってコードはめちゃくちゃ汚いですが、以下に動画作成までの全コードを示します。

3Dデータセットは入力層のin_featuresを3にしているのがポイントです(重回帰分析のテンソルを作った時と同じ)。

まとめ

同一のネットワークモデルで色々な関数に適応可能な所がすごい

本記事ではニューラルネットワーク初心者の僕が試行錯誤しながら、非線形関数の回帰問題をディープラーニングで解いてみました。

ディープといっても中間層が2層のコンパクトな構造ですが、三角関数や指数関数を始めとした非線形関数を上手い事回帰する事ができました。

また、3Dのデータセットとして\(z=\sin x \cos x\)を用意しましたが、入力層の形だけ揃えた同じ層構造のネットワークモデルで学習する事ができました。

最後にもう一度、観賞用を…。

軸無しの回帰結果

面白い!

当ブログでは「Pythonサポートベクターマシンで回帰分析!SVRの概要と実装」でも非線形回帰を行なってみました。

しかし、サポートベクターマシンはカーネルがハイパーパラメータであり、精度を出すためにはRBFやpolyといった引数をデータセットに合わせてプログラマが選択する必要がありました。

今回のネットワークモデルは入力時のデータ形式を合わせれば、あとは全く同じ設定で様々な非線形関数を近似できたという事が着目点だと思います。

回帰分析だけでも工学的な問題に色々応用が効きそうですね…。

ディープラーニングのデメリット考察

この界隈の人には普通なのかも知れませんが、僕には驚きの結果でした。
しかしこのネットワークモデル、デメリットは無いのかなと考えてみます。

ディープラーニングによる回帰は確かにすごいですが、例えば物理モデルを数式で定義して回帰問題を解き、その式の係数が意味を持つような研究にはあまり使わない方が良いと感じました。

バックプロパゲーションで自動的に調整された重みは非常に沢山あり、これらの重みから物理的な考察をするのは困難と思うからです。

研究で用いる回帰分析はできるだけ本質を捉えたエッセンシャルモデルで表現する方が良いでしょう。

但し、異音検知等の問題においては物理的な意義よりも判定できるという機能の方が重要なので、ディープラーニングは向いていると思います。

ディープラーニング恐るべし(ミーハー)!非線形回帰はやはり面白いですね!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

SNSでもご購読できます。

コメント

  1. はまぞう より:

    ダイセルイノベーションパークの久保田邦親博士(工学)の材料物理数学再武装ってもの品質工学と化学工学のあいの子みたいで結構面白いよ。

  2. tmp より:

    わかりやすい記事をありがとうございます!
    少し気になったことがあり、質問させて頂きたいのですが
    >>トレーニングデータは以下のコードで生成します。前回と同様に汎用的にNumpyで波形を作り、テンソルへの変換と1ベクトルの追加といったデータ整形を行なっています。

    とあるのですが、1ベクトルを追加するのは何のためでしょうか?2021.03.29の記事も拝読して、バイアス項のためかもしれないとは思ったのですが、もしバイアス項を手動で足す必要がある場合、2層目以降でも1を追加してから次の層にインプットする必要が出てくる必要があるのではないかと気になりました。

    上記のプログラムをコピーさせて頂き、手元で動かしてみたのですが、学習後にモデルを確認すると
    net.linear1.weightのサイズは[32,2]で、net.linear1.biasのサイズは[32]でした。もし入力データに1切片を含めるとすると、net.linear1.weightのサイズは[32,1]になるのではないかと少し気になりました。

    自分のpytorchの設定やバージョンなどが違うのかもしれず、また、勉強中のため自分が何か誤解をして読み飛ばしている箇所があったら申し訳ないのですが、もし何かご存じでしたら教えてもらえないでしょうか?

    1. wat より:

      ご訪問ありがとうございます。
      1のベクトルを追加しているのは、回帰問題をw0+w1*x1+…という問題に置き換え、
      w0は定数項で任意倍しないという意味で1をかけています。
      今回のネットワークモデルでは「https://watlab-blog.com/2021/03/29/pytorch-linear-regression/」や
      「https://watlab-blog.com/2021/06/13/pytorch-nn-class/」で書いたように、
          net = torch.nn.Linear(…bias=False)
      とbiasを使わないモデルにしている…はずだったのですが、
      この記事にはbiasのパラメータを設定していませんね。。
      エラーがなく回帰もできているので特に気づかなかったのですが、どちらでも動いてしまうようです。
      もしかしたらtmpさんが気になっているように、少し勘違いしたコードになっているかも知れません。

      PyTorch公式のLinearメソッド
      「https://pytorch.org/docs/stable/generated/torch.nn.Linear.html」
      を読むと、biasはデフォルトがTrueのようです。
      「If set to False, the layer will not learn an additive bias.」とあり、この記事のコードはbiasを使わない構想だったので、無駄な学習をしてしまっているかも知れません。
      確かにこの部分気になるところですので、もう少し調べた方が良さそうですね。
      …実はこちらも見様見真似で書いているのが正直なところです。明確な回答ができず申し訳ございません。。

コメントを残す

*