>> ต่อจาก 
บทที่ ๔    ออปทิไมเซอร์ใน pytorch  ออปทิไมเซอร์คืออุปกรณ์สำหรับช่วยในการปรับพารามิเตอร์ด้วยวิธีการเคลื่อนลงตามความชัน 
 ใน pytorch ได้เตรียมออปทิไมเซอร์ชนิดต่างๆไว้ในมอดูลย่อย torch.optim 
 ในบทที่ผ่านมาเป็นการใช้วิธีการเคลื่อนลงตามความชันธรรมดาโดยการเขียนเองโดยไม่ได้ใช้ออปทิไมเซอร์ช่วย 
 เมื่อใช้ออปทิไมเซอร์จะทำให้การเขียนดูเรียบง่ายลง อีกทั้งยังสามารถใช้วิธีที่เหนือกว่าแค่การเคลื่อนลงตามความชันธรรมดา เช่น Adagrad หรือ Adam ได้ง่ายๆ 
 เกี่ยวกับออปทิไมเซอร์ชนิดต่างๆอ่านรายละเอียดได้ใน 
https://phyblas.hinaboshi.com/20171002  ออปทิไมเซอร์ที่เตรียมไว้ใน pytorch ได้แก่
 - torch.optim.SGD
 - torch.optim.Adam
 - torch.optim.Adamax
 - torch.optim.Adadelta
 - torch.optim.Adagrad
 - torch.optim.Rprop
 - torch.optim.RMSprop 
 SGD ก็คือวิธีแบบดั้งเดิมที่แค่คำนวณอนุพันธ์แล้วใช้ปรับค่าเลยโดยตรง 
 ส่วนวิธีการที่มักถูกแนะนำให้ใช้มากที่สุดคือ Adam ในที่นี้ก็จะใช้วิธีนี้เป็นหลักด้วย    
การใช้ออปทิไมเซอร์  ขอยกตัวอย่างการนำออปทิไมเซอร์มาใช้ในการปรับพารามิเตอร์ใน pytorch 
 ตัวอย่างขอใช้เป็นการวิเคราะห์การถดถอยเชิงเส้นสองตัวแปรเช่นเดียวกับในบทที่แล้ว เขียนโดยใช้ออปทิไมเซอร์ได้ดังนี้
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import torch
X = np.random.uniform(-2,2,[300,2])
x,y = X.T
z = x*0.7+y*1.5 + np.random.normal(1,0.4,300)
lin = torch.nn.Linear(2,1)
X = torch.Tensor(X)
z = torch.Tensor(z)
eta = 0.05
ha_mse = torch.nn.MSELoss()
opt = torch.optim.Adam(lin.parameters(),lr=eta) # สร้างออปทิไมเซอร์
n_thamsam = 100
for i in range(n_thamsam):
    h = lin(X).flatten()
    J = ha_mse(h,z)
    J.backward()
    opt.step() # ปรับพารามิเตอร์
    opt.zero_grad() # ล้างอนุพันธ์
