見出し画像

Sympyを使ってみた

初めまして。
九州工業大学、本田あおい研修室所属の和田です。この度、noteで研究成果や大学生活等を投稿していくこととなったので、よろしくお願いします。

今回はPythonのライブラリ、Sympyを使う機会があったので、それについて少し書いていこうと思います。

きっかけ

以前、機械学習のライブラリを使わずにニューラルネットワークを構築する機会がありました。その際に「代数を使った数式の微分や積、値の代入ができるSympyを使えば、誤差逆伝播法が簡単にできるのでは」と、半ば見切り発車的にSympyを使い始めました。

Sympyについて

さて本題に入ろうと思います。

SympyはPythonで提供されているライブラリであり、代数計算を可能にしてくれます。先ほど述べた通り、代数を使った数式の微分や積、値の代入などができます。

今回は私が利用した関数や機能、具体的には変数と式の定義、四則演算、微分、値の代入について書いていこうと思います。私も少し利用した程度ですので理解が浅いですが、因数分解や積分、連立方程式を解くようなこともできるようです。詳しい話はSympyのリファレンスを読むのが一番だと思います。

変数の定義

Sympyを利用するにはライブラリをインポートする必要がありますが、それをしたからと言っていきなり「x」は使えません。まずは変数の定義を行う必要があります。以下のようにsympy.Symbol()によって定義を行います。

import sympy

x = sympy.Symbol('x')
y = sympy.Symbol('y')

この際、以下のようにsympy.Symbol()の引数と代入する変数の名前が違っていても問題ありません。ただしわかりにくくなるのでそろえるのが一般的なようです。

z = sympy.Symbol('A')
z

##出力##
# A

式の定義

次は式の定義です。といっても特に使う関数はなく、通常の算術演算子を使って定義できます。

expr = 2 * x + 1 
expr  

##出力## 
# 2x + 1

数字と変数の積は「2 * x」と記述する必要があり、ここで「expr = 2x + 1」と書くとエラーを吐いてしまうことに注意してください。

expr = 2x + 1

##出力##
# エラー

式同士の四則演算

式同士の四則演算も通常の算術演算子を使って行うことができます。

expr1 = x + 2
expr2 = 3 * x + 4

expr1 + expr2

##出力##
# 4x + 6

和や差は以上のように出力されますが、積や商については(expr1)*(expr2)というような形で出力されます。つまり展開されないまま出力されます。

expr1 = x + 2
expr2 = 3 * x + 4

expr1 * expr2

##出力##
# (x + 2)(3x + 4)

のちのち値を代入するのであれば全く問題ありませんが、もし展開までしたいのであれば、sympy.expand()という関数を使えば展開することができます。

expr1 = x + 2
expr2 = 3 * x + 4

sympy.expand(expr1 * expr2)

##出力##
# 3x^2 + 10x + 8(指数は右肩に乗った形で出力される)

微分

式の微分を行うにはsympy.diff()を使います。

expr = 2 * x ** 2 + 3 * x + 4

sympy.diff(expr)

##出力##
# 4x + 3

複数変数がある場合には、sympy.diff()の第二引数に任意の変数を指定することで偏微分も可能です。

expr = 2 * (x ** 2) * y + 3 * x + 4 * y

sympy.diff(expr)

##出力##
# 4xy + 3

変数への値の代入

変数への値の代入はsubs()メソッドを使って行います。第一引数に変数、第二引数に代入する値を渡します。

expr = 2 * x ** 2+ 3 * x + 4

expr.subs(x, 3)

##出力
# 31

数値を代入したり、

expr.subs(x, y)

##出力
# 2y^2 + 3y + 4

別の変数を代入したりすることができます。

2つ以上の変数に値を代入したいのであれば、subs()の引数に(変数, 代入する値)としたタプルのリストを渡します。

expr = 2 * (x ** 2) * y + 3 * x + 4 * y

expr.subs([(x, 1), (y, 2)])

##出力
# 15

代入する値として配列を渡したいと思うこともあると思います。実際私もニューラルネットワークで多くのデータを扱うので、配列の形で一気に代入したいと思いました。というわけで以下のようなコードを記述しましょう。

import numpy as np

expr = 2 * x + y

arrayX = np.arange(12).reshape(3, 4)
arrayY = np.ones((3, 4))

expr.subs([(x, arrayX), (y, arrayY)])

##出力
# 2x + y

しかしそれではこの通りうまくいきません。このことについて書いている日本語のサイトは少なかったのですが(SymPyのリファレンスを読めばいい話だったのですが)、以下のサイトによるとsympy.lambdify()というのを使えば可能なようです。

lambdifyFunc = sympy.lambdify([x, y], expr)
lambdifyFunc(arrayX, arrayY)

##出力
# array([[ 1.,  3.,  5.,  7.],
#        [ 9., 11., 13., 15.],
#        [17., 19., 21., 23.]])

まずsympy.lambdify()の第一引数に値を代入する変数の配列、第二引数にその式を渡して関数を作成します。そしてその関数の引数に先ほどの第一引数の配列と同じ順番で代入する配列を引数として渡します。

見ればわかると思いますがsubs()に比べると面倒です…。

終わりに

ネイピア数はSympy.Eで定義されており、それを踏まえた微分等も可能です。なので実装している最中は「シグモイド関数を使ったニューラルネットワークを実装するのにピッタリだ!」と思ったのですが、シグモイド関数の微分なんかは一度計算してそれをコードとして書けばよく、ほかの関数についても同じことがいえるので、わざわざSymPyを使わなくてもよかったかもと書き終わってから思いました。おそらく重くもなるでしょうし。

もしかしたらニューラルネットワークの実装には適していなかったSymPyですが、上で述べた以上に様々な機能があるようなので、何かに使ってみると面白いかもしれません。

この記事が気に入ったらサポートをしてみませんか?