1.SoftMax分类器?
在Mnist数据集中,我们要得到的输出是0-9,共有十类,这种情况下我们希望输出0-9的概率都大于0,且和为1。

使用SoftMax分类器进行多分类问题(其输入不需要Relu激活,而是直接连接线性层),经过SoftMax分类器后满足:1.大于等于0,2.所有类别概率和为1.
?

2.Loss function - Cross Entropy 交叉熵
NLLLoss(nagative log likelihood loss ):右边输入?Y是真实标签,另一个输入要求是 softmax之后求对数


?使用Numpy计算交叉熵损失的过程:(One-hot是一行或一列只有一位是1的矩阵)

?使用Pytorch计算交叉熵损失:

?在PyTorch中,交叉熵损失全部封装成了Torch.nn.CrossEntropyLoss()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 上图的交叉熵损失就包含了softmax计算和右边的标签输入计算。 所以在使用交叉熵损失的时候,神经网络的最后一层是不要做激活的,因为激活(就是把它做成分布),是包含在交叉熵损失里面的,最后一层不要做非线性变换,直接交给交叉熵损失。
举例:

3个类别,分别是2,0,1 Y_pred1 ,Y_pred2还是线性输出,没经过softmax,还不是概率分布,比如Y_pred1,0.9最大,表示对应为第3个的概率最大,和2吻合,1.1最大,表示对应为第1个的概率最大,和0吻合,2.1最大,表示对应为第2个的概率最大,和1吻合,那么Y_pred1 的损失会比较小 对于Y_pred2,0.8最大,表示对应为第1个的概率最大,和0不吻合,0.5最大,表示对应为第3个的概率最大,和2不吻合,0.5最大,表示对应为第3个的概率最大,和2不吻合,那么Y_pred2 的损失会比较大 参考代码:
import torch
y = torch.LongTensor([2, 0, 1]) # 注意此处是LongTensor
# z_1和z_2是最后一层输出,进入Softmax之前的值,所以每个分类之和不为1
# 每行元素代表对一个对象的分类情况,共三个对象
z_1 = torch.Tensor([[0.1, 0.2, 0.9],
[1.1, 0.1, 0.2],
[0.2, 2.1, 0.1]])
z_2 = torch.Tensor([[0.9, 0.2, 0.1],
[0.1, 0.1, 0.5],
[0.2, 0.1, 0.7]])
criterion = torch.nn.CrossEntropyLoss()
print(criterion(z_1, y), criterion(z_2, y))
运行结果:


?CrossEntropyLoss与NLLLoss的总结
?3.应用在MINIST数据集
?第零步:首先导入包
transoform是用来对图像处理的?,functional用的relu激活函数,optium优化器的包

??第一步:准备数据集
为什么要用transform?
transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307, ), (0.3081, )) ])
PyTorch读图像用的是python的imageLibrary,就是PIL,现在用的都是pillow,pillow读进来的图像用神经网络处理的时候,神经网络有一个特点就是希望输入的数值比较小,最好是在-1到+1之间,最好是输入遵从正态分布,这样的输入对神经网络训练是最有帮助的
transform把Pillow图像转换成Tensor,把0-255像素值转换成0-1,然后把维度28*28变成1*28*28的张量,这个过程用transform中的totensor实现,normalize(mean均值,std标准差)变成0-1分布~给神经网络训练


这里的0.1307,0.3081是对Mnist数据集所有的像素求均值方差得到的 也就是说,将来拿到了图像,先变成张量,然后Normalize,切换到0,1分布,然后供神经网络训练 ?如上图,定义好transform变换之后,直接把它放到数据集里面,为什么要放在数据集里面呢,是为了在读取第i个数据的时候,直接用transform处理
在视觉里面,灰度图就是一个矩阵,但实际上并不是一个矩阵,我们把它叫做单通道图像,彩色图像是RGB三通道图像,通道有宽度和高度,一般我们读进来的图像张量是WHC(宽高通道) 在PyTorch里面我们需要转化成CWH,把通道放在前面是为了在PyTorch里面进行更高效的图像处理,卷积运算
?第二步:设计模型?

改动:激活层采用ReLu,最后一个输出层并不做激活因为用交叉熵做softmax

