Deep Learning Specialization (Coursera) 自習記録 (C1W2)

# はじめに
[Deep Learning Specialization](https://www.coursera.org/specializations/deep-learning) の Course 1, Week 2 (C1W2) の内容です。

# (C1W2L01) Binary Classification
## 内容

- 画像データから「猫か否か」を判断する事例で binary classification を説明
- notation (記号の意味) の説明
  - $X$ ; 行方向に 1 データの特徴 ($n_x$)，列方向に training example ($m$ 個) を並べたもの ($X \in \mathbb{R}^{n_x \times m}$)
  - $Y$ ; $Y \in \mathbb{R}^{1\times m}$

## 感想

- Machine Learning の講義の場合と比べて，$X$ の行と列の意味が変わっている

# (C1W2L02) Logistic Regression
## 内容

- 予測値 $\hat{y} = P(y=1|x)$ ($y=1$ の確率)
- パラメタ $w \in \mathbb{R}^{n_x}$, $b \in \mathbb{R}$ を定義
- $\hat{y} = \sigma(w^T x + b)$ ; シグモイド関数
- $\sigma(z) = \frac{1}{1+e^{-z}}$

## 感想

- ここでも Machine Learning のときと記号が違う。$x_0^{(i)} = 1$ を使わない。定数項 $b$ は $w$ に含めない。

# (C1W2L03) Logistic Regression Cost Function
## 内容

- cost function ;  $J(w, b) = -\frac{1}{m} \Sigma^m_{i=1}\[y^{(i)}\log\hat{y}^{(i)} + \(1-y^{(i)}\)\log\(1-\hat{y}^{(i)}\)  \]$

# (C1W2L04) Gradient Descent
## 内容

- gradient descent (最急降下法) の直感的な説明
- $\frac{\partial J(w,b)}{\partial w}$ を，プログラムでは `dw` と書くことが多い
- $\frac{\partial J(w,b)}{\partial b}$ を，プログラムでは `db` と書くことが多い


# (C1W2L05) Derivatives
## 内容
- 微分の簡単な説明

## 感想
- 基本的な内容なので，動画は 1.75 倍で視聴

# (C1W2L06) More Derivatives Example
## 内容
- 微分の簡単な説明

## 感想
- 基本的な内容なので，動画は 1.75 倍で視聴

# (C1W2L07) Computation Graph
## 内容

- $J(a,b,c) = 3\(a + bc\)$ のような場合に，$u = bc$，$v = a + u$， $J = 3v$ のように分解して計算する方法を図解

# (C1W2L08) Derivatives With Computation Graph
## 内容
- Computational Graph を使いながら，微分 ($\frac{dJ}{da} = \frac{dJ}{dv}\frac{dv}{da}$) などを説明

# (C1W2L09) Logistic Regression Gradient Descent
## 内容
- logistic regression (ロジスティック回帰) の損失 $L\(a, y\)$ の微分の説明

# (C1W2L10) Gradient Descent on m Example
## 内容
- サンプル数が $m$ の場合に，cost function $J\(w, b\)$ を微分して，最急降下法に適用する方法の説明
- for ループで説明したが，for ループは非効率なので，vectorization (ベクトル化) が大事

# (C1W2L11) Vectorization
## 内容

- $z = w^T x + b$ の $w^T x$ を事例に，vectorization の考え方を説明
- Jupyter notebook 上で，for ループとベクトル計算 (`z = np.dot(w, x) + b`) の計算時間を実演。300 倍違う
- SIMD ; Single Instruction Multiple Data
- Python の Numpy が計算を並列化する

私も for ループとベクトル計算で時間を比較してみました。

```vectorization.py
import numpy as np
import time

a = np.random.rand(1000000)
b = np.random.rand(1000000)

tic = time.time()
c = np.dot(a, b)
toc = time.time()

print(c)
print("Vectorization version:" + str(1000*(toc-tic)) + "ms")

c = 0
tic = time.time()
for i in range(1000000):
    c += a[i]*b[i]
toc = time.time()

print(c)
print("for loop:" + str(1000*(toc-tic)) + "ms")
```

結果です。vectorization では 12ms，for ループでは 821ms と，700 倍弱の違いがありました。

```
249840.57440415953
Vectorization version:12.021541595458984ms
249840.57440415237
for loop:821.0625648498535ms
```
# (C1W2L12) More Vectorization Examples
## 内容

- neural network programming guideline ; **Whatever possible, avoid explicit for-loop / 可能な限り for-loop を避けよ**

```example.py
import numpy as np

u = np.dot(A, v) # 行列とベクトルの積

u = np.exp(v) # 要素ごとに exp を作用させる
u = np.log(v) # 要素ごとに log を作用させる
u = np.abs(v) # 要素ごとに abs (絶対値) を作用させる
u = np.maximum(v, 0) # 0 以下の要素は 0 にする
u = v ** 2 # 要素ごとに 2 乗
u = 1/v # 要素ごとに逆数
```
# (C1W2L13) Vectorizing Logistics Regression
## 内容
- Logistics regression の計算を vectorization (ベクトル化) する

```math
X = \left[x^{(1)} \ x^{(2)} \cdots \ x^{(m)}\right] \ (X \in \mathbb{R}^{n_x \times m}) \\
Z = \left[z^{(1)} \ z^{(2)} \cdots \ z^{(m)}\right] \ (Z \in \mathbb{R}^m ) \\
A = \left[a^{(1)} \ a^{(2)} \cdots \ a^{(m)}\right] \ (A \in \mathbb{R}^m ) \\
Z = w^T X + \left[b \ b \ \cdots b \right] \\
A = \mathrm{sigmoid}\left( Z \right) \ (\mathrm{sigmoid} \ 関数を適切に実装する)
```
- Python では ```Z = np.dot(w.T, X) + b``` となる (```b``` は自動的に [1, m] の列ベクトルに変換される)

# (C1W2L14) Vectorizing Logistics Regression's Gradient Computation
## 内容
- logistics regression の微分計算の vectorization (ベクトル化) の説明

```math
db = \frac{1}{m} \cdot \mathrm{np.sum}(Z) \\
dw = \frac{1}{m} \cdot X\ dZ^T
```
## 感想
- 普通の数式表現と Python のコードが混じってます。授業を聞きながらならわかるかもしれないけど，後で見返すとわかりづらいかも。

# (C1W2L15) Broadcasting in Python
## 内容
- Python のブロードキャストの説明
- (m, n) 行列と (1, n) 行列を足し算すると，(1, n) 行列は自動的に (m, n) 行列になる
- (m, n) 行列と (m, 1) 行列を足し算すると，(m, 1) 行列は自動的に (m, n) 行列になる
- 詳細は NumPy のブロードキャストのドキュメントを参照
- Matlab/Octave の bsxfun 関数はちょっと違う (?)

```example.py
>>> import numpy as np
>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> b = np.array([100, 200, 300, 400])
>>> a + b
array([[101, 202, 303, 404],
       [105, 206, 307, 408]])
````

# (C1W2L16) A Note on Python/numpy vectors
## 内容
- Python/NumPy の柔軟性は長所でもあり，短所でもある
- 行ベクトルと列ベクトルを足し算しても，エラーにならず，何らかの計算結果が出てくるので，エラーを見つけづらい
- **サイズが ```(n, )``` の配列を使わない (ランク 1 の配列を使わない)**

```example.py
>>> import numpy as np
>>> a = np.random.rand(5) # ランク 1 の配列
>>> print(a)
[0.4721318  0.73582028 0.78261299 0.25030022 0.69326545]
>>> print(a.T)
[0.4721318  0.73582028 0.78261299 0.25030022 0.69326545] # 転地しても表示は変わらない
>>> print(np.dot(a, a.T)) # 内積を計算しているが，内積・外積のいずれを計算すべきかがよく分からない
1.9200902050946715
>>>
>>> a = np.random.rand(5, 1) # (5, 1) の行列
>>> print(a) # 行ベクトル
[[0.78323543]
 [0.18639053]
 [0.45103025]
 [0.48060903]
 [0.93265189]]
>>> print(a.T)
[[0.78323543 0.18639053 0.45103025 0.48060903 0.93265189]] # 転地したら列ベクトル
>>> print(np.dot(a, a.T)) # 行ベクトルと列ベクトルの積を正しく計算
[[0.61345774 0.14598767 0.35326287 0.37643002 0.73048601]
 [0.14598767 0.03474143 0.08406777 0.08958097 0.17383748]
 [0.35326287 0.08406777 0.20342829 0.21676921 0.42065422]
 [0.37643002 0.08958097 0.21676921 0.23098504 0.44824092]
 [0.73048601 0.17383748 0.42065422 0.44824092 0.86983955]]
```
- 次元が分からないときは ```assert(a.shape == (5, 1))``` などと入れる
- ランクが 1 の配列は，```a = a.reshape((5,1))``` のように明示的にリシェイプする

## 感想
- 行列のサイズが分からなくなることは Machine Learning の受講時にも多々あったので，ここは大事


# (C1W2L17) Quick tour of Jupyter/ipython notebooks
## 内容
- Coursera で受講する際に，Jupyter/ipython notebook を使う方法の説明

# (C1W2L18) Explanation of Logistics Regression Cost Function (Optional)
## 内容
- logistics regression の cost function の (再) 説明
- $\hat{y} = \sigma(w^T x + b)$ {y}when $\sigma(z) = \frac{1}{1 + e^{-z}}$ 
- $\hat{y} = P(y=1 | x)$ ; $y=1$ である確率，なので， $y=1$ なら $p(y|x) = \hat{y}$，$y=0$ なら $p(y|x) = 1 - \hat{y}$
- これをまとめると，$p(y|x) = \hat{y}^y (1-\hat{y})^{(1-y)}$ とも書ける
- 対数関数も増加関数なので，上記の式の対数を最大化するのも同じこと
- $\log p(y|x) = y\log\hat{y} + (1-y)\log(1-\hat{y}) = -L(\hat{y}, y)$
- 複数の training set における確率を考えると，$\Pi^{m}_{i=1}p(y^{(i)} | x^{(i)})$
- これの対数を考えると，$\Sigma^{m}_{i=1}\log p(y^{(i)} | x^{(i)}) = - \Sigma L(\hat{y}^{(i)}, y^{(i)})$。これを最大化することになる。
- cost function は $\frac{1}{m}$ して，最小化するので負号を取って，$J(w, b) = \frac{1}{m} \Sigma^{m}_{i=1} L(\hat{y}^{(i)}, y^{(i)})$

## 感想
- 正直，あまり理解できてない :-p
- $L(\hat{y}, y) = - y\log\hat{y} - (1-y)\log(1-\hat{y})$ から話をするほうが，私にとっては直感的に分かりやすかった 


# 参考
- [Deep Learning Specialization (Coursera) 自習記録 (目次)](https://qiita.com/satomshr/items/dc4ae752c558798bbef6)