mx,my = np.meshgrid(np.linspace(-2,2,11),np.linspace(-2,2,11))
mX = torch.Tensor(np.array([mx.ravel(),my.ravel()]).T)
mz = lin(mX).data.numpy().reshape(11,11)
plt.figure(figsize=[6,6])
ax = plt.axes([0,0,1,1],projection='3d')
ax.scatter(x,y,z,c=z,edgecolor='k',cmap='coolwarm')
ax.plot_surface(mx,my,mz,color='g',rstride=1,cstride=1,alpha=0.1,edgecolor='k')
plt.show()
 
  สิ่งที่เปลี่ยนแปลงจากคราวก่อนก็คือ มีการสร้างออปทิไมเซอร์ขึ้น แล้วส่งพารามิเตอร์เข้าไปให้ออปทิไมเซอร์บันทึกไว้ว่าตัวนี้คือพารามิเตอร์ที่ต้องการปรับค่า ส่วนอัตราการเรียนรู้ก็กำหนดโดยคีย์เวิร์ด lr ก็ป้อนเข้าไปด้วย 
 นอกจากนี้ค่าตัวเลือกอื่นๆตามชนิดของออปทิไมเซอร์ ก็สามารถกำหนดค่าตรงนี้ด้วย เช่นกรณีของ Adam จะมีค่า β1,β2 ซึ่งกำหนดโดยใส่คีย์เวิร์ด betas โดยใส่พร้อมกันทั้ง ๒ ตัว เป็น betas=(β
1,β
2) กรณีที่ไม่ใส่ก็จะเป็นค่ามาตรฐานตั้งต้น คือ betas=(0.9,0.999) 
 จากนั้นในระหว่างวนซ้ำ จะใช้คำสั่ง .step() เพื่อทำการปรับค่าพารามิเตอร์ทั้งหมดตามความชันที่คำนวณได้หลังจาก .backward() ไป 
 สุดท้ายก็สั่ง .zero_grad() เพื่อล้างอนุพันธ์ทั้งหมดที่หามาในรอบนั้นให้เป็น 0 ก่อนที่จะคำนวณรอบถัดไป 
 ในการใช้งาน pytorch โดยทั่วไปก็จะใช้ออปทิไมเซอร์ในลักษณะนี้ตลอด เป็นขั้นตอนที่ค่อนข้างตายตัว (วิธีที่ผ่านมาในบทก่อนๆแค่เพื่ออธิบายหลักการที่อยู่เบื้องหลัง เพื่อความเข้าใจ) 
 .zero_grad() นี้บางคนก็วางไว้ก่อน .backward() ผลที่ได้ก็เหมือนกัน ที่สำคัญคือก่อนจะ backward รอบใหม่ต้องมี zero_grad ก่อน    
ดูข้อมูลของตัวออปทิไมเซอร์  ถ้าสั่ง print ตัวออปทิไมเซอร์ ข้อมูลของตัวออปทิไมเซอร์นั้นจะแสดงออกมา
lin = torch.nn.Linear(3,1)
opt = torch.optim.Adam(lin.parameters())
print(opt)
 ได้
Adam (
Parameter Group 0
    amsgrad: False
    betas: (0.9, 0.999)
    capturable: False
    eps: 1e-08
    foreach: None
    lr: 0.001
    maximize: False
    weight_decay: 0
)
 หากต้องการดูว่าพารามิเตอร์ที่ออปทิไมเซอร์ตัวนั้นดูแลอยู่มีอะไรบางให้ดูที่แอตทริบิวต์ .param_groups
print(opt.param_groups)
ได้
[{'params': [Parameter containing:
tensor([[-0.2683, -0.5022,  0.0204]], requires_grad=True), Parameter containing:
tensor([0.2068], requires_grad=True)], 'lr': 0.001, 'betas': (0.9, 0.999), 'eps': 1e-08, 'weight_decay': 0, 'amsgrad': False, 'maximize': False, 'foreach': None, 'capturable': False}]
   การทำให้พารามิเตอร์บางตัวไม่ปรับค่า  พารามิเตอร์โดยทั่วไปจะมีการคำนวณและบันทึกค่าอนุพันธ์เสมอ นั่นคือเป็นเทนเซอร์ที่ถูกตั้ง requires_grad=True 
 แต่เราสามารถตั้ง requires_grad=False ได้ ซึ่งจะทำให้พารามิเตอร์ตัวนั้นไม่มีการคำนวณอนุพันธ์ และต่อให้ถูกส่งให้ออปทิไมเซอร์ก็จะไม่มีการเปลี่ยนแปลงค่า
lin = torch.nn.Linear(2,2)
lin.weight.requires_grad = False
lin.bias.requires_grad = False
  >> อ่านต่อ 
บทที่ ๖