文章目录
一、Logistic Regression 逻辑回归
-
使用sigmoid激活函数,输出在(0,1)中,为逻辑概率值
-
之所以称之为regression,是因为使用MSE时存有回归的思想(即:使预测值不断地与真实值相拟合)
-
对于多分类的情况,使用softmax代替sigmoid实现
二、交叉熵
指一种用于(多)分类问题的损失函数(Loss function)
2.1 Entropy 熵
表示一种变量数值的分布稳定度
① 熵表示一种不确定性
② 熵是"惊喜"的度量,呈反比
③ 更高的熵意味着更少的信息
- pytorch中熵意义的展示
注:数值分布越不均衡,熵值越小,“惊喜”度越高;数值分布越均衡,熵值越大,“惊喜”度越低
2.2 Cross Entropy 交叉熵
表示两种变量之间数值的分布稳定度,即:两变量的差异程度。
① 交叉熵值越大,差异就越大;
② 交叉熵值越小,差异就越小
注意:①
D
K
L
(
p
∣
q
)
D_{KL}(p|q)
DKL(p∣q) 表示p,q之间的散列程度(即:差异度。最小值为0,表示完全相同);
② one-hot的熵为1,因此使用softmax输出预测值与真实值(one-hot)的交叉熵作为loss,实际上就是利用梯度下降算法,学习使
D
K
L
(
p
∣
q
)
D_{KL}(p|q)
DKL(p∣q)最小时的各个参数
- 二分类问题的交叉熵
- 为什么不使用sigmoid + MSE作为loss?
因为sigmoid容易导致梯度弥散
2.3 实际 softmax + 交叉熵 的神经网络结构
- pytorch实现
F.cross_entropy(pred,label)
① pred:softmax之前的逻辑值,cross_entropy内部集成了softmax
② label:真实值标签
三、多分类问题实战
# -*- coding: UTF-8 -*-
'''
@version: 1.0
@PackageName: pytorch_learning - multi_classificial_task.py
@author: yonghao
@Description: 使用softmax的多分类任务实现
@since 2021/02/22 14:32
'''
import torch
from torch import nn
from torch.nn import functional as F
from torch import optim
import torchvision
from matplotlib import pyplot as plt
# step1 .load dataset
batch_size = 200
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('mnist_data', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=True
)
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('mnist_data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=False)
# 测试显示数据集的函数
# x, y = next(iter(train_loader))
# print(x.shape, y.shape)
# print(test_loader)
# plot_image(x, y, 'image sample')
# print("train num = {}".format(len(train_loader)))
# print("test num = {}".format(len(test_loader)))
# 各层参数
w1, b1 = torch.randn(200, 784, requires_grad=True), \
torch.zeros(200, requires_grad=True)
w2, b2 = torch.randn(200, 200, requires_grad=True), \
torch.zeros(200, requires_grad=True)
w3, b3 = torch.randn(10, 200, requires_grad=True), \
torch.zeros(10, requires_grad=True)
# 归一化初始化参数
torch.nn.init.kaiming_normal_(w1)
torch.nn.init.kaiming_normal_(w2)
torch.nn.init.kaiming_normal_(w3)
# 前向传播
def forward(x):
# 第一层
x = x @ w1.t() + b1
x = F.relu(x)
# 第二层
x = x @ w2.t() + b2
x = F.relu(x)
# 第三层
x = x @ w3.t() + b3
'''
结构上,第三层后接softmax,由于F.cross_entropy() \ nn.CrossEntropyLoss()()
中集成了softmax,因此不加
'''
return x
# 定制损失函数
criteon = torch.nn.CrossEntropyLoss()
def train(learning_rate=1e-3, epochs=3):
# 定制梯度下降算法
optimizer = torch.optim.SGD([w1, b1, w2, b2, w3, b3],
lr=learning_rate)
for epoch in range(epochs):
for batch_idx, (data, target) in enumerate(train_loader):
# data[512,1,28,28] --- [batch,channel,width,high]
# target
x = data.view(-1, 28 * 28)
out = forward(x)
loss = criteon(out, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print("Train Epoch: {} [ {} / {} ({:.0f}%)] \tLoss:{:.6f}".format(epoch, batch_idx * len(data),
len(train_loader.dataset),
100. * batch_idx / len(train_loader),
loss.item()))
test()
def test():
test_loss = 0
correct = 0
for data, target in test_loader:
data = data.view(-1, 28 * 28)
logits = forward(data)
test_loss += criteon(logits, target).item()
'''
torch.return_types.max(
values=tensor([3.8633, 1.1342, 2.1234, 4.0602, 1.5560, 2.4548, 1.7076, 0.3221, 2.2707,
1.8102, 3.5904, 2.0732, 3.2593, 3.1444, 1.9958, 1.5838, 1.8781, 4.0043,
1.3015, 2.1974, 1.1577, 1.6341, 1.0409, 0.9338, 1.6599, 5.0379, 1.6286,
2.2781, 3.2942, 1.3084, 1.8294, 0.8334, 1.6603, 2.3102, 1.4659, 2.0831,
3.3193, 2.0926, 1.1460, 2.0719, 1.8322, 2.9939, 2.7286, 1.6922, 1.4366,
1.6781, 0.7989, 1.1130, 2.6508, 2.3851, 2.1800, 1.5593, 0.8155, 1.3700,
1.4524, 1.5950, 1.3836, 1.7490, 2.8187, 1.1883, 2.8747, 1.2394, 1.0875,
1.1922, 1.2175, 1.0536, 1.4930, 1.9070, 2.6303, 1.4158, 3.9770, 5.0760,
2.5707, 1.4038, 2.4803, 2.0588, 1.7179, 1.3302, 1.6299, 2.7487, 1.7509,
1.3756, 3.2491, 2.4213, 2.0927, 1.6925, 2.7642, 1.6193, 2.0069, 3.2022,
3.0192, 2.0880, 0.9797, 1.8892, 1.3852, 2.0761, 0.2547, 1.2510, 1.5551,
3.5185, 1.4161, 2.5499, 1.2127, 1.8156, 1.2402, 3.0171, 1.6358, 2.4431,
2.2417, 2.3887, 2.3319, 2.5108, 2.1927, 2.5257, 0.8823, 1.7362, 2.6230,
1.0698, 3.0363, 1.6185, 1.7557, 1.2216, 1.3646, 1.6201, 2.8482, 1.2175,
1.6491, 1.6084, 2.7334, 1.8698, 2.6683, 3.2892, 1.5597, 1.5743, 2.0407,
2.4459, 2.1924, 2.5402, 1.7932, 2.2482, 1.9891, 3.2956, 1.7338, 2.5945,
1.4240, 0.8017, 1.9140, 3.3289, 2.3059, 0.5516, 2.4731, 2.3968, 1.2000,
1.0932, 1.4315, 1.1982, 2.4990, 1.2116, 1.0757, 1.4297, 1.5673, 2.1672,
1.6048, 2.2509, 1.5569, 0.9611, 2.3082, 0.9639, 2.1466, 1.5069, 1.3205,
0.8926, 2.5498, 2.2480, 2.9354, 1.6319, 2.2041, 1.2657, 2.3851, 2.1824,
1.8579, 1.6606, 1.2478, 2.9512, 0.8424, 3.8835, 3.7343, 2.0680, 4.2154,
0.5133, 2.8562, 0.3973, 3.6630, 1.3394, 3.7544, 1.0674, 2.2276, 3.4979,
2.7416, 3.9014]),
indices=tensor([7, 2, 1, 0, 4, 1, 4, 1, 2, 7, 0, 2, 9, 0, 1, 8, 4, 7, 3, 4, 9, 6, 1, 8,
4, 0, 7, 9, 0, 1, 3, 1, 3, 6, 8, 2, 7, 1, 8, 1, 1, 7, 9, 4, 3, 3, 5, 9,
9, 4, 6, 5, 4, 4, 2, 0, 9, 1, 9, 4, 7, 8, 4, 2, 7, 4, 2, 9, 3, 0, 7, 0,
0, 8, 1, 7, 3, 9, 1, 7, 9, 6, 2, 7, 9, 9, 7, 3, 6, 1, 3, 6, 4, 3, 1, 4,
4, 8, 6, 9, 4, 0, 5, 9, 4, 9, 3, 1, 9, 4, 8, 1, 3, 9, 7, 9, 4, 9, 9, 7,
3, 6, 8, 6, 7, 9, 3, 9, 8, 9, 6, 6, 3, 4, 8, 1, 0, 1, 6, 4, 6, 7, 3, 1,
7, 1, 9, 2, 0, 1, 9, 9, 4, 3, 1, 5, 6, 0, 2, 4, 9, 6, 8, 9, 2, 3, 4, 6,
1, 4, 9, 8, 3, 3, 2, 1, 1, 8, 1, 8, 1, 8, 8, 2, 8, 4, 2, 3, 0, 1, 1, 7,
0, 3, 0, 6, 1, 6, 4, 2]))
'''
pred = logits.data.max(dim=1)[1]
correct += pred.eq(target.data).sum()
test_loss /= len(test_loader.dataset)
print("\nTest set:Average loss:{:.4f},Accuracy:{}/{} ({:.0f}%)\n".format(test_loss, correct,
len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
if __name__ == '__main__':
train(epochs=7)
注:① 使用torch.nn.init.kaiming_normal_(param),对要学习的参数进行初始化操作
② 对于F.cross_entropy(pred,label) 或 criteon = torch.nn.CrossEntropyLoss(),criteon(pred,label)
a) pred:传入softmax之前的逻辑输出值(不用手动使用softmax)
b) label:传入多分类时,各样本对应真实标签值的一维向量
eg. pred[5,10] — 表示batch=5,做10分类任务;label[5] — 表示5个样本的标签值(如:[0,2,9,1,8],数值表示该样本输入哪个分类(0-9))
四、全连接层
4.1 pytorch快捷实现
-
不用手动的编写各个参数(W,b等)
nn.Linear(pre_layer,cur_layer) — 线性全连接层
① pre_layer:前一层的输入量(in)
② cur_layer:当前层的神经元个数(out)
-
F.relu(in,inplace = True)
inplace operation在pytorch中是指改变一个tensor的值的时候,不经过复制操作,而是直接在原来的内存上改变它的值。设置inplace=True,计算结果不会有影响。利用in-place计算可以节省内(显)存,同时还可以省去反复申请和释放内存的时间。但是会对原变量覆盖,只要不带来错误就用。
4.2 使用torch.nn.XX 快捷设计结构的步骤
-
第一步:
① 创建继承nn.Module的类
② __init__中继承父类的__init__
-
第二步
在__init__中创建层次结构
利用nn.Sequential(),串联各层次结构
-
第三步
在自定义类中创建forward(self,x)的成员方法,用于定义前向传播过程
-
对比 nn.ReLu 与 F.relu()
① nn.ReLu:返回relu方法类
扩展:nn.XX:返回函数类
② F.relu():返回relu后的输出值
-
设计训练过程
① 在设计梯度下降算法的传入参数中,使用自定义类的对象的parameters()方法返回要学习的参数
五、激活函数与GPU加速
5.1 激活函数
-
sigmoid、tanh
-
relu
-
Leaky ReLU
nn.LeakyReLU(alpha,inspace)以实现激活函数,alpha为倾斜角(默认为0.02)
-
SELU:工程上代替ReLu的一种激活函数
-
softplus:对ReLu在x=0处做平滑处理
5.2 GPU加速
使用 to(device) 将Tensor计算放入指定的CPU\GPU中计算
六、MINIST测试实战
-
计算准确率的方法
-
应用到实战
七、Visdom可视化
7.1 第一种:tensorboardX
- 使用方式
7.2 第二种:Visdom
-
开启一个web服务器用以可视化服务
-
visdom实现画曲线的功能
# 创建一条直线
# ① 创建画框并设置起始位置
'''
参数说明:
a) [0.],[0.]表示起始变量位置Y,X
b) win:相当于当前线段所在画框对象的ID
c) opts:设置其他的配置
'''
viz.line([0.],[0.],win='train_loss',opts=dict(title='train loss'))
# ② 在 ① 创建的线段上更新数值
'''
a) [loss.item()],[global_step] 新添加的数值 Y,X
b) win:指定画框对象,向其添加新数值
c) update:设置更新的方式,追加还是覆盖等
'''
viz.line([loss.item()],[global_step],win='train_loss',update='append')
实现多条曲线的追踪
# 直接传入tensor对象即可展示图片
viz.image(data.view(-1,1,28,28),win='x')
# 将预测值的numpy对象显示为文本信息
viz.text(str(pred.detach().cpu().numpy()),win='pred',opt=dict(title='pred'))