欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++實(shí)現(xiàn)手寫數(shù)字識(shí)別的示例詳解

 更新時(shí)間:2023年10月23日 16:28:38   作者:crazybobo1207  
這篇文章主要為大家詳細(xì)介紹了如何使用C/C++實(shí)現(xiàn)手寫數(shù)字識(shí)別,分別處理 32*32 文本數(shù)據(jù)集和mnist 28*28 png數(shù)據(jù)集,感興趣的小伙伴可以跟隨小編一起了解一下

絕大多數(shù)人都是用python寫,俺就喜歡用C/C++一句一句寫。代碼還未認(rèn)真整理,寫的有點(diǎn)亂,應(yīng)該還有優(yōu)化的余地。

程序主要目的:對(duì)測(cè)試數(shù)據(jù)集的樣本進(jìn)行預(yù)測(cè),計(jì)算預(yù)測(cè)準(zhǔn)確率。

數(shù)據(jù)集:分別使用32*32數(shù)據(jù)集和28*28數(shù)據(jù)集(mnist數(shù)據(jù)集)。前者是txt文本,后者是png圖片。

算法:使用最簡(jiǎn)單的KNN算法。

加快程序執(zhí)行的主要思路:

1.使用多線程(10個(gè)線程)讀取0到9數(shù)字對(duì)應(yīng)的樣本文件;

2.使用多線程(10個(gè)線程)對(duì)0到9數(shù)字對(duì)應(yīng)的樣本進(jìn)行預(yù)測(cè);

3.求距離時(shí),使用歐式距離,但是不做開(kāi)平方運(yùn)算(開(kāi)平方運(yùn)算是多余的,浪費(fèi)時(shí)間);

4.求K個(gè)最近距離鄰居(類似于求TOP K),采用C++的push_heap和pop_heap(小根堆)。

對(duì)于32*32的數(shù)據(jù)集,由于樣本是txt文件,直接讀取txt文件中的0和1,很容易求出樣本之間的距離。

對(duì)于mnist數(shù)據(jù)集,因?yàn)樵紨?shù)據(jù)是png圖片,所以需要做一些預(yù)處理:可以使用opencv讀取像素?cái)?shù)據(jù),轉(zhuǎn)成類似于32*32的txt數(shù)據(jù)集,方便求距離。另外,方便程序處理,重命名了文件名,文件名分別是0_0.txt,0_1.txt……,轉(zhuǎn)換成txt文件的mnist數(shù)據(jù)集如下:

簡(jiǎn)便起見(jiàn),對(duì)樣本個(gè)數(shù)做了一些簡(jiǎn)化,32*32數(shù)據(jù)集的訓(xùn)練數(shù)據(jù)是180*10=1800個(gè),測(cè)試數(shù)據(jù)是80*10=800個(gè),mnist數(shù)據(jù)集的訓(xùn)練數(shù)據(jù)是5000*10=5萬(wàn)個(gè),測(cè)試數(shù)據(jù)是800*10=8千個(gè)(原始測(cè)試樣本是1萬(wàn)個(gè),每個(gè)數(shù)字的測(cè)試樣本個(gè)數(shù)不同)。從數(shù)據(jù)量對(duì)比來(lái)看,后者是前者的280倍(訓(xùn)練數(shù)據(jù)集是28倍,測(cè)試數(shù)據(jù)集是10倍),對(duì)應(yīng)的程序運(yùn)行時(shí)間(計(jì)算測(cè)試樣本的預(yù)測(cè)準(zhǔn)確率)后者也是前者的200多倍(排序算法很重要,如果不使用小根堆求TOP K,而是使用傳統(tǒng)的整體數(shù)據(jù)排序,估計(jì)運(yùn)行速度會(huì)差很多)。

部分代碼如下(代碼還未整理,比較亂,暫時(shí)只貼主要代碼,表明整體思路),具體代碼回頭整理好了再貼。

#define TrainingDataNum 50000	
#define TestDataNum 8000		
#define FeatureNum 784    //28*28	
#define ClassNum 10
#define FileMaxCol 40    //大于28
#define K 3
 
//……………………
 
struct DisAndLabel
{
	int distance;
	int classLabel;
};
 
bool Cmp(const DisAndLabel& a, const DisAndLabel& b)
{
	return a.distance < b.distance;
}
 
