汉字识别

news/2025/11/5 23:19:20/文章来源:https://www.cnblogs.com/2251LY/p/19194905
点击查看代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt
import random
import os
from torchvision import transforms
import matplotlib# 确保中文显示正常(只保留Windows系统默认存在的字体)
plt.rcParams["font.family"] = ["SimHei", "Microsoft YaHei"]  # 这两种字体Windows系统默认必装
matplotlib.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题# 1. 生成中文数据集
class ChineseCharacterDataset(Dataset):def __init__(self, num_samples=10000, img_size=(64, 64), transform=None):"""生成模拟手写中文字符数据集"""self.num_samples = num_samplesself.img_size = img_sizeself.transform = transform# 选择常用中文字符(可扩展)self.chars = "一二三四五六七八九十甲乙丙丁戊己庚辛壬癸金木水火土天地日月"self.classes = list(self.chars)self.num_classes = len(self.classes)self.char_to_idx = {char: i for i, char in enumerate(self.classes)}# 生成数据集self.images, self.labels = self._generate_data()def _generate_data(self):"""生成模拟手写中文字符图像"""images = []labels = []# 尝试加载中文字体,若失败则使用默认字体try:# 直接使用系统字体目录中的黑体font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", 40)  # 明确指定系统黑体路径except:try:# 尝试系统宋体font = ImageFont.truetype("C:/Windows/Fonts/simsun.ttc", 40)except:font = ImageFont.load_default()print("警告:未找到中文字体,使用默认字体可能导致显示异常")for _ in range(self.num_samples):# 随机选择一个字符char = random.choice(self.classes)label = self.char_to_idx[char]# 创建空白图像img = Image.new('L', self.img_size, color=255)  # 白色背景draw = ImageDraw.Draw(img)# 计算文字位置(居中)- 兼容新版本PILbbox = draw.textbbox((0, 0), char, font=font)char_width = bbox[2] - bbox[0]  # 宽度char_height = bbox[3] - bbox[1]  # 高度x = (self.img_size[0] - char_width) // 2y = (self.img_size[1] - char_height) // 2# 添加随机扰动,模拟手写效果x += random.randint(-5, 5)y += random.randint(-5, 5)# 绘制文字(黑色)draw.text((x, y), char, font=font, fill=0)# 添加随机噪声if random.random() > 0.5:img = self._add_noise(img)# 添加随机旋转if random.random() > 0.5:angle = random.randint(-10, 10)img = img.rotate(angle, expand=False, fillcolor=255)images.append(img)labels.append(label)return images, labelsdef _add_noise(self, img):"""为图像添加随机噪声"""img_np = np.array(img)noise = np.random.randint(-20, 20, size=img_np.shape)img_np = np.clip(img_np + noise, 0, 255).astype(np.uint8)return Image.fromarray(img_np)def __len__(self):return self.num_samplesdef __getitem__(self, idx):img = self.images[idx]label = self.labels[idx]if self.transform:img = self.transform(img)return img, label# 2. 数据预处理与加载
transform = transforms.Compose([transforms.ToTensor(),  # 转为张量transforms.Normalize((0.5,), (0.5,))  # 归一化到 [-1, 1]
])# 创建数据集
train_dataset = ChineseCharacterDataset(num_samples=8000,img_size=(64, 64),transform=transform
)
test_dataset = ChineseCharacterDataset(num_samples=2000,img_size=(64, 64),transform=transform
)# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)# 3. 定义适用于中文字符识别的CNN模型
class ChineseCNN(nn.Module):def __init__(self, num_classes):super(ChineseCNN, self).__init__()# 卷积层:输入为1通道(灰度图)self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)self.conv3 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)# 池化层self.pool = nn.MaxPool2d(2, 2)# 全连接层:64x64图像经过3次池化后变为8x8self.fc1 = nn.Linear(128 * 8 * 8, 512)self.fc2 = nn.Linear(512, 256)self.fc3 = nn.Linear(256, num_classes)# Dropout防止过拟合self.dropout = nn.Dropout(0.5)def forward(self, x):# 卷积 -> ReLU -> 池化x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = self.pool(F.relu(self.conv3(x)))# 展平特征图x = x.view(-1, 128 * 8 * 8)# 全连接层 -> ReLU -> Dropoutx = F.relu(self.fc1(x))x = self.dropout(x)x = F.relu(self.fc2(x))x = self.dropout(x)# 输出层x = self.fc3(x)return x# 创建模型实例
num_classes = train_dataset.num_classes
model = ChineseCNN(num_classes)# 4. 定义损失函数与优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)# 5. 模型训练
num_epochs = 15
model.train()for epoch in range(num_epochs):total_loss = 0correct = 0total = 0for images, labels in train_loader:# 前向传播outputs = model(images)loss = criterion(outputs, labels)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()total_loss += loss.item()# 计算训练准确率_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()train_accuracy = 100 * correct / totalavg_loss = total_loss / len(train_loader)print(f"Epoch [{epoch + 1}/{num_epochs}], 损失: {avg_loss:.4f}, 训练准确率: {train_accuracy:.2f}%")# 6. 模型测试
model.eval()
correct = 0
total = 0with torch.no_grad():for images, labels in test_loader:outputs = model(images)_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()test_accuracy = 100 * correct / total
print(f"测试准确率: {test_accuracy:.2f}%")# 7. 可视化测试结果
dataiter = iter(test_loader)
images, labels = next(dataiter)
outputs = model(images)
_, predictions = torch.max(outputs, 1)# 获取类别名称
classes = train_dataset.classes# 显示6个样本
fig, axes = plt.subplots(1, 6, figsize=(15, 3))
for i in range(6):# 反归一化显示图像img = images[i].numpy().squeeze()img = (img * 0.5) + 0.5  # 还原从[-1,1]到[0,1]axes[i].imshow(img, cmap='gray')true_char = classes[labels[i]]pred_char = classes[predictions[i]]axes[i].set_title(f"真实: {true_char}\n预测: {pred_char}")axes[i].axis('off')plt.tight_layout()
plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/957217.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AGC与AVC是什么

