编写C语言版本的卷积神经网络CNN之一:前言与Minst数据集

原创文章
转载请注册来源 http://blog.csdn.net/tostq

前言
       卷积神经网络是深度学习的基础,但是学习CNN却不是那么简单,虽然网络上关于CNN的相关代码很多,比较经典的是tiny_cnn(C++)、DeepLearnToolbox(Matlab)等等,但通过C语言来编写CNN的却比较少,本人因为想在多核DSP下运行CNN,所以便尝试通过C语言来编写,主要参考的代码是DeepLearnToolbox的内容,DeepLearnToolbox是用Matlab脚本编写,是我看过的最为简单的CNN代码,代码清晰,阅读方便,非常适合新手入门学习。
       本文的CNN代码是一个最基本的卷积网络,主要用于手写数字的识别,选择的训练测试是数据库是Minst手写数字库, 主要是包括了一个基本的多层卷积网络框架、卷积层、Pooling层、及全连接的单层神经网络输出层,不过CNN其他重要的概念如Dropout、ReLu等暂时没有涉及,但是个人对于新手,学习卷积网络的基本结构及其误差反向传播方法是完全足够。
       这里要注意的是,本文的方法并不是深度学习之父Yann. LeCun在1998年就已经提出的成熟算法LeNet-5卷积网络,而只是DeepLearnToolbox内的cnn代码的c语言实现,不过我们会比较二者之间的区别,因为二者的基本原理是相似的。另外,为了不使博客篇幅过长,所以博客中贴的代码并不完整,完整代码请见附件。
       这篇博客总共分为四节:
       第一节:前言,介绍项目结构及Minst数据集测试训练数据集
       第二节:主要介绍CNN的网络结构、相关数据结构
       第三节:重点介绍CNN学习训练过程的误差反向传播方法,采用的是 在线训练方式
       第四节:CNN的学习及测试结果的比较

论文参考文献:
Y. LeCun, L. Bottou, Y. Bengio and P. Haffner:   Gradient-Based Learning Applied to Document Recognition, Proceedings of the IEEE, 86(11):2278-2324, November 1998

一、代码结构
       本文的CNN代码是通过标准C编写,也不需要调用任何三方库文件,附件共享的文件是通过VS2010编译的项目文件(这里虽然是.cpp文件,但实际上完全是用C编写的,直接改成.c文件是完全可以使用的),当然也可以直接将相关的源文件导入到其他IDE上,也是能够运行的。
       文件结构:
       cnn.cpp cnn.h 存在关于CNN网络的函数、网络结构等
       minst.cpp minst.h 处理Minst数据库的函数、数据结构等
       mat.cpp mat.h 一些关于矩阵的函数,如卷积操作、180度翻转操作等
       main.cpp 主函数和测试函数

二、MINST数据库
       MINST数据库是由Yann提供的手写数字数据库文件,其官方下载地址 http://yann.lecun.com/exdb/mnist/
       这个里面还包含了对这个数据库进行识别的各类算法的结果比较及相关算法的论文

       数据库的里的图像都是28*28大小的灰度图像,每个像素的是一个八位字节(0~255)

       这个数据库主要包含了60000张的训练图像和10000张的测试图像,主要是下面的四个文件

       上述四个文件直接解压就可以使用了,虽然数据未压缩,但是我们还是需要将图像提取出来,方便我们进行操作
(1)存储图像数据的相关数据结构:
       单张图像结构及保存图像的链表
       
       图像实际数字标号(0~9),数据库里是用一个八位字节来表示,不过为了方便学习,需要用10位来表示。
       这里的10位表示网络输出层的10个神经元,某位为1表示数字标号即为该位。
       
(2) 读入图像数据 的相关函数
ImgArr read_Img(const char* filename) // 读入图像
{
    FILE  *fp=NULL;
    fp=fopen(filename,"rb");
    if(fp==NULL)
        printf("open file failed\n");
    assert(fp);

    int magic_number = 0;  
    int number_of_images = 0;  
    int n_rows = 0;  
    int n_cols = 0;  
    //从文件中读取sizeof(magic_number) 个字符到 &magic_number  
    fread((char*)&magic_number,sizeof(magic_number),1,fp); 
    magic_number = ReverseInt(magic_number);  
    //获取训练或测试image的个数number_of_images 
    fread((char*)&number_of_images,sizeof(number_of_images),1,fp);  
    number_of_images = ReverseInt(number_of_images);    
    //获取训练或测试图像的高度Heigh  
    fread((char*)&n_rows,sizeof(n_rows),1,fp); 
    n_rows = ReverseInt(n_rows);                  
    //获取训练或测试图像的宽度Width  
    fread((char*)&n_cols,sizeof(n_cols),1,fp); 
    n_cols = ReverseInt(n_cols);  
    //获取第i幅图像,保存到vec中 
    int i,r,c;

    // 图像数组的初始化
    ImgArr imgarr=(ImgArr)malloc(sizeof(MinstImgArr));
    imgarr->ImgNum=number_of_images;
    imgarr->ImgPtr=(MinstImg*)malloc(number_of_images*sizeof(MinstImg));

    for(i = 0; i < number_of_images; ++i)  
    {  
        imgarr->ImgPtr[i].r=n_rows;
        imgarr->ImgPtr[i].c=n_cols;
        imgarr->ImgPtr[i].ImgData=(float**)malloc(n_rows*sizeof(float*));
        for(r = 0; r < n_rows; ++r)      
        {
            imgarr->ImgPtr[i].ImgData[r]=(float*)malloc(n_cols*sizeof(float));
            for(c = 0; c < n_cols; ++c)
            { 
                // 因为神经网络用float型计算更为精确,这里我们将图像像素转为浮点型
                unsigned char temp = 0;  
                fread((char*) &temp, sizeof(temp),1,fp); 
                imgarr->ImgPtr[i].ImgData[r][c]=(float)temp/255.0;
            }  
        }    
    }

    fclose(fp);
    return imgarr;
}

