最大概率分词是一种最主要的统计方法分词。

一个待切割的字符串有多种分词结果,最大概率分词的原则是将当中概率最大的那个作为该字符串的分词结果。

第一部分 理论基础

如对一个字符串:

S:有意见分歧

分词结果1: w1:有/ 意见/ 分歧/

分词结果2: w2:有意/ 见/ 分歧/

最大概率分词就是要求得 Max(P(w1|s),P(w2|s)) 。

依据贝叶斯公式:

P(w|s)=P(s|w)P(w)/P(s)                                                                      (公式1)

在公式1中。由于P(s)和P(w|s)都基本一样,因此。就求最大的P(w)就可以。依据一元语法,词之间出现的概率互相独立。因此有以下的公式成:

P(w)=P(w1,w2,…,w3)=P(w1)P(w2)…P(w3)                                   (公式2)

即字符串出现的概率就是构成字符串的各个词的概率之积。而一个词的概率能够依照其出现的次数除以语料中总的词数得到。

分析以下的样例,我们能够计算得到各个词的概率为:

有:0.018

有意:0.0005

意见:0.001

见:0.0002

分歧:0.0001

则依据公式2有:

P(w1)=p(有)P(意见)P(分歧)=0.018*0.001*0.0001=1.8*10^(-9)

P(w2)=P(有意)P(见)P(分歧)=0.0005*0.0002*0.0001=1*10^(-11)

因为P(w1)>P(w2),故w1为该字符串的分词结果。

当然,在实际操作过程中,假设字符串比較长,分词的形式就会许多,计算量和长度呈指数增长关系,因此须要採用一定的算法来降低运算量,我们能够看到字符串的概率是累计相乘的,因此能够採用动态规划的方法来降低运算量。

这里记P`(w)为到达候选词wi时的累计概率,则

P`(wi)=P`(wi-1)P(wi)                                             (公式3)

依据公式3。有P`(意见)=P`(有)P(意见)

第二部分 算法实现

在算法的实现思路上。基本上是先记录全部可能出现的词,以及其相应的概率,也就是分段的代价函数,同一时候寻找每个词的最佳的前趋词。

然后就是回溯,从字符串的尾部向前搜索最优路径就可以。这也是动态规划的一般实现方法。

1.思路说明

(1)获取候选词

获取句子中可能出现的全部词作为候选词,但要满足下列条件:假设是长度大于1的词,则必须在词典中出现;假设是长度等于1,即为单字,能够不在词典中出现。

(2)构造前趋词:

假定字符串从左到右进行扫描,能够得到w1,w2,…,wi-1,wi,….等若干候选词,假设wi-1的尾字根wi的首字邻接。就称wi-1为wi的前趋词。比方上面例中,候选词“有”就是候选词“意见”的前趋词,“意见”和“见”都是“分歧”的前趋词。字串最左边的词没有前趋词。

(3)寻找最佳前趋词:

假设某个候选词wi有若干个前趋词wj,wk,…..等等,当中累计概率最大的候选词称为wi的最佳前趋词。比方候选词“意见”仅仅有一个前趋词“有”,因此“有”同一时候也就是“意见”的最佳前趋词;候选词“分歧”有两个前趋词“意见”和“见”,当中“意见”的累计概率大于“见”累计概率。因此“意见”是“分歧”的最佳前趋词。

(4)确定最优路径

回溯,从字符串的尾部依照最佳前趋词的指引,向前搜索最优路径。

2.详细步骤

(1)对一个待分词的字串S。依照从左到右的顺序取出所有候选词w1,w2,….,wi,…,wn;

(2)到词典中查出每一个候选词的概率值P(wi)。

(3)依照公式3计算每一个候选词的累计概率。同一时候比較得到每一个词的最佳前趋词。

(4)假设当前词wn是字符串S的尾词。且累计概率P’(wn)最大,则wn就是S的终点词。

(5)从wn開始,依照从右到左的顺序,因此将每一个词的最佳前趋输出,即为S的分词结果。

样例:

(1)对“有意见分歧”,从左到右进行一遍扫描,得到所有候选词:“有”,“有意”。“意见”,“见”,“分歧”;

(2)对每一个候选词,记录下它的概率值,并将累计概率赋初值为0;

(3)顺次计算各个候选词的累计概率值,同一时候记录每一个候选词的最佳前趋词:

P`(有)=P(有)。

