---
# System prepended metadata

title: PyTorch 輸出sin(x)函數 <舊版>
tags: [AI game]

---

# PyTorch 輸出sin(x)函數 <舊版>

### Part1、建立模型

由於輸入x與輸出sin(x)都只有一個值，因此輸入(D_in)與輸出(D_out)都只有1個神經元，因此使用兩層隱藏層(H1、H2)，每層都有10個神經元，猜想H1用來切分x的區間，H2用於擬合該區間的斜率、截距。

接著按照語法建立序列型(Sequential)模型，每一層都可再分為線性層(Linear，Wx+b)與激活層，在此激活層都先使用ReLU函數。


``` python=
import matplotlib.pyplot as plt
from math import sin,cos,pi
import random
import torch
# 設定每層神經元數目
D_in, H1, H2, D_out, = 1, 10, 10, 1 
# 建構序列型模型
model = torch.nn.Sequential(
    torch.nn.Linear(D_in,H1),
    torch.nn.ReLU(),
    torch.nn.Linear(H1,H2), 
    torch.nn.ReLU(),
    torch.nn.Linear(H2, D_out),
    torch.nn.ReLU()
)
```


### Part2、訓練模型

先設定要訓練幾回合(epochs)、每回合模型參數的修正量(learning_rate)、預測與目標的差異計算方式(loss function)，以及要用甚麼工具校正模型參數(optimizer)。

這次訓練的範圍只在x=0~2𝝿之間隨機選，由於輸出層的激活函數也是ReLU，輸出的值無法是負的，所以訓練的目標值為y=sin(x)+2，引申到之後的DQN，這裡的輸出值會變成在當前狀態(state=x)選擇某動作(action)的機率，機率也不會有負值。

儘管x、y只有一個值，仍須先以torch.Tensor()轉換成矩陣，才能放到pyTorch的系統中做計算，如moel()、loss_fn()，而預測值y_pred本身已經是矩陣格式。

接著就是以反向傳播計算梯度，再以此梯度校正模型的每個參數，也別忘記計算梯度前先清空之前算的梯度optimizer.zero_grad()，如此往復直到訓練回合結束。


``` python=16

epochs=10000 # 訓練回合數
learning_rate=0.01 # 學習率
loss_fn = torch.nn.MSELoss() # 損失函數 
# 設定優化器
optimizer = torch.optim.Adam(model.parameters(),lr=learning_rate)
for i in range(epochs):
    x=random.random()*2*pi # 在範圍內隨機選定輸入值
    y=sin(x)+2 # 以函式產出目標值
    Tx=torch.Tensor([x]) # 將單一數值轉化成矩陣
    Ty=torch.Tensor([y])
    y_pred=model(Tx) # 產出與測值
    loss = loss_fn(y_pred,Ty) # 計算預測與目標的差異
    optimizer.zero_grad() # 清空梯度
    loss.backward() # 反向傳播機算梯度
    optimizer.step() # 依照梯度做優化
```

### Part3、驗證模型

此階段就是要畫圖檢視預測結果，因此這裡的輸入值就不是隨機選取，而是在0~2𝝿切一百個等分點，算出預測與目標值後，分別裝進三者各自的串列，等計算完100個點後進行繪圖。

``` python=32

Ax=[] # 建立空串列
Ay=[]
Ay_pred=[]
for i in range(100):
    x=i*2*pi/100 # 循序產生輸入值
    y=sin(x)+2
    Tx=torch.Tensor([x])
    Ty=torch.Tensor([y])
    y_pred=model(Tx) # 使用模型算出預測值
    Ax.append(x) # 裝進串列方便畫圖
    Ay.append(y)
    Ay_pred.append(y_pred.item())
plt.plot(Ax,Ay,Ax,Ay_pred) # 畫出預測與目標兩組數據
plt.show()
```

### [結果討論]

訓練完成後常出現三種結果，前兩種結果都算是可接受，第三種結果有明顯的錯誤，可以輕易的發現並重新訓練，猜測可能是模型剛建立時隨機產生參數不佳的緣故，導致訓練進了死路。


1.最好的結果，每個區間都預測的非常貼近
![alt text](https://i.imgur.com/zV8NAGM.png)

2.前半段不錯，但後半段突然翹起
![](https://i.imgur.com/4e9Dy6U.png)

3.訓練半天，甚麼都沒有
![](https://i.imgur.com/GUlxl42.png)


延伸到之後的DQN，此處畫出的曲線就是在各種狀態下(state=x)，選擇某動作的期望價值或機率，假設另一個動作的曲線是cos(x)值，以目前的預測精準度，是能明顯判別出選擇哪個動作才是最好的。

針對此程式之後還可以進行以下延伸：
1.減少隱藏層的神經元數，甚至少一個隱藏層，還能夠維持預測精準度。
2.更換激活層，或者不加激活層，模型是否還能運行、預測結果如何。
3.嘗試手算每個神經元的參數，了解狀況2、3發生的原因

