本人本科毕业设计题目为:《基于摘要词频信息的论文分类方法的研究和实现》,属于文本分类(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