基于神经网络的手写数字识别系统

基于神经网络的手写数字识别系统

结合模板匹配和神经网络两种方法进行手写数字识别。这个系统包括图像预处理、特征提取、神经网络训练和可视化分析。

%% 基于神经网络的手写数字识别系统%% 清理工作区
clear; clc; close all;%% 加载手写数字数据集
% 使用MATLAB自带的手写数字数据集
digitDatasetPath = fullfile(toolboxdir('nnet'), 'nndemos', 'nndatasets', 'DigitDataset');
imds = imageDatastore(digitDatasetPath, ...'IncludeSubfolders', true, ...'LabelSource', 'foldernames');% 显示数据集信息
disp(['数据集包含 ', num2str(numel(imds.Files)), ' 张图像']);
countEachLabel(imds)% 显示随机样本图像
figure('Name', '手写数字样本', 'Position', [100, 100, 800, 400]);
perm = randperm(numel(imds.Files), 20);
for i = 1:20subplot(4, 5, i);img = readimage(imds, perm(i));imshow(img);title(char(imds.Labels(perm(i))));
end
sgtitle('手写数字样本展示');%% 数据预处理
% 将图像调整为28x28像素并转换为灰度图
processedImages = zeros(28, 28, 1, numel(imds.Files));
for i = 1:numel(imds.Files)img = readimage(imds, i);% 转换为灰度图if size(img, 3) == 3img = rgb2gray(img);end% 调整大小img = imresize(img, [28, 28]);% 归一化处理 [0, 1]img = im2double(img);% 图像二值化img = imbinarize(img);% 存储处理后的图像processedImages(:, :, 1, i) = img;
end% 显示预处理后的图像
figure('Name', '预处理后的图像', 'Position', [100, 100, 800, 400]);
for i = 1:20subplot(4, 5, i);imshow(processedImages(:, :, 1, perm(i)));title(char(imds.Labels(perm(i))));
end
sgtitle('预处理后的手写数字');%% 创建模板匹配系统
% 为每个数字创建平均模板
templates = zeros(28, 28, 10);
digitCounts = zeros(1, 10);for i = 1:numel(imds.Files)digit = double(imds.Labels(i));templates(:, :, digit+1) = templates(:, :, digit+1) + processedImages(:, :, 1, i);digitCounts(digit+1) = digitCounts(digit+1) + 1;
end% 计算平均模板
for i = 1:10if digitCounts(i) > 0templates(:, :, i) = templates(:, :, i) / digitCounts(i);end
end% 显示模板
figure('Name', '数字模板', 'Position', [100, 100, 1000, 400]);
for i = 0:9subplot(2, 5, i+1);imshow(templates(:, :, i+1));title(['数字 ', num2str(i), ' 模板']);
end
sgtitle('模板匹配使用的数字模板');%% 模板匹配测试
% 测试模板匹配的准确率
numTest = 200; % 测试样本数量
testIndices = randperm(numel(imds.Files), numTest);
templateResults = zeros(1, numTest);
templateCorrect = 0;figure('Name', '模板匹配结果', 'Position', [100, 100, 1200, 600]);
colormap gray;for i = 1:min(20, numTest) % 只显示前20个结果idx = testIndices(i);testImg = processedImages(:, :, 1, idx);trueLabel = double(imds.Labels(idx));% 计算与每个模板的相似度(使用相关系数)correlations = zeros(1, 10);for digit = 0:9corrMatrix = corrcoef(testImg(:), templates(:, :, digit+1)(:));correlations(digit+1) = corrMatrix(1, 2);end% 选择最相似的数字[~, predLabel] = max(correlations);predLabel = predLabel - 1;% 记录结果templateResults(i) = (predLabel == trueLabel);if predLabel == trueLabeltemplateCorrect = templateCorrect + 1;end% 显示结果subplot(4, 5, i);imshow(testImg);if predLabel == trueLabeltitle(sprintf('True: %d\nPred: %d', trueLabel, predLabel), 'Color', 'g');elsetitle(sprintf('True: %d\nPred: %d', trueLabel, predLabel), 'Color', 'r');end
end% 计算准确率
templateAccuracy = templateCorrect / numTest;
fprintf('模板匹配准确率: %.2f%%\n', templateAccuracy * 100);
sgtitle(sprintf('模板匹配结果 (准确率: %.2f%%)', templateAccuracy*100));%% 准备神经网络数据
% 划分训练集和测试集 (70% 训练, 30% 测试)
[trainIdx, testIdx] = dividerand(numel(imds.Files), 0.7, 0.3);% 创建训练集
XTrain = processedImages(:, :, :, trainIdx);
YTrain = categorical(imds.Labels(trainIdx));% 创建测试集
XTest = processedImages(:, :, :, testIdx);
YTest = categorical(imds.Labels(testIdx));% 显示数据集大小
fprintf('训练集大小: %d\n', numel(trainIdx));
fprintf('测试集大小: %d\n', numel(testIdx));%% 构建神经网络模型
layers = [imageInputLayer([28 28 1], 'Name', 'input')convolution2dLayer(5, 32, 'Padding', 'same', 'Name', 'conv1')batchNormalizationLayer('Name', 'bn1')reluLayer('Name', 'relu1')maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool1')convolution2dLayer(3, 64, 'Padding', 'same', 'Name', 'conv2')batchNormalizationLayer('Name', 'bn2')reluLayer('Name', 'relu2')maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool2')fullyConnectedLayer(128, 'Name', 'fc1')reluLayer('Name', 'relu3')dropoutLayer(0.4, 'Name', 'dropout')fullyConnectedLayer(10, 'Name', 'fc2')softmaxLayer('Name', 'softmax')classificationLayer('Name', 'output')
];% 可视化网络结构
figure('Name', '神经网络结构');
plot(layerGraph(layers));
title('卷积神经网络结构');%% 设置训练选项
options = trainingOptions('adam', ...'InitialLearnRate', 0.001, ...'MaxEpochs', 15, ...'MiniBatchSize', 128, ...'Shuffle', 'every-epoch', ...'ValidationData', {XTest, YTest}, ...'ValidationFrequency', 30, ...'Verbose', true, ...'Plots', 'training-progress', ...'ExecutionEnvironment', 'auto');%% 训练神经网络
disp('开始训练神经网络...');
net = trainNetwork(XTrain, YTrain, layers, options);
disp('神经网络训练完成!');%% 评估神经网络性能
% 在整个测试集上进行预测
YPred = classify(net, XTest);% 计算准确率
accuracy = sum(YPred == YTest) / numel(YTest);
fprintf('神经网络测试准确率: %.2f%%\n', accuracy * 100);% 混淆矩阵
figure('Name', '混淆矩阵', 'Position', [100, 100, 800, 700]);
cm = confusionmat(YTest, YPred);
confusionchart(cm, categories(YTest));
title(sprintf('混淆矩阵 (准确率: %.2f%%)', accuracy*100));%% 可视化神经网络预测结果
% 选择一些样本进行可视化
numSamplesToShow = 20;
testSampleIndices = randperm(numel(testIdx), numSamplesToShow);figure('Name', '神经网络预测结果', 'Position', [100, 100, 1200, 600]);
colormap gray;for i = 1:numSamplesToShowidx = testIdx(testSampleIndices(i));img = processedImages(:, :, 1, idx);trueLabel = char(imds.Labels(idx));predLabel = char(YPred(testSampleIndices(i)));subplot(4, 5, i);imshow(img);if strcmp(trueLabel, predLabel)title(sprintf('True: %s\nPred: %s', trueLabel, predLabel), 'Color', 'g');elsetitle(sprintf('True: %s\nPred: %s', trueLabel, predLabel), 'Color', 'r');end
end
sgtitle(sprintf('神经网络预测结果 (准确率: %.2f%%)', accuracy*100));%% 特征可视化
% 提取卷积层的激活
conv1Activations = activations(net, XTest, 'conv1');
conv2Activations = activations(net, XTest, 'conv2');% 显示卷积层特征图
sampleIdx = testSampleIndices(1); % 使用第一个测试样本
sampleImg = XTest(:, :, :, sampleIdx);figure('Name', '卷积层特征可视化', 'Position', [100, 100, 1200, 800]);% 原始图像
subplot(3, 1, 1);
imshow(sampleImg);
title('原始图像');% 第一卷积层的特征图
subplot(3, 1, 2);
montage(reshape(conv1Activations(:, :, :, sampleIdx), [28, 28]));
title('第一卷积层特征图');% 第二卷积层的特征图
subplot(3, 1, 3);
montage(reshape(conv2Activations(:, :, :, sampleIdx), [14, 14]));
title('第二卷积层特征图');%% 手写数字识别演示
% 创建一个简单的绘图界面,让用户手写数字
f = figure('Name', '手写数字识别演示', 'Position', [200, 200, 600, 500]);
ax = axes('Parent', f, 'Position', [0.1, 0.2, 0.8, 0.7]);
title('在下方区域手写一个数字');% 创建绘图区域
drawingArea = uicontrol('Style', 'text', 'Position', [60, 100, 280, 280], ...'BackgroundColor', 'white');
axes('Position', [0.1, 0.2, 0.8, 0.7]);% 初始化绘图数据
drawing = false;
lastPoint = [0, 0];
imgData = ones(280, 280) * 255; % 白色背景% 鼠标回调函数
set(gcf, 'WindowButtonDownFcn', @startDrawing);
set(gcf, 'WindowButtonUpFcn', @stopDrawing);
set(gcf, 'WindowButtonMotionFcn', @draw);% 创建按钮
uicontrol('Style', 'pushbutton', 'String', '识别', ...'Position', [100, 50, 100, 30], ...'Callback', @recognizeDigit);uicontrol('Style', 'pushbutton', 'String', '清除', ...'Position', [220, 50, 100, 30], ...'Callback', @clearDrawing);% 结果显示区域
resultText = uicontrol('Style', 'text', 'String', '结果将显示在这里', ...'Position', [100, 20, 200, 20], ...'FontSize', 12, 'FontWeight', 'bold');%% 绘图回调函数
function startDrawing(~, ~)drawing = true;
endfunction stopDrawing(~, ~)drawing = false;lastPoint = [0, 0];
endfunction draw(~, ~)if drawingcurrentPoint = get(gca, 'CurrentPoint');x = round(currentPoint(1, 1));y = round(currentPoint(1, 2));% 确保坐标在绘图区域内if x >= 1 && x <= 280 && y >= 1 && y <= 280if lastPoint(1) > 0 && lastPoint(2) > 0% 在两点之间画线lineX = linspace(lastPoint(1), x, 50);lineY = linspace(lastPoint(2), y, 50);for k = 1:50px = round(lineX(k));py = round(lineY(k));if px >= 1 && px <= 280 && py >= 1 && py <= 280% 绘制粗线for i = -2:2for j = -2:2if px+i > 0 && px+i <= 280 && py+j > 0 && py+j <= 280imgData(py+j, px+i) = 0; % 黑色endendendendendend% 更新图像imshow(imgData, 'Parent', gca);lastPoint = [x, y];endend
endfunction recognizeDigit(~, ~)% 预处理用户绘制的图像userImg = imresize(imgData, [28, 28]);userImg = imcomplement(userImg); % 反转为黑底白字userImg = im2double(userImg);% 使用神经网络进行预测[predLabel, scores] = classify(net, userImg);% 显示结果set(resultText, 'String', sprintf('识别结果: %s (置信度: %.2f%%)', char(predLabel), max(scores)*100));% 显示处理后的图像figure('Name', '预处理后的手写数字');subplot(1, 2, 1);imshow(imcomplement(imgData)); % 原始手写图像title('用户手写数字');subplot(1, 2, 2);imshow(userImg);title('预处理后的图像');
endfunction clearDrawing(~, ~)imgData = ones(280, 280) * 255; % 重置为白色背景imshow(imgData, 'Parent', gca);set(resultText, 'String', '结果将显示在这里');
end

