3 线性神经网络-代码详解

3.2 线性回归从零开始实现

%matplotlib inline
import random
import torch
from d2l import torch as d2l

3.2.1 生成数据集

def synthetic_data(w,b,num_examples):
    """生成y=Xw+b+噪声"""
    X=torch.normal(0, 1, (num_examples,len(w)))
    y=torch.matmul(X, w)+b
    """加上噪声"""
    y+=torch.normal(0,1,y.shape)
    """返回随机X以及形成的模拟y,y设置成一列"""
    return X,y.reshape((-1,1))
true_w=torch.tensor([2,-3.4])
true_b=4.2
features, labels=synthetic_data(true_w,true_b,1000)
# print('features:', features[0],'\nlabel:', labels[0])
d2l.set_figsize()#设置图表尺寸
"""使用d2l库中的绘图函数来创建散点图"""
"""三个参数,第一个参数代表x轴数据:是选择features(w)第2列所有的数据,
   第二个参数代表y轴数据,选择了虚拟模型的所有结果;
   第三个参数代表点的大小,可选"""
d2l.plt.scatter(features[:, 1].detach().numpy(),labels.detach().numpy(), 1)

3.2.2 读取数据集

def data_iter(batch_size,features,labels):
    num_examples=len(features)
    indices=list(range(num_examples)) #indices是【0-999】,共1000个数据
    random.shuffle(indices) #把indices列表数据随机
    for i in range(0, num_examples, batch_size): #从0到num_examples,步长为batch_size,例如1-6,步长为2,则i:1,3,6
        batch_indices =torch.tensor(indices[i:min(i+batch_size,num_examples)]) #这里怕最后一个不满batch_size
        yield features[batch_indices], labels[batch_indices] #print有batch_indices行的features和labels
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
#     print(X, '\n', y)
    break

3.2.3 初始化模型参数

w=torch.normal(0,1,size=(2,1),requires_grad=True)#requires_grad=True 表示当前的参数可以表示为梯度
b=torch.zeros(1,requires_grad=True) #b=tensor([0.], requires_grad=True)

3.2.4 定义模型

"""线性回归模型y=wx+b"""
def linreg(X,w,b):
    return torch.matmul(X,w)+b

3.2.5 定义损失函数

"""损失函数0.5*(y_hat-y)^2 预测值y_hat 真实值y"""
def squared_loss(y_hat, y):
    return (y_hat-y.reshape(y_hat.shape))**2/2

3.2.6 定义优化算法

"""小批量随机梯度下降法"""
def sgd(params, lr, batch_size): #params是【w】,【b】
    with torch.no_grad(): #停止对梯度计算和存储,减少内存消耗,不会进行反向传播
        for param in params:
            param-=lr*param.grad/batch_size 
            param.grad.zero_() #清除param的梯度值

3.2.7 训练

lr = 0.01 #是学习率,也是超参数,控制参数每次更新的步幅
num_epochs =3 #重复训练的次数
net = linreg 
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X,w,b), y)
        l.sum().backward() #反向传播,此时有了关于w和b中的梯度,w.grad,b,grad
        sgd([w, b],lr, batch_size) #向梯度减小的方向移动,为了使得loss最小
    with torch.no_grad():
        train_l =loss(net(features,w,b),labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')

3.3 线性回归的简洁实现

3.3.1 生成数据集

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l

true_w=torch.tensor([2, -3.4])
true_b=4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

 3 线性神经网络-代码详解_第1张图片

3.3.2 读取数据集

def load_array(data_arrays, batch_size, is_train=True):  #@save
    """构造一个PyTorch数据迭代器"""
    #data_arrays相当于(features, labels),TensorDataset规定参数的第一维度必须相同
    #data_arrays【0】=features,执行PyTotch提供的TensorDataset之后,dataset【0】是features【0】和labels【0】的元组
    dataset = data.TensorDataset(*data_arrays)
    #PyTorch提供的加载批量数据的迭代器
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

batch_size = 10
data_iter = load_array((features, labels), batch_size)
#iter-可迭代对象
next(iter(data_iter))

3 线性神经网络-代码详解_第2张图片

 3.3.3 定义模型参数,训练

# nn是神经网络的缩写
from torch import nn
# nn.Linear设置全连接层,输入张量大小为2,输出张量大小为1
# nn.Sequential一个有序的容器
net = nn.Sequential(nn.Linear(2, 1))
# 用标准正态分布(均值为0,方差为1)填充data,也就是第一层全连接层的weight
net[0].weight.data.normal_(0, 0.01)
# 用0填充bias
net[0].bias.data.fill_(0)
# L2范式来充当损失函数
loss = nn.MSELoss()
# net.parameters()返回一个迭代器,可迭代该模型所有可训练的参数
# print(list(net.parameters()))输出参数列表
# [Parameter containing:
# tensor([[-0.0064, -0.0044]], requires_grad=True), Parameter containing:
# tensor([0.], requires_grad=True)]
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:
        l =loss(net(X), y)
        # 梯度不清零的话,PyTorch会将梯度累加
        trainer.zero_grad()
        l.backward()
        # 更新参数
        trainer.step()
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')

3.6 softmax回归的从零开始实现

3.6.1 初始化模型参数

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
# 使用svg来显示图片,清晰度高一点
d2l.use_svg_display()
# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
# 并除以255使得所有像素的数值均在0~1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(
    root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
    root="../data", train=False, transform=trans, download=True)
