深度学习 笔记(线性神经网络)

3.1. 线性回归 — 动手学深度学习 2.0.0-beta1 documentation

 

目录

3.1. 线性回归 — 动手学深度学习 2.0.0-beta1 documentation

3.1 线性回归

3.1.1 线性回归的基本元素

3.1.1.2 损失函数

 3.1.1.3 解析解

3.1.1.4 随机梯度下降

3.1.2 矢量化加速

3.2 线性回归从零开始实现

3.3 线性回归的简洁实现 

3.4 softmax回归

分类和回归

独热编码(one-hot encoding):

置信度:

损失函数

L2 loss(均方损失)

L1 loss

Huber 's Robust Loss

softmax:

交叉熵:

3.5 图像分类数据集

3.6 softmax回归的从零开始实现 


3.1 线性回归

3.1.1 线性回归的基本元素

训练数据集(training data set)或训练集(training set):

想要开发一个具有一定功能的模型,必须要有大量的相关事例作为数据集,这些事例就是训练集。有了这一些事例,就可以根据自变量和因变量之间的关系来作为模型预测的依据。

样本(sample):

每组数据就是样本,也可以称为数据点(data point)或数据样本(data instance)

标签(label)或目标(target):试图预测的目标。

特征(feature)或协变量(covariate):预测所依据的自变量。

3.1.1.1 线性回归模型

b称为偏置(bias)、偏移量(offset)或截距(intercept) 不可缺少的

 w称为权重(weight)

数据集的意义是帮我们找到相对合适的b和w

即使确信特征与标签的潜在关系是线性的, 我们也会加入一个噪声项来考虑观测误差带来的影响。

3.1.1.2 损失函数

 3.1.1.3 解析解

一点点高数和线代知识

对以上求导,由于凸函数的性质,当导数为0时,损失最小,解的为

 

3.1.1.4 随机梯度下降

当没有解析解(显示解)时的优化方法

步长怎么找?

没看懂

3.1.2 矢量化加速

3.2 线性回归从零开始实现

生成数据集

X = torch.normal(0, 1, (num_examples, len(w)))

均值为0,方差为1,n个样本,列数为w

y += torch.normal(0, 0.01, y.shape)

同理,均值为0,方差为0.01,形状同y的长度

这里加的就是随机噪音,这段代码每次运行的结果都不一样

 

输出第一个样本 

 

detach作用是把数据从计算图里面拎出来,然后再转numpy

读取数据集 

def data_iter(batch_size, features, labels):
    num_examples = len(features)          #确定样本数量
    indices = list(range(num_examples))   #range()表示从0 - n-1,list()表示转成list格式
    # 这些样本是随机读取的,没有特定的顺序
    random.shuffle(indices)               #把下标随机打乱
    for i in range(0, num_examples, batch_size):  #表示从0到n,每次跳batch_size个大小
        batch_indices = torch.tensor(
            indices[i: min(i + batch_size, num_examples)])
        yield features[batch_indices], labels[batch_indices] #每次产生随机特征和标号
batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break

初始化模型参数

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)  #requires_grad计算梯度
b = torch.zeros(1, requires_grad=True)  #1表示直接就是个标量,一个数,同样计算梯度

定义模型

def linreg(X, w, b):  #@save
    """线性回归模型"""
    return torch.matmul(X, w) + b

定义损失函数

def squared_loss(y_hat, y):  #@save
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

定义优化算法

def sgd(params, lr, batch_size):  #params给定的所有参数,lr学习率,batch_size指的是批每次跳过的量
    """小批量随机梯度下降"""
    with torch.no_grad():  #更新的时候不需要计算梯度
        for param in params:
            param -= lr * param.grad / batch_size #自动求导
            param.grad.zero_()

训练

lr = 0.03    #学习率
num_epochs = 3   #整个数据扫3遍
net = linreg   #之前定义的模型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)  # X和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()   #求和之后算梯度
        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数
    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}') 

 学习率小还能多跑几遍,学习率过大直接超出浮点运算范围了(nan=not a number)

3.3 线性回归的简洁实现 

生成数据集

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                         #形成真实的w和b
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

读取数据集

