# 深度學習與光電應用 HW1
contributed by <[`tintinjian12999`](https://github.com/tintinjian12999)>
## 題目一:以下Python code 分別能產生21筆(x, y)資料,請以PyTorch架構建立給定x值以預測y值的神經網路(2層FC層, 第一層的神經元分別為2, 5, 12),並比較y的預測值與標籤值(將他們畫在同一張圖上)
```python
import numpy as np
import matplotlib.pyplot as plt
x=np.arange(0,10.5,0.5)
y=3.5*x*x-3*np.random.randn(len(x))*x+7.6+4*np.random.randn(len(x));
plt.scatter(x,y)
```
執行結果如下

接下來使用 PyTorch 進行實作
首先使用上面的 code 產生資料並轉換為 pyTorch 使用的 tensor 型態
```python
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
x=np.arange(0,10.5,0.5)
y=3.5*x*x-3*np.random.randn(len(x))*x+7.6+4*np.random.randn(len(x))
x = x.tolist()
y = y.tolist()
t_c = torch.tensor(x).unsqueeze(1)
t_u = torch.tensor(y).unsqueeze(1)
```
轉換為list是為了避免型態是 array 產生的錯誤
```
tensor([[ 0.0000],
[ 0.5000],
[ 1.0000],
[ 1.5000],
[ 2.0000],
[ 2.5000],
[ 3.0000],
[ 3.5000],
[ 4.0000],
[ 4.5000],
[ 5.0000],
[ 5.5000],
[ 6.0000],
[ 6.5000],
[ 7.0000],
[ 7.5000],
[ 8.0000],
[ 8.5000],
[ 9.0000],
[ 9.5000],
[10.0000]], dtype=torch.float64)
```
可以看到若 x 的資料型態為 array 的話在轉換為 tensor 的時候最後會多一項儲存資料型態的元素。
> 後來發現是因為數值儲存的型態為 64 bytes 的浮點數產生的問題,而 pytorch 能接受的為 32 bytes 的浮點數。
>
接著利用 python 提供的亂數 rand 用於產生訓練資料集和驗證資料集的 index
```python
n_samples = t_u.shape[0]
n_val = int(0.2 * n_samples)
shuffled_indices = torch.randperm(n_samples)
train_indices = shuffled_indices[:-n_val]
val_indices = shuffled_indices[-n_val:]
train_indices, val_indices
```
執行結果如下
```
(tensor([ 7, 2, 18, 8, 10, 3, 20, 6, 15, 11, 5, 0, 19, 14, 16, 12, 4]),
tensor([ 9, 13, 1, 17]))
```
產生訓練資料集與驗證資料集並進行正規化
```python
t_u_train = t_u[train_indices] #訓練資料集
t_c_train = t_c[train_indices]
t_u_val = t_u[val_indices] #驗證資料集
t_c_val = t_c[val_indices]
t_un_train = 0.1 * t_u_train #進行正規化
t_un_val = 0.1 * t_u_val
```
定義要使用的 Nural Network (這裡以2個神經元,共三層為例)
```python
seq_model = nn.Sequential(nn.Linear(1, 2),
nn.Tanh(),
nn.Linear(2, 1))
```
定義訓練流程
```python
def training_loop(n_epochs, optimizer, model, loss_fn, t_u_train, t_u_val,
t_c_train, t_c_val):
for epoch in range(1, n_epochs + 1):
t_p_train = model(t_u_train) #將訓練資料輸入模型
loss_train = loss_fn(t_p_train, t_c_train) #計算訓練損失
t_p_val = model(t_u_val) #將驗證資料輸入模型
loss_val = loss_fn(t_p_val, t_c_val)#計算驗證損失
optimizer.zero_grad()
loss_train.backward()
optimizer.step()
if epoch == 1 or epoch % 1000 == 0:
print(f"Epoch {epoch}, Training loss {loss_train.item():.4f}," #印出訓練損失及驗證損失
f" Validation loss {loss_val.item():.4f}")
```
進行訓練
```python
optimizer = torch.optim.SGD(seq_model.parameters(), lr=1e-4) #這裡為了讓結果更穩定,稍微將學習率調低了一點
training_loop(
n_epochs = 20000,
optimizer = optimizer,
model = seq_model,
loss_fn = nn.MSELoss(),
t_u_train = t_un_train,
t_u_val = t_un_val,
t_c_train = t_c_train,
t_c_val = t_c_val)
```
最後印出結果
```python
from matplotlib import pyplot as plt
t_range = torch.arange(0., 400.).unsqueeze(1)
fig = plt.figure(dpi=200)
plt.xlabel("X")
plt.ylabel("Y")
plt.plot(t_c.numpy(), t_u.numpy(), 'o') #描繪出真實的資料點
plt.plot(seq_model(0.1 * t_range).detach().numpy(), t_range.numpy(), 'c-') #描繪出圖6.9的曲線
plt.plot(seq_model(0.1 * t_u).detach().numpy(), t_u.numpy(), 'kx') #描繪出預測的資料點
```
結果如下圖

可以看到在使用 2 個神經元的情況下預測的模型有較大的誤差,接著分別將神經元的數量增加為 5 與 12 。
使用 5 個神經元

使用 12 個神經元

比起使用 2 個神經元,使用 5 個與 12 個神經元產生的結果都有明顯的改善。
## 題目二: 根據以下計算圖,請用Python / Matlab 的自動微分計算y對x的偏微分值 (在x = 4, 12, -8)

透過以下python code 即可達成目的
```python
import autograd.numpy as np
from autograd import grad, jacobian, elementwise_grad
def v(x):
return x * x
def u(x):
return np.exp(v(x))
def y(x):
return u(x) * x
dy_dx = grad(y)
x = 4.0
dy_dx_value = dy_dx(x)
print(dy_dx_value)
```
結果為
```
293241647.1767598 (x = 4)
```
```
9.98396929791371e+64 (x = 12)
```
```
8.043342314246985e+29 (x = -8)
```