## W1
### 設定環境
1-1. 使用python 3.10的虛擬環境
```
py -3.10 -m venv env
```
1-2. 進入虛擬環境
```
.\env\Scripts\activate
python -m pip install --upgrade pip
```
2. pytorch 2.2.2 (有GPU可自己安裝CUDA的驅動)
例如我是RTX 4070,我要下載torch就需要輸入
```
pip install torch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2 --index-url https://download.pytorch.org/whl/cu121
```
> pytorch網站版本說明: https://download.pytorch.org/whl/cu121
4. opencv
```
pip install opencv-python
```
6. matplotlib.pyplot
```
pip install matplotlib
```
8. numpy 1.26.4
```
pip install numpy==1.26.4
```
* 複製w1環境到w2:
先到w1虛擬環境中輸入 pip freeze > requirements.txt
把requirements.txt中torch那三個+121cuda拿掉,開頭加上
`--index-url https://download.pytorch.org/whl/cu121`
接著到w2
```
C:\114torch\w2> python -m venv env
C:\114torch\w2> .\env\Scripts\activate
(env) C:\114torch\w2> pip install -r ..\w1\requirements.txt
```
### 程式碼範例
```python=
#檢查torch版本
# import torch
# import torchvision
# print(torch.__version__)
# print(torchvision.__version__)
#numpy test
# import numpy as np
# a= np.arange(10)
# print(a)
#cv test
# import cv2
# img = cv2.imread("1.jpg")
# img = cv2.resize(img,(400,400))
# cv2.imshow("image",img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
#繪製隨機資料
# import torch
# import matplotlib.pyplot as plt
# torch.manual_seed(0)
# n = 100 #資料筆數
# x = torch.randn(n,1)
# w_true = torch.tensor([10.0])
# b_true = torch.tensor([3.0])
# y = w_true * x + b_true + torch.randn(n,1)*2.5
# plt.figure()
# plt.plot(x.numpy(),y.numpy(),'o',color='r')
# plt.title('Data(x,y)')
# plt.xlabel('x');plt.xlabel('y')
# plt.tight_layout()
# plt.savefig('tessst.png')
# plt.show()
#torch test
import torch
from torch import nn
class Model(nn.Module):
def __init__(self,in_dim=1,out_dim=1):
super().__init__()
self.lin = nn.Linear (in_dim , out_dim)
def forward(self,x):
return self.lin(x)
model = Model(1,1)
print(model)
```
### NOTE
* python:直譯式、物件導向
* numpy是python套件
* torch是matrix格式,放進GPU才能做平行運算,因為GPU是matrix運算
* 是經由torch運算,而x跟y都是由torch運算,所以只能用matpltlib中的numpy來跑,而非python本身np
* nn = neuron network
* Model是subclass,繼承nn.Module
* super()是nn.Modul裡的建構子
* 所有nn都須forward
* weight:鍵結值
* X:dimension
* Y:期望值(實際輸出)
* function set:所有y=a*x+b的y,即f1, f2, f3...,將所有x帶進去每個fi,得到的輸出稱yi(預測值)
* ,如x=(x1, x2, x3,...)帶入f1得y1,
* 而fi中error(歐幾里得距離最小)最小的輸出稱為期望值
* loss funciton:找error最小的值,其代表最佳funciton
## w2
### NOTE

> 期望值:
>
> 預測值
>

1. >  : 取得最小值時的變數值,代表「讓f(x)變最小的那個 x」
2. > :找到那個「函數 𝑓」,能讓 loss 𝐿(𝑓)最小。所以𝑓∗ 就是「最佳函數」
3. > :找到那對參數𝑤,𝑏 能讓 loss 最小。所以 𝑤∗,𝑏∗ 就是最佳的權重和偏差。
而最佳化問題的目標就是找到能讓 loss 最小的 𝑤,𝑏


