2

Laravel-VicWord分词,实现关键词提取

 2 years ago
source link: http://www.veiking.cn/blog/1040-page.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

VicWord是一个基于php语言的分词插件,分词功能,一般常见于全文搜索,语义识别等,我们此处是想用于做文本内容高频词、关键词提取。Jieba也是一个基于php语言的分词插件,关于使用偏好来说呢,结巴分词功能相对比较齐全,所以想实现一些特定功能的时候,是可以考虑结巴分词的,一般来说,VicWord分词是个不错的选择

  VicWord是一个基于php语言的分词插件,分词功能,一般常见于全文搜索,语义识别等,我们此处是想用于做文本内容高频词、关键词提取,这里我们用的php框架是laravel,接下来看看基于laravel,具体怎么操作。

第一步:VicWord拓展包安装

  基于composer的安装指令:

composer require lizhichao/word

  这时候可能会遇上一个问题,就是当我们执行安装操作时,composer会提示Allowed memory size of bytes exhausted的异常,其全部信息如下:

PHP Fatal error:  Allowed memory size of 1610612736 bytes exhausted (tried to allocate 67108864 bytes)Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errors.

  这个看字面意思就可以知道,下载的文件超大了,超出内存限制的大小,我们通过这个提示信息,先输出一下配置信息,指令如下:

php -r "echo ini_get('memory_limit').PHP_EOL;"

  结果打印出来:128M
  这个设置是php默认的最大单线程的独立内存使用量的定义,由于composer对插件的安装也是基于php服务的,那我们就得改下这个参数配置。
  由于VicWord的字库文件,肯定是比较大的,综一些不确定因素,我们就直接先把限制去掉,找到php.ini文件,修改memory_limit的值为 -1,修改完之后,运行:

php -r "echo ini_get('memory_limit').PHP_EOL;"

  结果打印出来:-1,-1即表示此处不做限制,接下来继续,就可以顺利安装了。

第二步:VicWord的使用

  由于我们是想要在页面做一个高频词、关键词提取的功能,所以系统要准备一个可供访问的接口,故我们需配置一下laravel的Route访问路由,以及其对应的Controllers入口:
  Route:

Route::any('gate/getKeywords.html','KeywordsController@getKeywords');

  Controllers:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

/**
 * 文本分词-关键词提取
 * @author vWork
 */
class KeywordsController extends Controller{
    /**
     * 获取高频词关键词
     * @param Request $request
     * @return array
     */
    public function getKeywords(Request $request){
        // 获取待处理文本
        $text = $request['text'];
        if(!$text){
            return null;
        }
        // 分词处理
        // 关键词提取
        $keywords = '';
        return $keywords;
    }
}

  基于这个controller骨架,我们开始添加分词功能

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Lizhichao\Word\VicWord;

/**
 * 文本分词-关键词提取
 * @author vWork
 */
class KeywordsController extends Controller{
    /**
     * 获取高频词关键词
     * @param Request $request
     * @return array
     */
    public function getKeywords(Request $request){
        // 获取待处理文本
        $text = $request['text'];
        if(!$text){
            return null;
        }
        ini_set('memory_limit','-1'); //升级为不限制内存
        // 分词处理
        $words = $this->doVicWords($text);
        // 关键词抽取
        $keywords = $this->doKeywords($words);
        return $keywords;
    }

    /**
     * 分词处理
     * @param $text
     * @return array
     */
    private function doVicWords($text){
        // 词库配置
        // define('_VIC_WORD_DICT_PATH_',dirname(dirname(dirname(__DIR__))).'/vendor/lizhichao/word/Data/dict.json');
        // 词库路径
        $dictPath = dirname(dirname(dirname(__DIR__))).'/vendor/lizhichao/word/Data/dict.json';
        // 分词初始化
        $vicWord = new VicWord($dictPath);
        // 分词处理
        $words = $vicWord->getAutoWord($text);
        return $words;
    }
}

  这里注意,关于这个dictPath的路径,很多地方给出的推荐代码是:

define('_VIC_WORD_DICT_PATH_',dirname(dirname(dirname(__DIR__))).'/vendor/lizhichao/word/Data/dict.json');

  其用意是为了配置词库路径,看了看源代码,并不是很理解这样的操作,并且自己程序里这样写也会报错,感兴趣的可以深入研究下。
  在分词的实现上,VicWord提供了三个的方法:

getWord($text); // 长度优先切分,最快
getShortWord($text); // 细粒度切分,比最快慢一点点
getAutoWord($text); // 自动切分 (在相邻词做了递归) ,从语义的角度理解,效果最好

  有人做过一段5000字的文本性能参考测试,其结果如下:

getWord() 每秒140w字
getShortWord() 每秒138w字
getAutoWord() 每秒40w字

  从这个数据上我们可以看出,一般长度比较短的文本,不同方法在性能上不会有太大区别,合适的就行,我们是用来做内容文本高频词抽取的,故选择了getAutoWord($text)。

第三步:VicWord的词库拓展

  有时候我们在做分词操作的时候,文本内容可能会倾向于某种行业和偏向,我们抽取的关键词也可能会有偏好,所以,词库的自定义拓展,是必须要考虑的。
  其实VicWord的词库拓展非常简单,加多这么几句:

