2
4

More than 1 year has passed since last update.

Chainerチュートリアル Step1演習問題の解答1 

Last updated at Posted at 2020-11-04

##背景

データサイエンスを始めるにあたり、Chainer Tutorialをやりました。
初学者にもわかりやすい文章で、数学やpythonの基本から教えてくれるので、機械学習の入門にはおすすめだと思います(しかも無料!)。
準備編の最後に演習問題がついていますが、復習がてら解答をまとめ、投稿しようと思った次第です。

Chainerチュートリアル Step1演習問題の解答1(本記事) → 問題2, 4
Chainerチュートリアル Step1演習問題の解答2 → 問題5, 6

##問2.1(組み込み関数)


a = [4, 8, 3, 4, 1]

#リストaの長さを求める
res = len(a)
print(res)

#リストaに含まれる値の最大値を求める。
res = max(a)
print(res)

#リストaに含まれる値の最小値を求める。
res = min(a)
print(res)

#リストaに含まれる値の合計値を求める。
res = sum(a)
print(res)

#リストaをソートして、[1, 3, 4, 4, 8] というリストを返す。
res = sorted(a)
print(res)

##問2.3(リストの基本操作)


#リストaの先頭の要素を取り除いて、[8, 3, 4, 1] となるようにして下さい。
a = [4, 8, 3, 4, 1]
a.pop(0)
print(a)

#リストaの末尾の要素を取り除いて、[4, 8, 3, 4] となるようにして下さい。
a = [4, 8, 3, 4, 1]
a.pop()
print(a)

配列の削除にはpopメソッドを使います。引数xを指定して、pop(x)とすると、インデックスがxに等しい場所の要素を取り除きます。
引数に何も指定しない場合は、デフォルトで末尾の要素1つを削除します。


#リストaの末尾に 100 という値を追加して、[4, 8, 3, 4, 1, 100] となるようにして下さい。
a = [4, 8, 3, 4, 1]
a.append(100)
print(a)

appendメソッドは末尾に引数の値を追加します。

ちなみに、先頭に追加したい時はinsertメソッドを用いて、第一引数に追加したい場所、第二引数に追加したい要素を指定します。


a = [4, 8, 3, 4, 1]
a.insert(0, 100)
print(a)

##問2.4(リスト内包表記)

#####1. a=[4, 8, 3, 4, 1] というリストに対し、要素が偶数なら 0, 奇数なら 1 に変換するコードをリスト内包表記を用いて書いて下さい。この結果、このリストは [0, 0, 1, 0, 1] に変換されるべきです。


a=[4, 8, 3, 4, 1]

def convert(list):
    result = [0 if x % 2 == 0 else 1 for x in list]
    print(result)

convert(a)

#####2. 1.で書いたコードと組み込み関数を組み合わせて、リスト a に含まれる奇数の個数を数えるコードを書いて下さい。

1の関数で、奇数は1・偶数は0に変換されてますから、変換後の配列の全ての要素を足した数は、リストaに含まれる奇数の個数と等しくなるはずです。

a=[4, 8, 3, 4, 1]

def odd_num(list):
    result = [0 if x % 2 == 0 else 1 for x in list]
    print(sum(result))

odd_num(a)

#####3.リスト内包表記を使ってリスト a から奇数の要素だけを残すコードを書いて下さい。

a=[4, 8, 3, 4, 1]

def odd_only(list):
    result = [x for x in list if x % 2 == 1]
    print(result)

odd_only(a)

##問2.5(文字列)

#####1. str.join() を使って、0 から 99 までの数をスペース区切りで並べた文字列 "0 1 2 3 4 ... 99" を構成して下さい。

joinメソッドは、文字列に対する組み込み関数です。引数に配列を与えることで、配列内の要素を文字列で区切った、一つの文字列を形成します。


a = [str(x) for x in range(100)] #[0,1,2, ・・・, 98, 99]という配列を作る

str = ' '.join(a) #半角スペースに対してjoinを使うことで、配列要素をスペースで区切った1つの文字列を作る
print(str)

#####2. str.format() を使って float の値 (1.0 / 7.0) の小数点以下9桁までを表示して下さい。

formatメソッドは、文字列内の{ }で囲まれたエリアに対し、引数で受けた値を埋め込みます。
{インデックス番号:書式設定}としてかき、インデックス番号は位置を、書式設定は桁数などを指定できます。
インデックス番号は省略できます。