系统功能与实现详解

1. 系统架构

本系统包含三个主要模块:

  • 模板匹配模块:创建数字模板并进行匹配识别
  • 神经网络模块:构建并训练卷积神经网络
  • 交互演示模块:允许用户手写数字进行实时识别

2. 数据处理流程

  1. 数据加载

    • 使用MATLAB自带的手写数字数据集
    • 包含0-9共10类数字图像
  2. 图像预处理

    % 转换为灰度图
    img = rgb2gray(img);% 调整大小为28x28像素
    img = imresize(img, [28, 28]);% 归一化处理
    img = im2double(img);% 图像二值化
    img = imbinarize(img);
    
  3. 模板创建

    • 对每个数字类别的图像求平均
    • 生成0-9的数字模板

3. 模板匹配算法

% 计算与每个模板的相关系数
correlations = zeros(1, 10);
for digit = 0:9corrMatrix = corrcoef(testImg(:), templates(:, :, digit+1)(:));correlations(digit+1) = corrMatrix(1, 2);
end% 选择最相似的数字
[~, predLabel] = max(correlations);

参考源码 手写体识别 模板匹配识别方法 youwenfan.com/contentcsa/78091.html

4. 神经网络架构

本系统使用了一个高效的卷积神经网络结构:

层类型参数设置输出尺寸
输入层28x28x1图像28x28x1
卷积层15x5核, 32个滤波器28x28x32
批量归一化层1-28x28x32
ReLU激活层1-28x28x32
最大池化层12x2池化, 步长214x14x32
卷积层23x3核, 64个滤波器14x14x64
批量归一化层2-14x14x64
ReLU激活层2-14x14x64
最大池化层22x2池化, 步长27x7x64
全连接层1128个神经元128
ReLU激活层3-128
Dropout层丢弃率40%128
全连接层210个神经元10
Softmax层-10
分类层--

