通过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之间,完成数值向概率分布的转换

image-20210130170213252

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())

posted @ 2021-02-10 21:17  lsxkugou  阅读(93)  评论(0编辑  收藏  举报