kNN文本分类器实现

 

      本人本科毕业设计题目为:《基于摘要词频信息的论文分类方法的研究和实现》,属于文本分类(Text Classification)方面的内容。本人实现了kNN文本分类器,使用语言为Matlab(因为该脚本语言使用极为直观,且非常适合数据运算,尤其是矩阵运算,故选用Matlab进行仿真),最终分类正确率达到64%左右,具有一定实际意义,故分享一下本人实现的思路,一下代码均为个人编写,仅供参考!【一些细节问题未列出,欢迎提问交流】

Part I.文本预处理:

【说明,请将path改为存放数据库database1的绝对路径!】

 

clear;clc;
path = 'F:\【毕业生】\毕设\00000大论文\5\database1'; %公共路径

% 1、分词(得到向量):【[A] = textread('1.txt','%s','delimiter',',. ()');】
% 2、(统一大小写->小写字母)【A = lower(A);】、去除单个数字【data = delnumber(A)】;
% 3、去除停用词;
% 4、最终得到一个5*65的cell类型矩阵AfterPrepro,矩阵中每个元素均为结构体.

Fold_num = 5; %总共有5类
File_num = 65; %每类均含65篇文本
AfterPrepro = cell(Fold_num,File_num); %创建5*65的矩阵,每个元素均为结构体类型
for label = 1:Fold_num
    for file = 1:File_num
        file_path = strcat(path,'\',num2str(label),'\',num2str(file),'.txt');%文件路径
        %分词(得到cell类型列向量):
        tmp = textread(file_path,'%s','delimiter',',. ()'); 
        %统一大小写->小写字母 :
        tmp = lower(tmp);
         
        %去除单个数字 :
        tmp = delNumber(tmp);
        %去除停用词 :
        AfterPrepro{label,file}.text = delStopWords(tmp);%去除停用词后的文本
        %其他属性:类别label,序号seq
        AfterPrepro{label,file}.label = label;
        AfterPrepro{label,file}.seq = file;
    end
end

 

 

Part II. 特征加权(TF/IDF算法):

 

%关键词在文本中出现的频数freq;
%此类别文档的总数目【N = 65】;
%此类别文档中出现该关键词的文档频数num;
%计算该关键词的权重weight;

%加载经过预处理后得到的cell类型矩阵AfterPrepro:
load('F:\【毕业生】\毕设\00000大论文\5\programs\AfterPrepro.mat')

Fold_num = 5; %总共有5类
File_num = 65; %每类均含65篇文本
N = File_num;
for label = 1:Fold_num
    for seq = 1:File_num 
        AfterPrepro{label,seq}.tmp = freqStatistic(AfterPrepro{label,seq}.text); %用临时变量tmp进行词频统计,得到x*2的cell类型矩阵.
        AfterPrepro{label,seq}.text2 = AfterPrepro{label,seq}.tmp(:,1);%经词频统计后的文本(即AfterPrepro的新域text2,是x*1的cell类型矩阵).
        for n = 1:length(AfterPrepro{label,seq}.tmp(:,1))
            AfterPrepro{label,seq}.freq(n,1) = AfterPrepro{label,seq}.tmp{n,2}/(length(AfterPrepro{label,seq}.text));%只保存经词频统计后文本中关键词对应的频数,并归一化。(即AfterPrepro的新域freq,是x*1的double类型矩阵).
        end
    end
end

%文档频数统计 及 特征权重计算:
for label = 1:Fold_num %每一类
    for seq = 1:File_num %每类下的所有文本  
        AfterPrepro{label,seq}.num = zeros(length(AfterPrepro{label,seq}.text2),1); %初始化文档频数矩阵,元素类型为double.
        AfterPrepro{label,seq}.weight = zeros(length(AfterPrepro{label,seq}.text2),1); %初始化特征权重矩阵,元素类型为double.
        for i = 1:N 
            tmp = docnumStatistic( AfterPrepro{label,seq}.text2 , AfterPrepro{label,i}.text2 );
            for j = 1:length(tmp)
                %特征文档频数DF:
                AfterPrepro{label,seq}.num(j,1) = AfterPrepro{label,seq}.num(j,1) + tmp{j};
                %特征权重:
                AfterPrepro{label,seq}.weight(j,1) = log( AfterPrepro{label,seq}.freq(j,1) + 1.0 ) * log( (N + 1.0) / AfterPrepro{label,seq}.num(j,1) );
            end
        end        
    end
end

 

 

Part III. 构建kNN分类器并统计结果:
 

%目的:kNN算法的实现

%加载经过特征加权后的数据:
load('F:\【毕业生】\毕设\00000大论文\5\programs\AfterWeighting.mat')
	count = 1;
        Fold_num = 5; %总共有5类
        File_num = 65; %每类均含65篇文本
        testFile_num = 15; %每类15篇测试文本
        trainFile_num = File_num - testFile_num; %每类50篇训练文本
        K = 4; %kNN分类器中K值的选择.

        testSet = zeros(5,testFile_num);
        trainSet = zeros(5,trainFile_num);
        %每类随机抽取15篇作为测试集testSet(5*15的矩阵,元素为某一类别下的文本序列号),
        %其余作为训练集trainSet(5*50的矩阵,元素为某一类别下的文本序列号):
        for label = 1:Fold_num %总共有5类
            tmp = randperm(File_num); %随机产生1:65的序列
            testSet(label,:) = tmp(1,1:testFile_num); %随机序列的前15个作为某类下测试文本的序号数.
            trainSet(label,:) = tmp(1,(testFile_num+1):File_num); %随机序列的剩余部分(即后50个)作为某类下训练文本的序号数.
        end        

        %测试集的一篇文本与训练集所有文本进行相似度计算:
        for testlabel = 1:Fold_num %测试集中文本共有5类,依次遍历.
            for m = 1:testFile_num %测试集中每类有15篇测试文本,依次遍历.
                 for label = 1:Fold_num %训练集中文本共有5类
                     for n = 1:trainFile_num %训练集中每类有50篇训练文本
                         text1 = AfterPrepro{testlabel,testSet(testlabel,m)}.text2; %测试文本的text2
                         text2 = AfterPrepro{label,trainSet(label,n)}.text2; %训练文本的text2
                         weight1 = AfterPrepro{testlabel,testSet(testlabel,m)}.weight; %测试文本的权重weight
                         weight2 = AfterPrepro{label,trainSet(label,n)}.weight; %训练文本的权重weight

                         %测试集中每篇文本的testResult的第1列为与之进行相似度比较的文本的类别标签;第2列为对应的相似度值::
                         AfterPrepro{testlabel,testSet(testlabel,m)}.testResult((label-1)*trainFile_num+n,1) = label;
                         AfterPrepro{testlabel,testSet(testlabel,m)}.testResult((label-1)*trainFile_num+n,2) = simCal( text1,weight1,text2,weight2 );
                         %------------------------------------------------------------------------------------------
                         AfterPrepro{testlabel,testSet(testlabel,m)}.testResult((label-1)*trainFile_num+n,3) = n;
                         %------------------------------------------------------------------------------------------
                     end
                 end
            end
        end


        %对相似度进行从大到小排序,选出前k个,并对每个类别的个数进行统计:
        for testlabel = 1:Fold_num %测试集中文本共有5类,依次遍历.
            for m = 1:testFile_num %测试集中每类有15篇测试文本,依次遍历.

                %依据测试集中每篇文本的testResult的第2列(即相似度值)进行行降序排序(则从大到小):
                simSort = sortrows(AfterPrepro{testlabel,testSet(testlabel,m)}.testResult,-2);
                %取出 依据相似度(从大到小)排序后得到的向量(相应训练集的类别标签) 的前K个作为近邻:
                AfterPrepro{testlabel,testSet(testlabel,m)}.voteTable = simSort(1:K,1);
                AfterPrepro{testlabel,testSet(testlabel,m)}.voteSeqTable = simSort(1:K,3);

        %         -------------------------待优化2015.05.19  13:20-----------------
                %mode 函数是用来求‘众数 和 众数的频数’的函数-->找出K近邻中最多的类别作为测试集的类别标签:
                [a,b] = mode(AfterPrepro{testlabel,testSet(testlabel,m)}.voteTable);
        %         ------------------------------------------------------------------

                %判决该文本的类别,并判断对错:
                AfterPrepro{testlabel,testSet(testlabel,m)}.vote = a;
                if isequal(AfterPrepro{testlabel,testSet(testlabel,m)}.vote,AfterPrepro{testlabel,testSet(testlabel,m)}.label)
                    AfterPrepro{testlabel,testSet(testlabel,m)}.isright = 1;
                else
                    AfterPrepro{testlabel,testSet(testlabel,m)}.isright = 0;
                end

            end
        end

        rRate = 0;
        for testlabel = 1:Fold_num %测试集中文本共有5类,依次遍历.
            for m = 1:testFile_num %测试集中每类有15篇测试文本,依次遍历.
                if AfterPrepro{testlabel,testSet(testlabel,m)}.isright
                    rRate = rRate + 1;
                end
            end
        end

        %计算百分比:
        count = count+1;
        kValue(count) = K;
        
        rRate = rRate/(Fold_num*testFile_num);
        testresult(count) = rRate;


Part IV. 部分函数:

 

%判断字符串str是否为数字:若是,flag为‘true’;否则,flag为‘false'
%返回结果为flag
%警告:未处理参数str为“空”的情况!
function flag = isNumber( str )
    flag = true;
    for i=1:length(str)
        if (str(i)<'0')||(str(i)>'9')
            flag = false;
            return; %跳出整个函数
        end
    end 

end

 

%去除一个矩阵中的数字元素
%输入参数A是cell类型的(列)向量
%输出参数data也是cell类型的(列)向量
function data = delNumber(A)
for i=length(A):-1:1
    if(isNumber(cell2mat(A(i))))
        A(i) = [];
    end
end
data = A;

 

%目的:关键词频数统计.
%Input:经过预处理后的文本(cell类型)【AfterPrepro{label,seq}.text】;
%Output:经过词频统计后的文本data(cell类型);
function output = freqStatistic( A )
    for i = 1:length(A)
        flag = 0;%标记改词是否在频数统计表里已经存在:1,已存在;0,不存在,新增改词.
        if i==1
            output{i,1} = A{1};
            output{i,2} = 1;
        else
            for k = 1:length(output(:,1))
                if isequal(A{i},output{k,1})
                    output{k,2} = output{k,2}+1;
                    flag = 1;
                    break;
                end
            end
            
            if(~flag)%该词不存在于频数统计表中
                output{(length(output(:,1))+1),1} = A{i};
                output{length(output(:,1)),2} = 1;%长度已变,无需+1.
            end            
        end
    end
end

 

%目的:统计出现文本中特征词的文档频数.
%Input:两个经过特征词频数统计后的文本1(cell类型的x*1矩阵)和文本2(cell类型的x*1矩阵),main文件中均通过AfterPrepro{label,seq}.text2调用;
%Output:经过含有该文本特征词的文档频数统计后文本1所对应的文档频数向量result(cell类型的x*1矩阵);
function result = docnumStatistic(text1,text2)
    for i = 1:length(text1)
        result{i} = 0; %特征词拷贝.
        for j = 1:length(text2)
            if isequal( text1{i},text2{j} )
                result{i} = result{i}+1; %统计第i个关键词的文档频数.
            end
        end
    end
end

 

%目的:文本间相似度的计算
%Input:两个文本向量( cell类型的x*1矩阵text1、cell类型的y*1矩阵text2 ).
%Output:文本text1和text2之间相似度sim.
function sim = simCal( text1,weight1,text2,weight2 )
    tmp = text1(:,1);
    for i = 1:length(tmp)
        tmp{i,2} = weight1(i,1);
        tmp{i,3} = 0;
    end
    
    for j = 1:length(text2(:,1))
        flag = 0; %flag = 0,表明该关键词不存在于text1中;
        for k = 1:length(text1(:,1))
            if isequal(text2{j,1},text1{k,1})
                tmp{k,3} = weight2(j,1);
                flag = 1; %flag = 1,表明该关键词已经存在于text1中.
                break;
            end            
        end
        if (~flag)
                tmp{length(tmp(:,1))+1,1} = text2{j,1};
                tmp{length(tmp(:,1)),2} = 0;
                tmp{length(tmp(:,1)),3} = weight2(j,1);
        end
    end
    tmp
    sum1 = 0; %计算相似度的分子部分
    sum2 = 0; %计算相似度的分母部分1
    sum3 = 0; %计算相似度的分母部分2
    for m = 1:length(tmp(:,1))
        sum1 = sum1 + tmp{m,2}*tmp{m,3};
        sum2 = sum2 + tmp{m,2}*tmp{m,2};
        sum3 = sum3 + tmp{m,3}*tmp{m,3};
    end
    
    sim = sum1/sqrt(sum2*sum3);
    
end

---------------------------------------------------------

2020.4.29 翻了下古董笔记本,找到了古董代码哈哈:

%去除停用词,包括:be\am\is\are\was\were、to、of、in、on、the、a\an、this\that、when\where\why\what\who\how、next\before\prior
function result = delStopWords(A)   
    a = textread('F:\【毕业生】\毕设\00000大论文\5\programs\StopWordsTable.txt','%s'); %读取停用词表   
    for i=length(A):-1:1
        for j=1:length(a)
            if isequal(A(i),a(j))
                A(i)=[];
                break;
            end
        end
    end
    result = A;
end

 

 

 

你可能感兴趣的:(kNN文本分类器,文本分类)