栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

NLP实验四:汉语分词

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

NLP实验四:汉语分词

一、 实验目的
  1. 深入理解汉语分词的基本概念。
  2. 掌握并实现前向最大匹配算法、后向最大匹配算法和最少分词法。
  3. 掌握分词的评价指标,学会计算正确率、召回率和F-测度值。
二、 实验内容

利用人民日报语料库或自己构建的语料库(30词以上)作为词典,任选五个句子,并基于正向最大匹配算法和最短路径法分别对这五个句子进行分词,并分别计算分词结果的正确率,召回率和F-测度值。输出句子,基于两种算法的分词结果和其对应的评价指标值。

三、 实验环境

操作系统:macOS Monterey 12.4
IDE:CLion 语言:C++
中文编码:UTF-8

四、 细节解析
4.1 建立词典
定义unordered_set类型全局变量dictionary,用于判断子串是否是词典中的词语,词典中的词语来自pku_training.txt。m表示词典中最长单词的字数*2,为什么要乘以2呢?因为在UTF-8编码中一个汉字占两个char。首先创建ifstream对象ifs,使用getlion方法按行读入字符串至read,再用read实例化stringstream 对象str,这样就可以根据空格提取该句子的每个词了,储存至dictionary中。
 
图1 建立词典
	前向最大匹配
前向最大匹配算法思路较简单,int L表示现在遍历到的左端点,len表示现在考虑的字符串长度。每次通过L和len,调用substr( )函数得到子串,判断是否在词典中。若不在词典中,则len--;若在词典中,则在该词末尾添加分词标记,并更新L = L+len。直到L 等于句子长度后结束算法。注意,由于在UTF-8编码中一个汉字占两个char,部分操作需要考虑乘2。
 
图2 前向最大匹配
	后向最大匹配
后向最大匹配算法与前向类似,int R表示现在遍历到的右端点len表示现在考虑的字符串长度。每次通过R和len,调用substr( )函数得到子串,判断是否在词典中。若不在词典中,则len--;若在词典中,则在该词末尾添加分词标记,并更新R = R - len。

 
图3 后向最大匹配

	最小分词
最小分词算法可分为两步进行。
第一步是建图,需要建立一个结点数为n+1的有限无环图,其中n为输入字符串的长度。首先在相邻节点之间建立有向边,再依次枚举j和k(这里k从j开始枚举到min(字符串长度,m+j ) 结束,m为最长的词语的长度。这样能有效优化时间复杂度。),如果下标从j到k的子串在词典中,那么就连一条从j到k+1的有向边。
 第二步是计算最短路,这里我使用了堆优化的迪杰斯特拉算法,时间复杂度为O(n logn)。在计算过程中需要记录最短路径,我定义了pre[]来记录某点是由哪个点转移过来的。
 
图4 最小分词算法









 
图5 堆优化Dijkstra求最短路





	结果展示与评价指标计算
我使用了pku_training.txt中的词语作为词典,最终的结果如下。
 
图6 实验结果展示
5.1 最大匹配算法验证
输入:他是研究生命科学的一位科学家。
前向最大匹配分词结果:他/是/研究生/命/科学/的/一/位/科学家/。
后向最大匹配分词结果:他/是/研究/生命科学/的/一/位/科学家/。
结果符合预期。
输入:青年大学习还没完成的同学抓紧时间完成。
前向最大匹配分词结果:青年/大学/习/还/没完/成/的/同学/抓紧/时间/完成/。
后向最大匹配分词结果:青年/大/学习/还/没/完成/的/同学/抓紧/时间/完成/。
结果符合预期。
5.2 最小分词算法验证
输入:他是研究生命科学的一位科学家。
最小分词算法结果:他/是/研究/生命科学/的/一/位/科学家/。(词个数:9)
另一种候选结果:他/是/研究生/命/科学/的/一/位/科学家/。 (词个数:10)
结果符合预期。

5.3 评价指标计算
正确的分词结果应该是:
1  他/是/研究/生命科学/的/一/位/科学家/。
2  青年大学习/还/没/完成/的/同学/抓紧/时间/完成/。
3  今天/是/肯德基/疯狂/星期四/。
4  明日/起/将/全面/恢复/全市/正常/生产/生活/秩序/。
5  在/北京大学/生活区/喝/进口/白酒/。    
前向最大匹配分词结果:
1  他/是/研究生/命/科学/的/一/位/科学家/。
2  青年/大学/习/还/没完/成/的/同学/抓紧/时间/完成/。
3  今天/是/肯德基/疯狂/星期四/。
4  明日/起/将/全面/恢复/全市/正常/生产/生活/秩序/。
5  在/北京大学/生活区/喝/进口/白酒/。
后向最大匹配分词结果:
1  他/是/研究/生命科学/的/一/位/科学家/。
2  青年/大/学习/还/没/完成/的/同学/抓紧/时间/完成/。
3  今天/是/肯德基/疯狂/星期四/。
4  明日/起/将/全面/恢复/全市/正常/生产/生活/秩序/。
5  在/北京大学/生活区/喝/进口/白酒/。
最小分词算法结果:
1  他/是/研究/生命科学/的/一/位/科学家/。
2  青年/大/学习/还/没/完成/的/同学/抓紧/时间/完成/。
3  今天/是/肯德基/疯狂/星期四/。
4  明日/起/将/全面/恢复/全市/正常/生产/生活/秩序/。
5  在/北京大学/生活区/喝/进口/白酒/。
对于FMM,正确切分的个数有38个,系统所有输出结果有46个;标准答案个数有43个。BMM和最小分词法输出结果相同,正确切分的个数有42个,系统所有输出结果有45个;标准答案个数有43个。其中F-测度值的beta=1,计算结果如下表。

