我是 雪天魚(yú)袍榆,一名FPGA愛(ài)好者印蓖,研究方向是FPGA架構(gòu)探索和數(shù)字IC設(shè)計(jì)碘箍。
關(guān)注公眾號(hào)【集成電路設(shè)計(jì)教程】,獲取更多學(xué)習(xí)資料鲸郊,并拉你進(jìn)“IC設(shè)計(jì)交流群
”丰榴。
QQIC設(shè)計(jì)&FPGA&DL交流群
群號(hào):866169462
。
一秆撮、數(shù)學(xué)基礎(chǔ):標(biāo)量四濒,向量,矩陣與張量
一個(gè)標(biāo)量(Scalar)就是一個(gè)單獨(dú)的數(shù)职辨;
一個(gè)向量就是一列數(shù)盗蟆,這些數(shù)是有序排列的。通過(guò)索引舒裤,喳资、可以確定對(duì)應(yīng)的每個(gè)單獨(dú)的數(shù);
矩陣是二維數(shù)組腾供,其中的每一個(gè)元素被兩個(gè)索引而非一個(gè)所確定仆邓。
幾何代數(shù)中定義的張量是基于向量和矩陣的推廣,通俗一點(diǎn)理解的話伴鳖,可以將標(biāo)量視為零階張量节值,向量視為一階張量,那么矩陣就是二階張量榜聂,任意一張彩色圖片表示成一個(gè)三階張量搞疗,三個(gè)維度分別是圖片的高度、寬度和色彩數(shù)據(jù)须肆。所以Tensor一般指三階及更高階的張量匿乃。
二、自動(dòng)求導(dǎo)
x = torch.tensor([2.0], requires_grad = True)
a = torch.tensor([4.0], requires_grad = True)
y = x * a
# 求計(jì)算圖各節(jié)點(diǎn)導(dǎo)數(shù)
y.backward()
print(x.grad) # y 對(duì) x 的偏導(dǎo) -> a
print(a.grad) # y 對(duì) a 的偏導(dǎo) -> x
結(jié)果:
pytorch 自動(dòng)求導(dǎo)方式很簡(jiǎn)單豌汇,定義一個(gè)表達(dá)式 y幢炸,然后 y 由變量(一般為標(biāo)量)x1,x2瘤礁,x3...計(jì)算得到阳懂,那么此時(shí)調(diào)用 y.backward()
進(jìn)行反向傳播,對(duì)各變量計(jì)算梯度(即偏導(dǎo)數(shù))柜思,然后這些梯度會(huì)保存在變量的 grad
屬性中岩调。
再舉個(gè)例子,如下所示赡盘,對(duì) y=x**4 求導(dǎo)
三号枕、線性回歸與擬合
在上圖中有很多藍(lán)點(diǎn),也就是數(shù)據(jù)陨享,我們所要做的是找出一條直線葱淳,能盡可能多的經(jīng)過(guò)這些數(shù)據(jù)點(diǎn)钝腺,也就是達(dá)到盡可能好的擬合效果,求出斜率W
和截距b
赞厕。
可采用迭代法艳狐,即先隨機(jī)初始化一個(gè) W 和 b,然后設(shè)置一個(gè) loss函數(shù)
(即能衡量擬合效果優(yōu)劣的指標(biāo))皿桑,然后根據(jù) loss 去修改 W 和 b毫目,目標(biāo)是使 loss 盡可能的小,即擬合效果盡可能的好诲侮。示意圖如下所示:
對(duì)于線性回歸而言镀虐,這個(gè)衡量指標(biāo)為:
即預(yù)測(cè)值與真實(shí)值之間差值的均方差,每次調(diào)整 w 和 b 沟绪,都是為了使此 loss 更小刮便,所以就可以轉(zhuǎn)化為優(yōu)化問(wèn)題:
那么具體怎么調(diào)整 w,b呢绽慈?即在當(dāng)前位置恨旱,w是變大還是變小,b是變大還是變小久信。
解決方法就是通過(guò)梯度(偏導(dǎo)數(shù))來(lái)判斷增大還是減小所要調(diào)整的參數(shù)窖杀,以 w 為例漓摩,當(dāng) loss 對(duì) w 的偏導(dǎo)數(shù)裙士,即梯度為正表示該處 loss 隨 w 增大而增大,故減小 w管毙,f反之梯度為負(fù)表示該處 loss 隨 w 增大而增大減小腿椎,故增大 w。
而具體增大多少夭咬,減小多少則由學(xué)習(xí)率(learning rate)
和梯度
共同決定啃炸,學(xué)習(xí)率越大,收斂越快卓舵,這很容易理解南用,因?yàn)橐苿?dòng)的步伐增大了嘛,但同時(shí)也可以錯(cuò)過(guò)最佳擬合點(diǎn)掏湾。
代碼示例:
x_train = torch.rand(100)
y_train = x_train * 2 + 3 # 目標(biāo)曲線:w = 2, b = 3, y = 2 * x + 3
# (x_train,y_train)即為要擬合的數(shù)據(jù)
# 初始化 w 和 b
w = torch.tensor([0.0],requires_grad = True)
b = torch.tensor([0.0],requires_grad = True)
# 擬合
lr = 0.015 # 學(xué)習(xí)率
loss_func = torch.nn.MSELoss() # loss 函數(shù)裹虫,衡量擬合效果,越小越好
for i in range(200):
y_pre = x_train * w + b # 預(yù)測(cè)值
loss = loss_func(y_train, y_pre) # 輸入預(yù)測(cè)值與實(shí)際值融击,計(jì)算 loss
if i % 10 == 0: # 每 10 輪輸出一次 w, b, loss
print("Iter: %d, w: %.4f, b: %.4f, training loss: %.4f" % (i, w.item(), b.item(), loss.item()))
loss.backward() # 反相傳播筑公,計(jì)算 loss 對(duì) w 和 b 的偏導(dǎo)數(shù)
# 調(diào)整 w 和 b
w.data -= w.grad * lr
b.data -= b.grad * lr
# 梯度清零
w.grad.data.zero_()
b.grad.data.zero_()
結(jié)果:
從結(jié)果上來(lái)看,可以發(fā)現(xiàn) loss 越來(lái)越小尊浪,說(shuō)明擬合結(jié)果越來(lái)越好匣屡, w 和 b 也是分別越來(lái)越靠近 2 和 3封救,這就是迭代法。
四捣作、Pytorch 寫(xiě)法
寫(xiě)法總結(jié)為:
示例代碼:
import torch
# 1 定義 model誉结,給訓(xùn)練輸入,輸出對(duì)應(yīng)預(yù)測(cè)值
class SimpleLinear:
def __init__(self):
self.w = torch.tensor([0.0], requires_grad=True)
self.b = torch.tensor([0.0], requires_grad=True)
# 前向傳播獲得預(yù)測(cè)值
def forward(self, x):
y = self.w * x + self.b
return y
def parameters(self):
return [self.w, self.b]
def __call__(self, x):
return self.forward(x)
# 2 定義優(yōu)化器
class Optimizer:
def __init__(self, parameters, lr):
self.parameters = parameters
self.lr = lr
# 更新參數(shù)
def step(self):
for para in self.parameters:
para.data -= para.grad * self.lr
# 清零參數(shù)的梯度
def zero_grad(self):
for para in self.parameters:
para.grad.data.zero_()
# 3 訓(xùn)練
def train():
model = SimpleLinear()
opt = Optimizer(model.parameters(), lr=0.3)
for epoch in range(10):
output = model(x_train)
loss = loss_func(y_train, output)
loss.backward()
opt.step()
opt.zero_grad()
print('Epoch {},w:{:.4f} b:{:.4f},loss is {:.4f}'.format(epoch,model.parameters()[0].item(),
model.parameters()[1].item(), loss.item()))
train()
結(jié)果:
五券躁、實(shí)戰(zhàn)
以 MNIST 數(shù)據(jù)集
為例搓彻,做手寫(xiě)數(shù)字識(shí)別網(wǎng)絡(luò)的訓(xùn)練與評(píng)估。
示例代碼:
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets, transforms
import torch.utils.data
# 定義 model
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# torch.nn.Conv2d(in_channels, out_channels, kernel_size,
# stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25)
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
def train(model, device, train_loader, optimizer, epoch):
model.train()
total = 0
for batch_idx, (data, target) in enumerate(train_loader):
# 獲取實(shí)際值嘱朽,可以簡(jiǎn)單理解: data -> x target -> y
data, target = data.to(device), target.to(device)
optimizer.zero_grad() # 梯度清零
output = model(data) # 獲得預(yù)測(cè)值
loss = F.nll_loss(output, target) # 計(jì)算 loss
loss.backward() # loss 反向傳播
optimizer.step() # 更新參數(shù)
# 訓(xùn)練進(jìn)度統(tǒng)計(jì)
total += len(data) # 目前訓(xùn)練圖片數(shù)
progress = math.ceil(batch_idx / len(train_loader) * 50)
print("\rTrain epoch %d: %d/%d, [%-51s] %d%%" %
(epoch, total, len(train_loader.dataset),
'-' * progress + '>', progress * 2), end='')
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
# 獲取實(shí)際值
data, target = data.to(device), target.to(device)
# 獲取預(yù)測(cè)值
output = model(data)
# 計(jì)算 loss
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest: average loss: {:.4f}, accuracy: {}/{} ({:.0f}%)'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
def main():
epochs = 2 # 訓(xùn)練總輪數(shù)
batch_size = 64
torch.manual_seed(0)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data/MNIST', train=True, download=False,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data/MNIST', train=False, download=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=1000, shuffle=True)
model = Net().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.025, momentum=0.9)
for epoch in range(1, epochs + 1):
train(model, device, train_loader, optimizer, epoch)
test(model, device, test_loader)
# torch.save(model.state_dict(), "mnist_cnn.pt")
main()
結(jié)果:
至此基于 Pytorch 的網(wǎng)絡(luò)訓(xùn)練與評(píng)估就講解完畢了旭贬,不知道你有沒(méi)有理解呢?
- 更多技術(shù)文章和學(xué)習(xí)資料搪泳,請(qǐng)關(guān)注我的公眾號(hào):【集成電路設(shè)計(jì)教程】
- 全平臺(tái)統(tǒng)一:【雪天魚(yú)】