(3)读入图像数据标号
LabelArr read_Lable(const char* filename)// 读入图像
{
    FILE  *fp=NULL;
    fp=fopen(filename,"rb");
    if(fp==NULL)
        printf("open file failed\n");
    assert(fp);

    int magic_number = 0;  
    int number_of_labels = 0; 
    int label_long = 10;

    //从文件中读取sizeof(magic_number) 个字符到 &magic_number  
    fread((char*)&magic_number,sizeof(magic_number),1,fp); 
    magic_number = ReverseInt(magic_number);  
    //获取训练或测试image的个数number_of_images 
    fread((char*)&number_of_labels,sizeof(number_of_labels),1,fp);  
    number_of_labels = ReverseInt(number_of_labels);    

    int i,l;

    // 图像标记数组的初始化
    LabelArr labarr=(LabelArr)malloc(sizeof(MinstLabelArr));
    labarr->LabelNum=number_of_labels;
    labarr->LabelPtr=(MinstLabel*)malloc(number_of_labels*sizeof(MinstLabel));

    for(i = 0; i < number_of_labels; ++i)  
    {
        // 数据库内的图像标记是一位,这里将图像标记变成10位,10位中只有唯一一位为1,为1位即是图像标记  
        labarr->LabelPtr[i].l=10;
        labarr->LabelPtr[i].LabelData=(float*)calloc(label_long,sizeof(float));
        unsigned char temp = 0;  
        fread((char*) &temp, sizeof(temp),1,fp); 
        labarr->LabelPtr[i].LabelData[(int)temp]=1.0;    
    }

    fclose(fp);
    return labarr;    
}

 项目代码地址: https://github.com/tostq/DeepLearningC/tree/master/CNN

  • 43
    点赞
  • 192
    收藏
    觉得还不错? 一键收藏
  • 48
    评论
