Python初心者がPytorchを触る(2)


今記事では前回書いた記事「Python初心者がPytorchを触る(1)」の続きを書いていきます。

1.Pytorchで微分

1-1.基本的な傾き

前回の最後で述べた通り、今回は微分について説明いたします。
微分は関数における "傾き" を求めることです。まずは微分を学ぶより先に傾きについて考えてみましょう。


y = 3x \\

上記の関数において傾きは "3" です。なぜなら、2次元平面においてxを「1」増やした場合、yは「3」ずつ増えるからです。

傾きとはグラフの変化量とも言えます。小学校で学んだグラフをイメージしていただけるとわかりやすいです。

1-2.2変数以上の場合の傾き

先ほどは関数1つにつき変数が1つでした。変数が1つとは関数の結果に関わる文字の数が1つしかないということです。下記の例で確認しましょう。

\\
y = 3x\\

y = x^2+3x+4

上記の関数は全て1変数の関数です。
では2変数以上の関数とはどんな関数でしょう


y = x_1+2x_2+3x_3

上記の関数は2変数の関数です。1変数の関数との大きな違いは変数の数です。xという変数が2種類、3種類と関数内に含まれています。

2変数以上の関数では変数それぞれに対して傾きを考えます。よって、

x_1についての傾き = 1\\x_2についての傾き = 2\\x_3についての傾き = 3 

                            のようになります。

傾きの種類が増えたことで関数の表現幅が広がっていることがわかります。(表現の幅とは "y" の答えのバリエーションの数のことです。)

ニューラルネットワークでは変数をたくさん使用することで複雑な形の関数を表現し、判別機器として機能しています。

詳しい関数と変数の関係については割愛させていただきます。興味のある方は書籍や信頼できるサイトでしっかり勉強することをお勧めします。

1-3.2次関数以上の傾きの求め方

次は2次関数以上の関数を微分し、傾きを求める方法を説明します。
基本的に微分は2次関数以上の関数に効力を発揮します。

y = x^2 + x\\
\\
y' = 2x + 1

y'は微分という意味です。上の式を微分すると下の式になります。基本原理を説明していきます。xを変数とした場合の微分のルールを下記に記述します。

y = ax^n \hspace{5mm} \rightarrow \hspace{5mm} y' = nax^{n-1} \\
y = x \hspace{5mm} \rightarrow \hspace{5mm} y' = 1

今回はこれが理解できれば困ることはありません。

これは微分の中でも本当に基礎の基礎です。複雑な微分方法、三角関数、対数などのここでは紹介できない微分はこちら

リンク先 : 高校数学の美しい物語 微分公式一覧(基礎から発展まで)

また、2次関数以上は関数のある点に焦点を当てた傾きを調べることができます。

例) \hspace{1mm} y=x^2 + 3  \hspace{5mm} \rightarrow \hspace{5mm} y' = 2x \hspace{2mm}(微分)\\
x=1の時の傾き\hspace{3mm}y` = 2 ・1=2\\
x=2の時の傾き\hspace{3mm}y` = 2 ・2=4

以上が理解できれば今回の記事を理解するのは容易です。

2.実際のコーディング

2-1.1変数の微分

では実際にPytorchで微分をしてみましょう。
まずは

y = 3x

                     をPytorchで微分し、傾き3を計算します。

import torch
x = torch.ones(3,3,requires_grad=True)
a = torch.tensor([[3,3,3],[3,3,3],[3,3,3]])
y = a*x

微分を扱うときは微分したい変数を宣言する時、「requires_grad=True」を追加します。これは微分を許可するコードです。さらに微分で使う変数の中身が整数型だとエラーがでます。浮動小数点型で宣言してください。

結果
tensor([[3., 3., 3.],
        [3., 3., 3.],
        [3., 3., 3.]], grad_fn=<MulBackward0>)

出力に"grad_fn="という文が追加されていることを確認してください。

次に微分するときの注意点です。Pytorchでは微分をする時、一次元でなければいけません。なので全部のテンソルを一つにまとめます。

y = torch.sum(y)

torch.sum()は前回の記事で紹介しています。前回の記事を参考にしてください。Python初心者がPytorchを触る(1)

いよいよ微分をしていきます。

y.backward()
print(x.grad)

.backward()は微分を行う時に必要な事前準備です。また、.backward()を行う際、微分の対象になっている変数が一次元ではない場合、エラーが起こります。

結果を見てみましょう

結果
tensor([[3., 3., 3.],
        [3., 3., 3.],
        [3., 3., 3.]])

しっかりと微分計算ができ、傾きが "3 " という結果になりました。

2-2.2変数以上の微分

次に2変数以上の関数を微分していきます。変数がわかりやすいように関数を「out」,変数を 「x,y,z」 とします。

out = x + 2y + 3z\\
 \\


xについての傾き  = 1\\
yについての傾き = 2\\
zについての傾き = 3

Pytorchで計算をしていきます。

x = torch.ones((3,3),requires_grad=True)
y = torch.ones((3,3),requires_grad=True)
z = torch.ones((3,3),requires_grad=True)
out = 1*x+2*y+3*z

out = torch.sum(out)
out.backward()

print('xについての傾き')
print(x.grad)
print('yについての傾き')
print(y.grad)
print('zについての傾き')
print(z.grad)
結果
xについての傾き
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
yについての傾き
tensor([[2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.]])
zについての傾き
tensor([[3., 3., 3.],
        [3., 3., 3.],
        [3., 3., 3.]])

2-3.2次関数の微分

最後に2次関数の微分を紹介します。
数式はこの記事で紹介した

 \hspace{1mm} y=x^2 + 3  \hspace{5mm} \rightarrow \hspace{5mm} y' = 2x \hspace{2mm}(微分)\\
x=1の時の傾き\hspace{3mm}y` = 2 ・1=2\\
x=2の時の傾き\hspace{3mm}y` = 2 ・2=4

                        これをPytorchで計算していきます。

a = torch.ones(3,3)
x1 = torch.ones((3,3),requires_grad=True)

x2 = torch.tensor([[2.0,2.0,2.0],
                  [2.0,2.0,2.0],
                  [2.0,2.0,2.0]],requires_grad=True)

b = torch.tensor([[3,3,3],
                  [3,3,3],
                  [3,3,3]])
out1 = a*x1*x1+b
out2 = a*x2*x2+b

out1 = torch.sum(out1)
out2 = torch.sum(out2)

out1.backward()
print("x = 1の時の傾き")
print(x1.grad)

out2.backward()
print("x = 2の時の傾き")
print(x2.grad)
結果
x = 1の時の傾き
tensor([[2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.]])
x = 2の時の傾き
tensor([[4., 4., 4.],
        [4., 4., 4.],
        [4., 4., 4.]])

しっかり計算ができていますね。

3.最後に

今回は簡単な微分をPytorchで行いました。少し、説明足らずでわかりにくい部分もあったと思います。また、演習問題のようなものを追加して読者の皆さんに手を動かして理解してもらえるように今後改良していきたいです。
最後まで読んでいただきありがとうございました。
次回はPytorchにおけるニューラルネットワークの構築方法を学んでいきたいと考えています。