疫情期间,借助《动手学深度学习》,学习pytorch的代码,顺便记录一些基础知识
1.线性回归--基本要素
1.1 模型
为了简单起见,这里我们假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)。接下来我们希望探索价格与这两个因素的具体关系。线性回归假设输出与各个输入之间是线性关系:
1.2 数据集
我们通常收集一系列的真实数据,例如多栋房屋的真实售出价格和它们对应的面积和房龄。我们希望在这个数据上面寻找模型参数来使模型的预测价格与真实价格的误差最小。在机器学习术语里,该数据集被称为训练数据集(training data set)或训练集(training set),一栋房屋被称为一个样本(sample),其真实售出价格叫作标签(label),用来预测标签的两个因素叫作特征(feature)。特征用来表征样本的特点。
1.3 损失函数
在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。一个常用的选择是平方函数。 它在评估索引为 的样本误差的表达式为
1.4 优化函数 - 随机梯度下降
当模型和损失函数形式较为简单时,上面的误差最小化问题的解可以直接用公式表达出来。这类解叫作解析解(analytical solution)。本节使用的线性回归和平方误差刚好属于这个范畴。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值。这类解叫作数值解(numerical solution)。
在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch),然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。
学习率: 代表在每次优化中,能够学习的步长的大小
批量大小: 是小批量计算中的批量大小batch size
1.5 Code
import torch
from torch import nn
import torch.utils.data as Data
from torch.nn import init
import torch.optim as optim
import numpy as np
torch.manual_seed(1)
torch.set_default_tensor_type('torch.FloatTensor')
batch_size = 10
num_inputs = 3
num_examples = 2000
true_w = [2, -3.4, 4]
true_b = 2.7
# 创建数据,3个特征
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_w[2] * features[:, 2] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
dataset = Data.TensorDataset(features, labels)
data_iter = Data.DataLoader(
dataset=dataset, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True, # whether shuffle the data or not
)
# 模型(1层),并初始化
net = nn.Sequential(
nn.Linear(num_inputs, 1)
)
init.normal_(net[0].weight, mean=0.0, std=0.01)
init.constant_(net[0].bias, val=0.0)
loss = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=0.003)
# 训练
num_epochs = 10
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
output = net(X)
l = loss(output, y.view(-1, 1))
optimizer.zero_grad() # reset gradient, equal to net.zero_grad()
l.backward()
optimizer.step()
print('epoch %d, loss: %f' % (epoch, l.item()))
# 结果
dense = net[0]
print(true_w, dense.weight.data)
print(true_b, dense.bias.data)
epoch 1, loss: 3.333120
epoch 2, loss: 0.142750
epoch 3, loss: 0.007569
epoch 4, loss: 0.002790
epoch 5, loss: 0.000443
epoch 6, loss: 0.000071
epoch 7, loss: 0.000115
epoch 8, loss: 0.000144
epoch 9, loss: 0.000040
epoch 10, loss: 0.000075
[2, -3.4, 4] tensor([[ 2.0002, -3.4003, 4.0002]])
2.7 tensor([2.7000])
简单的pytorch实现线性回归,代码也已经非常详细
2. softmax分类
在机器学习尤其是深度学习中,softmax是个非常常用而且比较重要的函数,尤其在多分类的场景中使用广泛。他把一些输入映射为0-1之间的实数,并且归一化保证和为1,因此多分类的概率之和也刚好为1。数学层面很好理解,如下图
秉着快速过渡的原则不展开讨论公式,更详细参考https://blog.csdn.net/bitcarmanlee/article/details/82320853
,直接pytorch代码走起
Code
数据与tf部分一样,用Fashion-MNIST
import torch
from torch import nn
from torch.nn import init
import torchvision.transforms as tranforms
import torchvision
from torch.utils.data import DataLoader
from collections import OrderedDict
import torch.optim as optim
data_dir = './data/FashionMNIST'
tranform = tranforms.Compose([tranforms.ToTensor()])
train_dataset = torchvision.datasets.FashionMNIST(data_dir, train=True, transform=tranform, download=True)
val_dataset = torchvision.datasets.FashionMNIST(root=data_dir, train=False, transform=tranform, download=True)
train_dataloader = DataLoader(dataset=train_dataset, batch_size=16, shuffle=True)
val_dataloader = DataLoader(dataset=val_dataset, batch_size=16, shuffle=False)
num_inputs = 784
num_outputs = 10
class LinearNet(nn.Module):
def __init__(self, num_inputs, num_outputs):
super(LinearNet, self).__init__()
self.linear = nn.Linear(num_inputs, num_outputs)
def forward(self, x): # x 的形状: (batch, 1, 28, 28)
y = self.linear(x.view(x.shape[0], -1))
return y
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()
def forward(self, x): # x 的形状: (batch, *, *, ...)
return x.view(x.shape[0], -1)
net = nn.Sequential(
OrderedDict([
('flatten', FlattenLayer()),
('linear', nn.Linear(num_inputs, num_outputs))]) # 或者写成我们自己定义的
# LinearNet(num_inputs, num_outputs) 也可以
)
init.normal_(net.linear.weight, mean=0, std=0.01)
init.constant_(net.linear.bias, val=0)
loss_fc = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.03, momentum=0.9)
epoch_num = 100
for epoch in range(epoch_num):
running_loss = 0.0
accuracy = 0.0
for i, sample_batch in enumerate(train_dataloader):
inputs = sample_batch[0]
labels = sample_batch[1]
net.train()
optimizer.zero_grad()
outputs = net(inputs)
loss = loss_fc(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 20 == 19:
correct = 0
total = 0
net.eval()
for inputs, labels in val_dataloader:
outputs = net(inputs)
_, prediction = torch.max(outputs, 1)
correct += ((prediction == labels).sum()).item()
total += labels.size(0)
accuracy = correct / total
print('step: {} loss: {:.5f} acc: {:.5f}'.format(i+1, running_loss / 20, accuracy))
running_loss = 0.0
step: 20 loss: 1.70235 acc: 0.58350
step: 40 loss: 1.10867 acc: 0.66410
step: 60 loss: 0.80811 acc: 0.74670
step: 80 loss: 0.96502 acc: 0.68270
step: 100 loss: 0.86838 acc: 0.71780
step: 120 loss: 0.87478 acc: 0.73320
step: 140 loss: 0.84810 acc: 0.77330
step: 160 loss: 0.80699 acc: 0.74910
初步的训练可以看出loss逐步的下降,准确率慢慢提升,本文并不注重准确率,而在于用pytorch的实现方式