基于matlab支持向量机SVM多分类手写体数字识别

此程序为本人模式识别大作业,参考了网上的代码,并进行了一定的修改,希望对大家有所帮助!

此代码主要参考了以下文章:

https://blog.csdn.net/Einperson/article/details/106769799?spm=1001.2014.3001.5506

前面的大部分我都是以这篇文章为基础的,我在此写一些注释:

  • 下载安装并配置LIBSVM工具箱、LIBSVM-FarutoUitimate工具箱,去B站找UP 中山大学王伟良  ,up有安装视频教程。up的视频里还有个SVM代码,挺详细的,真的很良心!!!

代码:

  1. 预处理函数设计

(1)先对手写体图像进行反色处理(即黑变白、白变黑),

(2)将此时的图像进行二值化处理(即将黑色变为1,白色变为0,也可能是相反的,这不重要,我偷个懒),

(3)是搜索整个图片中数字出现的位置(类似于图片裁剪,只留下手写字体的部分,可以大大降低图片数据量)

(4)将图片进行缩小,进一步减少数据量

%% sub function of pre-processing pic
function pic_preprocess = pic_preprocess(pic)
% 图片预处理子函数
%三原色图像反色处理
pic = 255-pic;
%设定阈值,将反色图像转成二值图像
pic = im2bw(pic,0.4) ;
%查找数字上所有像素点的行标y和列标x
[y,x] = find(pic == 1);
%截取包含完整数字的最小区域
pic_preprocess = pic (min(y):max(y),min(x):max(x));
%将截取的包含完整数字的最小区域图像转成20*20的标准化图像
pic_preprocess = imresize(pic_preprocess, [20, 20]);

2.主函数编写

2.1.载入数据

(1)uigetfile函数可以弹出一个窗口用于选择训练样本,运行程序后,可自动出现

(2)后面内容代码注释解释的很清楚,我不再多讲

注:FileName{k}(n)函数可用于提取训练集图片名称上第n位符号,因为我所用到的训练图片格式img001.png这种,所以取其第6位,大家要按照自己实际的情况进行改动。

%% 载入训练数据
%利用uigetfile函数交互式选取训练样本  ...是换行再度
[FileName,PathName,FilterIndex]=uigetfile(...
    {'*.png';'*.bmp'},'请导入训练图片','*.png','MultiSelect','on');
if ~FilterIndex   %如果用户取消,则返回上一步
    return;