5. 训练配置

options = trainingOptions('adam', ...'InitialLearnRate', 0.001, ...'MaxEpochs', 15, ...'MiniBatchSize', 128, ...'ValidationData', {XTest, YTest}, ...'Plots', 'training-progress');

6. 性能比较

方法准确率优点缺点
模板匹配75-85%实现简单,计算快速对形变和旋转敏感
神经网络98-99%鲁棒性强,识别精度高需要大量数据和训练时间

7. 交互演示功能

系统提供了一个绘图界面:

  1. 用户在白色画布上手写数字
  2. 点击"识别"按钮进行预测
  3. 点击"清除"按钮重置画布
  4. 显示识别结果和置信度

关键技术与创新点

  1. 多方法融合

    • 同时实现模板匹配和神经网络两种方法
    • 提供性能对比分析
  2. 特征可视化

    • 展示卷积层提取的特征图
    • 帮助理解神经网络工作原理
  3. 交互式界面

    • 实时手写识别演示
    • 显示预处理过程和识别结果
  4. 全面的评估

    • 混淆矩阵分析
    • 错误分类可视化
    • 准确率对比

系统扩展建议

  1. 数据增强

    % 添加旋转、平移、缩放等变换
    augmenter = imageDataAugmenter(...'RandRotation', [-15, 15], ...'RandXTranslation', [-3, 3], ...'RandYTranslation', [-3, 3]);
    
  2. 迁移学习

    % 使用预训练的ResNet或MobileNet
    net = resnet50;
    lgraph = layerGraph(net);
    
  3. 模型优化

    • 添加注意力机制
    • 尝试不同的网络架构
    • 使用贝叶斯优化调整超参数
  4. 实时视频识别

    • 集成摄像头输入
    • 实现实时手写数字识别
  5. 移动端部署

    • 使用MATLAB Coder生成C++代码
    • 部署到移动设备或嵌入式系统