def get_fashion_mnist_labels(labels): # labels是数集,返回对应文本的集合
    """返回Fashion-MNIST数据集的文本标签"""
    test_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [test_labels[int(i)] for i in labels]
"""绘制图像列表"""
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    figsize = (num_rows*scale, num_cols*scale)
    # _是一个Figure实例,为Figure(300x1350)
    # axes是AxesSubplot实例
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    # enumerate遍历序列并追踪索引
    # 设置arr1=['one','two'] arr2=['1','2'] 
    # print(list(zip(arr1,arr2))) 
    # 输出 [('one','1'),('two','2')]
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
        else:
            # PIL图片
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes

3.6.2 定义softmax模型,损失函数

import torch
from IPython import display
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

num_input=784
num_output=10
# w使用正态分布初始化
w=torch.normal(0,0.01,size=(num_input,num_output),requires_grad=True)
b=torch.zeros(num_output,requires_grad=True)
# 这里演示sum的作用
X=torch.tensor([[1.0,2.0,3.0],[4.0,5.0,6.0]])
# keepdim表示保持原有的维度
# print (tensor([[5.0, 7.0, 9.0]]))
X.sum(0, keepdim=True)
# print (tensor([[6.], [15.]]))
X.sum(1, keepdim=True)

def softmax(X):
    X_exp = torch.exp(X)
    # X_exp.sum(1,keepdim=True)按行相加
    # 这里直接相除用了广播机制
    return X_exp/X_exp.sum(1,keepdim=True)
X=torch.normal(0,1,(2, 5))
X_prob = softmax(X)
# 每行之和为1,相当于概率
X_prob.sum(1)
# 定义模型
def net(X):
    return softmax(torch.matmul(X.reshape(-1,w.shape[0]),w)+b)
# 获取预测概率的样例
y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
# 此处的意思是选择y_hat的第一个样本中的第一个概率以及第二个样本中的第三个概率
y_hat[[0, 1], y]
# 定义损失函数
def cross_entropy(y_hat, y):
    # range(len(y_hat))相当于[0,1,...,行数-1],对比上面的样例理解
    return -torch.log(y_hat[range(len(y_hat)),y])
cross_entropy(y_hat, y)   

3.6.3 定义精度函数

# 评估正确的精度
def accuracy(y_hat, y):
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat是一个二维矩阵,此时行数列数大于1
        y_hat = y_hat.argmax(axis=1) # 把y_hat每行最大的元素下标存到y_hat中
    cmp = y_hat.type(y.dtype) == y # 比较预测的值和实际的值是否相同,cmp是个Boolean类型的tensor
    return float(cmp.type(y.dtype).sum()) # 先转成和y一样的形状,然后求和,一般来说y_hat和y都是一列的tensor,这里试过把类型转化去掉结果一致
accuracy(y_hat ,y)/len(y)

def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module): # 判断模型是否是net的实例
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

3.6.4 定义Accumulator类用来保存数据

class Accumulator:  #@save
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

3.6.5 训练

# updater是更新参数的优化算法函数
def train_epoch_ch3(net, train_iter, loss, updater):
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]

动画略

预测略 

3.7 softmax回归的简洁实现

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
# nn.Flatten()是将维度展平成张量,因为nn.Flatten()一般接收一维数据
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

你可能感兴趣的:(动手学深度学习,神经网络,人工智能,深度学习,动手学深度学习)