你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

深度学习6---案例:人民币识别实现

2021/12/18 6:04:56

文章目录

  • 一、图像识别流程
  • 二、深度学习算法流程
  • 三、人民币识别实现
    • 1、数据集拆分
    • 2、主文件
    • 3、数据集读取
    • 4、网络结构
    • 5、预测
    • 6、单张预测

一、图像识别流程

1. 准备数据集
   1)采集图像
   2)拆分数据集
      训练集、测试集  8:2
   3)读图像和分批
   4)图像预处理(缩放、标准化处理)
2. 选择网络结构
    简单任务:lenet、Alexnet
    中等任务:vgg、googlenet
    复杂任务:resnet18、renset50
3. 选择损失函数
   衡量真实值和预测值之间的误差
   分类问题: 交叉熵损失函数
   回归问题:均方误差损失函数
4. 选择优化器(优化器作用:选择一种方法更新参数)
   优化器的一种为:随机梯度下降  w = w - lr*grad
5. 训练算法
for i in range(100):将训练集遍历N遍
   for 每批数据 in 所有训练数据:
       output = model(每批数据)  # 模型预测
       loss_val = loss_fn(output, target)
       根据loss进行反向传播
       利用优化器更新model中参数


# 保存模型
save(model中参数)

# -------------------------------------------------------------------------
数据集拆分
1元:100张
100元:100张
训练集:80张1元 + 80张100元
测试集:20张1元 + 20张100元

二、深度学习算法流程

在这里插入图片描述在这里插入图片描述

三、人民币识别实现

1、数据集拆分

RMB_1数据拆分.py

import os
import random
import shutil

random.seed(1)

"""
数据集拆分
1元:100张
100元:100张
训练集:80张1元 + 80张100元
测试集:20张1元 + 20张100元


拆分后RMB_split
>train
>> 1元 
    80张图像
>> 100元  
    80张图像
>test
>> 1元 
    20张图像
>> 100元  
    20张图像
"""

# 根路径、子路径、文件名
for root, subs, files in os.walk("E:\pycharm\pythonProject\深度学习./RMB_data"):
    for sub in subs:
        sub_path = os.path.join(root, sub)
        # print(sub_path)
        all_images = os.listdir(sub_path)

        random.shuffle(all_images)  # 打乱列表中的元素

        for ind, i in enumerate(all_images):
            org_img_name = os.path.join(sub_path, i)
            print(ind, org_img_name)
            if ind < 80:
                save_img_name = org_img_name.replace("RMB_data", "RMB_split/train")
            else:
                save_img_name = org_img_name.replace("RMB_data", "RMB_split/test")
            # save_path = save_img_name.split("\\")[0]
            save_path = os.path.dirname(save_img_name)
            # print(save_path)
            # 创建目录
            # exist_ok=True 如果文件夹存在,不再次创建,也不报错
            os.makedirs(save_path, exist_ok=True)

            # 源路径、目的路径
            shutil.copy(org_img_name, save_img_name)

在这里插入图片描述

2、主文件

RMB_2主文件.py

from torch.utils.data import DataLoader  # 数据分批
from 深度学习.RMB_3数据集读取 import RMB_Dataset
from torchvision import transforms
import pdb
from 深度学习.RMB_4网络结构 import Lenet
import torch.nn as nn
import torch.optim as optim
import numpy as np
import torch

# 随机种子
torch.manual_seed(1)

# -----------------------超参数--------------------------------
LR = 0.05  # 学习率
max_epoch = 20  # 最大迭代次数
interval = 2  # 每训练两次 测试一次
test_batch_size = 4
best_acc = 0

# ---------------------------1/5 数据模块-------------------------------------------------
train_data_path = "RMB_split/train"
test_data_path = "RMB_split/test"

# 图像处理的均值和标准差
# ImageNet数据集 上图像的均值和标准差如下
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
# 数据预处理的一些操作。
train_transform = transforms.Compose([
    transforms.Resize((32, 32)),  # 缩放到统一尺寸
    transforms.RandomCrop(32, padding=4),  # 图像四周补像素
    transforms.ToTensor(),  # 转换为tensor,会被归一化为0-1
    transforms.Normalize(norm_mean, norm_std),  # 数据标准化:减均值 除方法
])

test_transform = transforms.Compose([
    transforms.Resize((32, 32)),  # 缩放到统一尺寸
    transforms.ToTensor(),  # 转换为tensor,会被归一化为0-1
    transforms.Normalize(norm_mean, norm_std),  # 数据标准化:减均值 除方法
])

train_dataset = RMB_Dataset(data_path=train_data_path, transform=train_transform)
test_dataset = RMB_Dataset(data_path=test_data_path, transform=test_transform)

# 参数 dataset: 对哪个数据集进行分批 (数值类型)
# 参数batch_size 每个批次的样本个数
# shuffle 是否对数据集进行打乱,再分批
# drop_last 是否删除最后一个; 在分批过程中如果不能整除,是否删除一个批次
# 总样本数 84  每个批次8个样本
# 如果drop_last=True,删除最后4个样本; 如果drop_last=False,使用4个样本+前80个随机选4个
# 分批处理
train_loader = DataLoader(
    dataset=train_dataset,
    batch_size=16,  # 设置为8个倍数 通常范围32 64 128
    shuffle=False,    #True--->Falae要不然报错
    drop_last=True  # 训练集160个样本
)

# 测试数据集一共20个样本
test_loader = DataLoader(
    dataset=test_dataset,
    batch_size=test_batch_size,  # 设置为8个倍数 通常范围32 64 128
    shuffle=False,  #True--->Falae要不然报错
    drop_last=True  # 训练集160个样本
)
print(len(test_loader))


"""
# 对分批后的对象进行遍历
for batch in train_loader:
    # pdb.set_trace()
    # batch 的值 由 __getitem__返回值决定
    print(batch)
"""

# ---------------------------2/5 网络结构----------------------------------------------
model = Lenet(num_classes=2)

# ---------------------------3/5 损失函数----------------------------------------------
# 对于分类任务----交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()

# ---------------------------4/5 优化器----------------------------------------------
# 随机梯度下降
optimizer = optim.SGD(model.parameters(), lr=LR)

# ---------------------------5/5 训练算法----------------------------------------------
for epoch in range(max_epoch):
    acc_num = 0
    model.train()  # -----------接下来是训练
    for batch in train_loader:
        img, label, _ = batch
        # img 一个批次的数据; label 一个批次的标签
        # 预测
        output = model(img)  # 前向传播
        # 计算真实值和预测值预测
        loss_val = loss_fn(output, label)  # 计算损失值
        # 更新梯度
        optimizer.zero_grad()  # 梯度清零
        # 计算梯度
        loss_val.backward()  # 反向传播 计算梯度
        optimizer.step()  # 更新参数  w = w -lr* grad

        # y_pred = np.argmax(output.detach().numpy(), axis=1)
        _, y_pred = torch.max(output, dim=1)
        # pdb.set_trace()
        acc_num += int((y_pred == label).sum())
    print("第{}次训练 准确率{}".format(epoch, acc_num / 160))

    # 保留在测试集上 效果 最好的 一次模型
    if epoch % 2 == 0:
        # 训练N次,测试一次
        model.eval()  # 接下来进行模型测试

        acc_num = 0
        for batch in test_loader:
            img, label, _ = batch
            output = model(img)
            # 测试过程不需要计算损失
            _, y_pred = torch.max(output, dim=1)
            # pdb.set_trace()
            acc_num += int((y_pred == label).sum())

        test_accuracy = acc_num /((len(test_loader) * test_batch_size))
        print("第{}轮 测试 准确率{}".format(epoch, test_accuracy))

        # if test_accuracy > best_acc:
        #     best_acc = test_accuracy
        #     # 保存模型
        #     # 保存模型 通常只保存模型对应的状态字典
        #     # pdb.set_trace()
        #     torch.save(model.state_dict(), "rmb.pth")
        #     # model.state_dict()["conv1.weight"]

        if test_accuracy > best_acc:
            best_acc = test_accuracy
            # 保存模型
            # 保存模型 通常只保存模型对应的状态字典
            # pdb.set_trace()
            torch.save(model.state_dict(), "rmb_v2.pth")
            # model.state_dict()["conv1.weight"]


3、数据集读取

RMB_3数据集读取.py

from torch.utils.data import Dataset
import os
from PIL import Image


# 所有自定义数据集的类 集成自torch.utils.data.Dataset
# 必须包含__getitem__、__len__
class RMB_Dataset(Dataset):
    def __init__(self, data_path="", transform=None):
        # 将数据集整理为
        # self.data = [[图像名称1,标签1], [图像名称2,标签2], [图像名称3,标签3].....]
        self.data = []
        dict1 = {"100": 0, "1": 1}
        for root, sub, files in os.walk(data_path):
            for file in files:
                img_name = os.path.join(root, file)  # 获取图像路径
                label = img_name.split("\\")[-2]  # 获取标签
                label = dict1[label]  # label转换为0 1
                self.data.append([img_name, label])
        self.transform = transform  # 数据转换处理

    def __getitem__(self, index):
        # 负责读取其中一个图像,再进行图像转换
        img_name, label = self.data[index]
        img = Image.open(img_name)

        # 图像转换处理
        if self.transform is not None:
            img = self.transform(img)

        return img, label, img_name

    def __len__(self):
        # 数据集中有多少个样本,
        # 该函数必须有返回值
        return len(self.data)


"""

data_path = "dataset/RMB_split/train"
# 可以获取文件夹下 子文件夹下 的所有文件

# self.data = [[图像名称1,标签1], [图像名称2,标签2], [图像名称3,标签3].....]

data = []
dict1 = {"100元": 0, "1元": 1}
for root, sub, files in os.walk(data_path):
    for file in files:
        img_name = os.path.join(root, file)  # 获取图像路径
        label = img_name.split("\\")[-2]  # 获取标签
        label = dict1[label] # label转换为0 1
        data.append([img_name, label])

"""

4、网络结构

RMB_4网络结构.py

import torch.nn as nn
import torch
import pdb
import numpy as np


# 自定义的网络结构 都必须继承 nn.Module
class Lenet(nn.Module):
    def __init__(self, num_classes=10):
        super(Lenet, self).__init__()
        # 初始化的信息
        # 定义各个卷积运算、池化运算、激活、全连接
        self.conv1 = nn.Conv2d(in_channels=3,
                               out_channels=6,
                               kernel_size=(5, 5),
                               stride=1,
                               padding=0)
        self.pool = nn.MaxPool2d(kernel_size=(2, 2), stride=2)

        self.conv2 = nn.Conv2d(in_channels=6,
                               out_channels=16,
                               kernel_size=(5, 5),
                               stride=1,
                               padding=0)
        self.fc1 = nn.Linear(in_features=400,
                             out_features=120)
        self.fc2 = nn.Linear(in_features=120,
                             out_features=84)
        self.fc3 = nn.Linear(in_features=84,
                             out_features=num_classes)

        self.relu = nn.ReLU(inplace=True)

        pass

    def forward(self, X):
        # 前向传播  执行顺序
        # X 是输入数据 N*C*H*W  (样本数*通道数*H*W)
        # pdb.set_trace()
        out = self.conv1(X)
        out = self.relu(out)
        out = self.pool(out)

        out = self.conv2(out)
        out = self.relu(out)
        out = self.pool(out)

        out = out.reshape(out.shape[0], -1)  # 展平

        out = self.fc1(out)
        out = self.fc2(out)
        out = self.fc3(out)
        return out

        pass

5、预测

RMB_5预测.py

import torch
from 深度学习.RMB_4网络结构 import Lenet
from torchvision import transforms
from 深度学习.RMB_3数据集读取 import RMB_Dataset
from torch.utils.data import DataLoader  # 数据分批
import matplotlib.pyplot as plt
from PIL import Image
import pdb
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

model = Lenet(num_classes=2)
# 加载模型
checkpoint = torch.load("rmb.pth")
model.load_state_dict(checkpoint)

test_data_path = "dataset/RMB_split/test"
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]

test_transform = transforms.Compose([
    transforms.Resize((32, 32)),  # 缩放到统一尺寸
    transforms.ToTensor(),  # 转换为tensor,会被归一化为0-1
    transforms.Normalize(norm_mean, norm_std),  # 数据标准化:减均值 除方法
])

test_dataset = RMB_Dataset(data_path=test_data_path, transform=test_transform)

# 测试数据集一共20个样本
test_loader = DataLoader(
    dataset=test_dataset,
    batch_size=1,
    shuffle=False,
    drop_last=True  # 训练集160个样本
)

dict1 = {0: "100元", 1: "1元"}

plt.ion()

for batch in test_loader:
    plt.cla()

    img, label, img_name = batch
    output = model(img)
    # 测试过程不需要计算损失
    _, y_pred = torch.max(output, dim=1)

    # pdb.set_trace()
    img2 = Image.open(img_name[0])
    plt.imshow(img2)

    y_pred = dict1[int(y_pred[0])]
    label = dict1[int(label[0])]
    plt.title("预测结果是{}, 真实结果{}".format(y_pred, label))
    plt.pause(3)

plt.ioff()
plt.show()

6、单张预测

RMB_6单张预测.py

import torch
from PIL import Image
from 深度学习.RMB_4网络结构 import Lenet
import os
from torchvision.transforms import functional as F
import pdb

import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

model = Lenet(num_classes=2)
# 加载模型
checkpoint = torch.load("rmb_v2.pth")
model.load_state_dict(checkpoint)

dict1 = {0: "100元", 1: "1元"}


plt.ion()

for root, sub, files in os.walk("images/"):
    for file in files:
        img_name = os.path.join(root, file)  # 获取图像路径
        img = Image.open(img_name).convert('RGB')
        img_org = img.copy()
        # 缩放处理
        img = F.resize(img, (32, 32))

        # 图像转Tensor
        img = F.to_tensor(img)
        # pdb.set_trace()
        # 标准化处理
        norm_mean = [0.485, 0.456, 0.406]
        norm_std = [0.229, 0.224, 0.225]

        img = F.normalize(img, norm_mean, norm_std)

        # 将图像扩展为四维张量
        img = img.expand(1, 3, 32, 32)

        output = model(img)

        _, y_pred = torch.max(output, dim=1)

        y_pred = int(y_pred[0])
        y_pred = dict1[y_pred]
        print("预测结果", y_pred)

        plt.cla()

        plt.title("预测结果{}".format(y_pred))
        plt.imshow(img_org)
        plt.pause(3)

plt.ioff()
plt.show()