int FileToTrainingDataArray(int** pTrainingDataFeture, int* pTrainingDataClass, int tid);
int FileToTestDataArray(int** pTestDataFeture, int* pTestDataClass, int tid);
void ClassifyTest(int** pTestDataFeture, int** pTrainingDataFeture, int* pTestDataClass, int* pTrainingDataClass, int tid);
 
//……………………
 
int main()
{
	clock_t t1, t2, t3, t4, t5;
	t1 = clock();
 
	int** pTrainingDataFeture = new int* [TrainingDataNum];
	if (pTrainingDataFeture == NULL)
	{
		exit(-1);
	}
 
	for (int i = 0; i < TrainingDataNum; i++)
	{
		pTrainingDataFeture[i] = new int[FeatureNum + 1];
		if (pTrainingDataFeture[i] == NULL)
		{
			exit(-1);
		}
	}
	
	//……………………
 
	t2 = clock();
	double duration = (double(t2) - double(t1)) / CLOCKS_PER_SEC;
	cout << "分配內(nèi)存:" << duration << "秒" << endl;
	
	//……………………
 
	thread td[10];
 
	for (int tid = 0; tid < 10; tid++)
	{
		td[tid] = thread(&FileToArrayThread, pTrainingDataFeture, pTrainingDataClass, pTestDataFeture, pTestDataClass, tid);
	}
 
	for (int tid = 0; tid < 10; tid++)
	{
		td[tid].join();
	}
	
	//……………………
	
	int errorCountTotal = 0;
 
	for (int i = 0; i < 10; i++)
	{
		errorCountTotal += errorCount[i];
	}
 
	cout << "*********************************************************" << endl;
	cout << "測(cè)試集測(cè)試結(jié)果:" << endl;
	cout << "k=" << K << endl;
	cout << "測(cè)試集樣本個(gè)數(shù):" << TestDataNum << endl;
	cout << "預(yù)測(cè)錯(cuò)誤個(gè)數(shù):" << errorCountTotal << endl;
	cout << "錯(cuò)誤率:" << double(errorCountTotal * 100.0 / TestDataNum) << "%" << endl;
	cout << "正確率:" << 100.0 - double(errorCountTotal * 100.0 / TestDataNum) << "%" << endl;
	//cout << "*********************************************************" << endl;
 
	t4 = clock();
	duration = (double(t4) - double(t3)) / CLOCKS_PER_SEC;
	cout << "*********************************************************" << endl;
	cout << "8000個(gè)樣本預(yù)測(cè)準(zhǔn)確率分析:" << duration << "秒" << endl;
 
	duration = (double(t4) - double(t1)) / CLOCKS_PER_SEC;
	cout << "*********************************************************" << endl;
	cout << "總共用時(shí):" << duration << "秒" << endl;
	
	//……………………
}

運(yùn)行結(jié)果如下:

K=3時(shí),8000個(gè)測(cè)試樣本,預(yù)測(cè)錯(cuò)誤了78個(gè)(其實(shí)算法沒(méi)有問(wèn)題,很多情況是因?yàn)闇y(cè)試樣本里的數(shù)字寫的太奇葩了,數(shù)字X實(shí)在不像數(shù)字X),下面把K改為5,準(zhǔn)確率提高了一些。

從運(yùn)行時(shí)間看,8000個(gè)樣本的預(yù)測(cè)用時(shí)是5秒多,如果只預(yù)測(cè)一個(gè)樣本,用時(shí)大概是5.4秒/8000=0.7毫秒,主要是訓(xùn)練數(shù)據(jù)集數(shù)據(jù)有點(diǎn)多(5萬(wàn)個(gè)樣本)。

下面是32*32數(shù)據(jù)集的運(yùn)行結(jié)果,由于訓(xùn)練樣本和測(cè)試樣本的數(shù)據(jù)量比mnist數(shù)據(jù)集少很多,所以程序運(yùn)行速度快了200倍,平均每個(gè)樣本的預(yù)測(cè)時(shí)間大概是0.03秒/800=0.04毫秒。

程序總共用時(shí)不到50毫秒(用python寫,貌似要10秒左右),比python快了200倍。C語(yǔ)言直接操作內(nèi)存+高速緩存+宇宙第一IDE的代碼優(yōu)化+多線程,運(yùn)行速度比python快2個(gè)數(shù)據(jù)級(jí)很正常。但是代碼量也比python多很多,一般2到3倍,如果python是直接調(diào)算法庫(kù),比python多10倍以上 -_-。