利用tensorflow实现的卷积神经网络来进行MNIST手写数字图像的分类。 #导入numpy模块 import numpy as np #导入tensorflow模块,程序使用tensorflow来实现卷积神经网络 import tensorflow as tf #下载mnist数据集,并从mnist_data目录中读取数据 from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('mnist_data',one_hot=True) #(1)这里的“mnist_data” 是和当前文件相同目录下的一个文件夹。自己先手工建立这个文件夹,然后从https://yann.lecun.com/exdb/mnist/ 下载所需的4个文件(即该网址中第三段“Four files are available on this site:”后面的四个文件),并放到目录MNIST_data下即可。 #(2)MNIST数据集是手写数字字符的数据集。每个样本都是一张28*28像素的灰度手写数字图片。 #(3)one_hot表示独热编码,其值被设为true。在分类问题的数据集标注时,如何不采用独热编码的方式, 类别通常就是一个符号而已,比如说是9。但如果采用独热编码的方式,则每个类表示为一个列表list,共计有10个数值,但只有一个为1,其余均为0。例如,“9”的独热编码可以为[00000 00001]. #定义输入数据x和输出y的形状。函数tf.placeholder的目的是定义输入,可以理解为采用占位符进行占位。 #None这个位置的参数在这里被用于表示样本的个数,而由于样本个数此时具体是多少还无法确定,所以这设为None。而每个输入样本的特征数目是确定的,即为28*28。 input_x = tf.placeholder(tf.float32,[None,28*28])/255 #因为每个像素的取值范围是 0~255 output_y = tf.placeholder(tf.int32,[None,10]) #10表示10个类别 #输入层的输入数据input_x被reshape成四维数据,其中第一维的数据代表了图片数量 input_x_images = tf.reshape(input_x,[-1,28,28,1]) test_x = mnist.test.images[:3000] #读取测试集图片的特征,读取3000个图片 test_y = mnist.test.labels[:3000] #读取测试集图片的标签。就是这3000个图片所对应的标签
### 回答1: 利用卷积神经网络MINST数据集进行分类实验是一种常见和有效的图像识别方法。MINST数据集是一个手写数字图片集合,包括了60000个训练样本和10000个测试样本。 卷积神经网络(Convolutional Neural Network,CNN)是一种模拟人类视觉系统的深度学习模型,其在图像处理和模式识别任务中表现出色。使用CNNMINST数据集进行分类实验的步骤如下: 1. 数据准备:首先,将MINST数据集加载到程序中,并进行标准化处理,将图像的像素值归一化到0到1之间。 2. 构建CNN模型:这里可以使用Python的深度学习框架,如TensorFlow、Keras或PyTorch来构建CNN模型。定义卷积层、池化层和全连接层,并设置合适的卷积核大小、池化窗口大小和激活函数。 3. 模型训练:将训练集输入到CNN模型中,进行反向传播优化算法,如随机梯度下降(SGD)、Adam或RMSprop来优化模型参数。根据训练集不断调整卷积核权重、偏置和全连接层权重、偏置,直到模型收敛。 4. 模型评估:使用测试集评估已经训练好的模型。将测试集输入到CNN模型中,计算分类准确率、精确度、召回率等指标来评估模型的性能。 5. 结果分析:分析模型在测试集上的性能表现,可以观察混淆矩阵、绘制准确率和损失函数变化曲线等。 通过这样的实验,我们可以了解CNN模型在MINST数据集上的分类效果。MINST数据集是一个较为简单的图像分类任务,对于CNN来说可以很好地处理。这个实验也可以作为学习和理解CNN模型的基础,为后续更复杂的图像分类任务打下基础。 ### 回答2: 卷积神经网络(Convolutional Neural Networks, CNN)是一种深度学习模型,特别适合处理图像数据。下面我将介绍如何使用CNNMINST手写数字数据集进行分类实验。 MINST数据集是一个包含60000个训练样本和10000个测试样本的手写数字数据集。我们的目标是将这些手写数字图像正确地分类为0至9中的一个。 首先,我们需要导入所需的库和数据集。我们可以使用Python的TensorFlow库来实现卷积神经网络,并使用MINST数据集进行训练和测试。 接下来,我们定义一个CNN模型。一个典型的CNN模型由卷积层、池化层和全连接层组成。我们可以通过多次迭代来选择合适的参数和层数。 然后,我们将数据集分为训练集和测试集。训练集用于训练CNN的权重和参数,而测试集用于评估模型的性能。 接下来,我们进行训练和优化。我们使用训练集来训练CNN模型,并通过优化算法来调整权重和参数,以最小化损失函数。 在训练完成后,我们使用测试集来评估模型的性能。我们将模型应用于测试集中的图像,然后计算模型的准确率和损失。 最后,我们可以进行预测。我们可以将新的手写数字图像输入到CNN模型中,然后通过模型的输出确定该图像属于哪个数字类别。 通过上述步骤,我们可以使用CNNMINST手写数字数据集进行分类实验。这种方法在图像分类问题中已经证明了其有效性,并且在许多实际应用中得到了广泛应用。 ### 回答3: 卷积神经网络(Convolutional Neural Network, CNN)是一种深度学习模型,特别适用于图像处理任务。MINST数据集是一个常用的手写数字识别数据集,由60000个训练样本和10000个测试样本组成。 利用卷积神经网络MINST数据集进行分类实验的步骤如下: 1. 数据准备:首先需要下载MINST数据集,并将其分为训练集和测试集。MINST数据集提供了每个样本的数字标签和对应的图像数据。 2. 构建模型:使用深度学习框架如TensorFlow或PyTorch,搭建一个卷积神经网络模型。模型可以包含卷积层、池化层、全连接层等组件。可以选择不同的网络架构和超参数进行实验。 3. 数据预处理:在输入数据之前,进行一些预处理步骤,如将图像数据进行归一化、缩放和平衡处理等。这样可以提高模型的收敛速度和准确性。 4. 训练模型:将准备好的训练集输入模型,使用训练数据进行模型的训练。通过反向传播算法,不断调整模型的权重和偏差,使模型能够更好地拟合训练数据。 5. 测试模型:使用测试集对训练好的模型进行评估。将测试集的图像输入模型,得到输出的预测结果。计算预测结果与真实标签之间的差距,评估模型的准确性。 6. 调优和改进:根据测试结果分析模型的性能,可以进行一些调优和改进,如调整网络架构、增加层数、调整超参数等,以进一步提高模型的准确率。 通过以上步骤,可以利用卷积神经网络MINST数据集进行分类实验。可以通过测量准确度、损失函数等指标来评估模型的性能。不断优化模型,提高准确率,是卷积神经网络MINST数据集上进行分类实验的关键。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值