通过logistic预测digital number
02.Working with Images & Logistic Regression in PyTorch note
Imports
1.引包
import torch
import torchvision
from torchvision.datasets import MNIST #下载数据
import torchvision.transforms as transforms #将灰度图片转化为tensor
import matplotlib.pyplot as plt #画图,打印datasets里的图片数据
from torch.utils.data import random_split #随机划分,用于划分出验证集
import pylab #辅助plt绘图
2.下载训练数据,并预处理数据
MNIST是一个图片库,里面有很多label了的画了0~9数字的数据,全部用灰度表示。所有的图片均为28*28
#train默认为true 为true就是下载训练数据 为false就是测试数据 训练数据有6w张,测试数据有1w张
dataset = MNIST(root='data/', download=True,transform=transforms.ToTensor()) #训练数据
test_dataset = MNIST(root='data/', train=False,transform=transforms.ToTensor()) #测试数据
train_ds, val_ds = random_split(dataset, [50000, 10000]) #打乱训练集,并分为train和validation集
可以看到dataset中的单个数据结构为:
dataset[0]
#out:(<PIL.Image.Image image mode=L size=28x28 at 0x7F625B9FD710>, 5)
#可以用以下方式提出图像对象和 label
img,label=dataset[0]
print(img)
print(label)
#output
<PIL.Image.Image image mode=L size=28x28 at 0x214017FC490>
5
#可以用以下代码显示图片
image, label = dataset[0]
plt.imshow(image, cmap='gray')
pylab.show()
同时,可以在载入数据的时候同步将灰度图片数据转化为tensor数据
dataset = MNIST(root='data/', download=True,transforms.ToTensor())
test_dataset = MNIST(root='data/', train=False,transforms.ToTensor())
#此时再查看里头的数据
img,label=dataset[0]
print(img.shape)
print(label)
#output
torch.Size([1, 28, 28])
5
可以看到每个图片的维度为(1,28,28),其中1这个维度表示的是灰度,若使用rgb色彩系统,则要三个维度进行色彩的表示。28,28则是二维矩阵,横纵轴上图片点的个数。
训练集,验证集,测试集
验证集有点类似与测试集,只是时间不同。测试集是在训练完成之后进行测试。验证集是在一个batch训练完之后进行精确度测试,并以验证机得到的准确度去做修改模型等操作。
2.定义accuracu evaluate函数
#accuracy,evaluate
def accuracy(outputs, labels):
_, preds = torch.max(outputs, dim=1)
return torch.tensor(torch.sum(preds == labels).item() / len(preds))
def evaluate(model, val_loader):
outputs = [model.validation_step(batch) for batch in val_loader]
return model.validation_epoch_end(outputs)
3.构建模型,确定w与b的形状
现在有一张1*28*28的图片,经过这个模型,我们需要的出的是一个1*10的矩阵
右边的概率矩阵代表通过model后,得出来的识别结果概率,是0的概率为p1(0),以此类推,最大概率则是我们识别出来的结果。
但是这里会出现一个问题:
1.28*28的矩阵是无法得出1*10的矩阵的
解决方案:
1.将28*28矩阵进行降维处理,压成1*784的矩阵,压缩之后模型为
若有n条input,详细矩阵为:
所以,在这一步,可以简单的定义模型:
class MnistModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(748, 10)
def forward(self, xb):
xb = xb.reshape(-1, 784) #在foward的时候进行对矩阵的降维
out = self.linear(xb)
return out
4.前馈的处理
1).数值转概率
考虑前馈实际上就是考虑loss的计算,在考虑Loss的计算前,发现了一个问题,用x@w算出来的数据并非是概率
所以得先解决数值转概率这一个问题
解决方案:
如何保证结果为0~1的概率分布,简单的x@w是不可能自己转变为概率分布的
采用SOFTMAX将获得的值映射到0~1之间,完成数值向概率分布的转换
e可以避免负数情况
2).描述loss
在描述Loss时,就得思考一个问题:target里的是0~9的值,而输出变量是一个1*10的概率矩阵,这该如何描述loss呢?
解决方案:
将target单个值扩充为一个1*10的矩阵:
解决完上面的问题后,就可以用多元cross-entropy对两个概率矩阵的差值进行描述。公式
流程图为:
, torch.nn.CrossEntropyLoss可以直接帮我们运算红线框住区域内的步骤,包括了两步,softmax转概率和交叉熵处理。而0.2,0.1,-0.1则是通过x@w运算出来的predicated值。
。此时,loss知道了怎么算,w,b也知道了,便可以定义模型,代码为:
class MnistModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(input_size, num_classes)
def forward(self, xb):
xb = xb.reshape(-1, 784)
out = self.linear(xb)
return out
def training_step(self, batch):
images, labels = batch
out = self(images) # 这里实际上调用的是foward()函数
loss = F.cross_entropy(out, labels) # Calculate loss
return loss
def validation_step(self, batch):
images, labels = batch
out = self(images) # Generate predictions
loss = F.cross_entropy(out, labels) # Calculate loss
acc = accuracy(out, labels) # Calculate accuracy
return {'val_loss': loss, 'val_acc': acc}
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs]
epoch_loss = torch.stack(batch_losses).mean() # Combine losses
batch_accs = [x['val_acc'] for x in outputs]
epoch_acc = torch.stack(batch_accs).mean() # Combine accuracies
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
def epoch_end(self, epoch, result):
print("Epoch [{}], val_loss: {:.4f}, val_acc: {:.4f}".format(epoch, result['val_loss'], result['val_acc']))
model = MnistModel()
其中有点令人不解的是 out = self(images)
这一串代码,实际上就是调用foward
前馈函数,在父类的_call_函数内,就是调用了foward。
且值得注意的是,这里是一个一个batch进行训练的,一个batch为128条数据,而非简单的1条。
5.accuracy,evaluate函数
def accuracy(outputs, labels):
_, preds = torch.max(outputs, dim=1)
return torch.tensor(torch.sum(preds == labels).item() / len(preds))
5.训练函数
def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):
optimizer = opt_func(model.parameters(), lr)
history = [] # for recording epoch-wise results
for epoch in range(epochs):
# Training Phase
for batch in train_loader:
loss = model.training_step(batch)
loss.backward()
optimizer.step()
optimizer.zero_grad()
# Validation phase
result = evaluate(model, val_loader)
model.epoch_end(epoch, result)
history.append(result)
return history
epochs为训练一个batch的训练轮数
opt_func是已经在函数参数列表内已经已经定义好的采用torch.optim.SGD
优化
可以看到, 整个train是分batch进行训练的,每一次train_loader训练完后,便使用验证集进行验证。
该fit函数会返回一个列表,history,里面记载了每次验证集loss的变化
整体代码:
# 1.导包
import torch
import torchvision
from torchvision.datasets import MNIST # 下载数据
import torchvision.transforms as transforms # 将灰度图片转化为tensor
import matplotlib.pyplot as plt # 画图,打印datasets里的图片数据
from torch.utils.data import random_split # 随机划分,用于划分出验证集
import pylab # 辅助plt绘图
import torch.nn as nn
from torch.utils.data import DataLoader # 分割数据
import torch.nn.functional as F # 神经网络函数包,softmax从里面来的
# 2.下载数据并得出train,test集合
dataset = MNIST(root='data/', download=True, transform=transforms.ToTensor()) # 训练数据
test_dataset = MNIST(root='data/', train=False, transform=transforms.ToTensor()) # 测试数据
train_ds, val_ds = random_split(dataset, [50000, 10000]) # 打乱训练集,并分为train和validation集
# 3.分割训练数据
batch_size = 128
train_loader = DataLoader(train_ds, batch_size, shuffle=True)
val_loader = DataLoader(val_ds, batch_size)
# accuracy,evaluate 用于评估当前训练的好坏,不做梯度下降
def accuracy(outputs, labels):
_, preds = torch.max(outputs, dim=1) # dim指定为1维 视觉上是列向量 第一个返回值为每行的最大值,第二个为每行最大值的列索引
return torch.tensor(torch.sum(preds == labels).item() / len(preds)) # 与label相等的元素个数/所有元素个数
def evaluate(model, val_loader):
outputs = [model.validation_step(batch) for batch in val_loader]
return model.validation_epoch_end(outputs)
# 4.构建模型
input_size = 28 * 28
num_classes = 10
class MnistModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(input_size, num_classes)
def forward(self, xb):
xb = xb.reshape(-1, 784)
out = self.linear(xb)
return out
def training_step(self, batch):
# 直接一次喂128个数据
images, labels = batch
out = self(images) # Generate predictions
loss = F.cross_entropy(out, labels) # Calculate loss
return loss
def validation_step(self, batch):
images, labels = batch
out = self(images) # Generate predictions
loss = F.cross_entropy(out, labels) # Calculate loss
acc = accuracy(out, labels) # Calculate accuracy
return {'val_loss': loss, 'val_acc': acc}
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs]
epoch_loss = torch.stack(batch_losses).mean() # Combine losses
batch_accs = [x['val_acc'] for x in outputs]
epoch_acc = torch.stack(batch_accs).mean() # Combine accuracies
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
def epoch_end(self, epoch, result):
print("Epoch [{}], val_loss: {:.4f}, val_acc: {:.4f}".format(epoch, result['val_loss'], result['val_acc']))
# 4.训练
def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):
optimizer = opt_func(model.parameters(), lr)
history = [] # for recording epoch-wise results
for epoch in range(epochs):
# Training Phase
for batch in train_loader:
loss = model.training_step(batch)
loss.backward()
optimizer.step()
optimizer.zero_grad()
# Validation phase
result = evaluate(model, val_loader)
model.epoch_end(epoch, result)
history.append(result)
return history
if __name__ == '__main__':
model = MnistModel()
resul0 = result0 = evaluate(model, val_loader)
history1 = fit(5, 0.001, model, train_loader, val_loader)
history2 = fit(5, 0.001, model, train_loader, val_loader)
history3 = fit(5, 0.001, model, train_loader, val_loader)
history4 = fit(5, 0.001, model, train_loader, val_loader)
history = [result0] + history1 + history2 + history3 + history4
accuracies = [result['val_acc'] for result in history]
plt.plot(accuracies, '-x')
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.title('Accuracy vs. No. of epochs')
torch.save(model.state_dict(), 'mnist-logistic.pth') #保存模型
print(model.state_dict()) #查看模型参数
#当我们需要载入模型的时候
model2 = MnistModel()
model2.load_state_dict(torch.load('mnist-logistic.pth'))
print(model2.state_dict())