以MSE為error,需用chain rule偏微
* 超過3D稱高維,(high dimention)
* loss function:透過高維找出函數
* local miniman: 區域極小值,與全域最小值不同
* 極值在切線斜率(微分、gradient)=0,而切線斜率=瞬時斜率=微分,斜率需找兩點,而切線找不到兩點。
* 偏微分:對某部分weight去作微分而已,是partitial微分
* if 兩點很近(趨近於->0),可以當作一個點
* 微分又稱梯度(Gradiant),descent:負的,找極值的方法:Gradient Descent(負梯度法),因為要往斜率反方向找
* loss function需用負偏微分表示,
loss每次更新一梯度(=step),learning rate會影響更新速率,太大會直接越過極值而震盪=>調低
### HW_1
```python=
# taxi_fare.py
import torch
from torch import nn, optim
from model import Model
import matplotlib.pyplot as plt
import numpy as np
import os
data = np.loadtxt('taxi_fare_training.csv', delimiter=',', skiprows=1)
x = torch.tensor(data[:, 0:1], dtype=torch.float32) # 行駛距離
y = torch.tensor(data[:, 1:2], dtype=torch.float32) # 車資
net = Model()
opt = optim.SGD(net.parameters(), lr=0.01)
loss_f = nn.MSELoss()
loss_hist = []
for epoch in range(50):
y_hat = net(x)
loss = loss_f(y, y_hat) # loss這個物件有backward函示去執行gradient descent(SGD)
opt.zero_grad() # 因為backward()會把梯度累加,所以每次要先歸零,否則會影響GD
loss.backward()
opt.step()
loss_hist.append(float(loss.item()))
print(f'Epoch {epoch}, Loss: {loss.item()}')
print(loss)
OUT_DIR = "result"
os.makedirs(OUT_DIR, exist_ok=True)
plt.figure()
plt.plot(range(len(loss_hist)), loss_hist, marker='o')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Training Loss')
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
loss_path = os.path.join(OUT_DIR, 'loss.png')
plt.savefig(loss_path, dpi=150)
plt.show()
# plt.close()
net.eval()
with torch.no_grad():
y_hat = net(x)
arr = np.hstack([
# 固定記憶體不讓GD繼續更動值,GPU的運算在GPU memory,跟CPU不同,所以CPU不知道,GPU要告知CPU他做完了並使GPU清空DRAM中的gradient
x.detach().cpu().numpy(),
y.detach().cpu().numpy(),
y_hat.detach().cpu().numpy()
])
pred_csv_path = os.path.join(OUT_DIR, 'prediction.csv')
np.savetxt(pred_csv_path, arr, delimiter=',', header='x,y,y_hat', comments='')
idx = x.squeeze(1).argsort() # 未排序
x_sorted = x[idx]
yhat_sorted = y_hat[idx]
plt.figure()
plt.plot(x.detach().cpu().numpy(), y.detach().cpu().numpy(), 'o', alpha=0.6, label='Data(x, y)')
plt.plot(x.detach().cpu().numpy(), y_hat.detach().cpu().numpy(), 'x', alpha=0.8, label='Model output on training x')
plt.plot(x_sorted.detach().cpu().numpy(), yhat_sorted.detach().cpu().numpy(), '-', linewidth=2, label='Connected model outputs')
plt.xlabel('distance_km')
plt.ylabel('fare_ntd')
plt.title('Taxi_Fare_Training Result')
plt.legend()
plt.tight_layout()
scatter_outputs_path = os.path.join(OUT_DIR, 'Taxi_Fare_Training Result.png')
plt.savefig(scatter_outputs_path, dpi=150)
plt.show()
plt.close()
```
```python=
# model.py
import torch
from torch import nn
class Model(nn.Module):
def __init__(self, in_dim=1, out_dim=1):
super().__init__()
self.lin = nn.Linear(in_dim, out_dim)
def forward(self, x):
return self.lin(x)
# model = Model(1, 1)
# print(model)
```
### result


### 程式碼 NOTE
* Matplotlib 與 NumPy 都是基於 CPU 的工具,無法直接處理 GPU tensor
* 如果在 GPU tensor 上直接 .numpy(),會報錯
* .detach() 用來切斷計算圖,得到不需要梯度的 tensor,適合做可視化或存檔
* .cpu() 把資料從顯示卡記憶體複製回主記憶體,NumPy 才能讀取
* .numpy() 把 CPU tensor 轉成 NumPy 陣列,Matplotlib 或 CSV 存檔工具才能使用
* 在 GPU 時一定要 .cpu(),否則無法轉 NumPy
* 習慣統一使用 .detach().cpu().numpy(),可保證程式在 CPU/GPU 都能通用且安全
* 100epoch SGD只跑100次,
* GPU的運算在GPU memory,跟CPU不同,所以CPU不知道,GPU要告知CPU他做完了並使GPU清空DRAM中的gradient