输入维度(N,1,28,28):N个样本,每个样本是一维,28*28的图像,为4阶张量(4个参数),但在神经网络中要求输入样本为矩阵,所以第一步把1*28*28的三阶张量变成一阶的向量,怎么变呢?把图片的每一行拼起来构成一行,每一行有784个元素
view(-1,784)把张量变成2阶张量(2个参数),矩阵784列,-1表示自动去算batchsize
经过view之后变成了(N,784)的矩阵
torch.view()详解
然后经过第一个线性层变成512,然后做relu
然后512降成256,然后relu激活~数字一样就可以对接起来~
最后降成10,表示0-9对应的线性值,再用softmax把它变成概率
网络:
需要5层将784-10层
forward 第一步view改变形状,用relu对每一层结果激活,最后一层不激活,直接接softmax
将网络定义成model=Net()

第三步:损失函数和优化器
交叉熵损失,由于样本较大,优化器用带冲量的SGD梯度下降

第四步:训练、测试
train:把训练的一轮循环封装成函数
?每训练300轮打印一次running loss

test: 不用计算梯度~with torch.no_grad
步骤:
从test_loader拿数据,做预测,预测出了output,output是一个矩阵,每一个样本有一行,一行有10个量,求每一行里最大值的下标,把每行最大值的下标拿出来,对应他的分类
用max(,dim=1)dim=1表示第一个维度,行,找每一行里最大值的下标
然后torch.max 返回的值有两个,第一个是最大值,第二个是最大值的下标。找完之后total总数先加批量的总数,labels是一个N*1矩阵,size就是元组(N,1),labels.size(0) 就是取N。
?然后预测与标签做等于比较,真就是1,假就是0,然后求和,把标量拿出来,这就是我们猜对的数量。
等所有数据跑完之后用正确的/总数求accuracy

假设训练10轮:

结果:
?损失不断降低。测试的准确率上升!但最后准确率上不去是因为对图像用全连接神经网络忽略了对局部信息的利用,把所有的元素都全连接了,处理时权重不够多,处理图像时更关心高级别的特征~如果特征提取会好一点~人工特征提取方法:傅里叶变换、小波变化,深度学习中可以自动特征提取
完整代码:
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F # 用Relu函数
import torch.optim as optim # 优化器优化
batch_size = 64
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
# transform:把图像转化成图像张量
train_dataset = datasets.MNIST(root='../data',
train=True,
download=True,
transform=transform) # 训练数据集
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='../data',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(test_dataset,
shuffle=False,
batch_size=batch_size)
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.l1 = torch.nn.Linear(784, 512)
self.l2 = torch.nn.Linear(512, 256)
self.l3 = torch.nn.Linear(256, 128)
self.l4 = torch.nn.Linear(128, 64)
self.l5 = torch.nn.Linear(64, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x) # 最后一层不做激活,因为要用交叉熵损失
model = Net()
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
# 因为网络模型已经有点大了,所以梯度下降里面要用更好的优化算法,比如用带冲量的(momentum),来优化训练过程
# 把一轮循环封装到函数里面
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad() # 优化器,输入之前清零
# forward + backward + updat
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299: # 每300轮输出一次
print('[%d,%5d] loss:%.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_loss = 0.0
def test():
correct = 0 # 正确多少
total = 0 # 总数多少
with torch.no_grad(): # 测试不用算梯度
for data in test_loader: # 从test_loader拿数据
images, labels = data
outputs = model(images) # 拿完数据做预测
_, predicted = torch.max(outputs.data, dim=1) # 沿着第一个维度找最大值的下标,返回值有两个,因为是10列嘛,返回值
# 返回值一个是每一行的最大值,另一个是最大值的下标(每一个样本就是一行,每一行有10个量)(行是第0个维度,列是第1个维度)
total += labels.size(0) # 取size元组的第0个元素(N,1),
correct += (predicted == labels).sum().item() # 推测出来的分类与label是否相等,真就是1,假就是0,求完和之后把标量拿出来
print('Accuracy on test set:%d %%' % (100 * correct / total))
# 训练
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test() #训练一轮,测试一轮
参考链接
|