# 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.最好的結果,每個區間都預測的非常貼近

2.前半段不錯,但後半段突然翹起

3.訓練半天,甚麼都沒有

延伸到之後的DQN,此處畫出的曲線就是在各種狀態下(state=x),選擇某動作的期望價值或機率,假設另一個動作的曲線是cos(x)值,以目前的預測精準度,是能明顯判別出選擇哪個動作才是最好的。
針對此程式之後還可以進行以下延伸:
1.減少隱藏層的神經元數,甚至少一個隱藏層,還能夠維持預測精準度。
2.更換激活層,或者不加激活層,模型是否還能運行、預測結果如何。
3.嘗試手算每個神經元的參數,了解狀況2、3發生的原因