深圳大学信息检索:索引构建和压缩的实验
深 圳 大 学 实 验 报 告
课程名称: 信息检索
实验项目名称: 索引构建和压缩的实验
实验目的与要求: 实验目的:掌握文本数据集的统计分析方法,以及索引的构建和压缩技术。 实验要求: (1). 针对附件“HW3.txt”中的600个文档(每行表示一个document,文档ID为1至600):(i)使用jieba中文分词(https://pypi.org/project/jieba/)或其他中文分词工具进行分词;(ii)统计600个文档中的token的总数和term的总数;(iii)构建倒排索引,并输出以下七组查询的文档ID:“迁移”,“迁移学习”,“推荐”,“深度学习”,“隐私”,“跨领域”,“跨域”。 请在报告中附上代码截图(不要复制源代码,请用截图的方式)、运行结果截图和详细的文字说明。程序要有详细的注释。(40) (2). 阅读教材《Introduction to Information Retrieval》第97页Figure 5.8中所描述的采用VB的编码和解码过程(VB encoding and decoding),并用Java语言或其他常用语言实现该算法。要求在三个数字(113,309,720)上验证实现的算法的正确性。 注意使用合理的数据结构。 请在报告中附上代码截图(不要复制源代码,请用截图的方式)、运行结果截图和详细的文字说明。程序要有详细的注释。(20) (3). 阅读教材《Introduction to Information Retrieval》第98-99页(Section 5.3.2)中所描述的采用Gamma codes的编码和解码过程(encoding and decoding of Gamma codes),并用Java语言或其他常用语言实现该算法。要求在三个数字(113,309,720)上验证算法的正确性。 注意使用合理的数据结构。 请在报告中附上代码截图(不要复制源代码,请用截图的方式)、运行结果截图和详细的文字说明。程序要有详细的注释。(20) 报告写作。要求:主要思路有明确的说明,重点代码有详细的注释,行文逻辑清晰、可读性强,报告整体写作较为专业。(20分) |
(1). 针对附件“HW3.txt”中的600个文档(每行表示一个document,文档ID为1至600):(i)使用jieba中文分词(https://pypi.org/project/jieba/)或其他中文分词工具进行分词;(ii)统计600个文档中的token的总数和term的总数;(iii)构建倒排索引,并输出以下七组查询的文档ID:“迁移”,“迁移学习”,“推荐”,“深度学习”,“隐私”,“跨领域”,“跨域”。 请在报告中附上代码截图(不要复制源代码,请用截图的方式)、运行结果截图和详细的文字说明。程序要有详细的注释。(40) +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 代码截图和详细的文字说明: 在VirtualBox虚拟机,在Linux环境下运行Ubuntu系统,使用jieba分词工具的C++版本完成本题。 以下是完成实验的过程。完整代码截图和文字说明附在最后。 1.从github上下载cppjieba,下载链接https://github.com/yanyiwu/cppjieba。然后将下载的cppjieba-master文件夹以共享文件夹的方式,挂载到虚拟机内的share-dir/目录下。 2.下载jieba分词必须的依赖软件。在虚拟机内下载cmake和g++。 3.输入cmake..命令编译cppjieba软件包。 4.使用make命令编译代码,生成可执行文件。 5.使用test命令测试软件状态。Cppjieba在当前环境下运行正常。 6.输入./demo命令,运行测试程序。可见,cppjieba正确进行了分词。 7.将待分词的文件HW3.txt放进共享文件夹。在虚拟机内查看发现当前目录下存在该文件。这么做的原因是虚拟机Linux环境下运行的分词程序无法读取虚拟机外的文件。必须要把待分词文件放进虚拟机。 8.修改demo文件,让它可以对HW3.txt文件进行分词。需要注意的是。我们希望这个程序在虚拟机内运行,因此文件引用地址需要改为虚拟机内的文件地址。 分词结果,可见成功导入文件内容。 9.修改软件包中自带的demo文件。使之能够胜任分词HW3.txt文件的任务。注意,由于我使用的是DEV-C++,DEV的默认编码是ANSI。这与TXT文件和demo文件的中文编码不同。Cppjieba下载页面推荐使用UTF-8编码,因此将三种编码统一至UTF-8。 通过控制台查阅发现本机采用的默认文本编码是GBK编码,因此将HW3.txt另存为UTF-8格式的。然后用记事本打开demo.cpp文件,再另存为UTF-8格式。 具体代码参考实验1的代码。为了适应cppjieba的功能和实现中文分词,在一些细节上有所调整。 原始代码是直接输出limonp::Join(words.begin(), words.end(), "/")的返回结果。为了构建倒排列表,需要将输出的字符串暂时存储。然而定义一个string类型的数据来接收函数返回值会导致虚拟机内make指令报错。由于无法在程序包的大量程序文件中找到函数源代码,故猜测因为返回值数据类型不兼容。因此通过流操作来洗掉原类型,这样成功使分词结果以string类型变量存储。 词条化过程中,除了和实验一一样需要归一为小写,去除连字符以外的其他符号,还需要保留中文。因此我们通过判断ASCII码首位的方法来判断当前字符是否为中文。
读取文件、构建倒排列表的代码。 10.在虚拟机内运行初步修改过的代码(这里的代码还没有去掉输出原文本,上面的图是终版代码截的) 报错段错误。 11.网上查找ubuntu系统这样报错的原因。网络资料提出有可能是范围越界。初始给倒排文件开了450的大小,猜测可能是词项数量多于450。 将HW3数据放入word打开, 显示一万多字。显然450不够。于是将倒排列表改为1999的大小。 修改后运行,成功输出倒排索引表 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ (i)使用jieba中文分词(https://pypi.org/project/jieba/)或其他中文分词工具进行分词 分词结果如下: 实现代码只需修改原demo提供的代码即可。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ (ii)统计600个文档中的token的总数和term的总数 有token为词条,term为词项。预处理会将原始文本拆分为大量词条,然后将其统计归纳产生词项数据。 每读入一个词就使词条数量+1,每检索到一个不在词典中的词就将其添加到词典中,并使词项数量+1。 统计结果为:token数量为6007,term数量为1121。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ (iii)构建倒排索引,并输出以下七组查询的文档ID:“迁移”,“迁移学习”,“推荐”,“深度学习”,“隐私”,“跨领域”,“跨域” “迁移”:两种方法。一是包含“迁移学习”的必包含“迁移”。把“迁移”和“迁移学习”的文档ID全加起来即可。 二是使用FullSegment功能,调用CutAll函数,把所有包含“迁移”的内容滚动切词全部输出。 实际实验时发现,cppjieba的功能强大,不需要额外操作就把两种情况全部区分出来了。 在输出的倒排索引中寻找要求的词条。 迁移: 深度学习: 推荐: 发现在浩如烟海的词条中搜索这些词实在太过麻烦。而在代码中添加查询又会导致一些难以修改的BUG,因此决定将倒排记录表输出到文件。 修改代码后发现虚拟机终端无倒排文件显示。 从主系统进入共享文件夹目录,找到文件如下: 使用word打开,搜索待查找的七个词。 查找截图和结果如下: 迁移:25 376 380 384 423 424 435 436 437 438 439 440 447 448 449 463 481 490 491 492 507 508 512 513 522 523 524 525 530 538 546 558 562 565 568 568 569 585 593 594 迁移学习:246 288 364 365 366 367 368 369 372 373 374 375 378 379 381 382 383 385 386 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 408 409 410 411 412 413 414 415 416 417 418 419 420 425 426 427 428 429 430 431 432 433 434 441 442 443 444 445 446 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 504 505 506 509 510 511 514 515 516 517 518 519 520 521 526 527 528 529 531 532 533 534 535 536 537 539 550 551 554 556 563 566 570 573 579 581 582 584 590 596 597 598 599 600 推荐:1 5 6 7 8 14 18 20 22 23 24 25 26 27 29 30 31 32 33 34 35 36 38 39 40 41 42 43 44 45 46 47 49 50 51 52 53 54 55 56 60 62 65 66 67 68 72 73 74 75 77 78 79 81 82 84 85 86 87 90 91 94 97 98 99 100 101 102 105 107 108 111 112 115 116 117 118 120 121 123 124 127 130 131 132 133 134 135 136 137 138 139 140 141 143 145 147 148 149 151 152 153 154 155 156 157 158 160 161 162 165 166 167 168 169 173 174 177 178 183 185 188 192 194 195 198 202 203 203 205 206 207 208 210 211 213 215 216 217 219 220 221 222 223 224 226 228 231 232 233 234 235 236 237 238 239 240 241 242 243 246 247 250 251 252 253 254 255 261 263 264 265 267 268 269 270 271 272 273 274 275 276 277 279 282 283 285 286 287 288 289 290 291 292 293 296 297 298 299 300 301 302 303 305 307 309 310 312 313 320 322 323 324 325 326 327 330 330 331 332 333 336 342 343 344 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 370 371 377 405 417 461 532 546 548 579 582 隐私:5 14 93 96 128 140 149 255 319 350 351 跨领域:23 25 141 246 288 467 484 527 532 546 572 579 582 跨域:216 369 370 371 377 387 398 406 407 421 422 431 436 493 494 495 496 497 498 499 500 501 502 503 507 565 594 最终的token和term数量统计: 可见,toekn数量为6007,term数量为1121。 demo程序截图如下: (2). 阅读教材《Introduction to Information Retrieval》第97页Figure 5.8中所描述的采用VB的编码和解码过程(VB encoding and decoding),并用Java语言或其他常用语言实现该算法。要求在三个数字(113,309,720)上验证实现的算法的正确性。 注意使用合理的数据结构。 请在报告中附上代码截图(不要复制源代码,请用截图的方式)、运行结果截图和详细的文字说明。程序要有详细的注释。(20) +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 代码截图(不要复制源代码,请用截图的方式)和详细的文字说明: VB编解码实现代码如下,详细思路见注释: 进制转换和计算指数的工具函数。注意C++自带的pow函数返回浮点类型的数据。在处理整数运算时有可能产生误差。因此需要额外写一个计算指数的工具函数。 编码函数如下。依次拆出128取余的结果转化为二进制,然后凑成八位,加上空格拼到一起。 对输入的多个数字进行编码。多个数字的编码将被连接在一起,每八位以空格隔开。 解码代码。注意去掉第一位无效编码。通过判断第一位来判断当前数字的编码是否已经结束。 主函数。 程序利用教材例子检验,全部运行正确。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 第1个数字113上的运行结果截图和详细的文字说明: 可见,程序成功完成了对数字113的编解码。 String类型存储,总共占用9个字节(包含末尾\0),72位。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 第2个数字309上的运行结果截图和详细的文字说明: 可见,程序成功完成了对数字309的编解码。 String类型存储,总共占用18个字节(包含空格和\0),144位。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 第3个数字720上的运行结果截图和详细的文字说明: 可见,程序成功完成了对数字720的编解码。 String类型存储,总共占用18个字节(包含空格和\0),144位。 (3). 阅读教材《Introduction to Information Retrieval》第98-99页(Section 5.3.2)中所描述的采用Gamma codes的编码和解码过程(encoding and decoding of Gamma codes),并用Java语言或其他常用语言实现该算法。要求在三个数字(113,309,720)上验证算法的正确性。 注意使用合理的数据结构。 请在报告中附上代码截图(不要复制源代码,请用截图的方式)、运行结果截图和详细的文字说明。程序要有详细的注释。(20) +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 代码截图(不要复制源代码,请用截图的方式)和详细的文字说明: 本题代码可以在第二题代码的基础上进行修改。以下是改动的部分。 增添了一个工具函数。 Gamma编码编码函数。先把数字二进制化去掉首位作为偏移,然后数出偏移的位数在长度里塞1就可以了。最后连接两部分成为编码。 Gamma编码解码函数如下。解码过程为:先读取编码开始的1,数到多少个1,就读取相应位数作为偏移。然后将偏移开头的1补上,转回十进制即可。 主函数: +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 第1个数字113上的运行结果截图和详细的文字说明: 可见,函数成功地对数字113进行了Gamma编码和解码。 解码结果: 用string类型储存消耗的容量太大。因此可以转为int类型。Int类型四个字节32位,每个位都可以用来表示编码。对比而言,string类型1个0/1要消耗1个字节8个位,还需要考虑string末尾\0占用的空间。而int类型1个0/1仅需消耗1个位,只是最少就要消耗32位。二者同样消耗4个字节,string类型可以表示的编码是101,即为3。因此,大于3的所有数字,采用int类型来存储编码的二进制串都是更加节省的。 用如下语句输出int类型存储结果。 转化为int类型按位存储Gamma编码结果,用一个int类型数据表示,消耗4个字节(byte),共计32个位(bit)。使用string类型存储,消耗14个字节(包含字符串末尾\0),共计112个位。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 第2个数字309上的运行结果截图和详细的文字说明: 可见,函数成功地对数字309进行了Gamma编码和解码。 解码结果: 添加int类型存储结果: 转化为int类型按位存储Gamma编码结果,用一个int类型数据表示,消耗4个字节(byte),共计32个位(bit)。使用string类型存储,消耗18个字节(包含字符串末尾\0),共计144个位。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 第3个数字720上的运行结果截图和详细的文字说明: 可见,函数成功地对数字720进行了Gamma编码和解码。 解码结果: 添加int类型存储结果: 转化为int类型按位存储Gamma编码结果,用一个int类型数据表示,消耗4个字节(byte),共计32个位(bit)。使用string类型存储,消耗20个字节(包含字符串末尾\0),共计160个位。 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 其他(例如感想、建议等等)。 本实验耗费了我很多时间和精力。VB编码和Gamma编码都是比较快完成了。中文分词耗费了我很多工夫。我使用的语言是C++和Java,因此没法用jieba分词的python版。网上寻找了中科院的NLPIR分词,但是C++和Java版本均无法正确配置。CSDN等网站上也没有好用的工具使用教程。因此在不会使用的分词工具上浪费了几天。然后又是尝试使用C++的cppjieba分词工具。用VS2019和DEV试了好多次,跟着教程配置分词工具,却都没有成功。最后再用linux虚拟机进行尝试,成功使用jieba分词工具。又调代码,中文编码格式来回换,改了一些bug,写实验报告,花了一天时间。终于把本次实验的三个题全部完成。 完成本实验我感受到了我对各种编程语言掌握程度不足。不会python,使得我没法用jieba的python版本。其次,我对编程环境和各种配置的了解不够。编程工具并没有合理配置,使得我使用NLPIR并没有成功。而且以往使用其他人的软件包的经验不足,使得下载软件包之后花了很多时间才能让它们正常工作。 此外,我还体会到了课程知识融合的重要性。解决问题1的关键在于虚拟机的Linux环境。而虚拟机配置和ubuntu基本操作是计算机系统2课上教的东西。因此我认识到不能拘泥于本课之前内容,要活用所有知识。 本实验我学到了很多东西。比如如何进行中文编码,如何进行合理的编码来压缩编码长度。虽然花费了很多时间熬了好几天夜,但是收获是很多的。 |
以下为使用Jieba分词进行中文分词的代码。请先下载Jieba分词的C++版本,然后可以参照以下代码修改demo.cpp文件
#include "cppjieba/Jieba.hpp"using namespace std;const char* const DICT_PATH = "../dict/jieba.dict.utf8";
const char* const HMM_PATH = "../dict/hmm_model.utf8";
const char* const USER_DICT_PATH = "../dict/user.dict.utf8";
const char* const IDF_PATH = "../dict/idf.utf8";
const char* const STOP_WORD_PATH = "../dict/stop_words.utf8";int findword(vector Lexicon,string word){//找到返回序号,没找到返回-1 int len=Lexicon.size();int i;for(i=0;i=0x80;
}
int main(int argc, char** argv) {cppjieba::Jieba jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH);vector words;vector jiebawords;string result;fstream HWfile( "/home/suayu/share_dir/HW3.txt", ios::in|ios::out );//打开目标文件,注意地址写的是Linux虚拟机中的地址 if(HWfile)cout<<"success"< Lexicon;//单词词典vector > Invertedfile(1999);//倒排文件,内部容器存储单词词典对应单词的倒排列表。注意vector嵌套必须初始化外层容器长度 string word;//当前单词 int tokennum=0;while(getline(HWfile,line)) { //整行读入一行(一个文件)的全部内容 string s;jieba.Cut(line, words, true);//调用jieba进行分词 std::stringstream linestream;linestream<>word){//从第row行中读入一个单词,判断它是否在词典中 for(int i=0;i='A'&&word[i]<='Z')//英文字母统一为小写 word[i]=word[i]+'a'-'A';if((word[i]<'a'||word[i]>'z')&&word[i]!='-'&&ishanzi(word[i])!=1)//去掉不是英文字母、汉字和连字符的其他符号 word[i]=' ';//清理其他符号并保留连字符 }tokennum++;int pos=findword(Lexicon,word);//pos为这个单词在单词词典中的序号 if(pos!=-1)//词典中有这个单词 Invertedfile[pos].push_back(row+1);//在这个单词的倒排文件中添加当前document的序号else{//词典中没有这个单词 Lexicon.push_back(word);//把单词添加到词典,这个单词在词典中的下标为len Invertedfile[len].push_back(row+1);//在这个单词的倒排文件中添加当前document的序号 len++;//词典长度扩充 }}row++;//文件编号从1开始到60,因此文件编号为row+1 }HWfile.close();HWfile.open("/home/suayu/share_dir/output.txt",ios::out);HWfile.seekg(ios::beg);for(int i=0;i
VB编码解码的C++代码:
#include
using namespace std;
int base_2(int x){//返回2的x次方 int n=1;while(x--){n*=2;}return n;
}
string toBi(int x){//十进制int转二进制字符 并凑满七位 string str="";int temp=x;while(x){if(x%2==1)str="1"+str;elsestr="0"+str;x/=2;} while(str.size()<7){str="0"+str;}return str;
}
string VBEncodeNumber(int n){string bytes="";string temp;int flag=0;while(1){if(n<128)break;int modn=n%128;temp=toBi(modn);if(!flag){flag=1;temp="1"+temp;}elsetemp="0"+temp+" ";bytes=temp+bytes;//二进制化 n/=128; }temp=toBi(n);if(!flag)temp="1"+temp;elsetemp="0"+temp+" ";bytes=temp+bytes;//剩余的二进制化return bytes;//返回
}
string VBEncode(vector numbers){string bytestream="";for(int i=0;i &answer){int n=0,i;while(bytestream.length()>0){if(bytestream[0]==' ')bytestream.erase(bytestream.begin(),bytestream.begin()+1);//让前八位是有效编码 for(i=1;i<8;i++){if(bytestream[i]=='1')n+=base_2(7-i);} if(bytestream[0]=='1'){//当前处理的编码结束 answer.push_back(n);n=0;}elsen*=128;bytestream.erase(bytestream.begin(),bytestream.begin()+8);}return;
}
int main(){int x,n,i;vector numbers;cin>>n;//表示测试的数字数量for(i=0;i>x;numbers.push_back(x);}string results=VBEncode(numbers);cout<<"VB编码结果为: "< answer;VBDecode(str,answer);cout<<"VB译码结果为:";for(i=0;i
Gamma编码解码的C++代码:
#include
using namespace std;
int base_2(int x){//返回2的x次方 int n=1;while(x--){n*=2;}return n;
}
string toBi(int x){//十进制int转二进制字符string str="";int temp=x;while(x){if(x%2==1)str="1"+str;elsestr="0"+str;x/=2;} return str;
}
int toDe(string str){//二进制string转十进制 int n=0;for(int i=0;i numbers){string bytestream="";for(int i=0;i &answer){//解码 int n=0,i;int len=0;while(bytestream.size()>0){if(bytestream[0]=='1'){//计算长度 while(1){len++;bytestream.erase(bytestream.begin(),bytestream.begin()+1);//读取一位1,长度+1,删掉读取完的1 if(bytestream[0]=='0')break;}}else if(bytestream[0]=='0'){//计算偏移 string temp="";for(i=0;i numbers;cin>>n;//表示测试的数字数量for(i=0;i>x;numbers.push_back(x);}string results=GammaEncode(numbers);cout<<"Gamma编码结果为: "< answer;GammaDecode(str,answer);cout<<"Gamma译码结果为:";for(i=0;i
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!