网页排版精美的中文网站,高端网站搭建,教育+wordpress模板下载,网站开发运营职位文章目录6 分类任务6.1 前置知识6.1.1 分类6.1.2 分类的网络6.2 动手6.2.1 读取数据6.2.2 functional模块6.2.3 继续搭建分类神经网络6.2.4 继续简化6.2.5 训练模型6.3 暂退法6.3.1 重新看待过拟合问题6.3.2 在稳健性中加入扰动6.3.3 暂退法实际的实现6.4 后话6 分类任务
在这…
文章目录6 分类任务6.1 前置知识6.1.1 分类6.1.2 分类的网络6.2 动手6.2.1 读取数据6.2.2 functional模块6.2.3 继续搭建分类神经网络6.2.4 继续简化6.2.5 训练模型6.3 暂退法6.3.1 重新看待过拟合问题6.3.2 在稳健性中加入扰动6.3.3 暂退法实际的实现6.4 后话6 分类任务
在这一讲中我们打算探讨一下神经网络中是如何处理分类任务的。
6.1 前置知识
6.1.1 分类
如果只是二分分类这种分类我们大可只用0或者1来表示那么我们只需要采用sigmoid函数来修正为0或1即可如果不懂后面会重新讲sigmoid函数那如果是多种可能呢
假设每次输入是一个2×2的灰度图像。我们可以用一个标量表示每个像素值每个图像对应四个特征x1,x2,x3,x4x_1,x_2,x_3,x_4x1,x2,x3,x4此外假设每个图像属于类别猫,“鸡”,狗中的一个。
接下来我们要选择如何表示标签如同我们前面所说我们肯定不可能说P “猫”,我们有两个明显地选择最直接的想法是选择y∈{1,2,3}其中正数分别代表{“狗”,“猫”,“鸡”}。
但是统计学家很早之前就发明了一种表示分类数据的简单方法独热编码。独热编码是一个向量它的分量和类别一样多。类别对应的分量设置为1其他所有分量设置为0。比如说应用到我们这个例子上的话我们就可以用独热编码来表示我们的标签y其中猫对应(1,0,0)鸡对应(0,1,0)狗对应(0,0,1)。
6.1.2 分类的网络
在神经网络中我们通常使用softmax回归来进行分类任务。 在分类任务重我们本质上是在算这个图片是不是某一类的概率这个概率是要多大才能判定这个图片属于这个类这个概率要达到什么标准这个标准是我们自己定的也就是所谓的阈值。
我们希望把我们通过运算算出来的y^j\hat{y}_jy^j可以直接看做是类别j的概率但是这明显不可能因为我们知道概率是处于0到1的我们又没对这个模型做什么处理他凭什么输出的值刚好就在0和1之间是吧。
那么既然没有处理那接下来就要让输出结果某种处理后变成0-1之间这个处理我们就叫做校准。那我们要用什么东西来校准呢softmax函数。softmax函数可以将未规范化的预测变换为非负并且总和为1同时要求模型保持可导。其函数形式为y^softmax(o),其中y^iexp(oj)∑kexp(ok)\hat{y} softmax(o),其中\hat{y}_i \frac{exp(o_j)}{\sum_kexp(o_k)}y^softmax(o),其中y^i∑kexp(ok)exp(oj)。
这里对于所有的j总有0yj^\hat{y_j}yj^1。因此y^\hat{y}y^可以视为一个正确的概率发布。softmax运算不会改变未规范化的预测o之间的顺序只会确定分配给每个类别的概率。
从上面的公式我们可以看出这个公式明显不是线性函数但是由于我们是先通过输入特征的仿射变换做出o然后再把o出入上面的softmax函数中的所以softmax回归是一个线性模型。
6.2 动手
或许前置的知识再多也是花里胡哨。让我们试着动手搭建一个神经网络来进行分类。不过我们这一小节的目的是为了熟悉nn.Module模块和nn.functional模块。
6.2.1 读取数据
让我们先读取数据这里我们暂时不使用前面学过的torch.datasets和torch.utils.data.DataLoader。如果你不是很懂下面的读取数据的代码没关系这看得懂看不懂都不是我们学习的重点。你照做即可。
%matplotlib inlinefrom pathlib import Path
import requestsDATA_PATH Path(data)
PATH DATA_PATH / mnistPATH.mkdir(parentsTrue, exist_okTrue)URL http://deeplearning.net/data/mnist/
FILENAME mnist.pkl.gzif not (PATH / FILENAME).exists():content requests.get(URL FILENAME).content(PATH / FILENAME).open(wb).write(content)import pickle
import gzipwith gzip.open((PATH / FILENAME).as_posix(), rb) as f:((x_train, y_train), (x_valid, y_valid), _) pickle.load(f, encodinglatin-1)from matplotlib import pyplot
import numpy as nppyplot.imshow(x_train[0].reshape((28, 28)), cmapgray)
print(x_train.shape)out 对于分类任务不像回归它的输出并不是1而是类别数。 如果我们要将某样本归为十个类中的一个那么输出结果就是对应类的独热编码也就是向量中有10个数字。
举个例子如果我们输入的样本是1×784那么我可以在第二层将变为784×128输出层变为128×10然后输出的10个值再交由softmax回归将其每个数的范围降到0-1然后看其属于哪个类别的数字大即属于哪个类别。
6.2.2 functional模块
functional模块有的实际上Module都有但是在调用一些不涉及参数的例如不涉及w和b的方法时推荐使用functional。
如继续上面的例子我们先将数据分为训练集和测试集然后将数据转为张量
import torchx_train, y_train, x_valid, y_valid map(torch.tensor, (x_train, y_train, x_valid, y_valid)
)
n, c x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
print(x_train, y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())我们利用functional模块来搭建一个线性回归模型试试
import torch.nn.functional as F# 指定损失函数为交叉熵
loss_func F.cross_entropy# 定义模型
def model(xb):return xb.mm(weights) bias交叉熵实际上就是对数似然损失函数这个知识点我们在机器学习的练功方式十一——逻辑回归_弄鹊的博客-CSDN博客已经提过了忘记的可以去复习一下。
# 定义小批量随机梯度下降每次取多少样本
bs 64
xb x_train[0:bs] # a mini-batch from x
yb y_train[0:bs]
# 初始化随机参数
weights torch.randn([784, 10], dtype torch.float, requires_grad True)
bias torch.zeros(10, requires_gradTrue)# 打印损失值
print(loss_func(model(xb), yb))6.2.3 继续搭建分类神经网络
让我们继续以上的工作。我们利用nn.Module来搭建一个softmax回归分类器。
from torch import nnclass Mnist_NN(nn.Module):def __init__(self):super().__init__()self.hidden1 nn.Linear(784, 128)self.hidden2 nn.Linear(128, 256)self.out nn.Linear(256, 10)def forward(self, x):x F.relu(self.hidden1(x))x F.relu(self.hidden2(x))x self.out(x)return x在前面第三讲的时候我们忘记提到的一点是在利用torch框架搭建神经网络时无需自己去写一个反向传播只需写好正向传播即可对于反向传播torch框架内部已经提供给我们了。
在以上搭建的神经网络中我们在__ init __()方法中先写好了神经网络的每个层然后在forward()方法中我们让x首先输入到第一层中输出的结果用F中的relu激活函数激活一下再流往下一层。第二层同理得出的结果也是再次使用relu激活函数激活然后最终通过输出层输出。
定义好之后我们需要实例化该神经网络类
net Mnist_NN()
print(net)out Mnist_NN( (hidden1): Linear(in_features784, out_features128, biasTrue) (hidden2): Linear(in_features128, out_features256, biasTrue) (out): Linear(in_features256, out_features10, biasTrue) ) 对于net对象实际上torch为其写好了内置方法net_parameters其依次返回name,parameter。
#可以打印我们定义好名字里的权重和偏置项for name, parameter in net.named_parameters():print(name, parameter,parameter.size())out 6.2.4 继续简化
我们可以利用TensorDataset和DataLoader来继续简化上述的过程。
在前面我们学过我们小批量随机梯度下降。其简单来说就是取小批量的数据然后取其损失的均值来代替全局损失。在第二讲我们学过DataLoader它可以用于每次从总数据中每次抽取batch_size的数据。
而对于TensorDataset它可以将传入的数据张量转换为torch可识别的dataset格式类型的数据从而方便让DataLoader处理。
让我们看一下以下的代码
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader# 转换DataLoader可处理的类型并且批量提取数据
train_ds TensorDataset(x_train, y_train)
train_dl DataLoader(train_ds, batch_sizebs, shuffleTrue)valid_ds TensorDataset(x_valid, y_valid)
valid_dl DataLoader(valid_ds, batch_sizebs * 2)def get_data(train_ds, valid_ds, bs):return (DataLoader(train_ds, batch_sizebs, shuffleTrue),DataLoader(valid_ds, batch_sizebs * 2),)6.2.5 训练模型
一般在训练模型时我们都会加上model.train()方法这样模型在训练时会自动加入Bathc Normalization(批量归一化)和Dropout(暂退法)防止过拟合。而在测试模型时我们一般加上model.eval()方法这样就不会调用归一化和暂退法。 Dropout下一小节就讲了这里只要知道它是防止过拟合的即可。 让我们看一下下面的代码
def loss_batch(model, loss_func, xb, yb, optNone):# 计算损失值loss loss_func(model(xb), yb)# 如果优化器不为空if opt is not None:# 执行反向传播loss.backward()# 更新参数opt.step()# 梯度清零opt.zero_grad()# 返回损失总值和批量x的长度return loss.item(), len(xb)然后训练
import numpy as npdef fit(steps, model, loss_func, opt, train_dl, valid_dl):for step in range(steps):model.train()for xb, yb in train_dl:loss_batch(model, loss_func, xb, yb, opt)model.eval()with torch.no_grad():losses, nums zip(*[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl])val_loss np.sum(np.multiply(losses, nums)) / np.sum(nums)print(当前step:str(step), 验证集损失str(val_loss))指定优化器
from torch import optim
def get_model():model Mnist_NN()return model, optim.SGD(model.parameters(), lr0.001)调用上面所有写的函数即可
# 对总数据进行分批量
train_dl, valid_dl get_data(train_ds, valid_ds, bs)
# 拿到模型和优化器
model, opt get_model()
# 开始训练
fit(25, model, loss_func, opt, train_dl, valid_dl)out [{“metadata”:{“trusted”:true},“cell_type”:“code”,“source”:# 对总数据进行分批量\ntrain_dl, valid_dl get_data(train_ds, valid_ds, bs)\n# 拿到模型和优化器\nmodel, opt get_model()\n# 开始训练\nfit(25, model, loss_func, opt, train_dl, valid_dl),“execution_count”:null,“outputs”:[{“output_type”:“stream”,“text”:“当前step:0 验证集损失2.27946646194458\n当前step:1 验证集损失2.2453414649963377\n当前step:2 验证集损失2.1901785594940186\n当前step:3 验证集损失2.0985936725616456\n当前step:4 验证集损失1.9537098587036132\n当前step:5 验证集损失1.749969289779663\n当前step:6 验证集损失1.4996562250137329\n当前step:7 验证集损失1.2475741912841798\n当前step:8 验证集损失1.041046302986145\n当前step:9 验证集损失0.8889363418579102\n当前step:10 验证集损失0.7784037446022034\n当前step:11 验证集损失0.6955419854164123\n当前step:12 验证集损失0.6328013188362122\n当前step:13 验证集损失0.5834899019241333\n当前step:14 验证集损失0.5442861658096313\n当前step:15 验证集损失0.5122919623851776\n当前step:16 验证集损失0.4864934079647064\n当前step:17 验证集损失0.46465890626907347\n当前step:18 验证集损失0.4461087041854858\n当前step:19 验证集损失0.4306937822341919\n当前step:20 验证集损失0.4172893476009369\n当前step:21 验证集损失0.40593954257965087\n”,“name”:“stdout”}]}] 6.3 暂退法
在前一小节中我们提到了dropout暂退法。在这一小节中我们来着重谈论一下。
6.3.1 重新看待过拟合问题
当面对更多的特征而样本不足时线性模型往往会过拟合。相反当给出更多的样本而不是特征通常线性模型不会过拟合。不幸的是线性模型泛化的可靠性是由代价的。简单地说线性模型没有考虑到特征之间的交互作用。对于每个特征线性模型都必须指定正的或负的权重。
泛化小和灵活性之间的这种基本权衡被描述为偏差——方差权衡。线性模型有很高的偏差它们只能表示一小类函数。然而这些模型的方差很低它们在不同的随机数据样本上可以得出了相似的结果。
如果把偏差——方差看成一个色谱那么与之相反的一端的是深度神经网络。神经网络并不局限与单独查看每个特征而是学习特征之间的交互。例如神经网络可能推断“尼日利亚”和“西联汇款”一起出现在电子邮件中表示垃圾邮件但单独出现则不表示垃圾邮件。
即使我们有比特征多得多的样本深度神经网络也有可能过拟合。
6.3.2 在稳健性中加入扰动
在探究泛化性之前我们先来定义一个什么是一个“好”的预测模型。前面我们说过我们希望训练误差和泛化误差之间折中做到一个模型能够很好地拟合训练数据也能够很好地预测未知数据。所以根据经典泛化理论 为了缩小训练和测试性能之间的差距应该以简单的模型为目标。简单些以较小维度的形式展现。这也是为什么开头我们用了最简单的线性模型来讲解原理的缘故。而正则化也是一样为了说明我们是要减少特征向量的大小我们直接对特征向量大小对应的概念——范数来下手。
简单性的另一个角度是平滑性。即函数不应该对其输入的微小变化敏感(这个也叫鲁棒性)。这也是为什么我们第一章线性模型所需的数据中我们往里添加了噪声点。因为一个好的模型不应该因为某些微小的噪声就失效。
所以根据上面所说的原理科学家提出了一个想法在训练过程中在进行后续层的计算我们的计算比如第一层输入层是不计算的主要的计算在第二层之前我们先对网络添加小部分噪声。因为当训练一个有多层的深层网络时注入噪声只会在输入——输出映射上增强平滑性。
这个想法被称为暂退法也叫丢弃法。暂退法在前向传播的过程中计算每一内部层的同时注入噪声这已经称为训练神经网络的常用技术。这种方法之所以被称为暂退法是因为我们从表面上看是在训练的过程中丢弃一些神经元。在整个训练过程的每一次迭代中标准暂退法包括在计算下一层之前将当前层中的一些结点置零。
6.3.3 暂退法实际的实现
回想之前做的多层感知机。如果我们将暂退法应用到隐藏层以p的概率将隐藏单元置为零时结果可以看作是只包含原始神经元子集的网络。比如在下图中删除了h2和h5因此输出的计算不再依赖于h2和h5并且它们各自的梯度在执行反向传播时也会消失。这样输出层的计算不能过度依赖于h1-h5的任何一个元素。 通常我们在测试的时候不会用到暂退法。给定一个训练好的模型和一个新的样本我们不会丢弃任何节点因此不需要标准化。但是研究人员就需要去用暂退法去估计神经网络预测的不确定性了如果通过应用不同暂退法一种暂退法包含抹去某些节点得到的预测结果都是一致的那么我们可以说网络发挥更稳定。
6.4 后话
我知道这一小节你学的很不愉快。实际上太多细致的知识无法在一个小节中直接说完所以请不要紧张这里即使你不懂太多的东西你只要会一个就行了——搭建个网络即使不会训练也没有关系先会搭网络就足够了。在后面的学习中你会发现除了搭网络这个地方不一样实际上训练的过程都是一样的。