表1 评价指标值                               %       
分词方法	正确率	召回率	F-测度值
FMM	82.61	88.37	85.39
BMM	93.33	97.67	95.45
最小分词法	93.33	97.67	95.45

	复杂度分析
建立词典需要遍历文件,C++ 中unordered_set由哈希表实现,每次操作时间复杂度O(1),故建立词典需要O(n)的时间复杂度,n为数据集大小。
最大匹配法在最坏的情况下时间复杂度为O(nm),其中m为词典中最长词语的长度,n为输入的句子长度。
最小分词在建图的过程中,时间复杂度为O(nm),其中m为词典中最长词语的长度,n为输入的句子长度。求最短路用到了堆优化的迪杰斯特拉,时间复杂度为O(m logn),n 表示点数,m表示边数。
整个算法的空间复杂度为O(n),n为数据集大小。

代码
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
typedef long long LL;
typedef pair PII;
const int n=5;
string str[5];
unordered_set dictionary;//词典
int m; //词典中最长单词的字数*2

void creatDic(){
    ifstream ifs("/Users/a26012/Desktop/大二下/NLP/实验/实验4/pku_training.txt");
    ifs.unsetf(ios_base::skipws);

    string read,word;
    while(getline(ifs, read))
    {
        stringstream mystr(read);
        while(mystr>>word){
               if(!dictionary.count(word)) dictionary.insert(word);
               m = max(m,(int)word.size());
        }
    }
}
void maximumMatching(){
    vector ans;
    string temp;

    cout<<"The result of FMM:n";
    for(int i=0;i
        if(!ans.empty()) ans.clear();
        int sz=str[i].size(),l=0,len;
        while(l
            for(len=min(m,sz-l);len!=0;len-=2){
                temp=str[i].substr(l,len);
                if(len==2||dictionary.count(temp)){
                    ans.push_back(temp);
                    l=l+len;
                    break;
                }
            }
        }
        for(int j=0;j
        if(!ans.empty()) ans.clear();
        int sz=str[i].size(),r=sz-1,len;
        while(r>0){
            for(len=min(m,r+1);len!=0;len-=2){
                temp=str[i].substr(r-len+1,len);
                if(len==2||dictionary.count(temp)){
                    ans.push_back(temp);
                    r=r-len;
                    break;
                }
            }
        }
        for(int j=ans.size()-1;j>=0;j--)  cout< mp[510];
bool st[510];
int pre[510],nextt[510],dis[510];
void Dijkstra(int sz){

    priority_queue, greater> heap;
    memset(st,false,sizeof (st));
    memset(dis,0x3f,sizeof (dis));
    memset(pre,0x3f,sizeof (pre));
    memset(nextt,0,sizeof (nextt));
    dis[0]=0;
    heap.push({0, 0});

    while (!heap.empty())
    {
        auto t = heap.top();
        heap.pop();
        int ver = t.second, distance = t.first;
        if (st[ver]) continue;
        st[ver] = true;
        for (int nx:mp[ver])
        {
            if (dis[nx] > distance + 1)
            {
                dis[nx] = distance + 1;
                pre[nx] = ver;
                heap.push({dis[nx], nx});
            }
        }
    }
    int nw=sz-1;
    while(nw!=0){
        int nx=pre[nw];
        nextt[nx]=nw;
        nw=nx;
    }
}

vector res;
void leastWord(){
    cout<<"nThe result of Least word segmentation:n";
    for(int i=0;i
        if(!res.empty()) res.clear();
        int sz=(int)str[i].size()/2;
        //建图
        for(int j=0; j <= sz; j++){
            if(!mp[j].empty()) mp[j].clear();
            if(j!=sz) mp[j].push_back(j+1);
        }
        for(int j=0;j<=sz;j++){
            for(int k=j+1;k
                string temp = str[i].substr(j*2,(k-j+1)*2);
                if(dictionary.count(temp)){
                    mp[j].push_back(k+1);
                }
            }
        }
        Dijkstra(sz+1); //调用Dijkstra算法
        int nw=0;
        while(nw!=sz){
            int nx=nextt[nw];
            res.push_back(str[i].substr(nw*2,(nx-nw)*2));
            nw=nx;
        }

        for(int j=0;j
            cout<   cout<<"Please input five sentences:n";
    creatDic(); //建立词典
    for(int i=0;i
        cin>>str[i];
    }

    maximumMatching(); //最大匹配算法
    leastWord(); //最小分词法

    return 0;
}

//他是研究生命科学的一位科学家。
//青年大学习还没完成的同学抓紧时间完成。
//今天是肯德基疯狂星期四。
//明日起将全面恢复全市正常生产生活秩序。
//在北京大学生活区喝进口白酒。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1005148.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号