使用torch中自带的MNIST数据集,调用MNIST返回的结果中图形数据是一个Image对象,需要对其进行处理,为了进行数据的处理,接下来学习torchvision.transforms
的方法
把一个取值范围是[0,255]的PIL.Image或者shape为(H,W,C)的numpy.ndarray
,转换成形状为[C,H,W],取值范围是[0,1,0]的torch.FloatTensor
其中(H,W,C)意思为(高,宽,通道数),黑白图片的通道数只有1,其中每个像素点的取值为[0,255],彩色图片的通道数为(R,G,B),每个通道的每个像素点的取值为[0,255],三个通道的颜色互相叠加,形成了各种颜色。
实例如下:
from torchvision import transforms
import numpy as np
data = np.random.randint(0,255,size=12)
img = data.reshape(2,2,3)
print(img.shape)
img_tensor = transforms.ToTensor()(img) # 转换成tensor
print(img_tensor)
print(img_tensor.shape)
给定均值:mean,shape和图片的通道数相同(指的是每个通道的均值),方差:std,和图片的通道数相同(指的是每个通道的方差),将会把Tensor规范化处理。
即:Normalized_image=(image-mean)/std
例如:
from torchvision import transforms
import numpy as np
import torchvision
data = np.random.randint(0,255,size=12)
img = data.reshape(2,2,3)
img = transforms.ToTensor()(img) # 转换成tensor
print(img)
norm_img = transform.Normalize((10,10,10),(1,1,1))(img) # 进行规范化处理
print(norm_img)
将多个transform组合起来使用。
如:
transforms.Compose([
torchvision.transforms.Totensor(),# 先转化为Tensor
torchvision.transforms.Normalize(mean,std)# 再进行正则化,均值和标准差的形状和通道数相同
])
准备数据集
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision.transforms import Compose,ToTenser,Normalize
BATCH_SIZE = 128
#1. 准备数据
def get_dataloader(train=True):
transform_fn = Compose([
ToTenser(),
Normalize(mean=(0.1307,),std=(0.3081,)) # mean和std的行传个通道数相同,它们两个分别是均值和方差
])
dataset = MNIST('./data',train = train,trandorm = transform_fn)
data_loader = DataLoader(dataset,batch_size=BATCH_SIZE,shuffle=True)
return data_loader
补充:全连接层:当前一层的神经元和前一层的神经元相互连接,其核心操作就是 y = w x y=wx y=wx,即矩阵的惩罚,实现对前一层数据的变换。
模型的构建使用了一个三层的神经网络,其中包括两个全连接层和一个输出层,第一个全连接层会经过激活函数的处理,将处理后的结果交给下一个全连接层,进行变换后输出结果。
在这个模型中有两个地方需要注意:
前面介绍了激活函数的作用,常用的激活函数为Relu激活函数,它是有import torch.nn.functional as F
提供,F.relu(x)
即可对x进行处理。
如:x = F.relu(x)
import torch
from torch import nn
import torch.nn.functional as F
#2. 模型搭建
class MnistNet(nn.Module):
def __init__(self):
super(MnistNet,self).__init__()
self.fc1 = nn.Linear(28*28,28) # 参数是输入和输出特征的大小
self.fc2 = nn.Linear(28, 10)
def forward(self, input):
"""
:param input: [batch_size,1,28,28]
:return:
"""
# 1.修改形状
x = input.view(input.size(0),1*28*28)
# 2.进行全连接操作
x = self.fc1(x)
# 3.进行激活函数的处理,形状没有变化
x = F.relu(x)
# 4.输出层
out = self.fc2(x)
return out
首先,我们需要明确,手写数字识别是一个多分类的问题。对比之前在逻辑回归中,使用sigmoid进行计算对数似然损失定义2分类的损失。
那么在多分类的过程中我们应该怎么做呐?
我们把softmax概率传入对数似然损失得到的损失函数称为交叉熵损失。
在pytorch中有两种方法实现交叉熵损失
方法一:
criterion = nn.CrossEntropyLoss()
loss = criterion(input,target)
方法二:
# 1.对输出值计算softmax和取对数
output = F.log_softmax(x,dim=-1)
# 2.使用torch中带权损失
loss = F.null_loss(output,target)
训练的流程:
model = MnistNet() # 实例化模型
optimizer = Adam(model.parameters(),lr = 0.001) # 实例化优化器
criterion = nn.CrossEntropyLoss() # 实例化损失函数
def train(epoch):
"""
训练模型
:param epoch:
:return:
"""
dataloader = get_dataloader()
for idx,(data,target) in enumerate(dataloader):
optimizer.zero_grad()# 将梯度置为0
output = model(data) # 调用模型,得到预测值
loss = criterion(output,target) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 梯度更新
if idx%10 == 0:
print("epoch{},idx{},loss{:.6f}".format(epoch,idx,loss.item()))
# 模型的保存
if idx%100==0:
torch.save(model.state_dict(),"./model/model.pkl")
torch.save(optimizer.state_dict(), "./model/optimizer.pkl")
if os.path.exists("./model/model.pkl"):
model.load_state_dict(torch.load("./model/model.pkl"))
optimizer.load_state_dict(torch.load("./model/optimizer.pkl"))
注意:
with torch.no_grad()
tensor.max(dim=-1)[-1]
tensor.eq(tensor2).float().mean()
def test():
loss_list = []
acc_list = []
testloader = get_dataloader(train=False)
for idx,(input,target) in enumerate(testloader):
with torch.no_grad():
output = model(input)
cur_loss = criterion(output,target)
loss_list.append(cur_loss)
# 计算准确度
# output:[batch_size,10] target:[batch_size]
# max方法可以获取指定维度中数据的最大值,dim=-1指获取行的最大值,dim=0指获取列的最大值
pred = output.max(dim=-1)[-1]
acc = pred.eq(target).float().mean()
acc_list.append(acc)
print("平均准去率,平均损失:",np.mean(acc_list),np.mean(loss_list))