end
num_train=length(FileName);    % Filename是一个字符串数组  图片的个数
TrainData=zeros(num_train,20*20);  %生成训练样本,样本维度为20*20,由pic_preprocess函数决定
TrainLabel=zeros(num_train,1);   %生成训练样本标签
for k=1:num_train
    pic=imread([PathName,FileName{k}]);%逐个读入图片
    %pic是1200*900*3的数组,其中1200*900是图片的原始分辨率,3是三原色
    pic=pic_preprocess(pic);      %将一幅图转为20*20个二进制数
    TrainData(k,:)=double(pic(:)');     %单引号是将pic转一维数组,存放在TrainData的第K行
    if str2double(FileName{k}(5))~=0     %如img010(即数字“9”的文件名)
        TrainLabel(k)=str2double(FileName{k}(5))*10+str2double(FileName{k}(6))-1; 
    else      %其他文件,如img001是数字“0”的文件名
    TrainLabel(k)=str2double(FileName{k}(6))-1;    %文件名中第6个字符是该图片的数字
    end
end

2.2. 建立支持向量机!!!

之前参考的代码用到了gaSVMcgForClass函数,但是这个函数需要设置GA相关参数,本人小白一个,搞不懂这些东西就放弃了这个函数。

也有代码用到了svmtrain函数,由于本人的matlab为2019b,已经将此函数删除建议我用fitsvm。之后我又去查找fitsvm,之后发现它只能实现二分类,多分类问题只能通过建立多个两两二分类(建立二分之m方个,m为类别数)。对于手写体数字识别问题,就要50个实在是太麻烦了。

之后我又找到了fitcecoc函数,此函数可以用来实现SVM多分类问题,真的超级nice!找到关于这个函数的例子还是通过mathwork搜索得来的,其中举得例子就是分类iris鸢尾花、ionosphere电离层数据,这里我直接照搬了他们的代码,,,本人小白,真的不太会这里,不知道怎么改可以,只是发现它可以运行成功,并且分类效果不错就直接使用了

(1)templateSVM函数可以建立一个SVM模板函数,其参数值为默认值,试了下其默认核函数应该为线性核函数,想要具体了解可mathwork自行搜索。

(2)fitcecoc函数(训练集,训练标签,'Learners',t,'ClassNames',{这里填你要分类的那几种标签名,按顺序噢});关于fitcecoc函数可自行查阅mathwork进行学习。

通过查找资料发现这里训练的是ECOC模型,具体我也不懂,感兴趣的朋友可以自行学习。

(3)使用crossval函数可以实现10折交叉验证(10折交叉验证:将数据集分成平均分成互斥的十份,轮流将其中9份做训练1份做验证,10次的结果的均值作为对算法精度的估计,一般还需要进行多次10折交叉验证求均值),以测试算法准确性。

SVM——n倍交叉验证:

https://blog.csdn.net/weixin_46345400/article/details/124358046?spm=1001.2014.3001.5506

(4)通过kfoldLoss函数得到估计广义分类误差,误差越小说明分类器的泛化(泛化是指训练好的模型在前所未见的数据上的性能好坏)效果越好。

深入理解泛化:

https://blog.csdn.net/sc2079/article/details/103090727?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%B3%9B%E5%8C%96&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-103090727.142^v10^pc_search_result_control_group,157^v8^control&spm=1018.2226.3001.4187

%% 建立支持向量机
t = templateSVM('Standardize',1,'KernelFunction','linear'); %创建SVM模板t;
%templateSVM是fitcecoc函数中的SVM模板;
%standardize:数据标准化,可用help查看templateSVM其他参数
%训练该模型
Mdl = fitcecoc(TrainData,TrainLabel,'Learners',t,'ClassNames',{'0','1','2','3','4','5','6','7','8','9'});   
%验证该模型
CVMdl = crossval(Mdl);  %将模型进行交叉验证,平衡模型欠拟合和过拟合
%显示结果
oosLoss = kfoldLoss(CVMdl)  %10折交叉验证得到的泛化误差 oosloss = 0.0750

 2.3.载入测试样本

%% 载入测试样本
[FileName,PathName,FilterIndex]=uigetfile( ...
    {'*.png';'*.bmp'},'请导入测试图片','*.png','MultiSelect','on');
if ~FilterIndex %如果用户取消,则返回上一步
    return;
end
num_test=length(FileName);        %Filename是一个字符串数组
TestData=zeros(num_test,20*20);   %生成测试样本矩阵,维度为400
TestLabel=zeros(num_test,1);      %生成测试样本标签矩阵
for k=1:num_test
    pic=imread([PathName,FileName{k}]);   %逐个读图
    pic=pic_preprocess(pic);              %将一幅图转为20*20个二进制数
    TestData(k,:)=double(pic(:)');        %单引号是将pic转一维数组,存放在TrainData的第K行
    if str2double(FileName{k}(5))~=0      %如img010(即数字“9”的文件名)
    TestLabel(k)=str2double(FileName{k}(5))*10+str2double(FileName{k}(6))-1;
    else
    TestLabel(k)=str2double(FileName{k}(6))-1;  %文件名中第6个字符是该图片的数字
    end
end

2.4.提取Test测试集中图片名称(这一步是为了后面显示判断错误的图片名称,不需要可以删掉)

%% 对Test图片名称提取
fileFolder=fullfile('C:\Users\Test'); %引号内是需要遍历的路径,填绝对路径,然后保存在fileFolder
dirOutput=dir(fullfile(fileFolder,'*.png'));      %引号内是文件的后缀,写'.png'则读取后缀为'.png'的文件
fileNames={dirOutput.name};         %将所有文件名,以矩阵形式按行排列,保存到fileNames中

 2.5.对测试样本进行分类

predict函数的使用可以参考mathwork上官方解释,这里就不赘述了。

preTestLabel是通过SVM模型产生的预测标签,为cell型,如果要判断正确率就要与double型TestLabel进行比较,但是cell型不能直接与double型作比较,这里就是使用了preTestLabel=str2num(cell2mat(preTestLabel));来转换。

%% 对测试样本进行分类
preTestLabel = predict(Mdl,TestData);
preTestLabel=str2num(cell2mat(preTestLabel));   %将cell型preTestLabel转为double型,以供后面与测试样本标签进行比较
right =0;      %正确数量
k=0;           %错误数量
for i=1:numel(TestLabel)  
   if TestLabel(i)==preTestLabel(i)    %统计正确数量
       right =right+1;
   else
       k=k+1;                          
       error_name(k)=fileNames(i);      %记录错误文件名
   end
end
test_number=numel(TestLabel)   
right
accuracy =right/numel(TestLabel)   %测试样本正确率     %accuracy = 0.9333
error_name                   

结果:

对于SVM模型的建立那里本人还不是很明白应该怎么做,如果有比较了解的朋友欢迎评论区里讲解一下,非常感谢!!

  • 4
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值