目录

一、实验

1、实验目的

2、相关知识点

本实验的网络结构

实验结果

3、错误及解决方案

4、总结

二、代码实现

1、前期准备

2、构建网络

3、训练模型

4、结果可视化


一、实验

1、实验目的

①学习如何将没有分类的数据制作成数据集进行处理

②尽量做到对单张车牌进行识别

2、相关知识点

① classeNames=[str(path).split('\\')[1].split('_')[1].split('.')[0] for path in data_paths]代码解读

其实就是对path对象转化成字符串,然后利用分割符和索引得到车牌号,完整的字符串如下:

本实验的网络结构

实验结果

3、错误及解决方案

①shape '[2, 7, 69]' is invalid for input of size 1

注意这里fc函数的参数只有两个

②The size of tensor a (16) must match the size of tensor b (7) at non-singleton dimension 1

错误原因:这个错误是 PyTorch 中典型的张量维度不匹配问题,核心是两个张量(a 和 b)在第 1 维(PyTorch 是 0 索引,所以 dimension 1 是第二维)的尺寸不一致(一个是 16,一个是 7),而执行的操作(比如相加、计算损失、矩阵运算等)要求这两个维度必须完全相同

4、总结

       这次实验可以说是一夜回到解放前,感觉思路没问题但就是各种错误,这个张量维度愣是没搞清楚到底哪里错了,所以精度的曲线还没弄出来,学习了一些怎么把未分类数据制作成自己的数据集,逻辑差不多懂了,但估计离自己能敲出来的距离还远的不行

二、代码实现

1、前期准备

       ①导入库并设置GPU

from tkinter import Variable

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from torchvision import datasets,transforms
import matplotlib.pyplot as plt
import torch.nn.functional as F
import torchvision.models as models
from torch.utils.data import DataLoader
import os,PIL,random,pathlib,warnings

#支持中文,主要用于正常显示中文标签和显示负号
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
warnings.filterwarnings('ignore')

device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
device

       ②导入并测试数据集

#导入数据并转化为pathlib对象
data_dir="./data"
data_dir=pathlib.Path(data_dir)

data_paths=list(data_dir.glob('*'))
classeNames=[str(path).split('\\')[1].split('_')[1].split('.')[0] for path in data_paths]
classeNames
data_paths_str
#%%
#数据可视化
plt.figure(figsize=(14,5))
plt.suptitle("数据展示",fontsize=15)

for i in range(18):
    plt.subplot(3,6,i+1)
    #显示图像
    images=plt.imread(data_paths_str[i])
    plt.imshow(images)
plt.show()

获得的车牌读取结果和数据可视化结果如下

将标签数字化并加载

#标签数字化
import numpy as np
char_enum=['京','沪','津','渝','冀','晋','蒙','辽','吉','黑','苏','浙','皖','闽','赣','鲁','豫','鄂','湘','粤',\
           '桂','琼','川','贵','云','藏','陕','甘','青','宁','新','军','使']

number=[str(i) for i in range(0,10)]
alphabet=[chr(i) for i in range(65,91)]

char_set=char_enum+number+alphabet
char_set_len=len(char_set)
label_name_len=len(classeNames[0])

#将字符串数字化
def text2vec(text):
    vector=np.zeros([label_name_len,char_set_len])
    for i,c in enumerate(text):
        idx=char_set.index(c)
        vector[i][idx]=1.0
    return vector

all_labels=[text2vec(i) for i in classeNames]

#开始加载数据文件
import os
import pandas as pd
from torchvision.io import read_image
import torch.utils.data as data
from PIL import Image

class MyDataset(data.Dataset):
    def __init__(self,all_labels,data_paths_str,transform):
        #获取标签信息、图像目标路径和目标转换函数
        self.img_labels=all_labels
        self.img_dir=data_paths_str
        self.transform=transform

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, index):
        #将图像转换为RGB格式
        image=Image.open(self.img_dir[index]).convert('RGB')
        label=self.img_labels[index]

        if self.transform:
            image=self.transform(image)

        #返回图像和标签
        return image,label

接下来是一些基本操作,比如利用transforms对数据进行处理,对数据集进行划分、观察数据的数量、形状等等,代码和前几次实验一样,这里就省略了,将重心放在数据集的处理和网络模型的架构

2、构建网络

       ①了解网络架构

        网络结构如前面所展示的一样,这里省略

       ②实现模型

class Network_bn(nn.Module):
    def __init__(self):
        super(Network_bn, self).__init__()

        self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=5, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(12)
        self.conv2 = nn.Conv2d(in_channels=12, out_channels=12, kernel_size=5, stride=1, padding=0)
        self.bn2 = nn.BatchNorm2d(12)
        self.pool = nn.MaxPool2d(2,2)
        self.conv4 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=5, stride=1, padding=0)
        self.bn4 = nn.BatchNorm2d(24)
        self.conv5 = nn.Conv2d(in_channels=24, out_channels=24, kernel_size=5, stride=1, padding=0)
        self.bn5 = nn.BatchNorm2d(24)
        self.fc1 = nn.Linear(24*50*50, label_name_len*char_set_len)
        self.reshape = Reshape([label_name_len,char_set_len])

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))      
        x = F.relu(self.bn2(self.conv2(x)))     
        x = self.pool(x)                        
        x = F.relu(self.bn4(self.conv4(x)))     
        x = F.relu(self.bn5(self.conv5(x)))  
        x = self.pool(x)                        
        x = x.view(-1, 24*50*50)
        x = self.fc1(x)
        
        # 最终reshape
        x = self.reshape(x)

        return x
    
# 定义Reshape层
class Reshape(nn.Module):
    def __init__(self, shape):
        super(Reshape, self).__init__()
        self.shape = shape

    def forward(self, x):
        return x.view(x.size(0), *self.shape)

device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

model = Network_bn().to(device)
model

       ③打印模型(注意这里的参数量可以根据公式进行计算)

3、训练模型

训练函数和测试函数的编写,原理基本上没变,但是真的没想通这个acc为啥会报维度不匹配,这周暂时来不及找出来了,等我找到原因再补充上来

from torch.autograd import Variable

def test(model, test_loader, loss_model):
    size = len(test_loader.dataset)
    num_batches = len(test_loader)
    
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in test_loader:
            X, y = X.to(device), y.to(device)
            pred = model(X)

            test_loss += loss_model(pred, y).item()
            #correct+=(pred.argmax(1)==y).type(torch.float).sum().item()
            
    test_loss /= num_batches
    #correct/=size

    print(f"Avg loss: {test_loss:>8f} \n")
    return test_loss

def train(model,train_loader,loss_model,optimizer):
    model=model.to(device)
    model.train()
    
    for i, (images, labels) in enumerate(train_loader, 0): #0是标起始位置的值。

        images = Variable(images.to(device))
        labels = Variable(labels.to(device))

        optimizer.zero_grad()
        outputs = model(images)

        loss = loss_model(outputs, labels)
        loss.backward()
        optimizer.step()

        if i % 1000 == 0:    
            print('[%5d] loss: %.3f' % (i, loss))
#%%
test_acc_list  = []
test_loss_list = []
epochs = 30

for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(model,train_loader,loss_model,optimizer)
    #test_acc,test_loss = test(model, test_loader, loss_model)
    test_loss = test(model, test_loader, loss_model)
    #test_acc_list.append(test_acc)
    test_loss_list.append(test_loss)
print("Done!")

得到的部分epoch训练结果如下

4、结果可视化

更多推荐