樣本預(yù)測(cè)錯(cuò)誤的具體原因是什么,可以分析K個(gè)最近樣本的距離值,以數(shù)字8(預(yù)測(cè)錯(cuò)誤的重災(zāi)區(qū))為例:

到此這篇關(guān)于C/C++實(shí)現(xiàn)手寫數(shù)字識(shí)別的示例詳解的文章就介紹到這了,更多相關(guān)C++數(shù)字識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談c++11閉包的實(shí)現(xiàn)

    淺談c++11閉包的實(shí)現(xiàn)

    閉包有很多種定義,一種說(shuō)法是,閉包是帶有上下文的函數(shù)。說(shuō)白了,就是有狀態(tài)的函數(shù)。更直接一些,不就是個(gè)類嗎?換了個(gè)名字而已。本文將介紹c++11閉包的實(shí)現(xiàn),感興趣的同學(xué),可以參考下。
    2021-06-06
  • 一文弄懂C語(yǔ)言如何實(shí)現(xiàn)單鏈表

    一文弄懂C語(yǔ)言如何實(shí)現(xiàn)單鏈表

    單鏈表是由多個(gè)結(jié)點(diǎn)鏈接組成,它的每個(gè)結(jié)點(diǎn)包含兩個(gè)域,一個(gè)數(shù)據(jù)域和一個(gè)鏈接域(地址域),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言如何實(shí)現(xiàn)單鏈表的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • C語(yǔ)言超詳細(xì)講解線性表

    C語(yǔ)言超詳細(xì)講解線性表

    線性表,數(shù)據(jù)結(jié)構(gòu)中最簡(jiǎn)單的一種存儲(chǔ)結(jié)構(gòu),專門用于存儲(chǔ)邏輯關(guān)系為"一對(duì)一"的數(shù)據(jù)。線性表是基于數(shù)據(jù)在實(shí)際物理空間中的存儲(chǔ)狀態(tài),又可細(xì)分為順序表(順序存儲(chǔ)結(jié)構(gòu))和鏈表
    2022-07-07
  • 區(qū)分c++中的聲明與定義

    區(qū)分c++中的聲明與定義

    這篇文章主要介紹了如何區(qū)分c++中的聲明與定義,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下
    2020-08-08
  • C語(yǔ)言函數(shù)棧幀的創(chuàng)建和銷毀介紹

    C語(yǔ)言函數(shù)棧幀的創(chuàng)建和銷毀介紹

    大家好,本篇文章主要講的是C語(yǔ)言函數(shù)棧幀的創(chuàng)建和銷毀介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2021-12-12
  • C++實(shí)現(xiàn)選擇排序(selectionSort)

    C++實(shí)現(xiàn)選擇排序(selectionSort)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)選擇排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件算法操作總結(jié)

    C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件算法操作總結(jié)

    C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件提供了大量有用的算法,主要用于操作容器(如 vector, list, array 等),這些算法通常通過(guò)迭代器來(lái)操作容器元素,本文給大家介紹C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件算法總結(jié),感興趣的朋友一起看看吧
    2025-04-04
  • 模擬實(shí)現(xiàn)C語(yǔ)言中的內(nèi)存管理

    模擬實(shí)現(xiàn)C語(yǔ)言中的內(nèi)存管理

    這篇文章主要內(nèi)容是模擬C語(yǔ)言中的內(nèi)存管理,需要的朋友可以參考下
    2015-07-07
  • C++實(shí)踐分?jǐn)?shù)類中運(yùn)算符重載的方法參考

    C++實(shí)踐分?jǐn)?shù)類中運(yùn)算符重載的方法參考

    今天小編就為大家分享一篇關(guān)于C++實(shí)踐分?jǐn)?shù)類中運(yùn)算符重載的方法參考,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Qt中線程常用通信方式介紹

    Qt中線程常用通信方式介紹

    Qt中,線程通信無(wú)處不在,最核心的特性信號(hào)槽就是一種線程間通信,這篇文章主要為大家介紹了幾種常用的方式,需要的小伙伴可以參考一下
    2025-01-01

最新評(píng)論