關於利用機器學習進行手寫數字的的識別

學習機器學習也有段時間了,借《機器學習 實戰》的第一篇中的例子來記錄下自己的學習過程吧,《實戰》中的第一講即是利用k近鄰分類器進行手寫體的識別,原理很簡單,由於手寫體的數字已經被處理成用01表示的文本

wKioL1M-W-ugmy7nAAK5_5-6dfQ902.jpg

,如圖所示


   在進行識別的時候,把要識別的文本轉化成一個32*32的矩陣,爲了方便計算,又將該矩陣轉化爲一個1024維的向量,然後將該向量與訓練的樣本相減求模,選擇模最小的幾個樣本,又根據這個樣本所代表數字最多的作爲識別的結果,思路很簡單,但計算量比較大,每識別一次就得利用每個樣本進行大量的運算,這是書中所採用的方法,作爲訓練自己,我當然不會按步就班,於是,我就想,能否利用其他的監督學習方法呢?首先這是一個多分類的問題,一共有十個結果,既然是多分類,那麼第一個我能想到的便是樸素貝葉斯的分類方法,樸素貝葉斯原理也很簡單,它有一個很強的假設,對於這個手寫識別的特定問題而言,就是每個位置上出現的1和0是獨立事件,儘管不大合適,作爲練習實驗,也無不可,好,說幹就幹!


clear;
rate=1;
sour=dir('*.txt');%Get all *.txt
sourc=size(sour);
sourconut=sourc(1);
samto=cell(1,sourconut);
for k=1:sourconut
    title=int8(sour(k).name(1))-int8('0');%獲取txt文件的第一個字符,作爲值
    sample.title=title;
    f = fopen(sour(k).name,'rt');
    x = fread(f,'char');
    fclose(f);
    t=size(x);
    buff=[];
    for i=1:t(1)
        if( int8(x(i))~=10 && int8(x(i))~= 32)
            t1=size(buff);
            t2=t1(1)+1;
            buff(t2,1)=int8(int8(x(i))-int8('0'));
        end
    end
    sample.content=buff;
    samto{k}=sample;%此處samto爲所有樣本的元胞,格式爲{title content}
end
%samto={{title content},..},content=圖片按行取列
Qy=zeros(1,10); %Qy爲樣本中出現每個類型的概率,即樣本爲0的概率,1的概率...
Q=zeros(1024,10);
ty=zeros(1,10);
for i=0:9
    t=0;
    for k=1:sourconut
        t1=samto(k);
        if(t1{1}.title==i)
            t=t+1;
        end
    end
    ty(i+1)=t;
    Qy(i+1)=t/sourconut*rate;
end %Qy計算完
disp('begin to calculate Q');
for y=0:9
    disp(strcat('y=',int2str(y)));
    for k=1:1024
        t2=0;
        for i=1:sourconut
            t1=samto{i}.title;
            if(samto{i}.content(k)==1 &&t1==y)
                t2=t2+1;
            end
        end
        Q(k,y+1)=(t2+1)/(ty(y+1)+10)*rate;
        %disp(strcat('Q=',int2str(t2)));
    end
end
disp('done!');

   靠,代碼居然沒有Matlab,那就將就用Python的吧,應該也行吧,呵呵,Matlab剛學不久,表示代碼寫得有點不倫不類.....代碼中的*.txt是用的《實戰》中的訓練樣本,每個訓練樣本被都爲一個txt文件,如數字1的7號書寫體文件名爲1_7.txt,數字2的28號書寫體文件名2_28.txt,樣本中一共有近2000個樣本,上面的代碼,即可獲得Q及Qy兩個值,Qy表示樣本中出現數字n的概率,Q爲數字n中每個位置爲1的概率,還是截個圖吧。。。

Qy:

wKioL1M-Zu6hCKTLAACIxWmeqX4080.jpg

Q(太長了就部分截圖吧):

wKiom1M-ZxjxXWk4AAYzPWbjvzc093.jpg

稍微解釋一下數據的含義,比如Qy中的0.0977就表示樣本中數字0所佔得比例,即0出現的概率,可以發現Qy中每種樣本的概率接近1,這是因爲各種樣本數量相當,而Q是一個1024*10的矩陣,就那1行1列的數來說吧,表示數字0的手寫體中的1行1列爲1的概率爲0.0050,噢,對了,由於進行了拉格朗日平滑,所以沒有爲0的概率,其實這個0.0050算是最小的了, 這些相信只要懂得樸素貝葉斯的原理,應該很好理解吧...呵呵....

   最後,測試下識別率!

clear;
Qroot=load('Q');
Qroot=Qroot.Q;
Qyroot=load('Qy');
Qyroot=Qyroot.Qy;
sour=dir('*.txt');
sourc=size(sour);
sourcount=sourc(1);
samto=cell(1,sourcount);
for k=1:sourcount
    title=int8(sour(k).name(1))-int8('0');
    sample.title=title;
    sample.name=sour(k).name;
    f = fopen(sour(k).name,'rt');
    x = fread(f,'char');
    fclose(f);
    t=size(x);
    buff=[];
    for i=1:t(1)
        if( int8(x(i))~=10 && int8(x(i))~= 32)
            t1=size(buff);
            t2=t1(1)+1;
            buff(t2,1)=int8(int8(x(i))-int8('0'));
        end
    end
    sample.content=buff;
    samto{k}=sample;
end
r=0;
for j=1:sourcount
    test=samto{j}.content;
    testTitle=samto{j}.title;
    name=samto{j}.name;
    p=ones(1,10);
    for i=0:9
        for k=1:1024
            if(test(k)==1)
                p(i+1)=p(i+1)*Qroot(k,i+1);
            else
                p(i+1)=p(i+1)*(1-Qroot(k,i+1));
            end
        end
        p(i+1)=p(i+1)*Qyroot(i+1);
    end
    guess=find(p==max(p))-1;
    if guess==testTitle
        r=r+1;
    else
        disp(strcat('guess:',int2str(guess)));
        disp(strcat('title:',int2str(testTitle)));
        disp(strcat('name:',name));
        disp(p);
    end
end
disp(r/sourcount);


wKioL1M-aZHTVVCdAAATgLg-_Wo836.jpg

正確率爲0.9302,還算行吧,呵呵,噢,對了,代碼不但輸出了正確率,也輸出了識別錯誤的那些樣本,幫助自己分析錯誤原因,如果大家要實驗的話,建議大家先把《實戰》的樣本包下載下來,再實驗,至於網址吧,百度一下就行了,哈哈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章