def load_array(data_arrays, batch_size, is_train=True):  #@save
    """构造一个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)
              #每次从中挑选batch_size个数据出来,shuffle表示需不需要打乱顺序,is_train则表示需要

batch_size = 10
data_iter = load_array((features, labels), batch_size)
                     #如果已经有了features, labels,把它做成list,传到dataset
next(iter(data_iter))  #通过next函数得到x和y

定义模型

# nn是神经网络的缩写
from torch import nn

net = nn.Sequential(nn.Linear(2, 1))
#nn.Linear(2, 1)指的是输入维度为2,输出维度为1
#Sequential是一个容器,用来装层(层数多时非常有用)

初始化模型参数

net[0].weight.data.normal_(0, 0.01) #normal指使用正态分布替换掉data的值
net[0].bias.data.fill_(0)   #bias指偏差,这里直接置零

定义损失函数

loss = nn.MSELoss()   #MSELoss类

定义优化算法

trainer = torch.optim.SGD(net.parameters(), lr=0.03)
#net.parameters()包括所有参数,这里包括w和b

训练

num_epochs = 3   #迭代3个周期
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X) ,y)
        trainer.zero_grad()   #trainer预设器梯度清零
        l.backward()          #计算backward
        trainer.step()        #调用step函数来更新模型
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')

w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)

3.4 softmax回归

分类和回归

回归是一个连续值(房价分析)

分类预测一个类别(手写数字识别)

 分类输出的个数等于类别的个数

独热编码(one-hot encoding):

独热编码是一个向量,它的分量和类别一样多。 类别对应的分量设置为1,其他所有分量设置为0。 在我们的例子中,标签y将是一个三维向量, 其中(1,0,0)对应于“猫”、(0,1,0)对应于“鸡”、(0,0,1)对应于“狗”:

置信度:

【看完这篇就够了!!!通俗易懂】置信度理解(95%的置信度、置信区间)_醪糟小丸子的博客-CSDN博客_95%置信度

置信度:以测量值为中心,在一定范围内,真值出现在该范围内的几率。一般设定95%,是通常情况下置信度(置信水平)的设定值。
置信区间:在某一置信度下,以测量值为中心,真值出现的范围。一定概率下真值的取值范围(可靠范围)称为置信区间。其概率称为置信概率或置信度(置信水平)。

我的理解是

样本数目不变的情况下,做一百次试验,有95个置信区间包含了总体真值。置信度为95% 

损失函数

L2 loss(均方损失)

\frac{1}{2}正好在求导后抵消

L1 loss

 预测值和真实值较远时,不管多远,梯度都是常数+/- 1

优点:稳定性好

缺点:零点是尖点,不可导。 优化到末期,结果会变得不稳定

Huber 's Robust Loss

 预测值和真实值差距大时,绝对值大于1时,为绝对值误差,梯度为常数

 预测值和真实值差距小时,误差函数像高斯函数,是线性的

这样兼具了L1 L2的优点

大概理解就是这样了

这里没怎么看懂,应该是基础知识欠缺的原因,等我学了概率论再来看

softmax:

想要使得输出是一个概率

 y和^y的区别作为损失,此时所有y_{i}都是概率

指数的好处在于无论是什么数都能变成非负

个人理解:softmax算出来的就是置信度

交叉熵:

没懂为啥这么算

 一文搞懂交叉熵在机器学习中的使用,透彻理解交叉熵背后的直觉_史丹利复合田的博客-CSDN博客

额,还是没懂

3.5 图像分类数据集

MNIST数据集

Fashion-MNIST数据集

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()  #李沐老师写的包,用svg画图

读取数据集

# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()  #把图片转成张量
mnist_train = torchvision.datasets.FashionMNIST(
    root="../data", train=True, transform=trans, download=True) 
         #把数据集下载到上一级目录的data里面,train=True表示下载的是训练数据集
         #transform=trans表示图片拿出来要是个pytorch的tensor
         #download=True表示默认从网上下载
mnist_test = torchvision.datasets.FashionMNIST(
    root="../data", train=False, transform=trans, download=True)
         #mnist_test测试数据集,用来验证模型好坏
         #train=False 这就是说不下载训练数据集
len(mnist_train), len(mnist_test)  #图片数量

mnist_train[0][0].shape
#mnist_train[][].shape 第一个[]表示第一个例子,第二个[]表示第一个图片
#这里试了一下发现好像都是黑白图片

def get_fashion_mnist_labels(labels):  #@save
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):  #@save
    """绘制图像列表"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    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