这个系统全面展示了手写数字识别的关键技术和实现方法,通过交互式界面增强了用户体验,适用于教育演示和实际应用开发。

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

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

相关文章

机器学习?一文看懂这门热门技术

&#x1f31f; 什么是机器学习&#xff1f;一文看懂这门热门技术在人工智能&#xff08;AI&#xff09;的大潮中&#xff0c;机器学习&#xff08;Machine Learning, ML&#xff09; 无疑是最耀眼的明星之一。它让计算机具备了 “自我学习” 的能力&#xff0c;让自动驾驶、智能…

Spring的初始化钩子

1. PostConstruct JSR-250 标准注解&#xff08;不是 Spring 独有&#xff09;&#xff0c;用来标记 Bean 初始化完成后要执行的方法。会在 Bean 的构造方法执行完、依赖注入完成后执行。 使用实例&#xff1a; Component public class Demo {PostConstructpublic void init() …

【AI】Java生态对接大语言模型:主流框架深度解析

文章目录1. Deep Java Library (DJL)2. LangChain4j&#xff08;LLM&#xff09;3. HuggingFace Inference API4. OpenAI Java Client技术对比矩阵架构设计建议在人工智能浪潮下&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为技术核心。Java生态通过以下框架实现高效…

【06】C#入门到精通——C# 多个 .cs文件项目 同一项目下添加多个 .cs文件