定义AGC:自动发电控制 功能:实时调节有功功率,将电网频率控制在50HZ 电网频率的高低由“有功功率”决定 有功多了,频率升高。AVC : 自动电压控制 功能:调节无功补偿设备,将电网电压在额定值5% 内 电网电压高低由…

链表1

链表1线性表的链式表示与实现1 基本概念 线性表的链式表示又称为非顺序映像或链式映像 特点链表中元素的逻辑次序和物理次序不一定相同链表中的存储单元既可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任…

競プロ典型 90 問-难题

005倍增优化dp 题目大意(自己总结 只用数字 c1​,c2​,…,cK​ 可以构造出多少个 N 位正整数是 B 的倍数? 求除以 109+7 的余数。$1 \leq K \leq 9$ $1 \leq c_1 \lt c_2 \lt \cdots \lt c_K \leq 9$ $1 \leq N \leq…

c++函数调用的大致工作过程

c++函数调用的大致工作过程在C++中,函数调用是一个基本的编程概念,它允许我们将一组语句封装成一个独立的模块,以便重复使用或提高代码的可读性和可维护性。函数调用的工作过程可以分为以下几个步骤: 1、函数声明(…

Slack端到端测试管道优化:构建时间减半的技术实践

本文详细介绍了Slack开发体验团队如何通过条件性前端构建和预构建资产缓存策略,将端到端测试管道的构建时间从10分钟缩短至2分钟,显著提升开发效率并降低云存储成本。在 DevOps 和开发者体验(DevXP)领域,速度和效…

结构体与联合体的区别

1.内存分配方式 结构体为每一个结构体的成员分配独立的内存空间;总内存为所有成员大小之和。 联合体的成员共享同一片内存空间,总内存大小为最大成员的大小。 2.成员访问特性 结构体可以同时访问所有的成员 联合体同…

Day14综合案例二--

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">…

解决colcon编译卡死

下面这两个命令我这里不起作用:colcon build --parallel-workers 6 colcon build --executor sequential下面命令可以解决卡死问题:MAKEFLAGS=-j4 colcon build

铁杆粉丝占比20251105

序号 代码 名称 最新价(2025.11.05) 涨跌幅(2025.11.05) 铁杆粉丝占比 涨跌额(2025.11.05) 最高价(日线不复权)(2025.11.05) 最低价(日线不复权)(2025.11.05) 换手率(2025.11.05) 量比(2025.11.05) 成交量(股)(2025.11…

Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么? - Higurashi

基于:Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么?在 MyBatis 中,Executor 是一个关键的接口,负责执行映射的 SQL 语句。MyBatis 提供了四种类型的 Executor,每种 Executor 在执行 SQL 和处理事务方…

100小时学会SAP—问题10:ME51N提示物料XX的强制账户设置(输入账户设置类别)

100小时学会SAP—问题10:ME51N提示物料XX的强制账户设置(输入账户设置类别)执行事务码ME51N时,报错如下:物料XXXXXX的强制账户设置(输入账户设置类别)解决方法:SPRO-后勤常规-物料主数据-基本设置-物料类型-定义…

P8990 [北大集训 2021] 小明的树 题解

QwQP8990 [北大集训 2021] 小明的树 题解 首先刻画 “美丽”,考虑灭点,由于 1 始终灭,所以形如一个包含 1 的连通块,灭点连通块数量要为 1。 因为要刻画连通块数量,考虑点减边容斥,即连通块个数是灭点个数减 “灭…

100小时学会SAP—问题11:MIGO收货时报错不可能为条目BSX CN01确立账户

100小时学会SAP—问题11:MIGO收货时报错不可能为条目BSX CN01确立账户执行事务码MIGO收货时,报错如下:不可能为条目BSX CN01确立账户。解决方法: 1、首先按照100小时学会SAP步骤配置(在用MMR1、MMF1事务码创建物料…

【动态维护前 x 大元素】LeetCode 3321. 计算子数组的 x-sum II

View Post【动态维护前 x 大元素】LeetCode 3321. 计算子数组的 x-sum II题目 https://leetcode.cn/problems/find-x-sum-of-all-k-long-subarrays-ii/description/ 题解 定义两个有序集合 \(L, R\) 动态维护数组前 \(…

100小时学会SAP—问题8:财务凭证行项目BSEG及对应的六张表

100小时学会SAP—问题8:财务凭证行项目BSEG及对应的六张表FI会计模块中输入一笔业务将会记一个财务凭证,SAP系统中将凭证信息分为抬头信息和明细信息两部分加以存储。其中抬头(Header)信息存储在透明表BKPF中,明细(…

100小时学会SAP—问题9:MD03提示日期在有效工厂日历之后(请改正)

100小时学会SAP—问题9:MD03提示日期在有效工厂日历之后(请改正)执行事务码MD03时,报错如下:提示日期在有效工厂日历之后(请改正)解决方法:这是因为在创建工厂数据的时候工厂的日期没有更改造成的。 路径为:SP…

100小时学会SAP—问题6:创建采购收货时出现WE在年2025中编号不存在

100小时学会SAP—问题6:创建采购收货时出现WE在年2025中编号不存在前台 后勤—物料管理—库存管理—货物移动—收货—对采购订单—采购订单GR(MIGO) 报了如下错误:有关业务/事件类型WE在年2025的号码范围不存在解决…

100小时学会SAP—问题7:FB70提示过账码没有定义

100小时学会SAP—问题7:FB70提示过账码没有定义执行事务码FB70时,报错如下:Posting keys not defined原因:在文档“0528—FB70运行时提示在表T030B中AGD输入丢失”中,解决方法错误,其中的PK码自己瞎填,导致错误…

树剖

接dfs序。 https://www.cnblogs.com/ybjnb/p/19089551 树剖 (dfs序的性质依旧满足 即子树也是一段连续区间)将一颗树转化为一个序列 将树上任意一条路径转化成 log(n) 段连续区间 然后就可以用序列数据结构维护信息。…