X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
#构建python的iterater(迭代器),用next取小批量
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));
#2,9表示画两行,每行9张图片

读取小批量

训练前要先看读取有多块,以读取速度来决定训练速度

读取速度要比训练速度快很多

batch_size = 256

def get_dataloader_workers():  #@save
    """使用4个进程来读取数据"""
    return 4

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())

timer = d2l.Timer() #定义timer函数来测量速度
for X, y in train_iter:
    continue
f'{timer.stop():.2f} sec'

整合所有组件

def load_data_fashion_mnist(batch_size, resize=None):  #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)
    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)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))

train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:
    print(X.shape, X.dtype, y.shape, y.dtype)
    break

3.6 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_inputs = 784  #先粗暴的把图片信息拉成向量28*28=784,损失掉的信息要用之后的神经网络
num_outputs = 10  #模型有10个类,所以输出维度为10

W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
          #size=(num_inputs, num_outputs)形状,行数为num_inputs 784,列数num_outputs 10
b = torch.zeros(num_outputs, requires_grad=True) 
          #对于每一个输出都有偏移,长为num_outputs的向量

定义softmax操作

X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
         #2*3 的矩阵
X.sum(0, keepdim=True), X.sum(1, keepdim=True)
       #维度为0,列向量求和,得到行向量1*3;维度为1,行向量求和,得到列向量2*1

def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制

#此时做一些验证
X = torch.normal(0, 1, (2, 5)) #两行五列的矩阵,且所有值都是正的
X_prob = softmax(X)
X_prob, X_prob.sum(1) #按行来加,每行和为1

定义模型

def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
                          #-1表示自己算一下这里批量大小,之前设定过256,W.shape[0]=784
                          #加偏移b是通过广播机制

定义损失函数

y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
        #[0.1, 0.3, 0.6]为y0,[0.3, 0.2, 0.5]为y1
y_hat[[0, 1], y] #对第0样本拿出y0对应的元素,第1样本拿出y1对应的元素
        #所以最后输出值为0.1和0.5
        #y0拿出0对应的0.1,y1拿出2对应的0.5

 感谢b站大学

def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])
                    #生成长度n的向量,拿出真实标号的预测值

cross_entropy(y_hat, y)  #这里求交叉熵
#y_hat是2*3的预测值,y是长为2的向量

分类精度

def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1) #每行元素值最大的那个下标存y_hat里
    cmp = y_hat.type(y.dtype) == y 
    #把y_hat转成y的数据类型,然后作比较,变成bool型tensor
    return float(cmp.type(y.dtype).sum())

accuracy(y_hat, y) / len(y)
#找出来预测正确的样本数,除以y的长度,求出预测正确的概率
#这里结果是0.5,因为都是随机生成的

def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
                         #每次先把x放到net里算出评测值
                         #accuracy(net(X), y)预测正确的样本数
                         #这里求了总样本数
    return metric[0] / metric[1]
       #得到精度

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]

evaluate_accuracy(net, test_iter)

 

 由于都是随机生成,开始设置有10个类别,所以结果接近于0.1

训练

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()  #告诉pytorch,要计算梯度
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)  #长度为3的迭代器
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y)  #交叉熵损失函数
        if isinstance(updater, torch.optim.Optimizer):
            #如果updater是pytorch中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]

#这里主要是画图,没看懂,再学学
class Animator:  #@save
    """在动画中绘制数据"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """训练模型(定义见第3章)"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
   #可视化部分,以后再学
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        #train_metrics训练误差
        test_acc = evaluate_accuracy(net, test_iter)
        #测试精度
        animator.add(epoch + 1, train_metrics + (test_acc,))
      #显示训练误差,训练精度;测试误差,测试精度
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

num_epochs = 10
#训练十个迭代周期
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

预测

def predict_ch3(net, test_iter, n=6):  #@save
    """预测标签(定义见第3章)"""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)  #真实标号
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1)) #预测标号
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)] #转成字符串
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)

 准确率挺高,

  • 13
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ItsNorth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值