文章目录1 单个 .cs文件2 创建 多个 .cs文件2.1 添加Hero类2.1 添加ShowInfo类2.3 关于命名空间的引用2.4 所有.cs文件代码3 test3项目文件下载1 单个 .cs文件 上一讲中 描述游戏中英雄的角色 所有代码在一个.cs文件中&#xff0c; 如果代码很多&#xff0c;类很多&#xff0…

【MySQL基础篇】:MySQL常用数据类型的选择逻辑与正确使用

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;MySQL篇–CSDN博客 文章目录数据类型1.数据类型分类2.数值类型int整形类型bit位类型float小…

三、搭建springCloudAlibaba2021.1版本分布式微服务-springcloud loadbalancer负载均衡

什么是负责均衡 Spring Cloud LoadBalancer是一个客户端负载均衡器&#xff0c;类似于Ribbon&#xff0c;但是由于Ribbon已经进入维护模式&#xff0c;并且Ribbon 2并不与Ribbon 1相互兼容&#xff0c;所以Spring Cloud全家桶在Spring Cloud Commons项目中&#xff0c;添加了Sp…

Oracle不完全恢复实战指南:从原理到操作详解

核心提示&#xff1a;当误删表、日志损坏或控制文件丢失时&#xff0c;Oracle的不完全恢复是DBA最后的救命稻草。掌握关键恢复技术&#xff0c;可在数据灾难中力挽狂澜。一、不完全恢复核心概念 1. 核心特点 必须关闭数据库&#xff1a;在MOUNT状态下执行重做日志恢复权限要求&…

Linux之shell脚本篇(二)

一、shell编程之if语句引言Linux在shell编程中&#xff0c;通常都是以自上而下运行&#xff0c;但是为了提高其代码严谨性&#xff0c;我们即引入了多条件 控制语句例如&#xff1a;if、for、while、case等语句&#xff0c;有时候针对条件我们还会结合正则表达式去运用。将这些…

如何在android framewrok dump camera data