P`(意见)=P(意见),

P`(意见)=P`(有)P(意见),(“意见”的最佳前趋词为“有”)

P`(见)=P`(有意)P(见),(“见”的最佳前趋词为“有意”)

P`(意见) > P`(见)

(4) “分歧”是尾词,“意见”是“分歧”的最佳前趋词,分词过程结束。

第三部分 结果展示

对1998年1月《人民日报》进行分析,当中构造词典和測试用的语料比例为9:1。

分别用三种方法进行分词:正向最大概率匹配、逆向最大概率匹配、最大概率法。对它们的分词结果进行比較。结果例如以下:

第四部分 源码

源码分为三个文件,各自是:dictionary_2.h(词典头文件)、segmentwords.cpp(三种分词方法所在的文件)、main.cpp(结果输出、正确性比对等功能)。

1.dictionary_2.h(词典头文件)

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <map>
#include <cstdlib>using namespace std;/** 词典的定义,用于最大概率分词*/
class Dictionary{private:string strline;            //保存每行内容string word;            //保存一个词语map<string, int> word_map;    //词典,用map表示public:long size;         //词典规模long freq_all;long arr_1[20];double arr_2[20];Dictionary();           //构造函数。初始化词典~Dictionary();int findWord(string word);    //在词典中查找特定的词语
};Dictionary::Dictionary(){freq_all = 0;for(int i = 0; i < 20; i++){arr_1[i] = 0;arr_2[i] = 0.0;}//读取词典文件fstream fin("dict_3.txt");if(!fin){cerr << "open file error !" << endl;exit(-1);}//将每一个词语增加集合while(getline(fin, strline, '\n')){istringstream istr(strline);istr >> word;     //从流中读取单词++word_map[word];    //++arr_1[word.size()];++freq_all;}fin.close();//初始化词典大小size = word_map.size();for(int i = 0; i < 20; i++){arr_2[i] = (double)arr_1[i]/freq_all;}
}Dictionary::~Dictionary(){}int Dictionary::findWord(string word){map<string, int>::iterator p_cur = word_map.find(word); if(p_cur != word_map.end()){return p_cur -> second;}else{return -1;}
}

2.segmentwords.cpp(三种分词方法所在的文件)

#include <cmath>
#include <string>
#include <iostream>
#include "dictionary_2.h"const short MaxWordLength = 20; //词典中最大词的长度
const char Separator = '/';     //词界标记Dictionary word_dict;           //初始化一个词典/** 类定义:候选词的结构*/
class Candidate{public:short pos;   //候选词在输入串中的起点short length;  //输入串的长度short bestPrev; //最佳前趋词的序号float fee;    //候选词的费用float sumFee;   //候选词路径上的累计费用string word;   //候选词int freq;  //候选词的频数(不能用short,否则有可能溢出)
};/** 函数功能:取出字符串中的所有候选词* 函数输入:字符串的引用* 函数输出:该字符串中含有的所有的存在与词典中的词(或者单字。单字能够在词典中不存在)*/
vector<Candidate> getTmpWords(const string &s){int freq = 0;         //词典中词的频率short n = s.length();     //字符串的长度string word = "";        //存放候选词Candidate cand;          //存放候选词属性vector<Candidate> vec_cd;    //候选词队列//以每一个汉字为起点for(short i = 0; i < n; i += 2){//词的长度为 1~MaxWordLength/2 个汉字for(short len = 2; len <= MaxWordLength; len += 2){word = s.substr(i, len);freq = word_dict.findWord(word);//去词典中查找出现频率if(len > 2 && freq == -1){//若不止一字且词表中找不到则不予登录continue;}if(freq == -1){//假设为单字词。且词表中找不到freq = 0;}cand.pos = i;          //该候选词在汉字串中的起点cand.length = len;       //该候选词的长度cand.word = word;cand.fee = -log((double)(freq*1 + 1)/word_dict.freq_all);//该候选词的费用cand.sumFee = 0.0f;     //该候选词的累计费用置初值cand.freq = freq;//将获取的候选词增加队列vec_cd.push_back(cand);    }}return vec_cd;
}/** 函数功能:获取最佳前趋词序号* 函数输入:候选词列表的引用* 函数输出:无*/
void getPrew(vector<Candidate> &vec_cd){short min_id = -1;               //最佳前趋词编号short j = -1;short size = (short)vec_cd.size();      //计算队列长度for(short i = 0; i < size; i++){if(vec_cd[i].pos == 0){//假设候选词是汉字串中的首词vec_cd[i].bestPrev = -1; //无前趋词vec_cd[i].sumFee = vec_cd[i].fee;    //累计费用为该词本身费用}else{//假设候选词不是汉字串中的首词min_id = -1;            //初始化最佳前趋词编号j = i - 1;         //从当前对象向左找while(j >= 0){//向左寻找所遇到的所有前趋词if(vec_cd[j].pos + vec_cd[j].length == vec_cd[i].pos){if(min_id == -1 || vec_cd[j].sumFee < vec_cd[min_id].sumFee){min_id = j;}}--j;}vec_cd[i].bestPrev = min_id;  //登记最佳前趋编号vec_cd[i].sumFee = vec_cd[i].fee + vec_cd[min_id].sumFee;//登记最小累计费用}}
}/** 函数功能:最大概率法分词* 函数输入:待切分的字符串* 函数输出:切分好的字符串*/
string segmentSentence_MP(string s1){short len = s1.length();short min_id = -1;       //最小费用路径的终点词的序号//取出s1中的所有候选词vector<Candidate> vec_cd = getTmpWords(s1);//获得最佳前趋词序号、当前词最小累计费用getPrew(vec_cd);//确定最小费用路径的终点词的序号short n = (short)vec_cd.size();for(short i = 0; i < n; i++){if(vec_cd[i].pos + vec_cd[i].length == len){//假设当前词是s1的尾词if(min_id == -1 || vec_cd[i].sumFee < vec_cd[min_id].sumFee){//假设是第一个遇到的尾词。或者是当前尾词的最小累计费用小于//已经遇到过的任一尾词的最小累计费用,则将其序号赋给min_idmin_id = i;}}}//构造输出串string s2 = "";     //输出串初始化for(short i = min_id; i >= 0; i = vec_cd[i].bestPrev){//注意:是先取后面的词s2 = s1.substr(vec_cd[i].pos, vec_cd[i].length) + Separator + s2;}return s2;
}/** 函数功能:对字符串用最大匹配算法(正向)处理* 函数输入:汉字字符串* 函数输出:分好词的字符串*/
string segmentSentence_1(string s1){string s2 = "";      //用s2存放分词结果while(!s1.empty()){int len = s1.length();   //取输入串长度if(len > MaxWordLength){len = MaxWordLength;    //仅仅在最大词长范围内进行处理}string w = s1.substr(0, len);int n = word_dict.findWord(w);  //在词典中查找对应的词while(len > 2 && n == -1){len -= 2;   //从候选词右边减掉一个汉字,将剩下的部分作为候选词w = s1.substr(0, len);n = word_dict.findWord(w);}s2 = s2 + w + Separator;s1 = s1.substr(w.length(), s1.length() - w.length());}return s2;
}/** 函数功能:对字符串用最大匹配算法(逆向)处理* 函数输入:汉字字符串* 函数输出:分好词的字符串*/
string segmentSentence_2(string s1){string s2 = "";      //用s2存放分词结果while(!s1.empty()){int len = s1.length();   //取输入串长度if(len > MaxWordLength){len = MaxWordLength;    //仅仅在最大词长范围内进行处理}string w = s1.substr(s1.length() - len, len);int n = word_dict.findWord(w);  //在词典中查找对应的词while(len > 2 && n == -1){len -= 2;   //从候选词左边减掉一个汉字。将剩下的部分作为候选词w = s1.substr(s1.length() - len, len);n = word_dict.findWord(w);}w = w + Separator;s2 = w + s2;s1 = s1.substr(0, s1.length() - len);}return s2;
}

3.main.cpp(结果输出、正确性比对等功能)

#include <cstdlib>
#include <vector>
#include <iomanip>
#include <map>
#include <algorithm>
#include <sys/time.h>
#include <sys/stat.h>
#include "segmentwords.cpp"const long MaxCount = 50000;  //须要切分的最大句子数量。若该值大于文件里//实际的句子数量,以实际句子数量为准。//获取当前时间(ms)
long getCurrentTime(){struct timeval tv;gettimeofday(&tv, NULL);return tv.tv_sec*1000 + tv.tv_usec/1000;
}//获取文件大小
unsigned long getFileSize(string file_path){unsigned long filesize = -1;struct stat statbuff;if(stat(file_path.c_str(), &statbuff) < 0){return filesize;}else{filesize = statbuff.st_size;}return filesize;
}/** 函数功能:对句子进行最大匹配法处理。包括对特殊字符的处理* 函数输入:1.含有汉字、英文符号的字符串*         2.flag=1调用正向最大匹配算法。flag=2调用逆向最大匹配算法* 函数输出:分好词的字符串*/
string SegmentSentenceMM(string s1, int flag){string s2 = "";    //用s2存放分词结果int i;int dd;while(!s1.empty()){unsigned char ch = (unsigned char)s1[0];if(ch < 128){//处理西文字符i = 1;dd = s1.length();while(i < dd && ((unsigned char)s1[i] < 128) && (s1[i] != 10) && (s1[i] != 13)){//s1[i]不能是换行符或回车符i++;}//中止循环条件:出现中文字符、换行或者回车if(i == 1 && (ch == 10 || ch == 13)){//假设是换行或回车符,将它拷贝给s2输出s2 += s1.substr(0, i);}else{s2 += s1.substr(0, i) + Separator;}s1 = s1.substr(i, dd);continue;}else{if(ch < 176){//中文标点等非汉字字符i = 0;dd = s1.length();//获取中文双字节特殊字符(非汉字、非中文标点),中止循环条件:超过长度、出现中文标点符号、出现汉字while(i < dd && ((unsigned char)s1[i] < 176) && ((unsigned char)s1[i] >= 161)&& (!((unsigned char)s1[i] == 161 && ((unsigned char)s1[i+1] >= 162 && (unsigned char)s1[i+1] <= 168)))&& (!((unsigned char)s1[i] == 161 && ((unsigned char)s1[i+1] >= 171 && (unsigned char)s1[i+1] <= 191)))&& (!((unsigned char)s1[i] == 163 && ((unsigned char)s1[i+1] == 161 || (unsigned char)s1[i+1] == 168||   (unsigned char)s1[i+1] == 169 || (unsigned char)s1[i+1] == 172 || (unsigned char)s1[i+1] == 186 ||   (unsigned char)s1[i+1] == 187 || (unsigned char)s1[i+1] == 191)))){//假定没有半个汉字i = i + 2;}//出现中文标点if(i == 0){i = i + 2;}//中文标点每一个加一个分词标记;其它非汉字双字节字符连续输出。仅仅加一个分词标记s2 += s1.substr(0, i) + Separator;s1 = s1.substr(i, dd);continue;}}//下面处理汉字串i = 2;dd = s1.length();while(i < dd && (unsigned char)s1[i] >= 176){i += 2;}if(flag == 1){//调用正向最大匹配s2 += segmentSentence_1(s1.substr(0, i));}else if(flag == 2){//调用逆向最大匹配s2 += segmentSentence_2(s1.substr(0, i));}else if(flag == 3){//调用最大概率匹配s2 += segmentSentence_MP(s1.substr(0, i));}s1 = s1.substr(i, dd); }return s2;
}/** 函数功能:删除分词标记(即去掉字符串中的/)* 函数输入:含有分词标记的字符串* 函数输出:不含分词标记的字符串*/
string removeSeparator(string str_in){char s[10000];int j = 0;for(int i = 0; i < str_in.length(); i++){if(!(str_in[i] == '/')){s[j] = str_in[i];j++;}}s[j] = '\0';string str_out = s;return str_out;
}/** 函数功能:计算切分标记的位置* 函数输入:1.strline_in未进行切分的汉字字符串2.strline_right进行切分后的汉字字符串* 函数输出:vecetor。当中存放了strline_in中哪些位置放置了分词标记*         注意:vector中不包括最后标记的位置,可是包括位置0。*/
vector<int> getPos(string strline_right, string strline_in){int pos_1 = 0;int pos_2 = -1;int pos_3 = 0;string word = "";vector<int> vec;int length = strline_right.length();while(pos_2 < length){//前面的分词标记pos_1 = pos_2;//后面的分词标记pos_2 = strline_right.find('/', pos_1 + 1);if(pos_2 > pos_1){//将两个分词标记之间的单词取出word  = strline_right.substr(pos_1 + 1, pos_2 - pos_1 - 1);//依据单词去输入序列中查出出现的位置pos_3 = strline_in.find(word, pos_3);//将位置存入数组vec.push_back(pos_3);pos_3 = pos_3 + word.size();}else{break;}}return vec;
}/** 获取标准切分和程序切分的结果*/
string getString(string word, int pos, vector<int> vec_right){char ss[1000];int i = 0;int k = 0;while(vec_right[i] < pos){i++;}for(int j = 0; j < word.size(); j++){if(j == vec_right[i] - pos){if(j != 0){ss[k] = '/';++k;}++i;}ss[k] = word[j];++k;}ss[k] = '\0';string word_str = ss;return word_str;
}/** 函数功能:获取单个句子切分的结果统计* 函数输入:1.vec_right 正确的分词标记位置集合*           2.vec_out   函数切分得到的分词标记位置集合* 函数输出:返回一个veceor。含有4个元素,分别为:*          切分正确、组合型歧义、未登录词、交集型歧义的数量**/
vector<int> getCount_2(string strline, vector<int> vec_right, vector<int> vec_out, vector<string> &vec_err){vector<int> vec(4, 0);    //存放计算结果//建立mapmap<int, int> map_result;for(int i = 0; i < vec_right.size(); i++){map_result[vec_right[i]] += 1;}for(int i = 0; i < vec_out.size(); i++){map_result[vec_out[i]] += 2;}//统计map中的信息//若value=1。仅仅在vec_right中//若value=2,仅仅在vec_out中//若value=3。在vec_right和vec_out中都有map<int, int>::iterator p_pre, p_cur;int count_value_1 = 0;int count_value_2 = 0;int count_value_3 = 0;p_pre = map_result.begin();p_cur = map_result.begin();while(p_cur != map_result.end()){while(p_cur != map_result.end() && p_cur -> second == 3){p_pre = p_cur;++count_value_3;   //切分正确的数目++p_cur;     //迭代器后移}while(p_cur != map_result.end() && p_cur -> second != 3){if(p_cur -> second == 1){++count_value_1;}else if(p_cur -> second == 2){++count_value_2;}++p_cur;}//确定切分错误的字符串if(p_cur == map_result.end() && p_cur == (++p_pre)){continue;}int pos_1 = p_pre -> first;int pos_2 = p_cur -> first; string word = strline.substr(pos_1, pos_2 - pos_1); //切分错误的单词string word_right = getString(word, pos_1, vec_right);    //正确的切分方式string word_out = getString(word, pos_1, vec_out);    //得到的切分方式string str_err = "";//不同的错误类型       if(count_value_1 > 0 && count_value_2 == 0){str_err = "  组合型歧义: " + word + "    正确切分: " + word_right + "    错误切分: " + word_out;vec_err.push_back(str_err);cout << str_err << endl;vec[1] += count_value_1;      }else if(count_value_1 == 0 && count_value_2 > 0){str_err = "  未登录词语: " + word + "    正确切分: " + word_right + "    错误切分: " + word_out;vec_err.push_back(str_err);cout << str_err << endl;vec[2] += count_value_2;}else if(count_value_1 > 0 && count_value_2 > 0){str_err = "  交集型歧义: " + word + "    正确切分: " + word_right + "    错误切分: " + word_out;vec_err.push_back(str_err);cout << str_err << endl;vec[3] += count_value_2;    }//计数器复位count_value_1 = 0;count_value_2 = 0;}vec[0] += count_value_3;   return vec;
}/** 主函数:进行分词并统计分词结果**/
int main(int argc, char *argv[]){long time_1 = getCurrentTime();string strline_right;  //输入语料:用作标准分词结果string strline_in;    //去掉分词标记的语料(用作分词的输入)string strline_out_1; //正向最大匹配分词完成的语料string strline_out_2;    //逆向最大匹配分词完成的语料string strline_out_3;    //最大概率方法分词完成的语料ifstream fin("test.txt");  //打开输入文件if(!fin){cout << "Unable to open input file !" << argv[1] << endl;exit(-1);}/*ofstream fout("result.txt");    //确定输出文件if(!fout){cout << "Unable to open output file !" << endl;exit(-1);}*/long count = 0;         //句子编号long count_0 = 0;        //三种方法切分都正确的句子总数long count_1 = 0;      //正向最大匹配全然正确的句子总数long count_2 = 0;     //逆向最大匹配全然正确的句子总数long count_3 = 0;     //最大概率方法全然正确的句子总数long count_right_all = 0; //准确的切分总数long count_out_1_all = 0; //正向最大匹配切分总数long count_out_2_all = 0;  //逆向最大匹配切分总数long count_out_3_all = 0;  //最大概率方法切分总数long count_out_1_right_all = 0;    //正向最大匹配切分正确总数long count_out_2_right_all = 0;  //逆向最大匹配切分正确总数long count_out_3_right_all = 0;  //最大概率方法切分正确总数long count_out_1_fail_1_all = 0;//正向最大匹配(组合型歧义)long count_out_1_fail_2_all = 0;//正向最大匹配(未登录词语)long count_out_1_fail_3_all = 0;//正向最大匹配(交集型歧义)long count_out_2_fail_1_all = 0;//逆向最大匹配(组合型歧义)long count_out_2_fail_2_all = 0;//逆向最大匹配(未登录词语)long count_out_2_fail_3_all = 0;//逆向最大匹配(交集型歧义)long count_out_3_fail_1_all = 0;//最大概率方法(组合型歧义)long count_out_3_fail_2_all = 0;//最大概率方法(未登录词语)long count_out_3_fail_3_all = 0;//最大概率方法(交集型歧义)vector<string> vec_err_1; //正向最大匹配切分错误的词vector<string> vec_err_2;   //逆向最大匹配切分错误的词vector<string> vec_err_3;   //最大概率方法切分错误的词while(getline(fin, strline_right, '\n') && count < MaxCount){if(strline_right.length() > 1){//去掉分词标记strline_in = removeSeparator(strline_right);//正向最大匹配分词strline_out_1 = strline_right;strline_out_1 = SegmentSentenceMM(strline_in, 1);//逆向最大匹配分词strline_out_2 = strline_right;strline_out_2 = SegmentSentenceMM(strline_in, 2);//最大概率方法分词strline_out_3 = strline_right;strline_out_3 = SegmentSentenceMM(strline_in, 3);//输出分词结果count++;cout << "----------------------------------------------" << endl;cout << "句子编号:" << count << endl;cout << endl;cout << "待分词的句子长度: " << strline_in.length() << "  句子:" << endl;cout << strline_in << endl;cout << endl;cout << "标准比对结果长度: " << strline_right.length() << "  句子:" << endl;cout << strline_right << endl;cout << endl;cout << "正向匹配分词长度: " << strline_out_1.length() << "  句子:" << endl;cout << strline_out_1 << endl;cout << endl;cout << "逆向匹配分词长度: " << strline_out_2.length() << "  句子:" << endl;cout << strline_out_2 << endl;cout << endl;cout << "最大概率分词长度: " << strline_out_3.length() << "  句子:" << endl;cout << strline_out_3 << endl;cout << endl;//输出分词结果的数字序列表示vector<int> vec_right = getPos(strline_right, strline_in);vector<int> vec_out_1 = getPos(strline_out_1, strline_in);vector<int> vec_out_2 = getPos(strline_out_2, strline_in);vector<int> vec_out_3 = getPos(strline_out_3, strline_in);cout << "标准结果:" << endl;for(int i = 0; i < vec_right.size(); i++){cout << setw(4) << vec_right[i];}cout << endl;cout << "正向匹配结果:" << endl;for(int i = 0; i < vec_out_1.size(); i++){cout << setw(4) << vec_out_1[i];}cout << endl;cout << "逆向匹配结果:" << endl;for(int i = 0; i < vec_out_2.size(); i++){cout << setw(4) << vec_out_2[i];}cout << endl;cout << "最大概率结果:" << endl;for(int i = 0; i < vec_out_3.size(); i++){cout << setw(4) << vec_out_3[i];}cout << endl;//输出匹配的错误列表if(vec_right == vec_out_1 && vec_right == vec_out_2 && vec_right == vec_out_3){count_0++;}cout << endl;if(vec_right == vec_out_1){cout << "正向最大匹配全然正确!" << endl;count_1++;}else{cout << "正向最大匹配错误列表:" << endl;}vector<int> vec_count_1 = getCount_2(strline_in, vec_right, vec_out_1, vec_err_1);cout << endl;if(vec_right == vec_out_2){cout << "逆向最大匹配全然正确!" << endl;count_2++;}else{cout << "逆向最大匹配错误列表:" << endl;}vector<int> vec_count_2 = getCount_2(strline_in, vec_right, vec_out_2, vec_err_2);cout << endl;if(vec_right == vec_out_3){cout << "最大概率方法全然正确!" << endl;count_3++;}else{cout << "最大概率方法错误列表:" << endl;}vector<int> vec_count_3 = getCount_2(strline_in, vec_right, vec_out_3, vec_err_3);cout << endl;//准确的切分数量int count_right = vec_right.size();//切分得到的数量int count_out_1 = vec_out_1.size();int count_out_2 = vec_out_2.size();int count_out_3 = vec_out_3.size();//切分正确的数量int count_out_1_right = vec_count_1[0];int count_out_2_right = vec_count_2[0];int count_out_3_right = vec_count_3[0];cout << "正向最大匹配:" << endl;   cout << "  组合型歧义:" << vec_count_1[1] << endl;cout << "  未登录词语:" << vec_count_1[2] << endl;cout << "  交集型歧义:" << vec_count_1[3] << endl;cout << "逆向最大匹配:" << endl; cout << "  组合型歧义:" << vec_count_2[1] << endl;cout << "  未登录词语:" << vec_count_2[2] << endl;cout << "  交集型歧义:" << vec_count_2[3] << endl;cout << "最大概率方法:" << endl; cout << "  组合型歧义:" << vec_count_3[1] << endl;cout << "  未登录词语:" << vec_count_3[2] << endl;cout << "  交集型歧义:" << vec_count_3[3] << endl;count_right_all += count_right;count_out_1_all += count_out_1;count_out_2_all += count_out_2;count_out_3_all += count_out_3;count_out_1_right_all += count_out_1_right;count_out_2_right_all += count_out_2_right;count_out_3_right_all += count_out_3_right;count_out_1_fail_1_all += vec_count_1[1];count_out_1_fail_2_all += vec_count_1[2];count_out_1_fail_3_all += vec_count_1[3];count_out_2_fail_1_all += vec_count_2[1];count_out_2_fail_2_all += vec_count_2[2];count_out_2_fail_3_all += vec_count_2[3];count_out_3_fail_1_all += vec_count_3[1];count_out_3_fail_2_all += vec_count_3[2];count_out_3_fail_3_all += vec_count_3[3];}}long time_2 = getCurrentTime();unsigned long file_size = getFileSize("test.txt");//打印错误的切分内容    cout << endl;cout << "---------------------------------" << endl;cout << "错误例子(已排序):" << endl;//选取样本(600个),去掉反复的//vector<string> vec_small(vec_err.begin(), vec_err.begin() + 600);//sort(vec_small.begin(), vec_small.end());//vector<string>::iterator end_unique = unique(vec_small.begin(), vec_small.end());//对错误切分内容进行排序并掉反复的sort(vec_err_1.begin(), vec_err_1.end());sort(vec_err_2.begin(), vec_err_2.end());sort(vec_err_3.begin(), vec_err_3.end());vector<string>::iterator end_unique_1 = unique(vec_err_1.begin(), vec_err_1.end());vector<string>::iterator end_unique_2 = unique(vec_err_2.begin(), vec_err_2.end());vector<string>::iterator end_unique_3 = unique(vec_err_3.begin(), vec_err_3.end());int num_1 = end_unique_1 - vec_err_1.begin();int num_2 = end_unique_2 - vec_err_2.begin();int num_3 = end_unique_3 - vec_err_3.begin();cout << "----------------------------------" << endl;cout << "正向最大匹配切分错误数量:" << num_1 << endl;for(int i = 0; i < num_1; i++){cout << vec_err_1[i] << endl;}cout << endl;cout << "----------------------------------" << endl;cout << "逆向最大匹配切分错误数量:" << num_2 << endl;for(int i = 0; i < num_2; i++){cout << vec_err_2[i] << endl;}cout << endl;cout << "----------------------------------" << endl;cout << "最大概率方法切分错误数量:" << num_3 << endl;for(int i = 0; i < num_3; i++){cout << vec_err_3[i] << endl;}cout << endl;//计算准确率和召回率double kk_1 = (double)count_out_1_right_all / count_out_1_all;   //正向最大匹配准确率double kk_2 = (double)count_out_1_right_all / count_right_all;  //正向最大匹配召回率double kk_3 = (double)count_out_2_right_all / count_out_2_all;  //逆向最大匹配准确率double kk_4 = (double)count_out_2_right_all / count_right_all;  //逆向最大匹配召回率double kk_5 = (double)count_out_3_right_all / count_out_3_all;  //最大概率方法准确率double kk_6 = (double)count_out_3_right_all / count_right_all;  //最大概率方法召回率//集中输出结果cout << endl;cout << "---------------------------------" << endl;cout << "分词消耗时间:" << time_2 - time_1 << "ms" << endl;cout << "測试文件大小:" << file_size/1024 << " KB" << endl;cout << "分词速度为:  " << (double)file_size*1000/((time_2 - time_1)*1024) << " KB/s" << endl;cout << endl;cout << "词典规模:" << word_dict.size << endl;cout << endl;cout << "句子总数:" << count << endl;cout << "三种方法切分都正确的句子数目:   " << count_0 << "\t ( " << (double)count_0*100/count << " % )" << endl;cout << "正向最大匹配全然正确的句子数目: " << count_1 << "\t ( " << (double)count_1*100/count << " % )" << endl;cout << "逆向最大匹配全然正确的句子数目: " << count_2 << "\t ( " << (double)count_2*100/count << " % )" << endl;cout << "最大概率方法全然正确的句子数目: " << count_3 << "\t ( " << (double)count_3*100/count << " % )" << endl;cout << endl;cout << "准确的切分总数:" << count_right_all << endl;        //准确的切分总数cout << "正向匹配切分总数:" << count_out_1_all << endl;     //正向匹配切分总数cout << "逆向匹配切分总数:" << count_out_2_all << endl;        //逆向匹配切分总数cout << "最大概率切分总数:" << count_out_3_all << endl;        //最大概率切分总数cout << "正向匹配切分正确总数:" << count_out_1_right_all << endl;    //正向匹配切分正确总数cout << "逆向匹配切分正确总数:" << count_out_2_right_all << endl;  //逆向匹配切分正确总数cout << "最大概率切分正确总数:" << count_out_3_right_all << endl;  //逆向匹配切分正确总数cout << endl;cout << "正向最大匹配:" << endl;long count_out_1_fail_all = count_out_1_fail_1_all + count_out_1_fail_2_all + count_out_1_fail_3_all;  cout << "  组合型歧义:" << count_out_1_fail_1_all << "\t ( " << (double)count_out_1_fail_1_all*100/count_out_1_fail_all << " % )" << endl;cout << "  未登录词语:" << count_out_1_fail_2_all << "\t ( " << (double)count_out_1_fail_2_all*100/count_out_1_fail_all << " % )" << endl;cout << "  交集型歧义:" << count_out_1_fail_3_all << "\t ( " << (double)count_out_1_fail_3_all*100/count_out_1_fail_all << " % )" << endl;cout << "逆向最大匹配:" << endl;   long count_out_2_fail_all = count_out_2_fail_1_all + count_out_2_fail_2_all + count_out_2_fail_3_all;    cout << "  组合型歧义:" << count_out_2_fail_1_all << "\t ( " << (double)count_out_2_fail_1_all*100/count_out_2_fail_all << " % )" << endl;cout << "  未登录词语:" << count_out_2_fail_2_all << "\t ( " << (double)count_out_2_fail_2_all*100/count_out_2_fail_all << " % )" << endl;cout << "  交集型歧义:" << count_out_2_fail_3_all << "\t ( " << (double)count_out_2_fail_3_all*100/count_out_2_fail_all << " % )" << endl;cout << "最大概率方法:" << endl;   long count_out_3_fail_all = count_out_3_fail_1_all + count_out_3_fail_2_all + count_out_3_fail_3_all;    cout << "  组合型歧义:" << count_out_3_fail_1_all << "\t ( " << (double)count_out_3_fail_1_all*100/count_out_3_fail_all << " % )" << endl;cout << "  未登录词语:" << count_out_3_fail_2_all << "\t ( " << (double)count_out_3_fail_2_all*100/count_out_3_fail_all << " % )" << endl;cout << "  交集型歧义:" << count_out_3_fail_3_all << "\t ( " << (double)count_out_3_fail_3_all*100/count_out_3_fail_all << " % )" << endl;cout << endl;       cout << "统计结果:" << endl;cout << "正向最大匹配    准确率:" << kk_1*100 << "%  \t召回率:" << kk_2*100 << "%" << endl;cout << "逆向最大匹配    准确率:" << kk_3*100 << "%  \t召回率:" << kk_4*100 << "%" << endl;cout << "最大概率方法    准确率:" << kk_5*100 << "%  \t召回率:" << kk_6*100 << "%" << endl;return 0;
}

最大概率法分词及性能測试相关推荐

  1. 正向(逆向)最大匹配和最大概率法分词的错误分析

    正向最大匹配.逆向最大匹配.最大概率法是最简单的三种分词方式.本文从这三种分词方法产生的错误入手,观察他们分词的优缺点. 1.基本情况 从语料中选取了200个句子作为样本,分别用三种不同的分词方式进行 ...

  2. 最大概率法分词及性能测试

    最大概率分词是一种最基本的统计方法分词.一个待分割的字符串有多种分词结果,最大概率分词的原则是将其中概率最大的那个作为该字符串的分词结果. 第一部分 理论基础 如对一个字符串: S:有意见分歧 分词结 ...

  3. Linux 性能測试工具

    Linux 性能測试工具 linux performance 查看系统配置 查看CPU信息 lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64- ...

  4. 微博达人硅谷之歌:Testin云測移动搜索性能測试非常是让人信服

    微博达人硅谷之歌:Testin云測移动搜索性能測试非常是让人信服 2014/10/08 · Testin · 开发人员訪谈 2013年11月1日,谷歌运行董事长施密特(Eric Emerson Sch ...

  5. mongodb3.0 性能測试报告 一

    mongodb3.0 性能測试报告 一 mongodb3.0 性能測试报告 二 mongodb3.0 性能測试报告 三 測试环境: 服务器:X86 pcserver   共6台 cpu:  单颗8核 ...

  6. 最大概率法分词中词频惩罚因子的作用探究

    在最大概率法分词的程序中,由于每个词出现的次数分布非常不均匀,而且我们要计算每个词出现的概率,对于出现次数比较少的词概率就很小,求句子中词的概率之积的时候,需要将好多非常小的数作乘法,可能会将超出计算 ...

  7. 软件安全性能測试(转载)

    近来,在我负责的公司某软件产品的最后測试工作,经常被问到这样一个问题:在做測试过程中,我们的软件产品在安全性方面考虑了多少?应该怎样測评一个软件究竟有多安全? 这个软件因为涉及客户商业上重要的信息资料 ...

  8. H264编码器性能測试

    版本号:0.1.0-beta 作者:石硕 更新:2014-04-13 15:54:08 ======================================================== ...

  9. Android App性能測试

    一.内存 1.查看单个应用App最大内存限制 Command:adb shell "getprop|grep heapgrowthlimit" C:\Users\hujiachun ...

最新文章

  1. “上海名媛群”事件,我来说几句
  2. 成功解决\PyInstaller\compat.py, line 378  out = out.decode(encoding) UnicodeDecodeError: utf-8 codec c
  3. Android--ViewPager点击按钮切换下一页
  4. 论文浅尝 - ICLR2020 | 通过神经逻辑归纳学习有效地解释
  5. 关于java重载函数,参数为null时,调用的处理。(精确性原则)
  6. 中断占据CPU时间的计算问题
  7. 传智168期 day61 redis 笔记(2017年8月25日19:16:30)
  8. c语言sigaction,C语言中的Sigaction和setitimer
  9. 用户故事与敏捷方法—用户故事不良症兆
  10. RISCV-MCU启航篇之硬件选择-GD32VF103芯片
  11. 如何在计算机中快速新建TXT文本文档
  12. 计算机如何连接网络扫描仪,windows系统下怎么共享扫描仪?
  13. Yate for Mac(音乐标签管理工具)
  14. 【5G UP】5G QoS参数那点事儿
  15. html外链自动加nofollow,WordPress文章/页面外链自动添加nofollow属性的方法
  16. CPU卡调试助手(FMCOS)
  17. Linux OTA升级
  18. 积分-钉钉考勤-告警
  19. 【安全开发】IOS安全编码规范
  20. DIY 3D打印机——【有啥用啥版】

热门文章

  1. 外贸网站建设(转自xmfish)
  2. Eclipse编写第一个Java程序
  3. 什么是软件测试,软件测试需要注意些什么?
  4. stats | 广义线性模型(二)——泊松回归
  5. android8 压力触控,压力感应触摸屏的原理说明
  6. 学Python的女生好不好找工作?
  7. 学习前端开发,自学真的能成功?
  8. bp算法运行太慢_神经网络,BP算法的理解与推导
  9. windows linux内核版本,微软决定在Windows10中发布一个完整的Linux内核
  10. linux内存管理(九)-缺页异常分析