今年才开始接触深度学习,基本上是小白的状态,希望能利用疫情放假的这些日子好好学习一下深度学习。第一次打卡包括Task01和Task02两部分,Task01的学习任务又分为线性回归、Softmax与分类模型与多层感知机三个内容。
Task01
1.线性回归
首先提出要解决的问题是探索房价(price)与房屋面积(area)和房龄(age)两个要素之间的关系。
线性回归假设输出与各个输入之间是线性关系:
即价格=面积×面积权重+房龄×房龄权重+偏差。
其中房屋被称为样本(sample),实际房价被称作标签(label),用来预测标签的area和age被称作feature。
利用损失函数L来衡量价格预测值与真实值之间的误差,这里选用的损失函数为平方函数。最终想要达到的目的是使损失函数不断减小,实现更优的预测效果。
优化函数采用mini-batch梯度下降法,先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch),然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积(学习率)作为模型参数在本次迭代的减小量。
采用mini-batch梯度下降法的好处:mini-batch梯度下降法在普通梯度下降法的基础上进行改进,在一些情况下,输入样本数量较多,对整个训练集执行梯度下降法花费时间过长,采用mini-batch梯度下降法将训练集分割成多个小的子训练集,使一部分样本先执行梯度下降。
优化函数的两个步骤:
(i)初始化模型参数,一般来说使用随机初始化;
(ii)我们在数据上迭代多次,通过在负梯度方向移动参数来更新每个参数。
此外使用矢量计算方法来进行矢量运算,与使用for循环的方法相比大大提高了计算效率。
从0实现线性模型
首先引入相关包和组件
#import packages and modules
%matplotlib inline
import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random
利用线性模型生成1000个数据集
# 输入特征数为2,area和age
num_inputs = 2
# 输入样本数量为1000
num_examples = 1000
# 设定两个输入特征的权重w和偏差b
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn(num_examples, num_inputs,dtype=torch.float32)
#按照房价与面积和房龄的公式计算标签
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),dtype=torch.float32)
其中:
①torch.randn(sizes, out=None)→ Tensor
返回一个张量,包含了从标准正态分布(均值为0,方差为1,即高斯白噪声)中抽取的一组随机数。张量的形状由参数sizes定义。在这里函数的作用是返回float32类型的1000*2的随机矢量,作为age和area,存入feature中。
②torch.tensor(data, dtype=None, device=None, requires_grad=False)
torch.tensor()可以从data中的数据部分做拷贝(而不是直接引用),根据原始数据类型生成相应的torch.LongTensor,torch.FloatTensor,torch.DoubleTensor。
③numpy.random.normal(loc=0.0, scale=1.0, size=None)
此函数为高斯分布的概率密度函数,loc为概率分布的均值,对应着整个分布的中心center;scale为概率分布的标准差;size为输出的shape,默认为None。由于实际不可能完全符合线性关系,因此在上述计算labels的基础上再加上一个随机生成的高斯分布的偏差。
使用图像来展示生成的数据
plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);
读取数据集
def data_iter(batch_size, features, labels):
num_examples = len(features)#得到features的行数为1000
indices = list(range(num_examples))
random.shuffle(indices) # 打乱列表
for i in range(0, num_examples, batch_size):
j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # the last time may be not enough for a whole batch
yield features.index_select(0, j), labels.index_select(0, j
①for i in range(0, num_examples, batch_size)循环的含义是i从0到num_examples循环,步长为batch_size,即把数据集num_examples个样本分成了num_examples/batch_size个,每个mini-batch中包含batch_size个样本。
②.index_select(0, j)的含义是按行索引,索引序号为j
定义权重w及偏差b
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32)
b = torch.zeros(1, dtype=torch.float32)
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
Out:tensor([0.], requires_grad=True)
线性回归公式
def linreg(X, w, b):
return torch.mm(X, w) + b
定义损失函数
def squared_loss(y_hat, y):
return (y_hat - y.view(y_hat.size())) ** 2 / 2
定义优化函数
def sgd(params, lr, batch_size):
for param in params:
param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track
进行模型训练
# super parameters init
lr = 0.03
num_epochs = 5
net = linreg
loss = squared_loss
# training
for epoch in range(num_epochs): # training repeats num_epochs times
# in each epoch, all the samples in dataset will be used once
# X is the feature and y is the label of a batch sample
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y).sum()
# calculate the gradient of batch sample loss
l.backward()
# using small batch random gradient descent to iter model parameters
sgd([w, b], lr, batch_size)
# reset parameter gradient
w.grad.data.zero_()
b.grad.data.zero_()
train_l = loss(net(features, w, b), labels)
print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
out:
epoch 1, loss 0.045923
epoch 2, loss 0.000187
epoch 3, loss 0.000055
epoch 4, loss 0.000055
epoch 5, loss 0.000055
定义学习率为0.03,迭代次数为5,进行5次迭代,在每次迭代中计算损失函数,进行反向传播,将参数代入优化函数中,得到训练后的损失函数。
分别输出训练前后的权重及偏差
w, true_w, b, true_b
out:
(tensor([[ 0.1838], [-0.4229]], requires_grad=True), [2, -3.4],
tensor([4.3392], requires_grad=True), 4.2)
线性回归模型使用pytorch的简洁实现
引用相关包和组件
import torch
from torch import nn
import numpy as np
torch.manual_seed(1)
print(torch.__version__)
torch.set_default_tensor_type('torch.FloatTensor')
生成数据集,和之前相同
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
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_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
读取数据集,将特征和标签组合形成数据集,利用DataLoader取数据集。
import torch.utils.data as Data
batch_size = 10
# combine featues and labels of dataset
dataset = Data.TensorDataset(features, labels)
# put dataset into DataLoader
data_iter = Data.DataLoader(
dataset=dataset, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True, # 打乱数据
num_workers=2, # 双线程读取数据
)
接下来是利用pytorch模块实现模型,首先定义线性网络的类,然后调用类实现网络的实例化。
之前并没有接触过这样的概念,因此我查询了一下python中类和实例化的概念:
python类和实例化
简而言之,可以把类看作是一个模版,类下有不同的方法,在创建实例时候将必要的属性填写进去,在方法内部,self表示创建实例本身,因此类方法的参数传递中self不需要传参。
class LinearNet(nn.Module):
def __init__(self, n_feature):
super(LinearNet, self).__init__() # call father function to init
self.linear = nn.Linear(n_feature, 1) # function prototype: `torch.nn.Linear(in_features, out_features, bias=True)`
def forward(self, x):
y = self.linear(x)
return y
net = LinearNet(num_inputs)
print(net)
生成一个单层线性网络:
LinearNet( (linear): Linear(in_features=2, out_features=1, bias=True) )
上面只初始化了一个线性层,实际上还可以生成多层网络,这里用到了Sequential序贯模型,给出了三种方法。
深入学习Keras中Sequential模型及方法
# ways to init a multilayer network
# method one
net = nn.Sequential(nn.Linear(num_inputs, 1))
# method two
net = nn.Sequential()
net.add_module('linear', nn.Linear(num_inputs, 1))
# net.add_module ......
# method three
from collections import OrderedDict
net = nn.Sequential(OrderedDict([
('linear', nn.Linear(num_inputs, 1))
# ......
]))
print(net)
print(net[0])
可以看到生成的网络如下:Sequential( (linear): Linear(in_features=2, out_features=1, bias=True))
Linear(in_features=2, out_features=1, bias=True)
即全部网络只有一层线性层,网络的第一层,也就是唯一的一层如Linear所示。
初始化模型参数w和b,采用init模块来初始化参数,选择不同的初始化模式,输入需要初始化的变量以及特征。
from torch.nn import init
init.normal_(net[0].weight, mean=0.0, std=0.01)
init.constant_(net[0].bias, val=0.0) # or you can use `net[0].bias.data.fill_(0)` to modify it directly
直接调用nn中的均方误差损失函数
loss = nn.MSELoss()
调用torch中的随机梯度下降优化函数
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.03) # built-in random gradient descent function
print(optimizer) # function prototype: `torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)
进行训练,设定迭代次数为3,迭代过程同上,只不过利用pytorch实现中间函数
num_epochs = 3
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()))
out:
epoch 1, loss: 0.000422
epoch 2, loss: 0.000075
epoch 3, loss: 0.000176
比较一下训练后和真实的权重w以及偏差b
# result comparision
dense = net[0]
print(true_w, dense.weight.data)
print(true_b, dense.bias.data)
out:
[2, -3.4] tensor([[ 1.9996, -3.4005]])
4.2 tensor([4.1993])
2.softmax分类模型
softmax也是一个单层神经网络,其输出层是一个全连接层,即每个输出依赖于所有的输入。
用softmax实现图像分类可以进行如下理解:输入x为图片像素,输出o为分类结果。每个输出与输入之间都存在一个权重w,值最大的输出即可看作预测出的图像类别。有如下关系:
softmax运算符通过计算将输出值变换成值为正且和为1的概率分布,计算公式如下:
softmax回归的矢量计算表达式为:
损失函数采用交叉熵损失函数
使用准确率(accuracy)来评价模型的表现,其中准确率=正确预测数量/总预测数量。
下面是获取Fashion-MNIST训练集和读取数据
的一些代码:
导入需要的包。
# import needed package
%matplotlib inline
from IPython import display
import matplotlib.pyplot as plt
import torch
import torchvision
import torchvision.transforms as transforms
import time
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
print(torchvision.__version__)
获得数据集mnist_train和mnist_test。
mnist_train = torchvision.datasets.FashionMNIST(root='/home/kesci/input/FashionMNIST2065', train=True, download=True,