实现dump 函数 实现1 void dumpBufferToFile(buffer_handle_t* buffer, int width, int height, int frameNum) {void* data NULL;GraphicBufferMapper::getInstance().lock(*buffer, GRALLOC_USAGE_SW_READ_OFTEN, Rect(width, height), &data);char filename[128];sprin…

机器学习中的可解释性:深入理解SHAP值及其应用

机器学习可解释性的重要性在人工智能技术快速发展的2025年&#xff0c;机器学习模型已经深度渗透到医疗诊断、金融风控、司法量刑等关键领域。然而&#xff0c;随着模型复杂度的不断提升&#xff0c;一个根本性矛盾日益凸显&#xff1a;模型预测性能的提升往往以牺牲可解释性为…

.NET9 使用 OData 协议项目实战

.NET 中 ODate 协议介绍 OData(Open Data Protocol) 是一个开放的 Web 协议&#xff0c;用于查询和更新数据。在 .NET 生态系统中&#xff0c;OData 被广泛支持和使用。 主要特性 1. 统一的数据访问方式 提供标准化的查询语法支持 CRUD 操作支持元数据描述 2. 查询能力 标…

Android 性能优化:提升应用启动速度(GC抑制)

前言 在移动应用开发领域&#xff0c;启动速度是用户体验的重要指标。对于Android应用而言&#xff0c;垃圾回收&#xff08;Garbage Collection, GC&#xff09;机制虽然是内存管理的核心&#xff0c;但在应用启动期间频繁触发GC会显著拖慢启动速度。本文将深入探讨如何通过GC…

做了一款小而美的本地校验器

需求说明 前阵子收到一则读者留言&#xff0c;指出&#xff1a;市面上AI核稿工具&#xff08;ProWritingAid&#xff0c;WPS AI Spell Check&#xff0c;Writer&#xff0c;QuillBot&#xff0c;Grammarly&#xff09;要么收费太高&#xff0c;要么让人担心文章泄露。 如下图所…

uniapp + uview-plus 微信小程序二维码生成和保存完整解决方案

uniapp + uview-plus 微信小程序二维码生成和保存完整解决方案 📋 项目背景 在开发微信小程序时,经常需要实现二维码的生成和保存功能。本文档提供了一个基于 uniapp + uview-plus 框架的完整解决方案,彻底解决了以下常见问题: ✅ Canvas API 兼容性问题 ✅ 微信小程序权…

Linux中应用程序的安装于管理

Linux中应用程序的安装于管理 一 . rpm安装 1.挂载 光驱里面存放了很多rpm的软件包 光驱在系统中使用时&#xff0c;需要挂载 mount /dev/cdrom /mnt/ cd /mnt[rootstw mnt]# ls CentOS_BuildTag GPL LiveOS RPM-GPG-KEY-CentOS-7 EFI images Packag…

mysql重置密码

要区分 MySQL 是通过 systemd 还是传统 service 管理&#xff0c;以及对应的密码重置方案&#xff0c;可按以下步骤操作&#xff1a; 一、如何区分管理方式&#xff08;systemd 还是传统 service&#xff09; 通过以下命令判断系统默认的服务管理方式&#xff1a;检查系统是否使…

C++ TAP(基于任务的异步编程模式)

&#x1f680; C TAP&#xff08;基于任务的异步编程模式&#xff09;1. 引言&#xff1a;走进异步编程新时代&#xff08;&#x1f680;&#xff09; 在当今高性能计算领域&#xff0c;同步编程模型的局限性日益凸显。传统的回调地狱和线程管理复杂性促使微软提出了基于任务的…

利用C++手撕栈与队列的基本功能(四)

栈和队列详细教程可以观看 https://www.bilibili.com/video/BV1nJ411V7bd?spm_id_from333.788.videopod.episodes&vd_sourcedaed5b8a51d3ab7eb209efa9d0ff9a34&p48栈和队列概念 栈和队列是限定插入和删除只能在表的端点进行的线性表在装电池、装弹夹、拿放盘子时都会出…

net8.0一键创建支持(Redis)

Necore项目生成器 - 在线创建Necore模板项目 | 一键下载 RedisController.cs using CSRedis; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using UnT.Template.Application.Responses; using UnT.Template.Domain;namespace UnT.Template.Controllers {…

Leetcode——42. 接雨水

还记得第一次见该题根本无从下手。其实&#xff0c;我们不妨把问题拆解&#xff0c;简单化。不要怕自己写的是暴力算法&#xff0c;有很多算法技巧其实就是在暴力算法的基础上优化得来。题目目的是求所有可接雨水数量&#xff0c;我们可以求出每一个位置可接雨水数量&#xff0…