sentence = "{0}は{1}です".format("クジラ", "哺乳類")
print(sentence)

>>クジラは哺乳類です

小数点を指定する時は、{: .数字f}とします。


num = 1.0/7.0

print('{0:.9f}'.format(num)) #インデックス番号を省略し、print('{:.9f}'.format(num))でもok

##問2.6(クラス)


class DataManager:
    #3つの数 x, y, z をコンストラクタで受け取り、インスタンスの属性でそれぞれの値を記憶する。
    def __init__(self, x, y, z):
            self.x = x
            self.y = y
            self.z = z

    #add_x(self, delta): x に delta だけ足して、値を更新する。
    def add_x(self, delta):
            self.x += delta

    #add_y(self, delta): y に delta だけ足して、値を更新する。
    def add_y(self, delta):
            self.y += delta

    #add_z(self, delta): z に delta だけ足して、値を更新する。
    def add_z(self, delta):
            self.z += delta

    #sum(self): x, y, z の3つの数の合計値を返す。
    def sum(self):
        return self.x + self.y + self.z

##問2.8(制御構文)

list = [i+2 for i in range(98)] #2~100までのリスト
prime_numbers = [] #素数を格納するリスト

for a in list: 
    k = a - 1 #2~98までの数字について、自分自身未満の値で割っていくのを繰り返す
    while k > 1:
        if a % k == 0: #途中で割り切れる数字が出現したら素数ではないので終了
          break
        else:
            k -= 1
    if k == 1: #途中で割り切れる数字がなく、kが1となるまで下がりきれたら、それは素数である
        prime_numbers.append(a)
    
print(prime_numbers)

##問4.1(導関数)
スクリーンショット 2020-11-04 10.25.31.png

##問4.2(線形性)

スクリーンショット 2020-11-04 10.33.45.png

##問4.3(積の微分)
スクリーンショット 2020-11-04 10.39.06.png

##問4.4(合成関数の微分)

スクリーンショット 2020-11-04 11.05.35.png

##問4.5(合成関数の微分)
#####1.𝑔(𝑥)=𝑥^4+2^𝑥2+2 となることを確認して下さい。また、これより 𝑔′(𝑥)=4𝑥3+4𝑥 となることを確認して下さい。

スクリーンショット 2020-11-04 12.34.33.png

#####2.一方で、合成関数の微分則によって 𝑔′(𝑥) を計算し、(1) で求めた導関数と一致することを確認して下さい。
スクリーンショット 2020-11-04 12.32.57.png

1.で求めた式と一致しています。

#####3.ℎ(𝑥)=𝑓(𝑓(𝑓(𝑥))) とします。合成関数の微分によって ℎ′(𝑥) を計算して下さい。

スクリーンショット 2020-11-04 12.33.41.png

##問4.6(指数関数・対数関数)

#####1.


import math  # exp を呼び出すために math モジュールをインポート

def f(x):
    return math.exp(x)

def dfdx_approx(x):
    dh = 0.0001
    return (f(x + dh) - f(x)) / dh

x = [-3, -2, -1, 0, 1, 2, 3]

for i in x:
    print('df({})dx = {}'.format(i,dfdx_approx(i)))
    print('exp({}) = {}'.format(i, math.exp(i)))
    print()

x = 3 ~ 3までの代入を行うため、それらが入ったリストを作ります。
今回は、代入した数字と、その結果どう言った数値になったかを見やすくするため、formatメソッドを利用し、代入した数字iと、df(x)/dxおよびexp(x)を出力しています。

##問4.7(ニューラルネットワーク関数の微分)

#####1.

ReLU(x)は、xと0のうち大きい方である、ということを言っています。すなわち、x > 0 であれば ReLU(x) = x となり, x =< 0 であれば、 ReLU(x) = 0 になります。

\begin{eqnarray}
ReLU ( x )
=
\begin{cases}
x & ( x \gt 0 ) \\
0 & ( x \leqq 0 )
\end{cases}
\end{eqnarray}

これを微分するので、

\begin{eqnarray}
ReLU' ( x )
=
\begin{cases}
1 & ( x \gt 0 ) \\
0 & ( x \lt 0 )
\end{cases}
\end{eqnarray}

