# PyTorch學習記錄_2_非線性模型
線性模型本身是一種非常簡單的結構,不過現實中的資料多半是非線性模型才比較能夠有效的預測。非線性模型在架構上與線性模型之間就只是一個非常簡單的差異。
## 非線性模型
首先引入需求的模型:
```python
import torch
import torch.nn as nn
import torch.optim as optim
```
定義虛擬的資料集:
```python
X = torch.tensor([
[1.0], [1.5], [2.0], [2.5], [3.0], [3.5], [4.0], [4.5], [5.0], [5.5],
[6.0], [6.5], [7.0], [7.5], [8.0], [8.5], [9.0], [9.5], [10.0], [10.5],
[11.0], [11.5], [12.0], [12.5], [13.0], [13.5], [14.0], [14.5], [15.0], [15.5],
[16.0], [16.5], [17.0], [17.5], [18.0], [18.5], [19.0], [19.5], [20.0]
], dtype=torch.float32)
Y = torch.tensor([
[6.97], [9.66], [12.12], [14.55], [16.76], [21.71], [26.53], [32.46], [37.14], [42.36],
[46.11], [52.97], [57.75], [61.27], [66.16], [67.62], [69.44], [71.56], [72.81], [73.87],
[76.33], [76.37], [78.35], [80.06], [81.85], [84.46], [83.99], [86.54], [88.32], [86.84],
[89.25], [88.12], [88.15], [91.78], [92.26], [92.14], [90.74], [90.38], [92.97]
], dtype=torch.float32)
```
列印資料點:

從資料分佈來看,很明顯的這如果單純的使用線性模型是無法有效的擬合,所以我們需要的是非線性模型。
一般來說,我們在訓練模型之前都會將資料正規化,這對於模型的收斂有著極大的效益,一般結構式資料也許就是縮放為均值為0,變異數為1的資料,或是影像資料普遍除`255.0`都是一種資料縮放的正規化處理。
```python
X_mean = X.mean()
X_std = X.std()
Y_mean = Y.mean()
Y_std = Y.std()
X_norm = (X - X_mean) / X_std
Y_norm = (Y - Y_mean) / Y_std
```
請注意,如果我們有針對目標值來做縮放的話,那預測的結果就必需做相對應的『Inverse(反向)』處理,近期也確實有一些論文指出對目標值(`Y`)做正規化對於數值型的預測是有幫助的。
縮放之後的值域如下:

現在我們可以建構『非』線性模型:
```python
torch.manual_seed(25)
model = nn.Sequential(
nn.Linear(1, 3),
nn.ReLU(),
nn.Linear(3, 1)
)
```
可以注意到這跟線性模型最大的差異在於加入`ReLU`這個`activation funciton`,那這個`activation funciton`有很多中文,活化函數、激活函數、啟動函數,都好,總之就是一個非線性轉換的函數,只是不同的函數有著不一樣的行為就是。
然後設置損失函數與最佳化的方式:
```python
loss_function = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
```
最後就是Do、Re、Mi、So:
```python
for epoch in range(3000):
# 每一次迭代開始訓練之前記得清空梯度資訊
optimizer.zero_grad()
# 取得模型預測結果
outputs = model(X_norm)
# 利用損失函數計算實際資料與模型輸出之間的差異
loss = loss_function(outputs, Y_norm)
# 計算反向傳播的資訊
loss.backward()
# 更新學習參數
optimizer.step()
print("\nTraining Complete.")
print(f"\nFinal Loss: {loss.item()}")
# Final Loss: 0.003752670716494322
```
最終的學習結果如下:

上圖來看不難發現到,模型很好的捕捉到資料的趨勢,並且最後的`loss`也非常完美的收斂。
眼尖的你也許有注意到,上圖的單位並不正確。是的,所以標準化後的資料就必需要再反向處理還原:
```python
# 讓torch知道這次的推論不用計算梯度
with torch.no_grad():
predicted_norm = model(X_norm)
# 正規化是`(資料-均值)/標準差`,反向處理就是`(資料 * 標準差) + 均值`
predicted_Y = (predicted_norm * Y_std) + Y_mean
```
單位還原之後的擬合結果如下圖:

## 結論
簡單的非線性模型跟線性模型之間差了一個`activation function`,這是一個值得研究的議題,基本上常用的還是那幾個萬年不死的函數,經過時間驗證過的還是最靠譜。
另外,目標值的縮放並不一定要處理,只是研究顯示當值域落差很大的情況下,做一下對於模型的訓練也是有好處的。記得,分類任務絕對不要做。