深度学习基础之Datasets手写体数据集的卷积网络训练(二)

一、网络模型代码        

        作为深度学习的入门,这个数据集应该是入门必备,本网络以官方下载的手写体数据集作为训练样本,自定义神经网络包含三层卷积,两层线性网络,在3060GPU上训练50轮,batchsize设置为64,优化器选用Adam。注意:with torch.no_grad():在测试前需加上,代码遗漏了

from torch.utils.tensorboard import SummaryWriter
import torch
from torchvision import transforms
from torchvision import datasets
import torch.nn as nn
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim

# 在GPU上运行
device = torch.device("cuda")

# prepare data   1 channel, 28x28
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307), (0.3081))])  # 均值,方差

train_data = datasets.MNIST(root="./mydata/mnist/", train=True, transform=transform, download=True)
test_data = datasets.MNIST(root="./mydata/mnist/", train=False, transform=transform, download=True)

train_size = len(train_data)
test_size = len(test_data)
print("训练集长度:", train_size)  # 60000
print("测试集长度:", test_size)  # 10000

# load data

train_load = DataLoader(train_data, batch_size=64, shuffle=True, drop_last=True)
test_load = DataLoader(test_data, batch_size=64, shuffle=False, drop_last=True)


# create my module
class Mymodel(nn.Module):
    def __init__(self):
        super(Mymodel, self).__init__()
        # 较为简单的模型构建: 定义三层卷积,两层线性层的参数
        # input:(64,1,28,28)
        self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1,
                                     padding=1)  # ep.parameters
        self.conv2 = torch.nn.Conv2d(16, 8, 3, 1, 1)
        self.conv3 = torch.nn.Conv2d(8, 4, 3, 1, 1)
        self.pooling = torch.nn.MaxPool2d(kernel_size=2)  # defalt padding=0,stride=k_z
        self.l1 = torch.nn.Linear(in_features=784, out_features=64)
        self.l2 = torch.nn.Linear(64, 10)

    # 前向传播
    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.relu(self.conv3(x))
        x = F.relu(self.pooling(x))  # 输出为(64,4,14,14)
        x = x.view(64, -1)  # -1 ="784"得到(64,784)
        x = F.relu(self.l1(x))
        x = self.l2(x)  # last layer 不用激活函数
        return x


model = Mymodel()
model = model.to(device)  # to gpu

# 损失函数
loss = nn.CrossEntropyLoss()
loss = loss.to(device)  # to gpu

# 反向传播(优化器)
optimizer = optim.Adam(model.parameters(), lr=0.01)

# 设置训练的一些参数
# 训练轮数
epoch = 50
# 训练的次数每个batchsize为1次,1个epoch需要训练937.5次
train_step = 0
test_step = 0

# 可视化
writer = SummaryWriter("./mimistlog")

for i in range(epoch):
    # train---记录训练误差
    print(f"第{i + 1}轮训练开始")
    for data in train_load:
        inputs, targets = data
        inputs, targets = inputs.to(device), targets.to(device)  # to gpu
        optimizer.zero_grad()
        outputs = model(inputs)  # 输出为(64,10)
        loss_fun = loss(outputs, targets)  # 会自动将targets进行one-hot
        loss_fun.backward()
        optimizer.step()
        train_step += 1
        if train_step % 100 == 0:
            print(f"第{train_step}次训练,loss:{loss_fun.item()}")
            writer.add_scalar("train_loss", loss_fun.item(), train_step)  # 名称,y为损失,x为训练次数

    # test---记录测试误差,测试集的正确率
    correct = 0
    all = 0
    loss_all = 0

    for data in test_load:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        loss_fun = loss(outputs, labels)
        predicted = torch.max(outputs.data, dim=1)[1]  # 按行取出预测值10个当中最大的,进行acc计算
        test_step += 1
        all += labels.size(0)  # 累加每次的label数,也就是用于测试集的数量
        correct += (predicted == labels).sum().item()  # 进行张量的比较运算,计和正确个数
        loss_all += loss_fun  # 计算测试样本总损失
        # 可视化测试过程
        writer.add_scalar("test_loss", loss_fun.item(), test_step)


    acc = (correct / all)
    loss_average = (loss_all / all)
    writer.add_scalar("test_accuracy", acc, i)
    print(f"测试集上的正确率为:{acc}")
    print(f"测试集上的平均损失为:{loss_average}")
    if i % 9 == 0:
        torch.save(model, f"ct1_{i}.pth")
        print("模型已保存")
writer.close()

二、模型训练中遇到的问题与优化

1.batchsize问题

在第一次训练遇到如下报错

意思是矩阵形状不匹配,检查后发现是batchsize的设置问题,该手写体测试样本为60000个,设置的size=64,在一个epoch下面会剩下 (60000/64=937+32)32个图片,于是在卷积最后一层会输出32x4x14x14的张量,在经过前向传播view展平后会reshape成(64,392)的张量,与下层线性层要求输入的784的维度不符,报错

解决方法:

1.在dataload的参数加上        drop_last=True      就会自动抛弃最后一个不足64的数据。

2.调整batchsize大小

3.重新分割数据集使其可以整除。

2.过拟合问题

模型在训练集与测试集上一般是有一个很好的临界点,让其在两个集上的效果都较好

加载tensorboard可视化查看训练误差与测试误差

可以发现训练误差不断下降,但是测试误差先下降后上升,说明模型过拟合了,学习到了噪声或者

是学习的过于优秀,而泛化能力很差

解决方法:

1.early stopping观察测试误差何时开始增大,大约在测试集1700step测试,约10个epoch左右停止

效果更好,下次训练设置相应的训练轮数。也可设置save的过程,在进行预测时采用第10次的pth

经过调整后效果还可以。

2.dropout设置丢弃神经元的概率

3.采用更好的主干网络等等

target:需要学习更多优质的网络与数据集更好的进行特征提取,进行参数优化

三、Predict

测试模型效果

1.用手写七张数字图片,裁剪,转为单通道,然后进行识别。

首次识别效果不佳---- 8个只对了4个

继续调整模型,更换了优化器为SGD,训练25轮,此时效果变得更好

继续预测

2.利用画图书写十张0-9的图片,然后进行格式的转换,利用训练好的模型进行按顺序遍历预测

import os
import torch
from torchvision import transforms
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image

path="D:\Pycharm\Imageprocessing\workmyself\predict_data"
img_path=os.listdir(path) #读取图片的路径,以列表的形式存储

transform = transforms.Compose([transforms.ToTensor()])
device=torch.device("cuda")

class Mymodel(nn.Module):
    def __init__(self):
        super(Mymodel, self).__init__()
        # 较为简单的模型构建: 定义三层卷积,两层线性层的参数
        # input:(1,1,28,28)
        self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1,
                                     padding=1)  # ep.parameters
        self.conv2 = torch.nn.Conv2d(16, 8, 3, 1, 1)
        self.conv3 = torch.nn.Conv2d(8, 4, 3, 1, 1)
        self.pooling = torch.nn.MaxPool2d(kernel_size=2)  # defalt padding=0,stride=k_z
        self.l1 = torch.nn.Linear(in_features=784, out_features=64)
        self.l2 = torch.nn.Linear(64, 10)

    # 前向传播
    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.relu(self.conv3(x))
        x = F.relu(self.pooling(x))  # 输出为(1,4,14,14)
        x = x.view(1, -1)  # -1 ="784"得到(1,784)
        x = F.relu(self.l1(x))
        x = self.l2(x)  # last layer 不用激活函数
        return x

model =torch.load("ct2_19.pth")

for i in range(10):
    os_img_path = os.path.join(path,img_path[i])#拼接路径读取图片
    img=Image.open(os_img_path)
    img= img.convert("L")
    img_change1=transform(img)
    single_img = torch.reshape(img_change1, (1, 1, 28, 28))
    single_img = single_img.to(device)
    output = model(single_img)
    #print(output)

    # mean=torch.mean(img_change1,dim=0)
    # single_img=mean.unsqueeze(0)
    # single_img=torch.reshape(single_img,(1,1,28,28))
    # single_img=single_img.to(device)
    # output=model(single_img)
    print(torch.max(output.data,dim=1)[1])
 

3.图片的数字与label是对应的,采用训练25次后保存的权重数据

   预测结果10对7,达到70%

4.若采用仅训练10次的训练数据,可见效果不够好

        模型的优劣以及在未知数据集上的表现与很多参数有关,往后学习更加复杂模型的同时也要重视模型优化的经验。

卷积神经网络(Convolutional Neural Networks,简称CNN)是一种在图像处理中非常重要的神经网络结构。它可以用于手写体数字识别等任务。下面是一个使用CNN进行手写体数字识别的示例: ```python import tensorflow as tf from tensorflow.keras.datasets import mnist # 加载MNIST数据集 (x_train, y_train), (x_test, y_test) = mnist.load_data() # 数据预处理 x_train = x_train.reshape(-1, 28, 28, 1) / 255.0 x_test = x_test.reshape(-1, 28, 28, 1) / 255.0 y_train = tf.keras.utils.to_categorical(y_train, num_classes=10) y_test = tf.keras.utils.to_categorical(y_test, num_classes=10) # 构建卷积神经网络模型 model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(10, activation='softmax') ]) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 训练模型 model.fit(x_train, y_train, epochs=5, batch_size=32, validation_data=(x_test, y_test)) # 评估模型 test_loss, test_acc = model.evaluate(x_test, y_test) print('Test accuracy:', test_acc) ``` 这个示例使用了TensorFlow和Keras库来构建和训练一个卷积神经网络模型,用于手写体数字识别任务。模型首先加载了MNIST数据集,并进行了数据预处理。然后,通过添加卷积层、池化层、全连接层和输出层来构建了一个简单的卷积神经网络模型。最后,使用训练集对模型进行训练,并使用测试集评估模型的准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值