となります。
問題は x = 0 のときです。右から近づいてきたときは ReLU(x) = x なのでReLU'(0) = 1となるのに対し、左から近づくと ReLU(0) = 0 のため ReLU'(x) = 0となって、 x = 0において微分したときの値が異なります。この場合は、x=0において微分ができない、ということになります。

#####2.

\begin{align}
tanh' ( x )
&=\frac{(e^{x} - e^{-x})'(e^{x} + e^{-x}) - (e^{x} - e^{-x})(e^{x} + e^{-x})'}{(e^{x} + e^{-x})^2} \\ \\
&=\frac{(e^{x} + e^{-x})(e^{x} + e^{-x}) - (e^{x} - e^{-x})(e^{x} - e^{-x})}{(e^{x} + e^{-x})^2} \\ \\
&=\frac{(e^{2x} + 2 + e^{-2x}) - (e^{2x} - 2 + e^{-2x})}{(e^{x} + e^{-x})^2} \\ \\
&=\frac{4}{(e^{x} - e^{-x})^2}

\end{align}

#####3.
合成関数の微分を用います。
$ u = 1 + e^{-x} $ とおくと、$ Sigmoid(x) = 1/u $、 $ du/dx = - e^{-x} $

\begin{align}
Sigmoid' ( x )
&=\frac{df(u)}{du}・ \frac{du}{dx}\\ \\
&=-\frac{1}{u^2} ・ (- e^{-x})\\ \\
&=\frac{e^{-x}}{(1 + e^{-x})^2}\\ \\

\end{align}

これは実は、$ f'(x) = (1 - f(x)) ・ f(x) $となっていて、シグモイド関数の特徴です。

#####4.
合成関数の微分を用います。
$ u = 1 + e^{x} $ とおくと、$ Softplus(x) = log(u) $、 $ du/dx = e^{x} $

\begin{align}
Softplus' ( x )
&=\frac{df(u)}{du}・ \frac{du}{dx}\\ \\
&=\frac{1}{u} ・ e^{x}\\ \\
&=\frac{e^{x}}{1 + e^{x}}\\ \\
&=\frac{1}{e^{-x} + 1}\\ \\

\end{align}

シグモイド関数になっていますね。ソフトプラス関数は微分するとシグモイド関数になり、ニューラルネットワークの活性化関数の一つとして使われています。

##問4.8(偏微分)

#####1.
絶対値関数は、中の符号によって場合分けをし、絶対値を外す必要があります。

\begin{eqnarray}

|x|
= \begin{cases}
x & ( x \geqq 0 ) \\
-x & ( x \lt 0 )
\end{cases}
\end{eqnarray}

したがって、

\begin{eqnarray}
\frac{\partial f(x,y)}{\partial x}
= {|x|}'
= \begin{cases}
1 & ( x \gt 0 ) \\
-1 & ( x \lt 0 )
\end{cases} \\ \\

\frac{\partial f(x,y)}{\partial y}
= {|y|}'
= \begin{cases}
1 & ( y \gt 0 ) \\
-1 & ( y \lt 0 )
\end{cases}

\end{eqnarray}

$ x = 0 $ において、右から近づいたときは $ f'(x,y) = 1$ 、左から近づいたときは$ f'(x,y) = -1$ になります。ある点において右側極限と左側極限の値が異なるとき、その関数はその点において微分できません。よって、今回の式では、 $ x = 0 $ で $ x $ について偏微分することはできません。$ y $ の偏微分についても、同じように $ y = 0 $ で微分することができません。

#####2.

\begin{eqnarray}

|x + y|
= \begin{cases}
x + y & ( x + y \geqq 0 ) \\
- x - y & ( x + y \lt 0 )
\end{cases}
\end{eqnarray}

ですので、

\begin{eqnarray}
\frac{\partial f(x,y)}{\partial x}

= \begin{cases}
1 & ( x + y \gt 0 ) \\
-1 & ( x + y \lt 0 )
\end{cases} \\ \\

\frac{\partial f(x,y)}{\partial y}
= \begin{cases}
1 & ( x + y \gt 0 ) \\
-1 & ( x + y \lt 0 )
\end{cases}

\end{eqnarray}

以上です。数学あまり自信がないので、間違っていたら教えていただけると幸いです。 問題5, 6は次回。
2
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4