//拓展词库路径,extendsDict.json,自定义词库文件
$extendsDictPath = dirname(dirname(dirname(__DIR__))).'/vendor/lizhichao/word/Data/extendsDict.json';
// 词库拓展
$vicDict = new VicDict($extendsDictPath);
$vicDict->add('老狗啃骨头', 'n');

  这个extendsDict.json文件即是我们可以自定义的词库文件,后续add方法添加的拓展词汇,也会同步更新至这个文件。
  我们是为了完成特定行业文本内容高频词、关键词提取,所以也准备了一些专业术语及行业词汇标签集合,整个代码升级完成如下:

    /**
     * 分词处理
     * @param $text
     * @return array
     */
    private function doVicWords($text){
        // 词库路径
        $dictPath = dirname(dirname(dirname(__DIR__))).'/vendor/lizhichao/word/Data/dict.json';
        // 拓展词库文件
        $extendsDictPath = dirname(dirname(dirname(__DIR__))).'/vendor/lizhichao/word/Data/extendsDict.json';
        // 词库拓展
        $vicDict = new VicDict($extendsDictPath);
        $vicDict->add('老狗啃骨头', 'n');
        $tagsList = array(...); // 自定义标签集合
        foreach($tagsList as $tag){
            $vicDict->add($tag->name, 'n');
        }
        // 词库保存
        $vicDict->save();
        // 分词初始化
        $vicWord = new VicWord($dictPath);
        // 分词处理
        $words = $vicWord->getAutoWord($text);
        return $words;
    }

  这样,整个围绕着VicWord插件的使用,就算完成了。

第四步:高频词、关键词提取

  经过上面几个步骤,我们已经可以完成文本内容的拆解,这个拆解仅仅是基于词库的语义拆分,我们想要提取出现相对较多的高频词、关键词,还要做下升级,代码如下:

    /**
     * 获取高频词
     * @param $words
     * @return number[]
     */
    private function doKeywords($words){
        $keywords = array();
        foreach ($words as $word){
            if(mb_strlen($word[0]) >= 2){
                if(array_key_exists($word[0], $keywords)){
                    $keywords[$word[0]] = $keywords[$word[0]]+1;
                }else{
                    $keywords[$word[0]] = 1;
                }
            }
        }
        // 按照出现次数排序,得出出现频率最高的10个词儿
        arsort($keywords);
        $keywords = array_slice($keywords, 0, 10, true);
        return $keywords;
    }

  考虑到一些文本特征,忽略一些无提取意义的特殊字符等,再作如下功能添加:

    /**
     * 获取高频词
     * @param $words
     * @return number[]
     */
    private function doKeywords($words){
        $keywords = array();
        foreach ($words as $word){
            if(mb_strlen($word[0]) >= 2 && !$this->ingore($word[0])){
                if(array_key_exists($word[0], $keywords)){
                    $keywords[$word[0]] = $keywords[$word[0]]+1;
                }else{
                    $keywords[$word[0]] = 1;
                }
            }
        }
        // 按照出现次数排序,得出出现频率最高的20个词儿
        arsort($keywords);
        $keywords = array_slice($keywords, 0, 10, true);
        return $keywords;
    }
    /**
     * 忽略字符方法
     * @param $word
     * @return boolean
     */
    private function ingore($word){
        // 忽略特殊字符
        $pattern = '/\/|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\(|\)|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\||\s+/';
        if(preg_match($pattern, $word)){
            return true;
        }
        // 忽略部分词汇
        $ignores = array('怎么','这些','这个','那个',
            '一下','一样','一个','一定',
            '就是','都是','还是','东西','我们','所以','可以',
        );
        foreach ($ignores as $item){
            if($word==$item || strpos($word, $item)){
                return true;
            }
        }
        return false;
    }

  这样,基于laravel框架,利用VicWord插件,我们便实现了文本数据高频词、关键词提取功能。

补充:php的另一个分词插件,jieba(结巴)拓展包

  Jieba也是一个基于php语言的分词插件,其安装指令如下:

composer require fukuball/jieba-php:dev-master

  安装完成后呢,代码的使用也很方便:

use Fukuball\Jieba\Jieba;
use Fukuball\Jieba\Finalseg;

Jieba::init();
Finalseg::init();
$words = Jieba::cut("基数排序是一种不在数据值本身之间比较的排序算法,而是通过数据按位数“切割”对比,从而实现排序的算法,所以基数排序也被认为是一种典型的非比较排序算法。");

  这样几句即可实现文本的分词拆解,关于使用偏好来说呢,结巴分词功能相对比较齐全,齐全伴随的就是繁琐,所以想实现一些特定功能的时候,是可以考虑结巴分词的,一般来说,满足基本功能的前提下,VicWord分词是个不错的选择。

  注意:分词操作基本都需要添加词库,毕竟十几几十万个单元词汇,这种数量级的运算很容易引起内存问题,如我们开头说的插件安装,我们可以去修改php.ini,即可解决这个问题;但有时候,我们程序运行的服务容器的配置文件是无法修改的,这时候我们只能从代码上来处理这个问题,在程序加载的入口,或者执行运算的代码前加入以下代码(根据需要选一即可):

ini_set('memory_limit', '512M'); // 限制提高至512M
ini_set('memory_limit', '-1');// 不做限制

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK