文章目录
- 一、图像识别流程
- 二、深度学习算法流程
- 三、人民币识别实现
- 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()